P25 conventional decode + conventional-channel attribution (node-26#24, #20) #27

Merged
logan merged 1 commits from feat/24-p25-conventional into main 2026-10-04 17:21:14 -04:00
Owner

Fixes node-26#24 (P25 conventional), and fixes node-26#20 (analog recording) through the same attribution path.

op25-container

  • New p25_mode: "conventional" generate-config. It uses fixed fsk4 channels, up to 2 on one non-tunable SDR, and has no control channel. There is an optional NAC per channel. Built per boatbod gr310 p25_conventional_example.json.
  • op25_addons/tk_drb_conv.py subclasses tk_p25.rx_ctl. For conventional channels it reports voice, the NAC heard, and encryption (the LC svcopts E bit plus LDU2 ESS algid/keyid). It also applies the NAC filter. Stock tk_p25 does neither for FDMA conventional.
  • Two channels go to UDP 23456/23457 through audio.py -2, then mono mixdown (×2 gain).
  • Trunked config is unchanged when p25_mode is absent. Analog now also uses tk_drb_conv.

drb-edge-node

  • conventional.py relabels conventional channels. The talkgroup is the distinctive link-control TGID; failing that, the channel's TGID from the last 30 s; failing that, a synthetic per-channel id (freq in 100 Hz units, 460.400 MHz → 4604000).
  • Encrypted channels never get the label.
  • With exactly one conventional channel, audio that would otherwise be orphaned is attributed to that channel at close. That is what makes analog recordable (#20).
  • /api/listen mode p25conv plus a dashboard tab. The health checklist shows the NAC heard, or says the channels are encrypted.
  • The checkin sends radio_channels.

Verified: edge suite 296 pass, flake8 clean, op25-container 18 pass (new CI job). drb-audio-review found 2 HIGH split bugs (a header frame arriving before link control, and ping-pong between labels); both are fixed with tests, and the re-review says ship. drb-qa-review findings are fixed.
Unverified (no SDR here): real OP25 decode of White Plains, tk_drb_conv against GNU Radio, two-channel audio levels/clipping, whether WPPD encrypts.
Known gap: simultaneous traffic on both channels shares one mono stream, so it is mislabelled or mixed (#26, DEFERRED.md).

Merge order: server-26 PR first, then this one. They are independent at runtime: a missing mode means trunked everywhere.

🤖 Generated with Claude Code

Fixes node-26#24 (P25 conventional), and fixes node-26#20 (analog recording) through the same attribution path. **op25-container** - New `p25_mode: "conventional"` generate-config. It uses fixed `fsk4` channels, up to 2 on one non-tunable SDR, and has no control channel. There is an optional NAC per channel. Built per boatbod gr310 `p25_conventional_example.json`. - `op25_addons/tk_drb_conv.py` subclasses `tk_p25.rx_ctl`. For conventional channels it reports `voice`, the NAC heard, and encryption (the LC svcopts E bit plus LDU2 ESS algid/keyid). It also applies the NAC filter. Stock tk_p25 does neither for FDMA conventional. - Two channels go to UDP 23456/23457 through `audio.py -2`, then mono mixdown (×2 gain). - Trunked config is unchanged when `p25_mode` is absent. Analog now also uses tk_drb_conv. **drb-edge-node** - `conventional.py` relabels conventional channels. The talkgroup is the distinctive link-control TGID; failing that, the channel's TGID from the last 30 s; failing that, a synthetic per-channel id (freq in 100 Hz units, 460.400 MHz → 4604000). - Encrypted channels never get the label. - With exactly one conventional channel, audio that would otherwise be orphaned is attributed to that channel at close. That is what makes analog recordable (#20). - `/api/listen` mode `p25conv` plus a dashboard tab. The health checklist shows the NAC heard, or says the channels are encrypted. - The checkin sends `radio_channels`. **Verified:** edge suite 296 pass, flake8 clean, op25-container 18 pass (new CI job). drb-audio-review found 2 HIGH split bugs (a header frame arriving before link control, and ping-pong between labels); both are fixed with tests, and the re-review says ship. drb-qa-review findings are fixed. **Unverified (no SDR here):** real OP25 decode of White Plains, tk_drb_conv against GNU Radio, two-channel audio levels/clipping, whether WPPD encrypts. **Known gap:** simultaneous traffic on both channels shares one mono stream, so it is mislabelled or mixed (#26, DEFERRED.md). **Merge order:** server-26 PR first, then this one. They are independent at runtime: a missing `mode` means trunked everywhere. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
logan added 1 commit 2026-10-04 15:57:35 -04:00
P25 conventional decode mode + conventional-channel attribution (node-26#24, #20)
CI / lint (push) Successful in 6s
CI / test-op25 (push) Successful in 13s
CI / lint (pull_request) Successful in 7s
CI / test-op25 (pull_request) Successful in 13s
CI / test (push) Successful in 44s
CI / test (pull_request) Successful in 42s
d07d312e92
op25-container
- generate-config takes p25_mode "conventional" + conv_channels [{frequency,
  name, nac}]. It builds fixed fsk4 channels (up to 2 on one non-tunable SDR,
  DC spike kept off the channels) with no control channel to hunt. Two
  channels send audio to UDP 23456/23457, and audio.py -2 + amplify(2) keeps
  levels.
- tk_drb_conv.py (a tk_p25.rx_ctl subclass, copied into OP25 apps) reports
  voice / NAC / encryption (LC service options + LDU2 ESS algid/keyid) for
  conventional channels and applies the per-channel NAC filter, which stock
  tk_p25 does not. Analog uses it too.
- Trunked P25 config is unchanged when p25_mode is absent.

drb-edge-node
- radio_config: system kind from type + config.mode (missing = trunked).
- conventional.py normalizes the console: a distinctive LC TGID, else the
  channel's last TGID (30 s), else a synthetic per-channel id (freq/100 Hz).
  Encrypted rows are never talkers. With exactly one conventional channel,
  orphan audio is attributed to it at close, which fixes analog recording
  (#20). The synthetic id and the real TGID on one channel never split.
- Health/dashboard: P25 conventional listen tab (/api/listen p25conv), and
  conventional decode + encryption checks. The checkin carries radio_channels.
- Tests: edge 296 pass; op25-container 18 (new CI job). Reviewed by
  drb-audio-review (findings fixed; simultaneous two-channel mixdown filed
  as #26) and drb-qa-review.

Not verified on SDR hardware.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
logan merged commit 80a0e88fc4 into main 2026-10-04 17:21:14 -04:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: logan/node-26#27