Skip to content

Commit 07941e0

Browse files
drowe67claude
andcommitted
verification: add worked example with plots; loss.py FAIL exit code fixes
- Add 'Worked Example of Loss Tests' section to verification procedure, showing clip value selection with before/after loss plots - Add automated pass/fail subsection using --compare/--delta for CI use - loss.py: print FAIL and exit(1) for --compare failures; replace quit() with sys.exit(1) throughout for correct CI return codes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 473e366 commit 07941e0

4 files changed

Lines changed: 120 additions & 2 deletions

File tree

doc/verification/loss_clipped.png

33.4 KB
Loading
23.4 KB
Loading

doc/verification/verification_procedure.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,120 @@ Connect Tx and Rx via coax with appropriate attenuators. Run the same test.
107107

108108
Pass criterion: loss within ±10% of baseline.
109109

110+
## Worked Example of Loss Tests
111+
112+
The following example uses the rade_c WAV tools as the device under test
113+
to demonstrate the full loss measurement workflow, including how to identify
114+
and clip start/end transients. You may notice similar transients when testing
115+
your own application or radio — it is good practice to remove them, as they
116+
inflate the mean loss and can mask the true integration performance.
117+
118+
Run a V2 software loopback from the `rade_c/build` directory, exporting
119+
feature vectors at both ends:
120+
121+
```
122+
./src/rade_tx_wav --v2 -f features_tx.f32 ../wav/all.wav tx.wav
123+
./src/rade_rx_wav --v2 -f features_rx.f32 tx.wav decoded.wav
124+
```
125+
126+
First pass — no clipping, `--plot` to inspect the loss curve:
127+
128+
```
129+
python3 ~/radae/loss.py features_tx.f32 features_rx.f32 \
130+
--plot --png loss_unclipped.png
131+
```
132+
133+
![Loss before clipping](loss_unclipped.png)
134+
135+
The spike at the start (~22) is the RADE acquisition transient; the smaller
136+
spike at the end (~3) is the end-of-over frame. Both are expected behaviour
137+
from the state machine. Clip them out and re-run:
138+
139+
```
140+
python3 ~/radae/loss.py features_tx.f32 features_rx.f32 \
141+
--clip_start 100 --clip_end 300 \
142+
--plot --png loss_clipped.png
143+
```
144+
145+
![Loss after clipping](loss_clipped.png)
146+
147+
With transients removed, loss drops from 0.113 to 0.082 — consistent with
148+
the reference baseline. `--clip_start 100` (1 s) and `--clip_end 300` (3 s)
149+
are conservative defaults; your integration may need different values
150+
depending on acquisition time. Use `--plot` to check.
151+
152+
## Worked Example of Loss Tests
153+
154+
The following example uses the rade_c WAV tools as the device under test
155+
to demonstrate the full loss measurement workflow, including how to identify
156+
and clip start/end transients. You may notice similar transients when testing
157+
your own application or radio — it is good practice to remove them, as they
158+
inflate the mean loss and can mask the true integration performance.
159+
160+
Run a V2 software loopback from the `rade_c/build` directory, exporting
161+
feature vectors at both ends:
162+
163+
```
164+
./src/rade_tx_wav --v2 -f features_tx.f32 ../wav/all.wav tx.wav
165+
./src/rade_rx_wav --v2 -f features_rx.f32 tx.wav decoded.wav
166+
```
167+
168+
First pass — no clipping, `--plot` to inspect the loss curve:
169+
170+
```
171+
python3 ~/radae/loss.py features_tx.f32 features_rx.f32 \
172+
--plot --png loss_unclipped.png
173+
```
174+
175+
![Loss before clipping](loss_unclipped.png)
176+
177+
The spike at the start (~22) is the RADE acquisition transient; the smaller
178+
spike at the end (~3) is the end-of-over frame. Both are expected behaviour
179+
from the state machine. Clip them out and re-run:
180+
181+
```
182+
python3 ~/radae/loss.py features_tx.f32 features_rx.f32 \
183+
--clip_start 100 --clip_end 300 \
184+
--plot --png loss_clipped.png
185+
```
186+
187+
![Loss after clipping](loss_clipped.png)
188+
189+
With transients removed, loss drops from 0.113 to 0.082 — consistent with
190+
the reference baseline. `--clip_start 100` (1 s) and `--clip_end 300` (3 s)
191+
are conservative defaults; your integration may need different values
192+
depending on acquisition time. Use `--plot` to check.
193+
194+
### Automated pass/fail
195+
196+
To compare your application against the rade_c software reference without
197+
needing to record the baseline loss manually, use `--features_hat2` and
198+
`--compare`. First generate a software reference run:
199+
200+
```
201+
./src/rade_rx_wav --v2 -f features_rx_ref.f32 tx.wav /dev/null
202+
```
203+
204+
Then run your application (the DUT) on the same `tx.wav` to produce
205+
`features_rx_dut.f32`, and compare:
206+
207+
```
208+
python3 ~/radae/loss.py features_tx.f32 features_rx_ref.f32 \
209+
--features_hat2 features_rx_dut.f32 \
210+
--compare --delta 0.008 \
211+
--clip_start 100 --clip_end 300
212+
```
213+
214+
Output:
215+
```
216+
loss1: 0.082 loss2: 0.082 delta: 0.000
217+
PASS
218+
```
219+
220+
`loss.py` prints `PASS` or `FAIL` and exits with code 0 or 1 respectively,
221+
making it suitable for use in CI scripts. A `--delta` of 0.008 corresponds
222+
to approximately ±10% of the V2 software loopback baseline (0.082).
223+
110224
## Submitting Results
111225

112226
Copy `doc/verification/template.md` to `doc/verification/<serial>-<application>.md`

loss.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"""
3131

3232
import os
33+
import sys
3334
import argparse
3435
import numpy as np
3536
import torch
@@ -108,12 +109,12 @@ def find_loss(features_fn, features_hat_fn):
108109
if args.loss_test > 0.0:
109110
if min_loss > args.loss_test:
110111
print("FAIL")
111-
quit()
112+
sys.exit(1)
112113
if args.acq_time_test > 0:
113114
# one feature vector every 10ms
114115
if acq_time > args.acq_time_test:
115116
print("FAIL")
116-
quit()
117+
sys.exit(1)
117118
if args.loss_test > 0.0 or args.acq_time_test:
118119
print("PASS")
119120

@@ -123,6 +124,9 @@ def find_loss(features_fn, features_hat_fn):
123124
print(f"loss1: {min_loss:5.3f} loss2: {min_loss2:5.3f} delta: {np.abs(min_loss-min_loss2):5.3f}")
124125
if np.abs(min_loss-min_loss2) < args.delta:
125126
print("PASS")
127+
else:
128+
print("FAIL")
129+
sys.exit(1)
126130

127131
if args.stats:
128132
def print_stats(loss_arr, label):

0 commit comments

Comments
 (0)