Skip to content

RIQ Echo Extractor (pynasonde.vipir.riq.echo)

P pynasonde.vipir.riq.echo — Dynasonde-style seven-parameter echo extraction for VIPIR RIQ data.

Overview

This module implements the echo characterisation algorithm described in Zabotin et al. (2005) "Dynasonde 21" for VIPIR RIQ raw IQ data. For each sounding frequency it fits seven physical parameters to the complex I+Q samples stored across the receiver array:

Symbol Parameter Description
φ₀ gross_phase_deg Gross (mean) phase of the echo signal
V* velocity_mps Doppler / phase-path velocity
R′ height_km Group range (virtual height)
XL xl_km Eastward echolocation displacement
YL yl_km Northward echolocation displacement
PP polarization_deg Polarization chirality angle
EP residual_deg Least-squares fit residual
A amplitude_db Echo amplitude

Classes

pynasonde.vipir.riq.echo.Echo dataclass

All seven Dynasonde-style physical parameters for one ionospheric echo.

An echo is a single significant return at a specific (frequency, virtual height) point within one VIPIR pulse set. The parameter names follow the Dynasonde 21 convention (Zabotin et al., 2005).

Primary seven parameters

float

φ₀ — Mean complex phase of the pulse set, averaged over all pulses and all receivers at this gate (degrees). This is the "stationary-phase" carrier phase of the echo.

float

V* — Phase-path velocity derived from the linear temporal phase rate across the pulse set (m/s). Positive values indicate motion away from the sounder (receding layer).

float

R' — Stationary-phase group range / virtual height (km), computed from the range-gate index and the gate-step timing.

float

XL — Eastward echo echolocation (km). Derived from the spatial least-squares fit of inter-antenna phase differences, projected along the East axis: XL = R' · l where l is the East direction cosine.

float

YL — Northward echo echolocation (km): YL = R' · m.

float

PP — Chirality / polarization rotation (degrees). Estimated from the differential phase between quasi-orthogonally oriented antenna pairs. Returns NaN when the antenna array does not contain antennas with sufficiently different orientations.

float

EP — RMS residual of the planar-wavefront least-squares fit to all inter-antenna phase differences (degrees). Small values indicate a coherent, well-localised echo; large values suggest either multipath or a non-planar wavefront.

float

A — Coherent peak echo amplitude (dB), averaged over receivers.

Diagnostic / bookkeeping fields

float

Doppler frequency (Hz) from the linear phase-rate regression. Related to V* by V* = f_d · c / (2 · f₀).

float

Coherent SNR at the echo gate relative to the median noise floor (dB).

int

Range-gate index within the pulse set (0-based).

float

Universal-time stamp of the first pulse in the set (seconds from the file epoch, taken from PCT.pri_ut).

int

Number of receivers used in the spatial LS fit for XL/YL/EP.

float

Sounding frequency (kHz), taken from PCT.frequency.

to_dict()

Return a plain dict representation of this echo.

pynasonde.vipir.riq.echo.EchoExtractor

Extract Dynasonde-style echo parameters from VIPIR RIQ pulsets.

For each pulse set (one frequency step) the extractor:

  1. Assembles the complex IQ cube C[pulse, gate, rx].
  2. Computes the coherent mean phasor over pulses: C_mean[gate, rx].
  3. Estimates the noise floor from the per-gate amplitude distribution.
  4. Selects candidate echo gates whose coherent SNR exceeds snr_threshold_db.
  5. Computes all eight parameters at each qualifying gate.

Parameters

SctType

Sounder configuration table — must have been fully populated from a binary RIQ file. Key sub-structures used:

  • sct.station.rx_count — number of active receivers
  • sct.station.rx_position(rx_count, 3) array of [East_m, North_m, Up_m] receiver positions
  • sct.station.rx_direction(rx_count, 3) unit direction vectors (used for PP estimation)
  • sct.timing.{gate_start, gate_end, gate_step} — gate timing (µs)
  • sct.timing.pri — pulse repetition interval (µs)
list of Pulset

Grouped pulse data as produced by :class:~pynasonde.vipir.riq.parsers.read_riq.RiqDataset. Each element must expose a .pcts attribute that is a list of :class:~pynasonde.vipir.riq.datatypes.pct.PctType objects, each carrying pulse_i and pulse_q arrays of shape (gate_count, rx_count).

