Version 2 OpenDPD–MATLAB parity¶
Status: 115/115 scored items passed on the registered digital signals (2026-10-11, MATLAB R2026a). Scientific review is required before merging the proposed validated profiles. This result belongs to the separately registered v2 procedure; the original v1 comparison remains a recorded failure and its profile remains pending cross-validation.
What was validated¶
Two explicit production profiles pair like-for-like estimator configurations:
ofdm-lte20-evm-v2 (dataset-length symmetric Hann) and
ofdm-lte20-evm-auto-v2 (full-record symmetric Hamming). Both use enclosing
FFT-bin band integration and the independently specified frequency-tolerant
acquisition. All original signal hashes, 115 scored cases and budgets were
retained, including the +350 Hz cases that v1 refused.
The Python implementation was written by Codex. Claude Code implemented the
MATLAB comparator from the committed written protocol without reading the new
Python implementation or its outputs (session
claude:515477ff-8a91-42b2-ab0f-a65431301132). Neither side calls the other.
The MATLAB reference comm.ACPR configurations were unchanged. The existing
MathWorks OFDM demodulation and EVM functions remain the independent backend.
Run log¶
| Run | Date | Source | Outcome |
|---|---|---|---|
| v1 baseline rerun | 2026-10-11 | original implementation plus review-policy merge | 102/115 within budget, seven outside, six unevaluable; same result as the archived v1 record |
| v2 run 1 | 2026-10-11 | ba5819b (clean numerical source) |
115/115 passed; machine-readable record |
| v2 run 2 | 2026-10-11 | cd9ccb3 (proposed validated profile metadata; clean numerical source) |
115/115 passed; run2 record and diagnostics |
| v2 run 3 | 2026-10-12 | e6012df (application catalogue isolated from frozen Arena; clean numerical source) |
115/115 passed; run3 record and diagnostics |
| v2 run 4 | 2026-10-12 | 6962567 (prepare / native MATLAB / score phases used by CI; clean source) |
115/115 passed; run4 record and diagnostics |
| v2 run 5 | 2026-10-12 | 156f573 (UTF-8 report I/O and canonical Python LF checkout; clean source) |
115/115 passed; complete record and diagnostics |
Run 1 maximum absolute ACLR difference: 7.105427357601002e-15 dB (budget 0.1 dB). Maximum EVM difference: 4.036622438989357e-7 percentage points (budget 0.05). All timing/count checks were exact; the clean frequency offsets met 0.01 Hz. Every scored item was evaluable.
This is not retrospective success of the old v1 criteria. One old common-value comparison was mathematically impossible: the two MATLAB reference values were 0.280827902 dB apart against two 0.1 dB budgets. V2 makes those two measurement configurations explicit and independently validates each.
Reproduce¶
OMP_NUM_THREADS=2 MKL_NUM_THREADS=2 OPENBLAS_NUM_THREADS=2 \
python scripts/matlab_parity_v2.py --work /tmp/opendpd-parity-v2 \
--matlab /path/to/MATLAB/bin/matlab --matlab-prefdir /tmp/opendpd-parity-v2-prefs \
--date YYYY-MM-DD
The harness writes report.json, report.md, all independent MATLAB results
and the MATLAB log beneath the scratch directory. It rejects a changed signal
hash, missing/duplicate case, wrong product configuration, changed tolerance,
non-finite/refused score, or a verdict inconsistent with its actual numbers.
Additional diagnostic values do not decide the scored verdict.
Limits¶
This validates the registered digital procedure and configurations, not a 3GPP conformance EVM procedure, LTE physical channels, 5G NR, real instruments or arbitrary unbound/multi-carrier captures. Both profiles require the known waveform binding; incompatible data produces explicit unavailable statuses. The old legacy/general/carrier profiles and default training/checkpoint metric are unchanged. The Auto profile uses the full valid record, rather than dataset nperseg, so its spectral estimate may differ from a segmented display plot.
Python regression coverage additionally checks both CFO signs, 1/10 ms frames, partial-symbol capture delays, wrong references/noise, analytic tone power and Nyquist boundaries, mode-independent EVM, and exact-case/tolerance integrity.
Complete scored results¶
Run 5 — 2026-10-12: 115/115 passed; 0 failed.
This is the separately registered v2 result; it does not change the old v1 verdict. For frequency-offset rows the first value is the injected offset; the second is the estimate by the tool named in the item.
| Group | Scored item | OpenDPD | MATLAB | Difference | Budget | Pass |
|---|---|---|---|---|---|---|
| P1 | sample count | 307200 | 307200 | 0 | 0 | yes |
| P1 | first-sample alignment (peak lag) | 0 | 0 | 0 | 0 | yes |
| P1 | relative RMS difference after one real scale | 0 | 2.85482688436e-08 | 2.85482688436e-08 | 1e-06 | yes |
| P3 | S0 EVM_RMS (percent) | 1.93005662701e-06 | 2.33371887091e-06 | 4.03662243899e-07 | 0.05 | yes |
| P3 | S0 integer timing | 0 | 0 | 0 | 0 | yes |
| P3 | S1 EVM_RMS (percent) | 0.0298635358911 | 0.0298635365801 | 6.89032195883e-10 | 0.05 | yes |
| P3 | S1 integer timing | 0 | 0 | 0 | 0 | yes |
| P2 | S2 ACLR_L nperseg=1024 vs comm.ACPR aligned | -41.1966539645 | -41.1966539645 | 0 | 0.1 | yes |
| P2 | S2 ACLR_R nperseg=1024 vs comm.ACPR aligned | -41.3320607114 | -41.3320607114 | 0 | 0.1 | yes |
| P2 | S2 ACLR_L nperseg=1024 vs comm.ACPR default | -41.3348054335 | -41.3348054335 | 0 | 0.1 | yes |
| P2 | S2 ACLR_R nperseg=1024 vs comm.ACPR default | -41.3280846744 | -41.3280846744 | 0 | 0.1 | yes |
| P2 | S2 ACLR_L nperseg=2048 vs comm.ACPR aligned | -41.0539775315 | -41.0539775315 | 0 | 0.1 | yes |
| P2 | S2 ACLR_R nperseg=2048 vs comm.ACPR aligned | -41.2147242922 | -41.2147242922 | 0 | 0.1 | yes |
| P2 | S2 ACLR_L nperseg=2048 vs comm.ACPR default | -41.3348054335 | -41.3348054335 | 0 | 0.1 | yes |
| P2 | S2 ACLR_R nperseg=2048 vs comm.ACPR default | -41.3280846744 | -41.3280846744 | 0 | 0.1 | yes |
| P2 | S2 ACLR_L nperseg=4096 vs comm.ACPR aligned | -41.1694572583 | -41.1694572583 | 0 | 0.1 | yes |
| P2 | S2 ACLR_R nperseg=4096 vs comm.ACPR aligned | -41.3170629337 | -41.3170629337 | 0 | 0.1 | yes |
| P2 | S2 ACLR_L nperseg=4096 vs comm.ACPR default | -41.3348054335 | -41.3348054335 | 0 | 0.1 | yes |
| P2 | S2 ACLR_R nperseg=4096 vs comm.ACPR default | -41.3280846744 | -41.3280846744 | 0 | 0.1 | yes |
| P3 | S2 EVM_RMS (percent) | 0.983656768452 | 0.983656767379 | -1.07288300466e-09 | 0.05 | yes |
| P3 | S2 integer timing | 0 | 0 | 0 | 0 | yes |
| P2 | S3 ACLR_L nperseg=1024 vs comm.ACPR aligned | -37.3448269858 | -37.3448269858 | 0 | 0.1 | yes |
| P2 | S3 ACLR_R nperseg=1024 vs comm.ACPR aligned | -37.242812401 | -37.242812401 | 0 | 0.1 | yes |
| P2 | S3 ACLR_L nperseg=1024 vs comm.ACPR default | -37.4707838609 | -37.4707838609 | 0 | 0.1 | yes |
| P2 | S3 ACLR_R nperseg=1024 vs comm.ACPR default | -37.2891823665 | -37.2891823665 | 0 | 0.1 | yes |
| P2 | S3 ACLR_L nperseg=2048 vs comm.ACPR aligned | -37.3256934491 | -37.3256934491 | 0 | 0.1 | yes |
| P2 | S3 ACLR_R nperseg=2048 vs comm.ACPR aligned | -37.2310527509 | -37.2310527509 | 0 | 0.1 | yes |
| P2 | S3 ACLR_L nperseg=2048 vs comm.ACPR default | -37.4707838609 | -37.4707838609 | 0 | 0.1 | yes |
| P2 | S3 ACLR_R nperseg=2048 vs comm.ACPR default | -37.2891823665 | -37.2891823665 | 0 | 0.1 | yes |
| P2 | S3 ACLR_L nperseg=4096 vs comm.ACPR aligned | -37.3708942349 | -37.3708942349 | 0 | 0.1 | yes |
| P2 | S3 ACLR_R nperseg=4096 vs comm.ACPR aligned | -37.275051419 | -37.275051419 | 0 | 0.1 | yes |
| P2 | S3 ACLR_L nperseg=4096 vs comm.ACPR default | -37.4707838609 | -37.4707838609 | 0 | 0.1 | yes |
| P2 | S3 ACLR_R nperseg=4096 vs comm.ACPR default | -37.2891823665 | -37.2891823665 | 0 | 0.1 | yes |
| P3 | S3 EVM_RMS (percent) | 2.72583424914 | 2.72583424809 | -1.04984065885e-09 | 0.05 | yes |
| P3 | S3 integer timing | 0 | 0 | 0 | 0 | yes |
| P2 | S4 ACLR_L nperseg=1024 vs comm.ACPR aligned | -32.7500456134 | -32.7500456134 | 0 | 0.1 | yes |
| P2 | S4 ACLR_R nperseg=1024 vs comm.ACPR aligned | -32.5281587006 | -32.5281587006 | 7.1054273576e-15 | 0.1 | yes |
| P2 | S4 ACLR_L nperseg=1024 vs comm.ACPR default | -32.8674335507 | -32.8674335507 | 0 | 0.1 | yes |
| P2 | S4 ACLR_R nperseg=1024 vs comm.ACPR default | -32.5988686269 | -32.5988686269 | 0 | 0.1 | yes |
| P2 | S4 ACLR_L nperseg=2048 vs comm.ACPR aligned | -32.7893392641 | -32.7893392641 | 0 | 0.1 | yes |
| P2 | S4 ACLR_R nperseg=2048 vs comm.ACPR aligned | -32.5639905285 | -32.5639905285 | 0 | 0.1 | yes |
| P2 | S4 ACLR_L nperseg=2048 vs comm.ACPR default | -32.8674335507 | -32.8674335507 | 0 | 0.1 | yes |
| P2 | S4 ACLR_R nperseg=2048 vs comm.ACPR default | -32.5988686269 | -32.5988686269 | 0 | 0.1 | yes |
| P2 | S4 ACLR_L nperseg=4096 vs comm.ACPR aligned | -32.8040338408 | -32.8040338408 | 0 | 0.1 | yes |
| P2 | S4 ACLR_R nperseg=4096 vs comm.ACPR aligned | -32.5841163398 | -32.5841163398 | 0 | 0.1 | yes |
| P2 | S4 ACLR_L nperseg=4096 vs comm.ACPR default | -32.8674335507 | -32.8674335507 | 0 | 0.1 | yes |
| P2 | S4 ACLR_R nperseg=4096 vs comm.ACPR default | -32.5988686269 | -32.5988686269 | 0 | 0.1 | yes |
| P3 | S4 EVM_RMS (percent) | 5.31524716274 | 5.31524716174 | -9.9977004453e-10 | 0.05 | yes |
| P3 | S4 integer timing | 0 | 0 | 0 | 0 | yes |
| P2 | S5 ACLR_L nperseg=1024 vs comm.ACPR aligned | -27.1547328151 | -27.1547328151 | 0 | 0.1 | yes |
| P2 | S5 ACLR_R nperseg=1024 vs comm.ACPR aligned | -26.8605519596 | -26.8605519596 | 0 | 0.1 | yes |
| P2 | S5 ACLR_L nperseg=1024 vs comm.ACPR default | -27.268878264 | -27.268878264 | 0 | 0.1 | yes |
| P2 | S5 ACLR_R nperseg=1024 vs comm.ACPR default | -26.9447069485 | -26.9447069485 | 0 | 0.1 | yes |
| P2 | S5 ACLR_L nperseg=2048 vs comm.ACPR aligned | -27.2172902399 | -27.2172902399 | 0 | 0.1 | yes |
| P2 | S5 ACLR_R nperseg=2048 vs comm.ACPR aligned | -26.9144876756 | -26.9144876756 | 0 | 0.1 | yes |
| P2 | S5 ACLR_L nperseg=2048 vs comm.ACPR default | -27.268878264 | -27.268878264 | 0 | 0.1 | yes |
| P2 | S5 ACLR_R nperseg=2048 vs comm.ACPR default | -26.9447069485 | -26.9447069485 | 0 | 0.1 | yes |
| P2 | S5 ACLR_L nperseg=4096 vs comm.ACPR aligned | -27.2215408218 | -27.2215408218 | 0 | 0.1 | yes |
| P2 | S5 ACLR_R nperseg=4096 vs comm.ACPR aligned | -26.9278039661 | -26.9278039661 | 0 | 0.1 | yes |
| P2 | S5 ACLR_L nperseg=4096 vs comm.ACPR default | -27.268878264 | -27.268878264 | 0 | 0.1 | yes |
| P2 | S5 ACLR_R nperseg=4096 vs comm.ACPR default | -26.9447069485 | -26.9447069485 | 0 | 0.1 | yes |
| P3 | S5 EVM_RMS (percent) | 10.6382449213 | 10.6382449204 | -9.03558117216e-10 | 0.05 | yes |
| P3 | S5 integer timing | 0 | 0 | 0 | 0 | yes |
| P2 | S6 ACLR_L nperseg=1024 vs comm.ACPR aligned | -32.7431926628 | -32.7431926628 | 0 | 0.1 | yes |
| P2 | S6 ACLR_R nperseg=1024 vs comm.ACPR aligned | -32.5185633667 | -32.5185633667 | 0 | 0.1 | yes |
| P2 | S6 ACLR_L nperseg=1024 vs comm.ACPR default | -32.8470666555 | -32.8470666555 | 0 | 0.1 | yes |
| P2 | S6 ACLR_R nperseg=1024 vs comm.ACPR default | -32.5798863517 | -32.5798863517 | 0 | 0.1 | yes |
| P2 | S6 ACLR_L nperseg=2048 vs comm.ACPR aligned | -32.7826206671 | -32.7826206671 | 0 | 0.1 | yes |
| P2 | S6 ACLR_R nperseg=2048 vs comm.ACPR aligned | -32.5543121548 | -32.5543121548 | 0 | 0.1 | yes |
| P2 | S6 ACLR_L nperseg=2048 vs comm.ACPR default | -32.8470666555 | -32.8470666555 | 0 | 0.1 | yes |
| P2 | S6 ACLR_R nperseg=2048 vs comm.ACPR default | -32.5798863517 | -32.5798863517 | 0 | 0.1 | yes |
| P2 | S6 ACLR_L nperseg=4096 vs comm.ACPR aligned | -32.7965080082 | -32.7965080082 | 0 | 0.1 | yes |
| P2 | S6 ACLR_R nperseg=4096 vs comm.ACPR aligned | -32.5739466347 | -32.5739466347 | 0 | 0.1 | yes |
| P2 | S6 ACLR_L nperseg=4096 vs comm.ACPR default | -32.8470666555 | -32.8470666555 | 0 | 0.1 | yes |
| P2 | S6 ACLR_R nperseg=4096 vs comm.ACPR default | -32.5798863517 | -32.5798863517 | 0 | 0.1 | yes |
| P3 | S6 EVM_RMS (percent) | 5.31521474069 | 5.3152147397 | -9.93350290912e-10 | 0.05 | yes |
| P3 | S6 integer timing | 92160 | 92160 | 0 | 0 | yes |
| P2 | S7 ACLR_L nperseg=1024 vs comm.ACPR aligned | -31.67301359 | -31.67301359 | 0 | 0.1 | yes |
| P2 | S7 ACLR_R nperseg=1024 vs comm.ACPR aligned | -31.5068562752 | -31.5068562752 | 0 | 0.1 | yes |
| P2 | S7 ACLR_L nperseg=1024 vs comm.ACPR default | -31.7846587227 | -31.7846587227 | 0 | 0.1 | yes |
| P2 | S7 ACLR_R nperseg=1024 vs comm.ACPR default | -31.5759143039 | -31.5759143039 | 0 | 0.1 | yes |
| P2 | S7 ACLR_L nperseg=2048 vs comm.ACPR aligned | -31.7095439799 | -31.7095439799 | 0 | 0.1 | yes |
| P2 | S7 ACLR_R nperseg=2048 vs comm.ACPR aligned | -31.5427536127 | -31.5427536127 | 0 | 0.1 | yes |
| P2 | S7 ACLR_L nperseg=2048 vs comm.ACPR default | -31.7846587227 | -31.7846587227 | 0 | 0.1 | yes |
| P2 | S7 ACLR_R nperseg=2048 vs comm.ACPR default | -31.5759143039 | -31.5759143039 | 0 | 0.1 | yes |
| P2 | S7 ACLR_L nperseg=4096 vs comm.ACPR aligned | -31.7245924946 | -31.7245924946 | 0 | 0.1 | yes |
| P2 | S7 ACLR_R nperseg=4096 vs comm.ACPR aligned | -31.5616574697 | -31.5616574697 | 3.5527136788e-15 | 0.1 | yes |
| P2 | S7 ACLR_L nperseg=4096 vs comm.ACPR default | -31.7846587227 | -31.7846587227 | 0 | 0.1 | yes |
| P2 | S7 ACLR_R nperseg=4096 vs comm.ACPR default | -31.5759143039 | -31.5759143039 | 0 | 0.1 | yes |
| P3 | S7 EVM_RMS (percent) | 5.4497361357 | 5.44973613442 | -1.28037846991e-09 | 0.05 | yes |
| P3 | S7 integer timing | 0 | 0 | 0 | 0 | yes |
| P2 | S8 ACLR_L nperseg=2048 vs comm.ACPR aligned | -32.5392568545 | -32.5392568545 | 0 | 0.1 | yes |
| P2 | S8 ACLR_R nperseg=2048 vs comm.ACPR aligned | -32.4170402013 | -32.4170402013 | 0 | 0.1 | yes |
| P2 | S8 ACLR_L nperseg=2048 vs comm.ACPR default | -32.653257327 | -32.653257327 | 0 | 0.1 | yes |
| P2 | S8 ACLR_R nperseg=2048 vs comm.ACPR default | -32.4852801226 | -32.4852801226 | 0 | 0.1 | yes |
| P2 | S8 ACLR_L nperseg=4096 vs comm.ACPR aligned | -32.5785344073 | -32.5785344073 | 0 | 0.1 | yes |
| P2 | S8 ACLR_R nperseg=4096 vs comm.ACPR aligned | -32.4514532128 | -32.4514532128 | 0 | 0.1 | yes |
| P2 | S8 ACLR_L nperseg=4096 vs comm.ACPR default | -32.653257327 | -32.653257327 | 0 | 0.1 | yes |
| P2 | S8 ACLR_R nperseg=4096 vs comm.ACPR default | -32.4852801226 | -32.4852801226 | 0 | 0.1 | yes |
| P2 | S8 ACLR_L nperseg=8192 vs comm.ACPR aligned | -32.5920534999 | -32.5920534999 | 0 | 0.1 | yes |
| P2 | S8 ACLR_R nperseg=8192 vs comm.ACPR aligned | -32.4714540464 | -32.4714540464 | 0 | 0.1 | yes |
| P2 | S8 ACLR_L nperseg=8192 vs comm.ACPR default | -32.653257327 | -32.653257327 | 0 | 0.1 | yes |
| P2 | S8 ACLR_R nperseg=8192 vs comm.ACPR default | -32.4852801226 | -32.4852801226 | 0 | 0.1 | yes |
| P3 | S8 EVM_RMS (percent) | 5.32129909371 | 5.32129909274 | -9.73950697869e-10 | 0.05 | yes |
| P3 | S8 integer timing | 0 | 0 | 0 | 0 | yes |
| P3 | S9 EVM_RMS (percent) | 0.0264295413763 | 0.0264295433964 | 2.02012303066e-09 | 0.05 | yes |
| P3 | S9 integer timing | 92160 | 92160 | 0 | 0 | yes |
| P3 | S9 frequency offset, OpenDPD | 350 | 350.000039384 | 3.93841402229e-05 | 0.01 | yes |
| P3 | S9 frequency offset, MATLAB | 350 | 350.000039384 | 3.93841609707e-05 | 0.01 | yes |
| P3 | S6b EVM_RMS (percent) | 5.31521475814 | 5.31521475714 | -9.93349402734e-10 | 0.05 | yes |
| P3 | S6b integer timing | 92160 | 92160 | 0 | 0 | yes |
| P3 | S9b EVM_RMS (percent) | 0.0264295260791 | 0.0264295280991 | 2.01999395336e-09 | 0.05 | yes |
| P3 | S9b integer timing | 92160 | 92160 | 0 | 0 | yes |
| P3 | S9b frequency offset, OpenDPD | 30 | 30.0000393898 | 3.93898376423e-05 | 0.01 | yes |
| P3 | S9b frequency offset, MATLAB | 30 | 30.0000393899 | 3.93898583724e-05 | 0.01 | yes |
Evidence interpretation and Arena isolation¶
The 84 ACLR scored entries comprise 42 segmented comparisons and 42 Auto entries. Auto ignores nperseg, so those 42 entries are 14 distinct values repeated across the three segment-size rows for correspondence with the original matrix. They are not 84 statistically independent observations. Both implementations share the written procedure, so agreement does not independently validate every assumption of that procedure. The waveform is assumed to repeat: captures with silence-prefixed, non-circular framing are outside this registration.
Independent review caught that directly extending the old registry altered an
Arena-hashed file. The application catalogue is therefore extended separately in
opendpd.core.metric_profiles; Arena keeps the original registry, evaluator and
source bytes. No bundled hash or benchmark result is rewritten. Regression tests
verify the original evaluator binding, profile catalogue and sealed Arena rows.