Raw IQ Processing (pynasonde.digisonde.raw)¶
This section documents the raw IQ processing pipeline for DPS4D Digisonde recordings.
Contents
raw_parse.md—process()function: full sounding pipeline (IQ → complementary-code correlation → ionogram NetCDF)iq_reader.md—IQStreamreader: time-partitioned binary file managementraw_plots.md—RawPlots/AFRLPlots: PSD and spectrogram visualisation helpers
Example quick start¶
from pynasonde.digisonde.raw.raw_parse import process
import datetime as dt
program = {
"Epoch": dt.datetime(2023, 10, 14, 16, 0, tzinfo=dt.timezone.utc),
"ID": "DPS4D_Kirtland0",
"FFTMode": False,
"rxTag": "ch0",
"Save Phase": False,
"Freq Stepping Law": "linear",
"Lower Freq Limit": 2e6,
"Upper Freq Limit": 15e6,
"Coarse Freq Step": 30e3,
"Number of Fine Steps": 1,
"Fine Freq step": 5e3,
"Fine Multiplexing": False,
"Inter-Pulse Period": 10e-3,
"Number of Integrated Repeats": 8,
"Interpulse Phase Switching": False,
"Wave Form": "16-chip complementary",
"Polarization": "O and X",
}
process(program, dir_iq="/media/chakras4/69F9D939661D263B", out_dir="out/")
pynasonde.digisonde.raw.raw_parse
¶
Python translation of the Julia Digisonde module.
The process function reproduces the DPS4D Digisonde ionogram pipeline:
reading complex baseband IQ recordings, performing complementary-code
correlation, assembling range–frequency power grids, and writing results to a
NetCDF product. The logic mirrors the original Julia implementation as closely
as possible while adopting Pythonic structure and naming.
Integration depth and the "how many .bin files?" question¶
Each sounding epoch requires reading a block of one-second IQ recordings. The total duration (and therefore the number of files) is determined by the program parameters::
n_coarse = (upper_freq - lower_freq) / coarse_step # e.g. 435
pulses_per_freq = n_rep × 2 (comp. pair) × n_pol # e.g. 32
total_pulses = n_coarse × pulses_per_freq # e.g. 13 920
sounding_duration_s = total_pulses × IPP # e.g. 139 s
For the default Kirtland schedule (IPP = 10 ms, nRep = 8, O+X, 2–15 MHz): each sounding reads ~139 one-second .bin files. The 12-minute cadence gives enough margin so that the next sounding starts only after all data for the current one has been collected.
Reducing Number of Integrated Repeats (nRep) trades SNR for cadence:
+------+-------------------+-------------------+------------+ | nRep | Sounding duration | Min. cadence | SNR loss | +======+===================+===================+============+ | 8 | ~139 s | ~3 min | baseline | | 4 | ~70 s | ~2 min | −1.5 dB | | 2 | ~35 s | ~1 min | −3.0 dB | | 1 | ~17 s | ~30 s | −4.5 dB | +------+-------------------+-------------------+------------+
SNR scales as √nRep because the correlation voltage averages coherently across repeats before the Doppler FFT. For strong daytime F-layer echoes nRep = 4 is usually sufficient; for weak or disturbed conditions keep nRep = 8.
process(program, dir_iq='/mnt/Data/', out_dir='out/', min_range=-math.inf, max_range=math.inf, nc_flag=True, verbose=True)
¶
Run a single Digisonde sounding program and return an :class:IonogramResult.
The pipeline mirrors the Julia Digisonde.process function:
- Open the IQ stream via :class:
~pynasonde.digisonde.raw.iq_reader.IQStream. - For every (frequency, polarisation, repeat, complementary-code) tuple:
a. Read n_samples raw IQ samples for the pulse's sub-time.
b. FFT → frequency-shift to baseband + decimate to chip bandwidth.
c. IFFT → optional fine-frequency residual mix.
d. Interpolate onto the 30 kHz chip grid.
e. Correlate with the reversed phase code via FFT convolution.
f. Accumulate into the CIT voltage array.
- Vectorised Doppler FFT across all range gates → peak power and phase.
- Optionally write a compressed NetCDF product.
Parameters¶
program
Dictionary of sounding-program parameters. Required keys:
"Epoch"– :class:datetime.datetimeor Unix timestamp"ID"– string identifier written into the NetCDF"Freq Stepping Law"– must be"linear""Wave Form"– must be"16-chip complementary""Lower Freq Limit"/"Upper Freq Limit"– Hz"Coarse Freq Step"/"Fine Freq step"– Hz"Number of Fine Steps"– int"Fine Multiplexing"– bool"Inter-Pulse Period"– seconds (IPP)"Number of Integrated Repeats"– int (nRep)"Interpulse Phase Switching"– bool"Polarization"–"O and X"or"O"
Optional keys: "rxTag" (default "ch0"),
"Save Phase" (default False), "FFTMode" (default False).
dir_iq
Root directory containing the time-partitioned IQ recordings.
out_dir
Root directory for NetCDF output. A sub-path
<hostname>/<ID>/<YYYY-mm-dd>/ is created automatically.
min_range, max_range: Reserved for future range-gate masking (not yet applied).
nc_flag
Write the NetCDF product when True (default).
verbose
Emit progress messages via loguru.
Returns¶
IonogramResult or None
None only if the output file already existed and was skipped.
Notes¶
Integration depth and file count — see module docstring for the full
table. For the default Kirtland schedule (IPP = 10 ms, nRep = 8, O+X,
2–15 MHz at 30 kHz steps) each call reads ≈139 one-second .bin files
and takes about 139 s of wall-clock IQ data. The 12-minute cadence in
:func:main leaves margin before the next sounding. Reducing nRep
proportionally shortens the sounding and trades SNR (scales as √nRep).