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
fftfeature) - 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::ScopeBusyerror
§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())?;- Only one subscription can be active per client
- Acquisitions are delivered via an async broadcast channel
- Must provide at least one symbol (returns
errors::SubscriptionUpdateError::EmptySymbolsif empty) - Attempting to start a second subscription returns
errors::SubscriptionError::AlreadyActive
§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` ...
- Must provide at least one symbol (returns
errors::SubscriptionUpdateError::EmptySymbolsif empty) - Requires an active subscription (returns
errors::SubscriptionUpdateError::NotActiveif no subscription exists) - There might still be acquisitions in the channel from the previous subscription
§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::ChannelData::Waveform: Successfully decoded waveform with acquisition ID, symbol, header, and waveform datadata::ChannelData::DecodeError: Failed decoding with symbol, header, and error detailsdata::ChannelData::AcquisitionError: Failed acquisition with symbol and error details
data::Waveform variants:
data::Waveform::Analog: Analog voltage samples (I8/I16/F32/F64) from oscilloscope analog channelsdata::Waveform::Digital: Digital bit samples (I8/I16) from digital inputs or logic probesdata::Waveform::Iq: Complex IQ samples (I8/I16/I32) from spectrum or RF analysis
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 fordata::Waveform::Digitalthat 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
fftfeature flag. - If you are repeatedly computing FFTs the consider the
_cachedvariants - Uses the
rustfftpackage 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
SmolStris a string type that has the following properties: - Subscribe
Options - Options for starting a subscription.
- TekHsi
Client - TekHSI client for connecting, subscribing, and listing symbols.
- Waveform
Header