Skip to content

Waveform

Waveform acquisition and data handling

Waveform acquisition and data processing for Siglent oscilloscopes.

WaveformData dataclass

WaveformData(time: ndarray, voltage: ndarray, channel: Union[int, str], sample_rate: Optional[float] = None, record_length: Optional[int] = None, timebase: Optional[float] = None, voltage_scale: Optional[float] = None, voltage_offset: float = 0.0, provenance: Optional[AcquisitionProvenance] = None)

Container for waveform data and metadata.

Attributes:

Name Type Description
time ndarray

Time values in seconds (numpy array)

voltage ndarray

Voltage values in volts (numpy array)

channel Union[int, str]

Source channel number

sample_rate Optional[float]

Sampling rate in samples/second

record_length Optional[int]

Number of samples

timebase Optional[float]

Timebase setting (seconds/division)

voltage_scale Optional[float]

Voltage scale (volts/division)

voltage_offset float

Voltage offset in volts

provenance Optional[AcquisitionProvenance]

Instrument settings snapshot at acquisition time (optional)

Waveform

Waveform(oscilloscope: Oscilloscope)

Waveform acquisition and data processing.

Handles downloading waveform data from oscilloscope channels and converting to voltage/time arrays.

Initialize waveform acquisition.

Parameters:

Name Type Description Default
oscilloscope Oscilloscope

Parent Oscilloscope instance

required
Source code in scpi_control/waveform.py
def __init__(self, oscilloscope: "Oscilloscope"):
    """Initialize waveform acquisition.

    Args:
        oscilloscope: Parent Oscilloscope instance
    """
    self._scope = oscilloscope

acquire

acquire(channel: int, format: str = 'BYTE', provenance: bool = True, stride: Optional[int] = None) -> WaveformData

Acquire waveform data from a channel.

Parameters:

Name Type Description Default
channel int

Channel number (1-4)

required
format str

Data format - 'BYTE' or 'WORD' (default: 'BYTE')

'BYTE'
provenance bool

Snapshot instrument settings alongside the data (default True; pass False on high-rate paths)

True
stride Optional[int]

Forwarded to the transfer's acquire() -- see Oscilloscope.get_waveform for why None still writes 1 rather than skipping the write.

None

Returns:

Type Description
WaveformData

WaveformData object with time and voltage arrays

Raises:

Type Description
InvalidParameterError

If channel number is invalid

CommandError

If acquisition fails

FeatureNotSupportedError

If the active dialect's transfer doesn't support the requested format (e.g. 'WORD' on legacy Siglent or Tektronix)

Source code in scpi_control/waveform.py
def acquire(self, channel: int, format: str = "BYTE", provenance: bool = True, stride: Optional[int] = None) -> WaveformData:
    """Acquire waveform data from a channel.

    Args:
        channel: Channel number (1-4)
        format: Data format - 'BYTE' or 'WORD' (default: 'BYTE')
        provenance: Snapshot instrument settings alongside the data
            (default True; pass False on high-rate paths)
        stride: Forwarded to the transfer's acquire() -- see
            Oscilloscope.get_waveform for why None still writes 1 rather
            than skipping the write.

    Returns:
        WaveformData object with time and voltage arrays

    Raises:
        InvalidParameterError: If channel number is invalid
        CommandError: If acquisition fails
        FeatureNotSupportedError: If the active dialect's transfer
            doesn't support the requested format (e.g. 'WORD' on legacy
            Siglent or Tektronix)
    """
    validate_channel(self._scope, channel)

    logger.info(f"Acquiring waveform from channel {channel}")

    from scpi_control.waveform_transfer import make_transfer

    data = make_transfer(self._scope).acquire(channel, format, stride=stride)
    if provenance:
        try:
            data.provenance = AcquisitionProvenance.from_scope(self._scope, channels=[channel])
        except Exception:
            logger.warning("Provenance snapshot failed; waveform returned without provenance", exc_info=True)
    return data

get_waveform_preamble

get_waveform_preamble(channel: int) -> dict

Get waveform preamble information.

