Document why there is no audio to model

Records the scope plainly, because "add a speaker and a microphone" is the obvious
request and the answer is that neither is on the MCU: no audio samples exist in its
address space, so there is nothing to capture, nothing to play, and nothing for a
browser permission to carry. Same line as the analogue RF limit.

Also records the stub lesson: QmpClient.command returns the unwrapped value, the test
stub returned an envelope, and the divergence let 88 tests pass while the live UI
returned 500. A stub more forgiving than the real client is worse than none.
This commit is contained in:
mckero committed 2026-08-29 06:06:55 +01:00
1 parent 7d299dbad2
commit a1aca9395c
2 files changed
+63

No files matched your search

+37
View File
@@ -510,6 +510,43 @@ plausible way to stall a scan, and the S-meter work made the receiver always bus
Page 4 is *not* the meter row, incidentally — it stayed byte-identical across all six
samples while the frequency changed.
### Audio: there is nothing to model, and that is the finding
"Add a speaker and a microphone, then grant the browser audio permission" is the
obvious request, and it cannot be done — not for lack of effort but because neither
device is on the MCU. Receive audio is demodulated inside the BK4819 and leaves as
analogue on its AF pin; transmit audio goes from the microphone into the chip's own ADC.
The firmware touches only:
PA8 amplifier enable (GPIO_EnableAudioPath, driver/gpio.h:34)
REG_47 which AF source the chip routes
REG_64 a level it displays
**No audio samples exist anywhere in the MCU's address space.** There is nothing to
capture, nothing to play, and nothing for a browser permission to carry. Generating
sound would be inventing data the firmware never produced — the same line as the
analogue RF limit.
What is real is the *intent*. `TYPE_UVK5_AUDIO` watches PA8 and exposes read-only
`speaker-on`; the UI shows a speaker glyph and `/api/status` reports `speaker`.
Read-only on purpose: a writable one would only let a test lie to itself. A unit test
also asserts the page never asks for audio permission — no `getUserMedia`, no
`AudioContext`, no `<audio>` — because prompting the user to approve something that
cannot happen is worse than not offering it.
### A stub that is more forgiving than the real client is worse than no stub
`QmpClient.command` returns the **unwrapped** value and raises on error. The test stub
returned `{"return": ...}`. So `webui.py` was written to unwrap a second time, all 88
tests passed, and the live UI returned 500 with
TypeError: argument of type 'bool' is not iterable
Two lessons, both of which cost time here. The stub is now pinned to the real contract
by an explicit test. And the failure was originally swallowed by a bare
`except: return None`, which made a broken call indistinguishable from a radio that was
simply silent — and sent me hunting a stale process that did not exist. Log the reason.
### PTT, and the transmit level bar
PTT is not a matrix key. `GPIO_IsPttPressed` reads PB10 directly
+26
View File
@@ -44,6 +44,7 @@ has no public datasheet, so its driver is the only specification available.
| BK4819 register interface | works, RSSI and status readable |
| S-meter | works via monitor (SIDE1); reads -53 dBm, S9+40 |
| PTT and transmit | works; TX annunciator, timer, and mic level bar |
| Speaker / microphone audio | **no samples exist to model**, see [Audio](#audio) |
| Timing accuracy | deliberately wrong, see [Timing](#timing) |
| Analogue RF behaviour | **not modelled and never will be**, see [AGENTS.md](AGENTS.md#the-bk4819-and-where-modelling-it-stops) |
@@ -86,6 +87,7 @@ keypresses silently stop working. Run the test after touching that code;
test_smeter.py the S-meter reads a signal when monitoring
test_ptt.py PTT keys the radio and releases cleanly
test_scan.py a busy band does not stall a scan
test_audio_path.py the amplifier turns on when the firmware wants sound
run_tests.sh runs all of the above, build-checked first
test_run_tests.sh that the runner actually notices failures
lib_kill_emulator.sh cleanup that only ever kills emulators
@@ -143,6 +145,7 @@ that was never compiled. Individual tests still run standalone:
python3 tools/test_smeter.py
python3 tools/test_ptt.py
python3 tools/test_scan.py
python3 tools/test_audio_path.py
This matters more than it looks. The keypad can break silently under -O2 without
any compiler warning -- see the `volatile` note in [Status](#status) -- so a clean
@@ -276,6 +279,29 @@ or `POST /api/release-all` — so a client going away cannot leave the radio key
The `press` property still rejects "PTT" as a key name; unknown keys get a 400 rather
than being forwarded.
## Audio
There is no audio, and there is nothing to add. On the real radio neither the speaker
nor the microphone passes through the MCU: receive audio is demodulated inside the
BK4819 and leaves it as analogue on its AF output, and transmit audio goes from the
microphone straight into the chip's own ADC. The firmware only ever touches three
things:
| | |
|---|---|
| PA8 | the amplifier enable, on or off |
| `REG_47` | which AF source the chip routes |
| `REG_64` | a level the firmware displays |
No audio samples exist anywhere in the MCU's address space, so the emulator has nothing
to capture or play — and the browser page needs no microphone or playback permission,
because there would be nothing for it to carry. Generating sound here would mean
inventing data the firmware never produced.
What *is* real is whether the firmware currently wants sound, which PA8 states exactly.
The UI shows it as a speaker glyph next to the power state, and `/api/status` reports it
as `speaker`. Press SIDE1 to engage monitor and it lights up.
## How the machine is put together
Register layouts come from the vendor CMSIS header shipped with the firmware