BanterLab
Acoustic communication testing and parameter tuning tool
BanterLab
BanterLab is an interactive testing and parameter tuning tool for the acoustic communication system. It provides a multi-node bus for digital loopback testing, over-the-air testing with real audio hardware, automated parameter sweeps, and result recording to a database.
Launching
From the hub application:
# Open BanterLab activity
hub --activity BanterLabActivity
# Load a test specification
hub --activity BanterLabActivity --open test/testBanter/specs/tier1_smoke/smoke_fsk_bus.json
# Load and run with auto-close
hub --activity BanterLabActivity --open spec.json --close-on-completion
# Override project name for database grouping
hub --activity BanterLabActivity --open spec.json --project my-experiment
Bus widget
The main interface shows a horizontally scrollable row of node widgets. Each node represents a BanterClient instance that can transmit and receive on a shared digital bus.
Node modes
| Mode | Description |
|---|---|
| Bus only | TX output mixed into shared bus, bus fed to RX. No audio hardware. |
| Audio out | TX output goes to a real speaker. Bus still feeds RX. |
| Audio in | Real microphone feeds RX. TX goes to bus. |
| Audio both | Full over-the-air: speaker out, microphone in. |
Bus audio flow
Each TX node generates audio -> mixed into bus buffer
Bus buffer -> fed to each RX node's processRx()
Mixed output -> Speaker (if any node has audio out)
Thread safety: the bus audio callback runs on AudioPump's thread. All parameter changes from the UI are protected by mNodeMutex.
Device presence as a first-class invariant
Per the internal audio-device-presence spec, every TX or RX node that names a real hardware device must resolve to a present device before a run starts and for every iter that is dispatched. If the device is missing:
- Pre-run: the test is refused.
aborted("DEVICE_MISSING")is emitted on the BanterAutoTest signal bus, the per-node banner turns red with reasonDEVICE_MISSING, and the queued-runner skips the offending row and continues to the next row in a test set. - Mid-run: the iter scheduler is paused (multi-reason pause; the existing
AUDIO_UNHEALTHYreason continues to use the same atomic). The banner turns red. On replug, the node callssyncAudioDevices()to recreate the audio node, the pause clears, and the next iter fires fresh.
The bus and off route sentinels never require a device; they are first-class operator choices. See diagnose-missing-audio-device for the recovery procedure.
Test specifications
Test specs are JSON files that define parameter configurations and test execution settings. Two formats are supported.
Version 2 (single test)
A single test configuration with one set of parameters and one auto-test run:
{
"version": 2,
"nodes": {
"tx": { "name": "Alice", "audioMode": true },
"rx": { "name": "Bob", "audioMode": true }
},
"parameters": [
{ "id": "fsk_baudRate", "current": 300, "sweepMode": "fixed" },
{ "id": "fsk_baseFreq", "current": 2000, "min": 1000, "max": 3000,
"sweepMode": "stepped", "numSteps": 10 }
],
"autoTest": {
"testName": "my_test",
"connectionString": "results.db",
"codec": "FSK",
"payload": "Hello World",
"maxIterations": 100,
"ncores": 4,
"settleTimeMs": 1000,
"timeoutPerTestMs": 60000,
"quitOnComplete": false
}
}
Version 3 (test set)
Multiple named configurations that run sequentially, each with their own parameters and iteration counts:
{
"version": 3,
"name": "AGC Characterization",
"description": "Sweep AGC parameters over air",
"configs": [
{
"name": "baseline_no_agc",
"iterations": 20,
"payload": "HELLO WORLD",
"codec": "FSK",
"parameters": [
{ "id": "agcMode", "current": 0, "sweepMode": "fixed" }
]
},
{
"name": "agc_maxgain_sweep",
"iterations": 50,
"payload": "HELLO WORLD",
"codec": "FSK",
"parameters": [
{ "id": "agcMode", "current": 2, "sweepMode": "fixed" },
{ "id": "agcMaxGain", "current": 500, "min": 10, "max": 2000,
"sweepMode": "stepped", "numSteps": 9 }
]
}
]
}
When loaded via --open, the test set table shows all configs with status indicators (pending, running, completed).
Parameter sweep modes
| Mode | Description | Example |
|---|---|---|
| fixed | Single value, no variation | baudRate = 300 |
| stepped | Grid of evenly-spaced values | baudRate = [25, 89, 153, ..., 600] (10 steps) |
| random | Uniform random within range | agcMaxGain in [10, 2000] |
When multiple parameters use stepped mode, the total iteration count is the product of all step counts.
Available test specs
Located in test/testBanter/:
Tiered spec corpus
Specs live under test/testBanter/specs/ in four tiers. See test/testBanter/specs/README.md for the full convention.
| Tier | Directory | Purpose |
|---|---|---|
| 1 — smoke | tier1_smoke/ |
Build-gate. Per-codec bus loopback at validated baseline. Should always pass at near-100% decode. |
| 2 — codec sweep | tier2_codec_sweep/ |
Per-codec parameter ranges over bus. Detects codec regressions. |
| 3 — OTA ablation | tier3_ota_ablation/ |
Feature on/off matrix, real audio, static mic rig. Drives the OTA decode-rate ceiling work. |
| 4 — RL training | tier4_rl_training/ |
Search spaces for BanterRLController. Not pass/fail; measure reward over iterations. Reserves a physicalAxis block for robot-controlled axes. |
Tier 1 ships with one smoke spec per codec (smoke_fsk_bus.json, smoke_ofdm_bus.json, smoke_dpsk_bus.json, smoke_css_bus.json, smoke_mcfsk_bus.json). Higher tiers are populated as work progresses.
Test execution engine
Interactive mode
BanterAutoTest runs tests with settle time between iterations. Connected to the UI for real-time feedback.
Headless mode
BanterTestEngine runs batch tests without UI. Supports multi-threaded execution:
- Each thread creates independent BanterBus + 2 BanterClients
- Zero shared state between threads
- Linear speedup with core count
- Deterministic via master seed
Results database
Test results are stored in SQLite (or PostgreSQL) via BanterTestDB. Both interactive and headless tests write to the same unified schema:
| Table | Purpose |
|---|---|
test_sets |
Collection metadata |
test_configs |
Per-config parameters and iteration count |
sessions |
Session: config + code version + git info + seed |
runs |
Individual iterations: score, decode success, stats, parameter values |
The runs table includes 72 parameter columns (one per parameterRegistry() entry, e.g. fsk_baseFreq, txAmplitude, agcMode) added automatically via ALTER TABLE ADD COLUMN. Interactive tests populate these from the parameter table; headless tests leave them NULL (parameters are reconstructable from sweep + seed). An optional diagnostics_json TEXT column stores extended RX diagnostics (chirp detection, channel response, audio health) from interactive tests.
Connection string format: sqlite:///path/to/file.db or bare file.db.
Headless CLI testing
To run a test set headless, use banter-bench test-set. It loads a test set or test JSON, runs every config on all cores, and writes the runs to a results database in the lab's schema:
# Run every config of a test set; the results go to fsk_results.db next to the file
banter-bench test-set --file test/testBanter/specs/tier1_smoke/smoke_fsk_bus.json
# Write the results somewhere else
banter-bench test-set --file my_sweep.json --db /tmp/sweep.db
# Run specific test functions
./testBanter testBanterBusRoundTrip testBanterBusFEC testBanterBusParamSweep
# Run an OTA ablation set through the lab (the shell runner that used to
# do this is gone; the specs it named are, too)
hub --activity BanterLab --open test/testBanter/specs/tier3_ota_ablation/ablation_dsp_features.json --close-on-completion
Key parameters
FSK codec parameters
| Parameter | Range | Default | Description |
|---|---|---|---|
fsk_baseFreq |
500-4000 | 2000 | Center frequency (Hz) |
fsk_freqSeparation |
400-2000 | 800 | Mark-space gap (Hz) |
fsk_baudRate |
10-600 | 300 | Symbol rate |
fsk_toneShaping |
0-0.5 | 0.1 | Raised cosine ramp fraction |
fsk_goertzelWindow |
0.3-1.0 | 0.85 | Detection integration window |
fecEnabled |
0/1 | 0 | Convolutional FEC + interleaver |
diffEncoding |
0/1 | 0 | Differential encoding |
AGC parameters
| Parameter | Range | Default | Description |
|---|---|---|---|
agcMode |
0-4 | 0 | 0=off, 1=normal, 2=freeze, 3=blockRms, 4=primerLocked |
agcMaxGain |
1-2000 | 500 | Maximum gain multiplier |
agcTargetPeak |
0.1-1.0 | 0.8 | Target peak amplitude |
agcPrimer |
0/1 | 0 | Enable 300ms noise primer |
agcPrimerMs |
50-1000 | 300 | Primer duration |
Pipeline feature flags
| Parameter | Default | Description |
|---|---|---|
bandpass |
0 | Post-chirp bandpass filter |
coherentSumming |
0 | N-chirp coherent summing |
chirpSequenceDetector |
0 | False positive rejection |
adaptiveChirpThreshold |
0 | Dynamic detection threshold |
subSampleChirpTiming |
0 | Parabolic peak interpolation |
adaptiveEQ |
0 | NLMS-trained equalization |
noiseReduction |
0 | FFT spectral subtraction |
cfoCorrection |
0 | Clock offset correction |
txRepeatCount |
1 | Repeat-and-vote transmissions |
Source files
| Component | Location |
|---|---|
| BanterBusWidget | src/libs/libbanter/banter/lab/BanterBusWidget.hpp/.cpp |
| BanterNodeWidget | src/libs/libbanter/banter/lab/BanterNodeWidget.hpp/.cpp |
| BanterAutoTest | src/libs/libbanter/banter/lab/BanterAutoTest.hpp/.cpp |
| BanterTestEngine | src/libs/libbanter/banter/lab/BanterTestEngine.hpp/.cpp |
| BanterTestDB | src/libs/libbanter/banter/lab/BanterTestDB.hpp/.cpp |
| BanterTestSpec | src/libs/libbanter/banter/lab/BanterTestSpec.hpp/.cpp |
| BanterParamSet | src/libs/libbanter/banter/lab/BanterParamSet.hpp/.cpp |
| BanterTransport | src/libs/libbanter/banter/BanterTransport.hpp/.cpp |
| BanterLinkStats | src/libs/libbanter/banter/BanterLinkStats.hpp |
| BanterMcsTable | src/libs/libbanter/banter/BanterMcsTable.hpp |
| Test specs | test/testBanter/specs/tier{1,2,3,4}_*/*.json |