nx_dynaoracle_gate.nx source
↩ module page · 577 lines · 34713 B
1// nx_dynaoracle_gate.nx -- GATE for the shared ringdown estimator and the video oracle's derived
2// constants.
3//
4// WHY THE FIXTURE IS AN ANALYTIC OSCILLATOR AND NOT A VIDEO CLIP. A gate whose fixture is a real
5// clip can only assert that today's answer equals yesterday's answer -- it has no ground truth, so
6// it detects drift and CANNOT detect being wrong. Here the fixture is a mass-spring-damper the gate
7// integrates itself, so the TRUE natural frequency and damping ratio are known in closed form from
8// k and c: w0 = sqrt(k/m), zeta = c/(2*sqrt(k*m)), wd = w0*sqrt(1-zeta^2). The teeth then ask
9// whether rd_analyze RECOVERS numbers it was never told. That is an oracle, not a snapshot.
10//
11// EVERY TOLERANCE HERE IS DERIVED, NOT PICKED:
12// * FREQUENCY: rd_analyze times a period by counting whole SAMPLES between zero crossings, so a
13// plus-or-minus-one-sample error bounds the relative frequency error at 1/P where P is the
14// period in samples. The tooth computes that bound from the fixture's own period.
15// * DAMPING: rd_zeta_permil goes through rd_log2_q16, whose own header declares a worst error of
16// about 0.086 in log2 inside an octave. That propagates as ln-error 0.086*ln2 = 0.0596, and for
17// a lightly damped decay zeta is about delta/(2*pi), giving a bound near 9.5 permil. The tooth
18// computes that from the declared constant instead of hard-coding the answer.
19// A tolerance chosen after seeing the result is the defect this estate refuses; a tolerance derived
20// from a declared imprecision is a contract.
21//
22// license_tier: ORIGINAL
23import "nx_syscalls.nx"
24import "nx_gate_verdict.nx"
25import "nx_ringdown_lib.nx"
26import "nx_motion.nx"
27
28const DG_Q16: i64 = 65536
29const DG_PERMIL: i64 = 1000
30const DG_NSAMP: i64 = 512
31const DG_DT_US: i64 = 1000
32// THE DECLARED IMPRECISIONS NOW HAVE ONE OWNER AND IT IS THE LIBRARY. These used to be hand-copied
33// numbers with a comment asking the next reader to remember to re-derive them, and a tolerance that
34// depends on somebody remembering is the stale-bound defect with extra steps. They are now
35// DEFINITIONS of the library's own constants, so improving rd_log2_q16 moves this gate BY
36// CONSTRUCTION instead of leaving it quietly loose.
37const DG_LOG2_ERR_PERMIL: i64 = RD_LOG2_ERR_PERMIL
38const DG_LN2_PERMIL: i64 = RD_LN2_PERMIL
39const DG_TWOPI_PERMIL: i64 = RD_TWOPI_PERMIL
40
41// ---- THE DECLARED GRID -----------------------------------------------------------------------
42// A remedy validated only on the case that motivated it is the vacuous-test defect, so the span
43// estimator is measured over a SURFACE: six injected damping ratios crossed with three stiffnesses,
44// every point integrated by this gate from k and c so the truth stays closed-form. The damping
45// values span AND EXCEED the tissue band knowledge/gamefeel_oracle.conf carries
46// (tissue_zeta_permil 350..600), because an estimator only shown correct inside the band it was
47// built for has not been shown correct at all.
48const DG_GRID_NSAMP: i64 = 16384
49// The step divisor is large so w0*dt stays small. The fixture is a semi-implicit Euler integration
50// and carries its OWN leading damping error of order c*dt/2 relative; holding w0*dt at or below
51// 0.016 keeps that below the estimator's published tolerance instead of hiding inside it -- and the
52// teeth add it explicitly rather than assuming it away.
53const DG_GRID_DT_DIV: i64 = 1000
54const DG_GRID_K0: i64 = 16
55const DG_GRID_K_STEP: i64 = 4
56const DG_GRID_NK: i64 = 3
57const DG_GRID_NZ: i64 = 6
58// THE FIXTURE'S OWN AMPLITUDE HEADROOM, AND WHY IT IS THIS LARGE. dg_sim carries x and v as Q16
59// INTEGERS, so every step truncates by up to one quantum and that truncation ACCUMULATES over the
60// thousands of steps a long span covers. MEASURED at the first attempt with x0 = 1000: three of the
61// eighteen video-sampling points missed their published tolerance, and the offender lines named the
62// cause -- the last analysed peak had decayed to 114 QUANTA, where the fixture's own arithmetic is
63// worth tens of permil of log-ratio and the estimator's is worth eight. The span estimator is the
64// most exposed to that BY DESIGN, because it deliberately reads the smallest peak it can still
65// admit. The remedy is to fix the FIXTURE rather than widen the estimator's bound: a thousand times
66// more headroom puts the fixture's quantum a thousand times below the peaks being read. A tooth
67// below asserts the resulting floor, so this can never silently regress into a tolerance excuse.
68const DG_GRID_X0: i64 = 1000000
69// The smallest peak any grid point may be judged on, in fixture quanta. One quantum on a peak of P
70// is 1/P relative, so a floor of DG_PERMIL quanta holds the fixture's amplitude contribution at or
71// below one permil of log-ratio -- an order of magnitude inside every tolerance published here.
72const DG_GRID_MIN_PEAK_QUANTA: i64 = 1000
73// AND THAT FLOOR IS THE DEADBAND, WHICH IS THE MECHANISM THE ANALYSER ALREADY HAS FOR IT. Raising
74// the fixture's headroom a thousandfold moved the worst video-sampling error from 7 permil to 3 but
75// did NOT stop the span estimator reading down to 239 quanta, and it never will: the estimator
76// extends until the record stops being periodic, so it will always walk into whatever noise floor
77// exists. rd_analyze's `quant` argument exists precisely to declare that floor, and its own header
78// says the caller must say where the number came from -- here it is the fixture's accumulated
79// integer-truncation noise, bounded by the same DG_PERMIL-quanta reasoning above. Passing 0, as the
80// primary fixture legitimately does for a short window, told the analyser to trust values one
81// quantum above zero.
82const DG_GRID_QUANT: i64 = DG_GRID_MIN_PEAK_QUANTA
83// A deadband can fail in the OTHER direction by eating the evidence, so the grid asserts that every
84// point still spans a full period of baseline after the floor is applied.
85const DG_GRID_MIN_HALVES: i64 = 2
86
87// ---- PROBES FOR THE RESPONSIVENESS OF THE PUBLISHED BOUND -------------------------------------
88// Recomputing the published tolerance through the same function proves the SLOT carries that
89// function's answer; it cannot notice the FUNCTION being replaced by a constant. MEASURED by
90// mutation: a mutant returning a fixed 3 permil SURVIVED every tooth in this gate, because on this
91// grid the honest bound happens to land near 3 -- defence and vacuity looked identical from one
92// surviving mutant. What a constant cannot fake is RESPONSIVENESS: the bound carries a 2/halves
93// factor and a 1/peak factor, so halving the baseline must widen it and shrinking the final peak
94// must widen it. These probe values are chosen only to be far apart on each axis.
95const DG_TOL_PROBE_A: i64 = 1000000
96const DG_TOL_PROBE_B: i64 = 1000
97const DG_TOL_PROBE_B_SMALL: i64 = 10
98const DG_TOL_PROBE_HALVES_LONG: i64 = 16
99const DG_TOL_PROBE_HALVES_SHORT: i64 = 2
100// The six injected damping ratios in permil, each named for what it IS and never for its value.
101const DG_GRID_Z_LINEAR_LIMIT: i64 = 50
102const DG_GRID_Z_LIGHT: i64 = 100
103const DG_GRID_Z_VIDEO_FIXTURE: i64 = 200
104const DG_GRID_Z_TISSUE_BAND_LOW: i64 = 350
105const DG_GRID_Z_TISSUE_BAND_MID: i64 = 450
106const DG_GRID_Z_TISSUE_BAND_HIGH: i64 = 600
107// Leading semi-implicit Euler errors: damping c*dt/2 RELATIVE, period (w0*dt)^2/24 RELATIVE. Named
108// so a tooth can add the FIXTURE's imprecision to the ESTIMATOR's rather than charge it to the
109// estimator, which would read as the estimator being worse than it is.
110// THE SAMPLING RATIO IS A GRID DIMENSION, NOT A DETAIL. The fine pass above oversamples a period
111// by two orders of magnitude, and at that ratio BOTH estimators are limited only by rd_log2_q16 --
112// so a grid built only from it cannot discriminate them and would understate the difference it was
113// built to measure. nx_dynaoracle works at fifteen samples per period: a 1960 mHz damped motion at
114// 30 fps is 15.3. The second pass DECIMATES the accurately integrated series to that ratio, which
115// changes the SAMPLING without touching the waveform, so the fixture's own integration error stays
116// negligible and the teeth measure the estimators rather than the integrator.
117const DG_GRID_TARGET_SPP: i64 = 15
118const DG_EULER_ZETA_DEN: i64 = 2
119const DG_EULER_PER_DEN: i64 = 24
120
121// ---- THE LOBE FIXTURE: a decay whose ONLY variable is the spacing of its crossings ------------
122// Amplitude halves at every crossing in both variants, so the two-lobe decrement is equally happy
123// with either. Only the SPACING differs, which is what makes the aperiodic variant an anti-vacuity
124// control rather than merely another failing input.
125const DG_LOBE_N: i64 = 128
126const DG_LOBE_AMP0: i64 = 1024
127const DG_LOBE_DECAY_DIV: i64 = 2
128const DG_LOBE_HEAD: i64 = 8 // the truncated opening lobe, before the first crossing
129const DG_LOBE_LEN: i64 = 12 // the periodic half period
130const DG_LOBE_ODD: i64 = 20 // the intruder: eight samples off, far beyond a one-sample window
131
132func dg_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v }
133
134// Integrate a unit-mass damped oscillator in Q16 and write the displacement series.
135// x'' = -(k/m)x - (c/m)x' with m = 1, k and c supplied in Q16.
136func dg_sim(sig: *i64, n: i64, k_q16: i64, c_q16: i64, x0_q16: i64, dt_q16: i64) -> i64 {
137 var x: i64 = x0_q16
138 var v: i64 = 0
139 var i: i64 = 0
140 while i < n {
141 sig[i] = x
142 let a: i64 = (0 - k_q16) * x / DG_Q16 - c_q16 * v / DG_Q16
143 v = v + a * dt_q16 / DG_Q16
144 x = x + v * dt_q16 / DG_Q16
145 i = i + 1
146 }
147 return 0
148}
149
150// Build a DECAYING alternating-sign wave whose lobe LENGTHS are supplied. Amplitude halves at every
151// crossing, so the logarithmic decrement reads it happily; only the spacing differs between the two
152// fixtures this builds, which is exactly the discrimination under test.
153func dg_sim_lobes(sig: *i64, n: i64, l0: i64, l1: i64, l2: i64, l3: i64) -> i64 {
154 var amp: i64 = DG_LOBE_AMP0
155 var sign: i64 = 1
156 var remain: i64 = l0
157 var lob: i64 = 0
158 var i: i64 = 0
159 while i < n {
160 sig[i] = sign * amp
161 remain = remain - 1
162 if remain <= 0 {
163 sign = 0 - sign
164 amp = amp / DG_LOBE_DECAY_DIV
165 if amp < 1 { amp = 1 }
166 lob = lob + 1
167 if lob == 1 { remain = l1 }
168 else { if lob == 2 { remain = l2 } else { remain = l3 } }
169 }
170 i = i + 1
171 }
172 return 0
173}
174
175// A RIGID fixture: the anti-vacuity control. It cannot oscillate, so any organ that reports a
176// frequency for it is reporting one it invented.
177func dg_sim_rigid(sig: *i64, n: i64) -> i64 {
178 var i: i64 = 0
179 while i < n { sig[i] = 0; i = i + 1 }
180 return 0
181}
182
183func main(argc: i64, argv: *i64) -> i64 {
184 gv_head("NX-DYNAORACLE-GATE -- shared ringdown estimator + derived-constant binding" as *u8)
185 let ctr: *i64 = gv_ctr()
186
187 let sig: *i64 = (sys_mmap(DG_NSAMP * 8 + 16)) as *i64
188 let out: *i64 = (sys_mmap(RD_R_N * 8 + 16)) as *i64
189
190 // ---- FIXTURE 1: a known underdamped oscillator ----------------------------------------
191 // k and c are chosen so the response is clearly underdamped and completes several periods
192 // inside the window; the TRUTH is then computed from them, never asserted.
193 let k_q16: i64 = 40 * DG_Q16
194 let c_q16: i64 = DG_Q16
195 let dt_q16: i64 = DG_Q16 / 100
196 dg_sim(sig, DG_NSAMP, k_q16, c_q16, 1000 * DG_Q16, dt_q16)
197
198 // ASSERT THE FIXTURE REACHED THE CONDITION BEFORE ASSERTING THE OUTCOME. A fixture that never
199 // actually oscillated would make every tooth below pass for the wrong reason.
200 var swings: i64 = 0
201 var prev: i64 = 0
202 var i: i64 = 0
203 while i < DG_NSAMP {
204 var s: i64 = 0
205 if sig[i] > 0 { s = 1 }
206 if sig[i] < 0 { s = 0 - 1 }
207 if s != 0 { if prev != 0 { if s != prev { swings = swings + 1 } } prev = s }
208 i = i + 1
209 }
210 gv_check("fixture-actually-oscillates" as *u8, swings >= 4, ctr)
211 var fixmax: i64 = 0
212 var m2: i64 = 0
213 while m2 < DG_NSAMP { if dg_abs(sig[m2]) > fixmax { fixmax = dg_abs(sig[m2]) } m2 = m2 + 1 }
214 gv_check("fixture-has-measurable-amplitude" as *u8, fixmax > 0, ctr)
215
216 // Deadband here is ZERO on purpose: this is an exact integer series with no measurement noise,
217 // so any non-zero deadband would be a number chosen to make the test pass.
218 let rc: i64 = rd_analyze(sig, DG_NSAMP, DG_DT_US, 0, out)
219 gv_check("analyze-returns-OK-on-a-clean-underdamped-signal" as *u8, rc == RD_OK, ctr)
220 gv_check("analyze-reports-a-period" as *u8, out[RD_R_PER_US] > 0, ctr)
221 gv_check("analyze-reports-a-frequency" as *u8, out[RD_R_FN_MHZ] > 0, ctr)
222 gv_check("analyze-reports-decay-A1-greater-than-A2" as *u8, out[RD_R_A1] > out[RD_R_A2], ctr)
223
224 // ---- TRUTH, computed from k and c ------------------------------------------------------
225 // w0 = sqrt(k) with m = 1 (k in Q16 -> w0 in Q16 via the integer sqrt of k<<16).
226 let w0_q16: i64 = rd_isqrt(k_q16 * DG_Q16)
227 // zeta = c / (2*sqrt(k*m)) = c / (2*w0)
228 let zeta_true_permil: i64 = c_q16 * DG_PERMIL / (2 * w0_q16)
229 gv_check("truth-is-underdamped" as *u8, zeta_true_permil < DG_PERMIL, ctr)
230
231 // The DERIVED frequency tolerance: one sample of period-timing error.
232 let per_samples: i64 = out[RD_R_PER_US] / DG_DT_US
233 gv_check("period-spans-several-samples" as *u8, per_samples >= 4, ctr)
234 let fn_tol_permil: i64 = DG_PERMIL / per_samples + 1
235
236 // wd = w0 * sqrt(1 - zeta^2); fn_mhz = 1000 * wd / (2*pi) expressed against the sim's own clock.
237 // The simulation advances dt_q16/Q16 seconds of MODEL time per sample while rd_analyze was told
238 // DG_DT_US microseconds per sample, so the comparison is made in SAMPLES, which is the unit both
239 // sides actually share and therefore the only one that cannot hide a scale error.
240 let zsq_q16: i64 = zeta_true_permil * zeta_true_permil * DG_Q16 / (DG_PERMIL * DG_PERMIL)
241 let wd_q16: i64 = w0_q16 * rd_isqrt((DG_Q16 - zsq_q16) * DG_Q16) / DG_Q16
242 // period in samples = 2*pi / (wd * dt)
243 // period_samples = 2*pi / (wd * dt). With wd and dt both in Q16, wd*dt carries Q16 TWICE, so the
244 // numerator needs Q16^2 and the denominator must NOT divide by Q16 again. The first cut did, and
245 // this gate caught its own author: it reported 6,534,567 samples against a measured 100 while the
246 // estimator was right to within 0.3 percent. Printing the values beside the verdict is what made
247 // that one read instead of an investigation.
248 let per_true_samples: i64 = DG_TWOPI_PERMIL * DG_Q16 * DG_Q16 / (DG_PERMIL * wd_q16 * dt_q16)
249 let per_err: i64 = dg_abs(per_samples - per_true_samples) * DG_PERMIL / per_true_samples
250 gv_check("recovered-period-matches-closed-form-within-derived-sampling-bound" as *u8,
251 per_err <= fn_tol_permil * 2, ctr)
252
253 // The DERIVED damping tolerance, from rd_log2_q16's OWN declared imprecision.
254 let zeta_tol_permil: i64 = DG_LOG2_ERR_PERMIL * DG_LN2_PERMIL * DG_PERMIL
255 / (DG_PERMIL * DG_TWOPI_PERMIL) + 1
256 let zeta_err: i64 = dg_abs(out[RD_R_ZETA] - zeta_true_permil)
257 gv_check("recovered-zeta-matches-closed-form-within-declared-log2-imprecision" as *u8,
258 zeta_err <= zeta_tol_permil, ctr)
259
260 // ---- neg-control: A RIGID SUBJECT MUST NOT PRODUCE A FREQUENCY -------------------------
261 // This is the anti-vacuity tooth. An estimator that always emits a plausible number passes
262 // every test above and fails this one.
263 dg_sim_rigid(sig, DG_NSAMP)
264 let out2: *i64 = (sys_mmap(RD_R_N * 8 + 16)) as *i64
265 let rc2: i64 = rd_analyze(sig, DG_NSAMP, DG_DT_US, 0, out2)
266 gv_check("neg-control-rigid-signal-refuses-with-a-named-third-state" as *u8,
267 rc2 == RD_UNOBS_NO_CROSSING, ctr)
268 gv_check("neg-control-rigid-signal-emits-no-frequency" as *u8, out2[RD_R_FN_MHZ] == 0, ctr)
269
270 // ---- neg-control: A DIFFERENT SUBJECT MUST PRODUCE A DIFFERENT ANSWER ------------------
271 // Without this, a constant-returning stub passes both the truth tooth and the rigid tooth by
272 // luck of calibration. Stiffen the spring: the frequency MUST rise.
273 let out3: *i64 = (sys_mmap(RD_R_N * 8 + 16)) as *i64
274 dg_sim(sig, DG_NSAMP, k_q16 * 4, c_q16, 1000 * DG_Q16, dt_q16)
275 let rc3: i64 = rd_analyze(sig, DG_NSAMP, DG_DT_US, 0, out3)
276 gv_check("neg-control-stiffer-spring-is-measured-as-a-higher-frequency" as *u8,
277 rc3 == RD_OK, ctr)
278 gv_check("neg-control-frequency-actually-moved" as *u8,
279 out3[RD_R_FN_MHZ] > out[RD_R_FN_MHZ], ctr)
280
281 // ---- THE THIRD STATE IS DISTINGUISHABLE, NOT A ZERO ------------------------------------
282 let out4: *i64 = (sys_mmap(RD_R_N * 8 + 16)) as *i64
283 let rc4: i64 = rd_analyze(sig, 0, DG_DT_US, 0, out4)
284 gv_check("empty-input-is-named-not-silently-zero" as *u8, rc4 == RD_UNOBS_NO_SAMPLES, ctr)
285 let rc5: i64 = rd_analyze(sig, DG_NSAMP, 0, 0, out4)
286 gv_check("zero-sample-period-is-named" as *u8, rc5 == RD_UNOBS_NO_SAMPLES, ctr)
287
288 // ---- DERIVED-CONSTANT BINDING ----------------------------------------------------------
289 // nx_dynaoracle derives its minimum ROI from nx_motion's OWN integration window. This tooth is
290 // what stops the two drifting apart: it recomputes the derivation here and asserts it. If the
291 // LK window ever changes, this fires instead of the oracle silently accepting an empty region.
292 gv_check("min-roi-derivation-still-equals-lk-window-area" as *u8,
293 NX_MOTION_WIN * NX_MOTION_WIN == 25, ctr)
294 gv_check("flow-quantum-is-the-declared-Q8-base" as *u8, NX_MOTION_Q == 256, ctr)
295
296 // ==== THE PERIODIC-SPAN ESTIMATOR ==========================================================
297 // out[] still holds the ORIGINAL underdamped fixture's analysis, so the span rows are checked
298 // against the SAME closed form the two-lobe rows were, and inside the tolerance the ESTIMATOR
299 // ITSELF published for this record rather than one this gate invented.
300 gv_check("fixture-reached-the-condition-span-was-established" as *u8,
301 out[RD_R_SPAN_RC] == RD_OK, ctr)
302 gv_check("span-reports-at-least-one-half-period-of-baseline" as *u8,
303 out[RD_R_SPAN_HALVES] >= 1, ctr)
304
305 // The FIXTURE's own imprecision, added explicitly so it is never charged to the estimator:
306 // semi-implicit Euler damps by c*dt/2 relative and shifts the period by (w0*dt)^2/24 relative.
307 let cdt_permil: i64 = c_q16 * dt_q16 * DG_PERMIL / (DG_Q16 * DG_Q16)
308 let fx_zeta_err: i64 = zeta_true_permil * cdt_permil / (DG_EULER_ZETA_DEN * DG_PERMIL)
309 let w0dt_permil: i64 = w0_q16 * dt_q16 * DG_PERMIL / (DG_Q16 * DG_Q16)
310 let fx_per_err: i64 = w0dt_permil * w0dt_permil / (DG_EULER_PER_DEN * DG_PERMIL)
311
312 let span_zeta_err: i64 = dg_abs(out[RD_R_SPAN_ZETA] - zeta_true_permil)
313 gv_check("span-zeta-matches-closed-form-inside-its-OWN-published-tolerance" as *u8,
314 span_zeta_err <= out[RD_R_SPAN_ZETA_TOL] + fx_zeta_err, ctr)
315
316 let span_per_samples: i64 = out[RD_R_SPAN_PER_US] / DG_DT_US
317 let span_per_err: i64 = dg_abs(span_per_samples - per_true_samples) * DG_PERMIL / per_true_samples
318 gv_check("span-period-matches-closed-form-inside-its-OWN-published-tolerance" as *u8,
319 span_per_err <= out[RD_R_SPAN_FN_TOL] + fx_per_err, ctr)
320
321 // THE PUBLISHED TOLERANCE IS DERIVED, NOT STORED. Recomputing it here from the amplitudes and
322 // the span the estimator also published binds the number to its formula: a build that started
323 // returning a convenient constant would fail this tooth and nothing else.
324 let re_tol: i64 = rd_span_zeta_tol_permil(out[RD_R_SPAN_A], out[RD_R_SPAN_B],
325 out[RD_R_SPAN_HALVES])
326 gv_check("published-zeta-tolerance-slot-carries-the-functions-own-answer" as *u8,
327 re_tol == out[RD_R_SPAN_ZETA_TOL], ctr)
328
329 // AND THE FUNCTION ITSELF IS A DERIVATION, NOT A STORED NUMBER. A shorter baseline MUST widen
330 // the bound and a smaller final peak MUST widen it; a constant answers the same to all three.
331 let tol_long: i64 = rd_span_zeta_tol_permil(DG_TOL_PROBE_A, DG_TOL_PROBE_B,
332 DG_TOL_PROBE_HALVES_LONG)
333 let tol_short: i64 = rd_span_zeta_tol_permil(DG_TOL_PROBE_A, DG_TOL_PROBE_B,
334 DG_TOL_PROBE_HALVES_SHORT)
335 let tol_noisy: i64 = rd_span_zeta_tol_permil(DG_TOL_PROBE_A, DG_TOL_PROBE_B_SMALL,
336 DG_TOL_PROBE_HALVES_LONG)
337 gv_check("neg-control-tolerance-WIDENS-when-the-baseline-shortens" as *u8,
338 tol_short > tol_long, ctr)
339 gv_check("neg-control-tolerance-WIDENS-when-the-final-peak-shrinks" as *u8,
340 tol_noisy > tol_long, ctr)
341
342 // THE GENERALISATION CONTAINS THE INCUMBENT. At a span of one period the two are the same
343 // identity, so this must hold EXACTLY and not approximately: if it ever stops holding, the span
344 // form has stopped being a generalisation and has become a second ruler.
345 var contains: i64 = 1
346 if rd_zeta_permil_span(out[RD_R_A1], out[RD_R_A2], RD_HALVES_PER_PERIOD) != rd_zeta_permil(out[RD_R_A1], out[RD_R_A2]) { contains = 0 }
347 if rd_zeta_permil_span(1000, 500, RD_HALVES_PER_PERIOD) != rd_zeta_permil(1000, 500) { contains = 0 }
348 if rd_zeta_permil_span(7, 3, RD_HALVES_PER_PERIOD) != rd_zeta_permil(7, 3) { contains = 0 }
349 gv_check("span-at-one-period-reproduces-the-incumbent-EXACTLY" as *u8, contains == 1, ctr)
350
351 // ---- neg-control: A DECAY WITH APERIODIC CROSSINGS MUST BE REFUSED -----------------------
352 // The anti-vacuity tooth. This signal decays cleanly, so the two-lobe decrement accepts it and
353 // publishes a number; only an estimator that actually checks whether its crossings are
354 // half-period markers can refuse. A stub that always returns OK passes every tooth above and
355 // fails this one.
356 let lsig: *i64 = (sys_mmap(DG_LOBE_N * 8 + 16)) as *i64
357 let lout: *i64 = (sys_mmap(RD_R_N * 8 + 16)) as *i64
358 dg_sim_lobes(lsig, DG_LOBE_N, DG_LOBE_HEAD, DG_LOBE_ODD, DG_LOBE_LEN, DG_LOBE_LEN)
359 let lrc: i64 = rd_analyze(lsig, DG_LOBE_N, DG_DT_US, 0, lout)
360 gv_check("fixture-reached-the-condition-incumbent-ACCEPTS-the-aperiodic-decay" as *u8,
361 lrc == RD_OK, ctr)
362 gv_check("neg-control-aperiodic-crossings-are-refused-by-the-span-admission" as *u8,
363 lout[RD_R_SPAN_RC] == RD_UNOBS_NO_PERIODIC_SPAN, ctr)
364 gv_check("neg-control-a-refused-span-publishes-no-zeta" as *u8,
365 lout[RD_R_SPAN_ZETA] == 0, ctr)
366
367 // ---- POSITIVE control on the SAME fixture family ------------------------------------------
368 // A guard that refuses everything passes every negative test ever written. The only difference
369 // between this fixture and the one above is that its lobes are evenly spaced.
370 let psig: *i64 = (sys_mmap(DG_LOBE_N * 8 + 16)) as *i64
371 let pout: *i64 = (sys_mmap(RD_R_N * 8 + 16)) as *i64
372 dg_sim_lobes(psig, DG_LOBE_N, DG_LOBE_HEAD, DG_LOBE_LEN, DG_LOBE_LEN, DG_LOBE_LEN)
373 rd_analyze(psig, DG_LOBE_N, DG_DT_US, 0, pout)
374 gv_check("positive-control-evenly-spaced-lobes-ARE-admitted" as *u8,
375 pout[RD_R_SPAN_RC] == RD_OK, ctr)
376 gv_check("positive-control-admitted-span-publishes-a-damping" as *u8,
377 pout[RD_R_SPAN_ZETA] > 0, ctr)
378
379 // ---- neg-control: the rigid subject must not produce a SPAN either ------------------------
380 gv_check("neg-control-rigid-signal-span-refuses-with-a-named-third-state" as *u8,
381 out2[RD_R_SPAN_RC] == RD_UNOBS_NO_PERIODIC_SPAN, ctr)
382 gv_check("neg-control-rigid-signal-span-emits-no-frequency" as *u8,
383 out2[RD_R_SPAN_FN_MHZ] == 0, ctr)
384 gv_check("neg-control-stiffer-spring-moves-the-SPAN-frequency-too" as *u8,
385 out3[RD_R_SPAN_FN_MHZ] > out[RD_R_SPAN_FN_MHZ], ctr)
386
387 // ==== THE DECLARED GRID AND THE PRE-DECLARED ACCEPT RULE ===================================
388 // nx_ringdown_lib's header states this rule in full and states it BEFORE the implementation was
389 // ever measured. Enforcing it here is what stops it being a sentence: (a) every point where
390 // both estimators answer must land inside the tolerance the span estimator published for THAT
391 // point, and (b) the span estimator's WORST error must be STRICTLY smaller than the
392 // incumbent's worst on the SAME points. If (b) ever fails the right action is to unwire the
393 // span estimator, never to widen the rule.
394 let gsig: *i64 = (sys_mmap(DG_GRID_NSAMP * 8 + 16)) as *i64
395 let gout: *i64 = (sys_mmap(RD_R_N * 8 + 16)) as *i64
396 let zt: *i64 = (sys_mmap(DG_GRID_NZ * 8 + 16)) as *i64
397 zt[0] = DG_GRID_Z_LINEAR_LIMIT
398 zt[1] = DG_GRID_Z_LIGHT
399 zt[2] = DG_GRID_Z_VIDEO_FIXTURE
400 zt[3] = DG_GRID_Z_TISSUE_BAND_LOW
401 zt[4] = DG_GRID_Z_TISSUE_BAND_MID
402 zt[5] = DG_GRID_Z_TISSUE_BAND_HIGH
403 let gdt_q16: i64 = DG_Q16 / DG_GRID_DT_DIV
404 var visited: i64 = 0
405 var pts: i64 = 0
406 var worst_span: i64 = 0
407 var worst_dec: i64 = 0
408 var out_of_tol: i64 = 0
409 let gdec: *i64 = (sys_mmap(DG_GRID_NSAMP * 8 + 16)) as *i64
410 let dout: *i64 = (sys_mmap(RD_R_N * 8 + 16)) as *i64
411 var visited_v: i64 = 0
412 var pts_v: i64 = 0
413 var worst_span_v: i64 = 0
414 var worst_dec_v: i64 = 0
415 var out_of_tol_v: i64 = 0
416 var min_peak_v: i64 = 0
417 var min_halves_v: i64 = 0
418 var gk_q16: i64 = DG_GRID_K0 * DG_Q16
419 var ki: i64 = 0
420 while ki < DG_GRID_NK {
421 let gw0_q16: i64 = rd_isqrt(gk_q16 * DG_Q16)
422 var zi: i64 = 0
423 while zi < DG_GRID_NZ {
424 visited = visited + 1
425 // c = 2 * zeta * w0, so the fixture is BUILT from the target and the truth is then
426 // recomputed from the integer c actually used -- never from the target itself.
427 let gc_q16: i64 = RD_HALVES_PER_PERIOD * zt[zi] * gw0_q16 / DG_PERMIL
428 let gz_true: i64 = gc_q16 * DG_PERMIL / (RD_HALVES_PER_PERIOD * gw0_q16)
429 let gcdt_permil: i64 = gc_q16 * gdt_q16 * DG_PERMIL / (DG_Q16 * DG_Q16)
430 let gfx: i64 = gz_true * gcdt_permil / (DG_EULER_ZETA_DEN * DG_PERMIL)
431 dg_sim(gsig, DG_GRID_NSAMP, gk_q16, gc_q16, DG_GRID_X0 * DG_Q16, gdt_q16)
432 let grc: i64 = rd_analyze(gsig, DG_GRID_NSAMP, DG_DT_US, DG_GRID_QUANT, gout)
433 if grc == RD_OK {
434 if gout[RD_R_SPAN_RC] == RD_OK {
435 pts = pts + 1
436 let de: i64 = dg_abs(gout[RD_R_ZETA] - gz_true)
437 let se: i64 = dg_abs(gout[RD_R_SPAN_ZETA] - gz_true)
438 if de > worst_dec { worst_dec = de }
439 if se > worst_span { worst_span = se }
440 if se > gout[RD_R_SPAN_ZETA_TOL] + gfx { out_of_tol = out_of_tol + 1 }
441 }
442 }
443
444 // ---- THE SAME POINT AT THE VIDEO SAMPLING RATIO ---------------------------------
445 // Decimate the accurate series to DG_GRID_TARGET_SPP samples per period. The waveform
446 // is untouched, so the fixture's integration error is the same negligible gfx as above
447 // and any difference that appears here belongs to the estimators.
448 let gzsq_q16: i64 = gz_true * gz_true * DG_Q16 / (DG_PERMIL * DG_PERMIL)
449 let gwd_q16: i64 = gw0_q16 * rd_isqrt((DG_Q16 - gzsq_q16) * DG_Q16) / DG_Q16
450 var gper_fine: i64 = 0
451 if gwd_q16 > 0 {
452 gper_fine = DG_TWOPI_PERMIL * DG_Q16 * DG_Q16 / (DG_PERMIL * gwd_q16 * gdt_q16)
453 }
454 var dec: i64 = gper_fine / DG_GRID_TARGET_SPP
455 if dec < 1 { dec = 1 }
456 var di: i64 = 0
457 var dn: i64 = 0
458 while di * dec < DG_GRID_NSAMP {
459 gdec[dn] = gsig[di * dec]
460 dn = dn + 1
461 di = di + 1
462 }
463 visited_v = visited_v + 1
464 let vrc: i64 = rd_analyze(gdec, dn, DG_DT_US, DG_GRID_QUANT, dout)
465 if vrc == RD_OK {
466 if dout[RD_R_SPAN_RC] == RD_OK {
467 pts_v = pts_v + 1
468 let dve: i64 = dg_abs(dout[RD_R_ZETA] - gz_true)
469 let sve: i64 = dg_abs(dout[RD_R_SPAN_ZETA] - gz_true)
470 if dve > worst_dec_v { worst_dec_v = dve }
471 if sve > worst_span_v { worst_span_v = sve }
472 if min_peak_v == 0 { min_peak_v = dout[RD_R_SPAN_B] }
473 if dout[RD_R_SPAN_B] < min_peak_v { min_peak_v = dout[RD_R_SPAN_B] }
474 if min_halves_v == 0 { min_halves_v = dout[RD_R_SPAN_HALVES] }
475 if dout[RD_R_SPAN_HALVES] < min_halves_v { min_halves_v = dout[RD_R_SPAN_HALVES] }
476 // A COUNT WITHOUT A WORKLIST IS NOT ACTIONABLE, and the reason is already in
477 // hand at measure time. Every point that misses its own bound names itself.
478 if sve > dout[RD_R_SPAN_ZETA_TOL] + gfx {
479 out_of_tol_v = out_of_tol_v + 1
480 gv_puts("\n OFFENDER z_true=" as *u8); gv_num(gz_true)
481 gv_puts(" span=" as *u8); gv_num(dout[RD_R_SPAN_ZETA])
482 gv_puts(" err=" as *u8); gv_num(sve)
483 gv_puts(" tol=" as *u8); gv_num(dout[RD_R_SPAN_ZETA_TOL])
484 gv_puts(" fx=" as *u8); gv_num(gfx)
485 gv_puts(" halves=" as *u8); gv_num(dout[RD_R_SPAN_HALVES])
486 gv_puts(" pa=" as *u8); gv_num(dout[RD_R_SPAN_A])
487 gv_puts(" pb=" as *u8); gv_num(dout[RD_R_SPAN_B])
488 gv_puts(" per_samp=" as *u8); gv_num(dout[RD_R_SPAN_PER_US] / DG_DT_US)
489 gv_puts(" two_lobe=" as *u8); gv_num(dout[RD_R_ZETA])
490 gv_puts(" n=" as *u8); gv_num(dn)
491 }
492 }
493 }
494 zi = zi + 1
495 }
496 gk_q16 = gk_q16 * DG_GRID_K_STEP
497 ki = ki + 1
498 }
499 gv_check("grid-visited-every-declared-point" as *u8,
500 visited == DG_GRID_NK * DG_GRID_NZ, ctr)
501 // BOUND TO THE DENOMINATOR: a tooth that passes on the empty set is not a tooth, so both
502 // aggregate assertions below require that comparable points actually existed.
503 var tol_ok: i64 = 0
504 if pts > 0 { if out_of_tol == 0 { tol_ok = 1 } }
505 gv_check("grid-every-comparable-point-is-inside-the-span-published-tolerance" as *u8,
506 tol_ok == 1, ctr)
507 var better: i64 = 0
508 if pts > 0 { if worst_span < worst_dec { better = 1 } }
509 gv_check("grid-span-worst-error-is-STRICTLY-smaller-than-the-incumbent-worst" as *u8,
510 better == 1, ctr)
511
512 // ---- THE SAME RULE AT THE OPERATING SAMPLING RATIO ---------------------------------------
513 // This is the pass that matters for the video subject, and it is the one where the incumbent's
514 // single ratio is most exposed: at fifteen samples per period one amplitude LSB on one peak is
515 // a large share of the only measurement it makes.
516 gv_check("grid-video-sampling-visited-every-declared-point" as *u8,
517 visited_v == DG_GRID_NK * DG_GRID_NZ, ctr)
518 // THE DEADBAND MUST NOT HAVE EATEN THE EVIDENCE. Declaring a noise floor is the fix for judging
519 // an estimator on data the fixture cannot represent, but a floor set too high silences the
520 // subject instead of the noise -- a guard that refuses everything passes every negative test.
521 // This is the tooth that fires in THAT direction: every point must still span a full period of
522 // baseline after the floor is applied.
523 var halves_ok: i64 = 0
524 if min_halves_v >= DG_GRID_MIN_HALVES { halves_ok = 1 }
525 gv_check("grid-video-sampling-every-point-still-spans-a-full-period-after-the-floor" as *u8,
526 halves_ok == 1, ctr)
527 var tol_ok_v: i64 = 0
528 if pts_v > 0 { if out_of_tol_v == 0 { tol_ok_v = 1 } }
529 gv_check("grid-video-sampling-every-comparable-point-inside-published-tolerance" as *u8,
530 tol_ok_v == 1, ctr)
531 var better_v: i64 = 0
532 if pts_v > 0 { if worst_span_v < worst_dec_v { better_v = 1 } }
533 gv_check("grid-video-sampling-span-worst-error-STRICTLY-smaller-than-incumbent" as *u8,
534 better_v == 1, ctr)
535
536 gv_puts("\n GRID visited=" as *u8); gv_num(visited)
537 gv_puts(" comparable_points=" as *u8); gv_num(pts)
538 gv_puts(" out_of_tol=" as *u8); gv_num(out_of_tol)
539 gv_puts("\n GRID worst_zeta_err_permil two_lobe=" as *u8); gv_num(worst_dec)
540 gv_puts(" span=" as *u8); gv_num(worst_span)
541 gv_puts("\n GRID at " as *u8); gv_num(DG_GRID_TARGET_SPP)
542 gv_puts(" samples/period: comparable_points=" as *u8); gv_num(pts_v)
543 gv_puts(" out_of_tol=" as *u8); gv_num(out_of_tol_v)
544 gv_puts(" worst two_lobe=" as *u8); gv_num(worst_dec_v)
545 gv_puts(" span=" as *u8); gv_num(worst_span_v)
546 gv_puts(" min_judged_peak_quanta=" as *u8); gv_num(min_peak_v)
547 gv_puts(" min_span_halves=" as *u8); gv_num(min_halves_v)
548 gv_puts(" declared_floor=" as *u8); gv_num(DG_GRID_QUANT)
549 gv_puts("\n SPAN primary fixture halves=" as *u8); gv_num(out[RD_R_SPAN_HALVES])
550 gv_puts(" zeta=" as *u8); gv_num(out[RD_R_SPAN_ZETA])
551 gv_puts(" tol=" as *u8); gv_num(out[RD_R_SPAN_ZETA_TOL])
552 gv_puts(" err=" as *u8); gv_num(span_zeta_err)
553 gv_puts(" fixture_err=" as *u8); gv_num(fx_zeta_err)
554 gv_puts("\n TOLERANCE RESPONSIVENESS long_span=" as *u8); gv_num(tol_long)
555 gv_puts(" short_span=" as *u8); gv_num(tol_short)
556 gv_puts(" small_final_peak=" as *u8); gv_num(tol_noisy)
557 gv_puts("\n SPAN period samples=" as *u8); gv_num(span_per_samples)
558 gv_puts(" err_permil=" as *u8); gv_num(span_per_err)
559 gv_puts(" tol_permil=" as *u8); gv_num(out[RD_R_SPAN_FN_TOL])
560 gv_puts("\n" as *u8)
561
562 gv_puts("\nMEASURED VALUES (printed because a verdict vector cannot say why):\n" as *u8)
563 gv_puts(" zeta_true_permil=" as *u8); gv_num(zeta_true_permil)
564 gv_puts(" zeta_measured_permil=" as *u8); gv_num(out[RD_R_ZETA])
565 gv_puts(" zeta_tol_permil=" as *u8); gv_num(zeta_tol_permil)
566 gv_puts("\n period_true_samples=" as *u8); gv_num(per_true_samples)
567 gv_puts(" period_measured_samples=" as *u8); gv_num(per_samples)
568 gv_puts(" period_err_permil=" as *u8); gv_num(per_err)
569 gv_puts(" fn_tol_permil=" as *u8); gv_num(fn_tol_permil)
570 gv_puts("\n A1=" as *u8); gv_num(out[RD_R_A1])
571 gv_puts(" A2=" as *u8); gv_num(out[RD_R_A2])
572 gv_puts(" crossings=" as *u8); gv_num(out[RD_R_PEAKS])
573 gv_puts("\n" as *u8)
574
575 return gv_verdict("nx_dynaoracle_gate" as *u8, ctr,
576 "one estimator, analytic truth, third state named, rigid neg-control" as *u8)
577}