Parameters:

Name Type Description Default
channel int

Channel number (1-4)

required

Returns:

Type Description
dict

Dictionary with waveform metadata

Source code in scpi_control/waveform.py
def get_waveform_preamble(self, channel: int) -> dict:
    """Get waveform preamble information.

    Args:
        channel: Channel number (1-4)

    Returns:
        Dictionary with waveform metadata
    """
    validate_channel(self._scope, channel)

    ch = f"C{channel}"

    return {
        "channel": channel,
        "voltage_scale": self._get_voltage_scale(ch),
        "voltage_offset": self._get_voltage_offset(ch),
        "timebase": self._get_timebase(),
        "sample_rate": self._get_sample_rate(),
    }

save_waveform

save_waveform(waveform: WaveformData, filename: str, format: Optional[str] = None, metadata: Optional[dict] = None, bare: bool = False) -> None

Save waveform data to file.

Parameters:

Name Type Description Default
waveform WaveformData

WaveformData object to save

required
filename str

Output filename

required
format Optional[str]

File format - 'CSV', 'CSV_ENHANCED', 'NPY', 'MAT', 'HDF5' If None, auto-detect from file extension

None
metadata Optional[dict]

Optional metadata dictionary to include in file

None
bare bool

CSV only: suppress the provenance comment header, reproducing the fully headerless legacy layout (default: False)

False
Supported formats
  • CSV: Simple CSV with time and voltage columns
  • CSV_ENHANCED: CSV with metadata header
  • NPY: NumPy compressed archive (.npz)
  • MAT: MATLAB format (.mat) - requires scipy
  • HDF5: HDF5 format (.h5, .hdf5) - requires h5py
Source code in scpi_control/waveform.py
def save_waveform(
    self,
    waveform: WaveformData,
    filename: str,
    format: Optional[str] = None,
    metadata: Optional[dict] = None,
    bare: bool = False,
) -> None:
    """Save waveform data to file.

    Args:
        waveform: WaveformData object to save
        filename: Output filename
        format: File format - 'CSV', 'CSV_ENHANCED', 'NPY', 'MAT', 'HDF5'
               If None, auto-detect from file extension
        metadata: Optional metadata dictionary to include in file
        bare: CSV only: suppress the provenance comment header, reproducing the
              fully headerless legacy layout (default: False)

    Supported formats:
        - CSV: Simple CSV with time and voltage columns
        - CSV_ENHANCED: CSV with metadata header
        - NPY: NumPy compressed archive (.npz)
        - MAT: MATLAB format (.mat) - requires scipy
        - HDF5: HDF5 format (.h5, .hdf5) - requires h5py
    """
    # Auto-detect format from extension if not specified
    if format is None:
        import os

        ext = os.path.splitext(filename)[1].lower()
        format_map = {
            ".csv": "CSV",
            ".npz": "NPY",
            ".npy": "NPY",
            ".mat": "MAT",
            ".h5": "HDF5",
            ".hdf5": "HDF5",
        }
        format = format_map.get(ext, "CSV")
        logger.debug(f"Auto-detected format: {format} from extension {ext}")

    format = _FORMAT_ALIASES.get(format.upper(), format.upper())

    if format == "CSV":
        self._save_csv(waveform, filename, include_metadata=False, metadata=metadata, bare=bare)

    elif format == "CSV_ENHANCED":
        self._save_csv(waveform, filename, include_metadata=True, metadata=metadata)

    elif format == "NPY":
        self._save_npy(waveform, filename, metadata=metadata)

    elif format == "MAT":
        self._save_mat(waveform, filename, metadata=metadata)

    elif format == "HDF5":
        self._save_hdf5(waveform, filename, metadata=metadata)

    else:
        raise exceptions.InvalidParameterError(f"Invalid format: {format}. Supported: CSV, CSV_ENHANCED, NPY, MAT, HDF5")

See Also

  • Oscilloscope - Main oscilloscope control class for SCPI communication
  • Channel - Channel configuration and control
  • Analysis - Signal analysis (FFT, THD, SNR)