Skip to content

Models

Oscilloscope model capabilities and registry

Model capability definitions for different Siglent oscilloscope series.

ModelCapability dataclass

ModelCapability(model_name: str, series: str, num_channels: int, max_sample_rate: float, memory_depth: int, bandwidth_mhz: int, has_math_channels: bool, has_fft: bool, has_protocol_decode: bool, supported_decode_types: List[str], scpi_variant: str)

Defines capabilities and features for a specific oscilloscope model.

This dataclass contains all model-specific information including hardware specifications and supported features.

detect_model_from_idn

detect_model_from_idn(idn_string: str) -> ModelCapability

Detect oscilloscope model and return its capability profile.

Parameters:

Name Type Description Default
idn_string str

The response from *IDN? command Format: "Manufacturer,Model,Serial,Firmware" Example: "Siglent Technologies,SDS824X HD,SERIAL123,1.0.0.0"

required

Returns:

Type Description
ModelCapability

ModelCapability object for the detected model

Raises:

Type Description
ValueError

If model cannot be detected from IDN string

Source code in scpi_control/models.py
def detect_model_from_idn(idn_string: str) -> ModelCapability:
    """Detect oscilloscope model and return its capability profile.

    Args:
        idn_string: The response from *IDN? command
                   Format: "Manufacturer,Model,Serial,Firmware"
                   Example: "Siglent Technologies,SDS824X HD,SERIAL123,1.0.0.0"

    Returns:
        ModelCapability object for the detected model

    Raises:
        ValueError: If model cannot be detected from IDN string
    """
    # Parse the model name from IDN string
    parts = idn_string.split(",")
    if len(parts) < 2:
        raise ValueError(f"Invalid *IDN? response format: {idn_string}")

    model_from_idn = parts[1].strip()
    logger.info(f"Detecting model from IDN: {model_from_idn}")

    # Try exact match first
    if model_from_idn in MODEL_REGISTRY:
        logger.info(f"Exact match found: {model_from_idn}")
        return MODEL_REGISTRY[model_from_idn]

    # Try fuzzy matching - handle variations in model name format
    # Remove spaces, dashes, underscores for comparison
    normalized_model = re.sub(r"[\s\-_]", "", model_from_idn).upper()

    for registered_model, capability in MODEL_REGISTRY.items():
        normalized_registered = re.sub(r"[\s\-_]", "", registered_model).upper()
        if normalized_model == normalized_registered:
            logger.info(f"Fuzzy match found: {model_from_idn} -> {registered_model}")
            return capability

    # Try partial matching - check if registry key is contained in model name
    for registered_model, capability in MODEL_REGISTRY.items():
        # Remove spaces from both for comparison
        if registered_model.replace(" ", "").upper() in model_from_idn.replace(" ", "").upper():
            logger.info(f"Partial match found: {model_from_idn} -> {registered_model}")
            return capability

    # Model not found - create a generic fallback capability
    logger.warning(f"Model '{model_from_idn}' not in registry, using generic fallback")

    # Try to infer series and channel count from model name
    series = "Unknown"
    num_channels = 4  # Default to 4 channels

    if "SDS8" in model_from_idn.upper():
        series = "SDS800XHD"
        scpi_variant = "hd_series"
    elif "SDS1" in model_from_idn.upper():
        series = "SDS1000XE"
        scpi_variant = "x_series"
    elif "SDS2" in model_from_idn.upper():
        series = "SDS2000XPlus"
        scpi_variant = "plus_series"
    elif "SDS5" in model_from_idn.upper():
        series = "SDS5000X"
        scpi_variant = "x_series"
    else:
        scpi_variant = "standard"

    # Try to determine channel count from model number
    # Most Siglent models have format: SDSxxYZ where Y can indicate channels
    # e.g., SDS1202X-E = 2 channels, SDS1104X-E = 4 channels
    match = re.search(r"SDS\d+([024])(\d+)", model_from_idn)
    if match:
        potential_channels = int(match.group(1))
        if potential_channels in [2, 4]:
            num_channels = potential_channels

    # Create generic capability
    generic_capability = ModelCapability(
        model_name=model_from_idn,
        series=series,
        num_channels=num_channels,
        max_sample_rate=1.0,
        memory_depth=10_000_000,
        bandwidth_mhz=100,
        has_math_channels=True,
        has_fft=True,
        has_protocol_decode=False,
        supported_decode_types=[],
        scpi_variant=scpi_variant,
    )  # Conservative default  # Conservative default  # Most models support this  # Most models support this  # Conservative - don't assume

    logger.info(f"Created generic capability: {generic_capability}")
    return generic_capability

list_supported_models

list_supported_models() -> List[str]

Get list of all explicitly supported model names.

Returns:

Type Description
List[str]

List of model names that have full capability definitions

Source code in scpi_control/models.py
def list_supported_models() -> List[str]:
    """Get list of all explicitly supported model names.

    Returns:
        List of model names that have full capability definitions
    """
    return sorted(MODEL_REGISTRY.keys())

get_model_by_series

get_model_by_series(series: str) -> List[ModelCapability]

Get all models in a specific series.

Parameters:

Name Type Description Default
series str

Series identifier (e.g., "SDS1000XE", "SDS2000XPlus")

required

Returns:

Type Description
List[ModelCapability]

List of ModelCapability objects for models in that series

Source code in scpi_control/models.py
def get_model_by_series(series: str) -> List[ModelCapability]:
    """Get all models in a specific series.

    Args:
        series: Series identifier (e.g., "SDS1000XE", "SDS2000XPlus")

    Returns:
        List of ModelCapability objects for models in that series
    """
    return [cap for cap in MODEL_REGISTRY.values() if cap.series == series]

See Also

  • Oscilloscope - Main oscilloscope control class for SCPI communication