float, default 3.0

Minimum coherent SNR (dB) a gate must exceed to qualify as an echo candidate. Gates below this value are silently discarded.

float, default 50.0

Minimum virtual height (km) considered for echo detection. Gates below this threshold are skipped unconditionally. Set to 0 to disable the filter. The default (50 km) excludes direct-wave and near-field clutter, which can easily dominate the first few gates and crowd out ionospheric returns when max_echoes_per_pulset is small.

float, default 1000.0

Maximum virtual height (km) considered for echo detection. Gates above this threshold are skipped. Ionospheric echoes above ~1000 km are extremely rare; setting this avoids aliased end-of-range samples that are common in the last few percent of range gates.

int, default 3

Minimum number of receivers required to attempt the spatial least-squares fit that yields XL, YL, and EP. When fewer receivers are available those three parameters remain NaN. Set to 0 or 1 to attempt the fit even with only two antennas (only one baseline — the system will be exactly determined rather than overdetermined).

int or None, default 5

Maximum number of echo candidates retained per frequency step. Candidates are ranked by descending amplitude before truncation. Pass None to keep all candidates above the SNR threshold.

Examples

riq = RiqDataset.create_from_file("WI937_2022233235902.RIQ") extractor = EchoExtractor(riq.sct, riq.pulsets) extractor.extract() df = extractor.to_dataframe() ds = extractor.to_xarray()

echoes: List[Echo] property

Read-only list of extracted :class:Echo objects.

Raises

RuntimeError If :meth:extract has not been called.

__init__(sct, pulsets, snr_threshold_db=3.0, min_height_km=50.0, max_height_km=1000.0, min_rx_for_direction=3, max_echoes_per_pulset=5)

extract()

Run the full extraction pipeline over all pulse sets.

Iterates every pulset, assembles the IQ cube, and accumulates :class:Echo objects in self._echoes.

Returns

EchoExtractor self, enabling method chaining (e.g., EchoExtractor(...).extract().to_dataframe()).

to_dataframe()

Return all extracted echoes as a :class:pandas.DataFrame.

Each row is one :class:Echo; columns correspond to its fields. The DataFrame index is the sequential echo number.

Returns

pandas.DataFrame Empty DataFrame when no echoes were found.

Raises

RuntimeError If :meth:extract has not been called.

to_xarray()

Return all extracted echoes as an :class:xarray.Dataset.

Each :class:Echo field becomes a 1-D data variable indexed by echo_index. CF-convention units and long_name attributes are attached to every variable.

Returns

xarray.Dataset Empty Dataset when no echoes were found.

Raises

RuntimeError If :meth:extract has not been called.

Quick start

from pynasonde.vipir.riq import RiqDataset, EchoExtractor

dataset = RiqDataset.create_from_file("WI937_2024058061501.RIQ")

ext = EchoExtractor(
    sct=dataset.sct,
    pulsets=dataset.pulsets,
    snr_threshold_db=3.0,
    min_rx_for_direction=3,
    max_echoes_per_pulset=5,
).extract()

# Pandas DataFrame — one row per echo
df = ext.to_dataframe()

# CF-convention xarray Dataset
ds = ext.to_xarray()

Algorithm notes

  1. I/Q cube — for each Pulset (fixed frequency), raw I and Q samples are assembled into a (pulse, gate, rx) complex array.
  2. SNR gate selection — gates whose mean amplitude exceeds snr_threshold_db above the noise floor are retained.
  3. Gross phase (φ₀)angle(mean(I + jQ)) averaged across all pulses and receivers.
  4. Doppler / V* — phase is unwrapped along the pulse-time axis; a linear regression yields the Doppler frequency; V* = f_d·c / (2·f₀).
  5. Direction (XL, YL) — inter-antenna phase differences are fit to a planar-wavefront model via least squares; the recovered direction cosines are projected to km offsets using the virtual height.
  6. Polarization (PP) — quasi-orthogonal antenna pairs are identified from rx_direction dot products; the circular-polarization ratio is converted to an angle.
  7. Residual (EP) — the RMS residual of the planar-wavefront LS fit.

References

Zabotin, N. A., Wright, J. W., & Zhbankov, G. A. (2006). NeXtYZ: Three-dimensional electron density inversion for dynasonde ionograms. Radio Science, 41(6). https://doi.org/10.1029/2005RS003352