acoustic_ofdm/
debug.rs

1// Copyright (c) 2026 Elias S. G. Carotti
2
3use rustfft::num_complex::Complex32;
4
5use crate::config::OfdmConfig;
6use crate::modem;
7
8/// Packet-level metadata produced during oracle encoding.
9///
10/// Rationale:
11/// - The oracle path needs exact packet boundaries and the corresponding
12///   complex baseband samples in order to separate modem failures from search
13///   failures.
14#[derive(Clone, Debug)]
15pub struct EncodedPacketMeta {
16    /// Zero-based fragment index inside the encoded burst.
17    pub frag_index: usize,
18    /// Total number of fragments in the encoded burst.
19    pub frag_count: usize,
20    /// Packet start offset, in passband audio samples, inside [`EncodedBurst::audio`].
21    pub packet_start: usize,
22    /// Packet length, in passband audio samples.
23    pub packet_len: usize,
24    /// Complex baseband packet waveform before passband conversion.
25    pub xbb: Vec<Complex32>,
26}
27
28/// Full encoded burst with passband audio plus oracle packet metadata.
29#[derive(Clone, Debug)]
30pub struct EncodedBurst {
31    /// Complete burst waveform at the audio sample rate.
32    pub audio: Vec<f32>,
33    /// Per-packet metadata for oracle decode and debugging.
34    pub packet_meta: Vec<EncodedPacketMeta>,
35}
36
37/// Summary diagnostics for one decoded passband packet window.
38///
39/// Rationale:
40/// - This is the compact debug summary used by the CLI to decide whether a run
41///   failed because of timing, CFO, channel estimation, or payload demodulation.
42#[derive(Clone, Debug)]
43pub struct PassbandDiagnostics {
44    /// Whether the packet window contained enough samples for sync, training,
45    /// and at least one OFDM data symbol.
46    pub enough_samples: bool,
47    /// Chosen sync offset, in baseband samples, relative to the start of the
48    /// post-wake/post-guard window.
49    pub sync_off: usize,
50    /// Coarse carrier-frequency offset estimate in hertz.
51    pub cfo_hz: f32,
52    /// RMS magnitude over the repeated-half sync region.
53    pub sync_rms: f32,
54    /// Peak magnitude over the repeated-half sync region.
55    pub sync_peak: f32,
56    /// RMS magnitude over the post-sync OFDM payload region.
57    pub post_rms: f32,
58    /// Peak magnitude over the post-sync OFDM payload region.
59    pub post_peak: f32,
60    /// RMS magnitude over the training symbol after CP removal.
61    pub train_rms: f32,
62    /// Minimum channel-estimate magnitude observed across used bins.
63    pub hest_mag_min: f32,
64    /// Mean channel-estimate magnitude observed across used bins.
65    pub hest_mag_mean: f32,
66    /// Maximum channel-estimate magnitude observed across used bins.
67    pub hest_mag_max: f32,
68    /// Training-symbol reconstruction EVM after equalization.
69    pub train_recon_evm: f32,
70    /// Residual pilot EVM before the final pilot-based cleanup.
71    pub pilot_residual_evm: f32,
72    /// Pilot EVM after the final pilot-based cleanup.
73    pub pilot_post_evm: f32,
74    /// Decision-directed EVM over data carriers after equalization.
75    pub post_eq_evm: f32,
76    /// Whether the packet parser accepted the decoded packet.
77    pub decoded: bool,
78    /// Decoded payload length when parsing succeeded.
79    pub decoded_payload_len: Option<usize>,
80}
81
82/// Equalizer input/output samples for a constellation plot.
83#[derive(Clone, Debug)]
84pub struct PassbandConstellationDump {
85    /// Raw FFT-bin samples on data carriers before equalization.
86    pub pre_eq: Vec<Complex32>,
87    /// Equalized data-carrier samples after pilot/training correction.
88    pub post_eq: Vec<Complex32>,
89}
90
91/// Per-symbol pilot-tracking summary for one packet.
92#[derive(Clone, Debug)]
93pub struct PassbandPilotTrackDump {
94    /// Residual pilot phase estimate per data symbol, in radians.
95    pub pilot_phase_rad: Vec<f32>,
96    /// Pilot EVM before the final pilot cleanup.
97    pub pilot_evm_pre: Vec<f32>,
98    /// Pilot EVM after the final pilot cleanup.
99    pub pilot_evm_post: Vec<f32>,
100    /// Mean magnitude of the current channel estimate per tracked symbol.
101    pub hest_mag_mean: Vec<f32>,
102    /// Maximum magnitude of the current channel estimate per tracked symbol.
103    pub hest_mag_max: Vec<f32>,
104}
105
106/// One per-bin debug row for the passband bin dump.
107#[derive(Clone, Debug)]
108pub struct PassbandBinDumpRow {
109    /// One-based OFDM data-symbol index inside the packet payload section.
110    pub data_symbol_idx: usize,
111    /// FFT bin index used by this carrier.
112    pub used_bin: usize,
113    /// Carrier role: `"pilot"` or `"data"`.
114    pub role: &'static str,
115    /// Complex carrier value before equalization.
116    pub pre_eq: Complex32,
117    /// Complex carrier value after equalization.
118    pub post_eq: Complex32,
119    /// Known reference symbol when available, mainly for pilot carriers.
120    pub reference: Option<Complex32>,
121}
122
123/// Full per-bin dump for a few decoded OFDM symbols.
124#[derive(Clone, Debug)]
125pub struct PassbandBinDump {
126    /// Flat row list across symbols and used bins.
127    pub rows: Vec<PassbandBinDumpRow>,
128}
129
130/// One row of the channel-comparison dump.
131#[derive(Clone, Debug)]
132pub struct PassbandChannelCompareRow {
133    /// One-based OFDM data-symbol index inside the packet payload section.
134    pub data_symbol_idx: usize,
135    /// FFT bin index used by this carrier.
136    pub used_bin: usize,
137    /// Carrier role: `"pilot"` or `"data"`.
138    pub role: &'static str,
139    /// Actual per-bin channel computed from known transmitted and received bins.
140    pub actual_h: Complex32,
141    /// Training-only channel estimate used as the baseline equalizer model.
142    pub estimated_h_train: Complex32,
143    /// Pilot-adjusted channel estimate after residual per-symbol correction.
144    pub estimated_h_pilot: Complex32,
145}
146
147/// Full channel-comparison dump for a few OFDM symbols.
148#[derive(Clone, Debug)]
149pub struct PassbandChannelCompareDump {
150    /// Flat row list across symbols and used bins.
151    pub rows: Vec<PassbandChannelCompareRow>,
152}
153
154/// Timing metric dump around the repeated-half synchronizer.
155#[derive(Clone, Debug)]
156pub struct PassbandSyncDump {
157    /// Coarse Schmidl-Cox timing estimate.
158    pub coarse_sync_off: usize,
159    /// Refined timing estimate after the local refinement step.
160    pub refined_sync_off: usize,
161    /// Schmidl-Cox metric trace used for visualization/debugging.
162    pub metrics: Vec<f32>,
163}
164
165/// IQ-chain dump used to inspect the RX downconversion path.
166#[derive(Clone, Debug)]
167pub struct PassbandIqChainDump {
168    /// Complex signal after IQ downconversion and low-pass filtering at the audio rate.
169    pub downconverted_audio_rate: Vec<Complex32>,
170    /// Complex signal after optional resampling to the baseband rate.
171    pub baseband_rate: Vec<Complex32>,
172    /// Audio sample rate in hertz.
173    pub fs_audio: f32,
174    /// Decoder baseband sample rate in hertz.
175    pub fs_baseband: f32,
176}
177
178/// Extract equalizer input/output constellation samples for a packet window.
179pub fn dump_passband_constellation(
180    pkt_audio: &[f32],
181    cfg: &OfdmConfig,
182    sync_off: f32,
183) -> Option<PassbandConstellationDump> {
184    modem::dump_passband_constellation_impl(pkt_audio, cfg, sync_off)
185}
186
187/// Extract per-symbol pilot tracking diagnostics for a packet window.
188pub fn dump_passband_pilot_tracking(
189    pkt_audio: &[f32],
190    cfg: &OfdmConfig,
191) -> Option<PassbandPilotTrackDump> {
192    modem::dump_passband_pilot_tracking_impl(pkt_audio, cfg)
193}
194
195/// Extract pre/post-equalization carrier values for a packet window.
196pub fn dump_passband_bins(pkt_audio: &[f32], cfg: &OfdmConfig) -> Option<PassbandBinDump> {
197    modem::dump_passband_bins_impl(pkt_audio, cfg)
198}
199
200/// Extract pre/post-equalization carrier values using a known sync offset.
201pub fn dump_passband_bins_with_sync(
202    pkt_audio: &[f32],
203    cfg: &OfdmConfig,
204    sync_off: f32,
205) -> Option<PassbandBinDump> {
206    modem::dump_passband_bins_with_sync_impl(pkt_audio, cfg, sync_off)
207}
208
209/// Compare actual and estimated channel samples using a known transmitted packet.
210pub fn dump_passband_channel_compare_with_sync(
211    payload: &[u8],
212    pkt_audio: &[f32],
213    cfg: &OfdmConfig,
214    sync_off: f32,
215) -> Option<PassbandChannelCompareDump> {
216    modem::dump_passband_channel_compare_with_sync_impl(payload, pkt_audio, cfg, sync_off)
217}
218
219/// Extract Schmidl-Cox timing metrics for one packet window.
220pub fn dump_passband_sync_metric(pkt_audio: &[f32], cfg: &OfdmConfig) -> Option<PassbandSyncDump> {
221    modem::dump_passband_sync_metric_impl(pkt_audio, cfg)
222}
223
224/// Dump the RX IQ chain before and after resampling.
225pub fn dump_passband_iq_chain(pkt_audio: &[f32], cfg: &OfdmConfig) -> Option<PassbandIqChainDump> {
226    modem::dump_passband_iq_chain_impl(pkt_audio, cfg)
227}