101 lines
3.2 KiB
Python
101 lines
3.2 KiB
Python
"""
|
|
Data structures for oscilloscope measurements.
|
|
"""
|
|
|
|
from dataclasses import dataclass
|
|
from typing import List, Dict, Optional
|
|
import numpy as np
|
|
|
|
|
|
@dataclass
|
|
class ChannelData:
|
|
"""Represents data from a single oscilloscope channel."""
|
|
|
|
channel_name: str
|
|
voltage_values: np.ndarray # in volts
|
|
time_values: np.ndarray # in seconds
|
|
metadata: Dict[str, str]
|
|
|
|
@property
|
|
def frequency(self) -> Optional[float]:
|
|
"""Extract frequency from metadata if available."""
|
|
freq_str = self.metadata.get("Frequency", "")
|
|
if freq_str.startswith("F="):
|
|
# Extract frequency value and convert units
|
|
freq_value = freq_str[2:]
|
|
if "kHz" in freq_value:
|
|
return float(freq_value.replace("kHz", "")) * 1000
|
|
elif "Hz" in freq_value:
|
|
return float(freq_value.replace("Hz", ""))
|
|
return None
|
|
|
|
@property
|
|
def vpp(self) -> Optional[float]:
|
|
"""Peak-to-peak voltage in volts."""
|
|
pp_str = self.metadata.get("PK-PK", "")
|
|
if pp_str.startswith("Vpp="):
|
|
value = float(pp_str[4:].replace("mV", "").replace("V", ""))
|
|
# Convert mV to V if needed
|
|
if "mV" in pp_str:
|
|
return value / 1000.0
|
|
return value
|
|
return None
|
|
|
|
@property
|
|
def average(self) -> Optional[float]:
|
|
"""Average voltage in volts (computed from data)."""
|
|
return float(np.mean(self.voltage_values))
|
|
|
|
@property
|
|
def metadata_average(self) -> Optional[float]:
|
|
"""Average voltage from metadata in volts."""
|
|
avg_str = self.metadata.get("Average", "")
|
|
if avg_str.startswith("V="):
|
|
value = float(avg_str[2:].replace("mV", "").replace("V", ""))
|
|
# Convert mV to V if needed
|
|
if "mV" in avg_str:
|
|
return value / 1000.0
|
|
return value
|
|
return None
|
|
|
|
@property
|
|
def time_interval(self) -> Optional[float]:
|
|
"""Time interval between samples in seconds."""
|
|
interval_str = self.metadata.get("Time interval", "")
|
|
if interval_str:
|
|
if "uS" in interval_str:
|
|
return float(interval_str.replace("uS", "")) * 1e-6
|
|
elif "nS" in interval_str:
|
|
return float(interval_str.replace("nS", "")) * 1e-9
|
|
elif "mS" in interval_str:
|
|
return float(interval_str.replace("mS", "")) * 1e-3
|
|
elif "S" in interval_str:
|
|
return float(interval_str.replace("S", ""))
|
|
return None
|
|
|
|
|
|
@dataclass
|
|
class ScopeData:
|
|
"""Container for all oscilloscope measurement data."""
|
|
|
|
channels: Dict[str, ChannelData]
|
|
metadata: Dict[str, str]
|
|
|
|
def __getitem__(self, channel_name: str) -> ChannelData:
|
|
"""Allow dictionary-like access to channels."""
|
|
return self.channels[channel_name]
|
|
|
|
def __iter__(self):
|
|
"""Allow iteration over channels."""
|
|
return iter(self.channels.values())
|
|
|
|
@property
|
|
def channel_names(self) -> List[str]:
|
|
"""Get list of all channel names."""
|
|
return list(self.channels.keys())
|
|
|
|
@property
|
|
def num_channels(self) -> int:
|
|
"""Get number of channels."""
|
|
return len(self.channels)
|