1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
// Copyright (c) 2026 G & R Associates LLC
// SPDX-License-Identifier: MIT OR Apache-2.0
// src/demodulate/dvb_t_probe.rs
//
// The DVB-T receive probe: the DVB-T counterpart of `ofdm_probe`, exposing the
// two per-frame quantities an analyzer's constellation / decoder display needs,
// opt-in and at zero cost when unused.
//
// 1. The equalizer's output — the 1512 complex data-carrier symbols per OFDM
// symbol exactly as the demapper saw them (after `OfdmEqualizer` against the
// scattered/continual pilots, before `dvb_t_soft_llr`). This is where a
// vector signal analyzer takes its constellation.
//
// 2. A per-coded-bit correction map — for each coded bit, whether the channel
// corrupted it and whether the inner Viterbi decoder fixed it.
//
// Both are observations of a decode that happens anyway; neither changes what
// decodes. The generic path's `BitOutcome` is reused rather than duplicated: it
// is defined over coded bits and a re-encode comparison, with no COFDM-specific
// assumption in it.
//
// The DVB-T shape differs from the generic one in two ways that are deliberate
// rather than incidental, and both are argued at the type that carries them:
// there is no sequence number (DVB-T has no frame header — see
// `DvbTProbeFrame::tps`), and there is no codeword geometry (its inner code is
// convolutional — see `DvbTProbeFrame`).
use crateBitOutcome;
use crateConstellationOrder;
use crateTpsWord;
use Complex32 as C32;
use Range;
/// One DVB-T frame's probe record: where its symbols and correction map live
/// inside the owning [`DvbTRxProbe`]'s flat buffers, plus the metadata needed to
/// render them.
///
/// # No codeword geometry
///
/// [`OfdmProbeFrame`](crate::demodulate::OfdmProbeFrame) carries `codeword_bits`
/// / `codeword_info_bits` so a display can draw codeword boundaries across the
/// map. DVB-T's inner code is always `ConvCode::DvbK7`, a convolutional code
/// that terminates once per frame and has no block structure to draw — the
/// generic path already reports `(0, 0)` for that arm. Carrying a pair of
/// permanent zeroes would invite a consumer to divide by them; the fields are
/// omitted instead.
/// Reusable per-call diagnostic buffers for
/// [`DvbTFrameStreamDemod::feed_probed`](crate::demodulate::DvbTFrameStreamDemod::feed_probed).
///
/// Cleared and refilled by each probed call; **capacity is retained**, so
/// steady-state probing does not reallocate. That is why the caller owns this
/// rather than each frame carrying its own `Option<Vec<_>>`: a DVB-T frame is
/// 1512 × 68 = 102,816 complex symbols (~823 KB) plus its correction map, and
/// `feed` can return several frames per call — a per-frame allocation would be
/// paid on every frame of a continuous broadcast stream.
///
/// # Layout
///
/// [`symbols`](Self::symbols) and [`correction`](Self::correction) are flat
/// across every frame the call produced — read them directly for a bulk view
/// that does not care about frame boundaries. [`iter`](Self::iter) is the
/// per-frame view, and hands out resolved slices rather than spans so a record
/// cannot outlive the call that filled it.
///
/// ```ignore
/// let mut probe = DvbTRxProbe::new();
/// for chunk in stream {
/// // Read the probe after the call that filled it: every probed entry
/// // point clears first, so records do not accumulate across calls.
/// for frame in rx.feed_probed(chunk, &mut probe) { /* ... */ }
/// for f in probe.iter() {
/// plot_constellation(f.symbols, f.meta.constellation);
/// if f.meta.decoded {
/// plot_corrections(f.correction);
/// }
/// }
/// }
/// ```
/// One DVB-T frame's probe record with both of its slices resolved, as yielded
/// by [`DvbTRxProbe::iter`].
///
/// # Symbols and bits do not index 1:1
///
/// `correction[i]` is coded bit `i`, in the order the bits were mapped to
/// subcarriers; `symbols[j]` is the `j`th data carrier in demap order. So the
/// bits of `symbols[j]` are
///
/// ```text
/// let bps = meta.constellation.bits_per_symbol();
/// &correction[j * bps .. (j + 1) * bps] // NOT always in range
/// ```
///
/// — but **the symbols carry more bit-slots than the map covers.** A DVB-T frame
/// is a whole number of OFDM symbols (68 of them), each carrying exactly 1512
/// data cells, and the coded payload does not in general fill that grid to the
/// last bit; the remainder is padding with no outcome behind it.
///
/// A consumer walking symbols and colouring each by its bits must therefore stop
/// at `correction.len()` rather than run to the end of `symbols`. Walking the map
/// and finding each bit's symbol is always in range and is the safer direction.
/// The buffer lengths at the start of one frame's probe, for
/// [`DvbTRxProbe::rollback`].
pub