Crate tekhsi_rs

Crate tekhsi_rs 

Source
Expand description

Tektronix TekHSI gRPC client with strict validation and waveform decoding.

This crate connects to HSI-compatible Tektronix oscilloscopes, validates waveform headers, and decodes analog/digital/IQ data into typed waveforms.

  • Tektronix TekHSI gRPC client for HSI-compatible oscilloscopes
  • Strict waveform header validation that rejects invalid/unsupported data
  • High-throughput acquisition with parallel download and decode loops
  • Decodes analog (I8/I16/F32/F64), digital (I8/I16), and IQ waveforms
  • FFT utilities with calibrated output for frequency analysis (requires fft feature)
  • Streaming acquisitions via broadcast channels

§Creating a Client

Connect to a TekHSI server using TekHsiClient::connect with the scope’s address.

let client = TekHsiClient::connect("tek-scope-address:5000").await?;
  • Establishes a gRPC channel to the specified address
  • Performs a handshake with the scope’s TekHSI service
  • All Tektronix scopes tested allow a single connection at a time
  • Attempting to create a second connection to the same scope returns errors::ConnectionError::ScopeBusy error

§Listing Available Symbols

Query the scope for available channels using TekHsiClient::list_available_symbols.

let symbols = client.list_available_symbols().await?;
println!("Available channels: {:?}", symbols);
  • Returns symbols normalized to lowercase (e.g., “ch1”, “ch2_iq”, “ch3_dall”)
  • List of available symbols depends on which channels and modes are currently enabled on the scope

§Subscribing

After connecting, start streaming data with TekHsiClient::subscribe.

let symbols = client.list_available_symbols().await?;
let mut rx = client.subscribe(symbols, SubscribeOptions::default())?;

§Updating Subscriptions

Dynamically change the active symbols without reconnecting by calling TekHsiClient::update_symbols.

let symbols = vec!["ch1".to_string()];
let mut rx = client.subscribe(symbols, SubscribeOptions::default())?;
// ... receive ch1 acquisitions from `rx` ...
let new_symbols = vec!["ch2".to_string(), "ch3".to_string()];
client.update_symbols(new_symbols)?; // Update to different symbols
// ... receive ch2 and ch3 acquisitions from `rx` ...

§Acquisitions

Each data::Acquisition represents a single acquisition across all subscribed symbols. It is recommended that you access a channel’s waveform by symbol name using data::Acquisition::get_by_symbol for example:

let mut rx = client.subscribe(vec!["ch1".to_string()], SubscribeOptions::default())?;
while let Ok(acquisition) = rx.recv().await {
    let ch1_waveform = acquisition.get_by_symbol("ch1").unwrap();
    // ... use the ChannelData in ch1_waveform ...
}

data::ChannelData variants:

data::Waveform variants:

Reading acquisition data:

  • Each waveform type provides iterator-based iter_normalized_* helpers for calibrated values
  • I highly recommend using these functions unless you have a very good reason not to
  • There is a as_scope_digital8() helper for data::Waveform::Digital that returns an 8-channel logic probe bit mapping (tested with a TLP058)

§Disconnecting

Explicitly disconnect to stop acquisition and clean up resources:

let client = TekHsiClient::connect("tek-scope-address:5000").await?;
// ... use client ...
client.disconnect().await?;
  • Stops all active acquisition loops via cancellation tokens
  • Waits 500ms for the scope to exit acquisition mode
  • Sends a disconnect request to the scope

The client also implements Drop for automatic cleanup

§FFT Analysis

The fft::FftWaveform trait is implemented on data::Waveform::Analog and data::Waveform::Iq for convenience.

Use fft_real or fft_complex to compute FFT bins and then convert to dBm:

let mut rx = client.subscribe(symbols, SubscribeOptions::default())?;
while let Ok(acquisition) = rx.recv().await {
    for channel in acquisition.data.iter() {
        match channel {
            ChannelData::Waveform { waveform, .. } => match waveform {
                Waveform::Analog(analog) => {
                    let mut fft = analog.fft_mag(FftWindow::Rectangular, None);
                    let _fft_dbm = fft.as_dbm_single_sided(Some(50.0));
                }
                Waveform::Iq(iq) => {
                    let mut fft = iq.fft_complex(FftWindow::Rectangular, None);
                    let _fft_dbm = fft.as_dbm(Some(50.0));
                }
                Waveform::Digital(_digital) => {}
            },
            _ => { warn!("Acquisition or decode failed!") }
        }
    }
}

Notes:

  • FFT functionality requires enabling the fft feature flag.
  • If you are repeatedly computing FFTs the consider the _cached variants
  • Uses the rustfft package for CPU-bound FFT computations (no GPU acceleration)
  • Calibrated output requires optional impedance parameter (typically 50Ω)
  • Provided as a convenience helper for common frequency analysis tasks

Re-exports§

pub use fft::ComplexFftWaveform;
pub use fft::FftWaveform;

Modules§

data
Waveform data types and acquisition containers.
errors
Error types returned by TekHSI client APIs.
fft
FFT utilities and calibrated spectrum helpers.

Structs§

SmolStr
A SmolStr is a string type that has the following properties:
SubscribeOptions
Options for starting a subscription.
TekHsiClient
TekHSI client for connecting, subscribing, and listing symbols.
WaveformHeader