Skip to content

Gateway Analysis Compute

Poll-loop FFT, filter, and reference-stat computations

Pure analysis computations for the session poll loop.

Everything here operates on already-acquired WaveformData (no instrument I/O) and returns stream-frame dicts or None when uncomputable. The poll loop turns None into a one-shot cleared frame (the _shown pattern).

filtered_waveform

filtered_waveform(config: Dict[str, Any], acquired: Dict[str, Any])

Apply the configured Butterworth filter to its source channel's waveform.

Source code in scpi_control/server/compute.py
def filtered_waveform(config: Dict[str, Any], acquired: Dict[str, Any]):
    """Apply the configured Butterworth filter to its source channel's waveform."""
    data = acquired.get("C{0}".format(config["source"]))
    if data is None or len(data.voltage) < 2:
        return None
    kind = config["kind"]
    low = config["cutoff_low"]
    high = config["cutoff_high"]
    order = config["order"]
    if kind == "lowpass" and high is not None:
        return _analyzer.apply_lowpass_filter(data, high, order)
    if kind == "highpass" and low is not None:
        return _analyzer.apply_highpass_filter(data, low, order)
    if kind == "bandpass" and low is not None and high is not None:
        return _analyzer.apply_bandpass_filter(data, low, high, order)
    return None

reference_stats

reference_stats(reference: Dict[str, Any], acquired: Dict[str, Any]) -> Dict[str, Any]

Correlation + max deviation of the live source-channel trace vs the reference.

Mirrors ReferenceWaveform.calculate_correlation/_difference semantics (interpolate onto the live grid when lengths differ) without instantiating the store. Degrades every failure to nulls — never raises.

Source code in scpi_control/server/compute.py
def reference_stats(reference: Dict[str, Any], acquired: Dict[str, Any]) -> Dict[str, Any]:
    """Correlation + max deviation of the live source-channel trace vs the reference.

    Mirrors ReferenceWaveform.calculate_correlation/_difference semantics
    (interpolate onto the live grid when lengths differ) without instantiating
    the store. Degrades every failure to nulls — never raises.
    """
    stats: Dict[str, Any] = {"type": "reference_stats", "correlation": None, "max_deviation": None}
    channel = reference.get("channel")
    data = acquired.get("C{0}".format(channel)) if channel else None
    if data is None or len(data.voltage) < 2:
        return stats
    try:
        ref_voltage = np.asarray(reference["data"]["voltage"], dtype=float)
        if len(data.voltage) != len(ref_voltage):
            ref_time = np.asarray(reference["data"]["time"], dtype=float)
            ref_voltage = np.interp(data.time, ref_time, ref_voltage)
        stats["max_deviation"] = float(np.max(np.abs(data.voltage - ref_voltage)))
        with np.errstate(divide="ignore", invalid="ignore"):
            correlation = float(np.corrcoef(data.voltage, ref_voltage)[0, 1])
        stats["correlation"] = correlation if np.isfinite(correlation) else None
    except Exception:
        return {"type": "reference_stats", "correlation": None, "max_deviation": None}
    return stats