fw: NET/ROM sweep + RF origination + kiss_serial pump + Tait CCDI transport (fixes fw-build) - #60
Merged
Merged
Conversation
Gap A (recon netrom.md §3): `NetRomService::sweep()` existed in core but no fw transport ever called it, so the routing table never aged — obsolescence never decremented, dead routes never purged, and the OBSMIN advertise-gate never engaged. The node advertised stale routes indefinitely. Wire a `next_sweep_at` cadence into the axudp beacon-tick loop that calls `netrom.sweep()` once per NODES interval, BEFORE origination so a broadcast reflects the freshly-aged table — the C# `NetRomService.OnInterval` order (Sweep() then BroadcastNodes()). Runs whether or not we originate: aging is a property of the table, and OBSINIT is calibrated to one broadcast period per decrement. First sweep is one interval after boot (never age a freshly flash-restored table immediately). Also restores the crate build: the merged core added `StatusReport` / `RssiReading` variants to `NinoTncInboundEvent`, which left kiss_serial's classify match non-exhaustive. Add the two arms (log-only telemetry; the full kiss_serial pump is rewritten in a later commit). Compile-validated only (no hardware): `cargo build --release --locked` + `cargo test --release --locked --no-run` both green. Sweep timing / actual purge behaviour needs bench validation with a live NODES feed. Co-Authored-By: Claude Code <noreply@anthropic.com>
Gap B (recon netrom.md §3): NODES origination was AXUDP/LAN-only, so the node was invisible in peers' nodes tables on the RF-shaped paths. Wire a `NetRomOriginator` into kiss_tcp's per-connection `serve` loop — build our broadcasts from the live routing table, wrap each payload as a UI frame (dest `NODES`, PID 0xCF), KISS-frame it and send — plus the obsolescence sweep on the same NODES interval (Gap A parity with axudp: age before advertise). The observe-tap was already present on this path. The header alias is the node mnemonic (`cfg.identity.alias`), falling back to the callsign base, matching the axudp originator. The routing table is per-connection (recreated on reconnect, as `netrom` already was), so a fresh connection announces itself with a header-only "I'm here" frame until it hears its first NODES broadcast — a pre-existing property of kiss_tcp's per-serve netrom, noted for bench validation. main.rs: pass `cfg.netrom` + `cfg.identity.alias` to the kiss_tcp spawn. The kiss_serial observe-tap + origination (also Gap B) is folded into the next commit, because on that transport it is inseparable from the task signature/spawn/pump changes. Compile-validated only (no hardware): `cargo build --release --locked` + `cargo test --release --locked --no-run` both green. On-air NODES visibility, sweep timing and the reconnect table-reset behaviour need bench validation against a live KISS-TCP/net-sim peer. Co-Authored-By: Claude Code <noreply@anthropic.com>
Services Gap D + netrom Gap B: the KISS-over-UART transport was stubbed (`let _ = ax25;` dropped every inbound frame) and never spawned. Fill it, mirroring the KISS-TCP transport precisely: - Pump: `select(modem.read_frame(), ticker.next())` — wake on an inbound KISS frame or the periodic tick. `read_frame` is cancel-safe (its only await is the UART read; decode state lives in the modem), so a tick that pre-empts the read loses no buffered bytes. - Inbound: classify with NinoTNC awareness, then run the read-only NET/ROM tap (`session::observe_inbound`) on every AX.25 frame BEFORE any address filter, so NODES broadcasts are heard on real RF. Connected-mode session routing stays the deferred supervisor seam (as kiss_tcp leaves it). - Outbound drain to the UART on the tick: beacon UI frame, obsolescence sweep (Gap A parity), and NODES origination (Gap B) built from the live table. Wiring: - main.rs: spawn `kiss_serial::task` past the callsign gate on UART1 GP20/21, passing netrom config + callsign + alias. - config.rs: extend `KissSerialConfig` with `startup_mode: Option<u8>` (a boot NinoTNC SETHW mode, RAM-only; `None` default preserves behaviour; from `NINOTNC_MODE`). COMPILE-VALIDATED ONLY (no hardware): `cargo build --release --locked` + `cargo test --release --locked --no-run` both green. Everything from the UART byte exchange onward — SETHW at boot, the read pump under real KISS/NinoTNC traffic, beacon/NODES TX timing, cancel-safety of read_frame under the live UART — needs bench validation with a NinoTNC on GP20/21 @ 57600. Co-Authored-By: Claude Code <noreply@anthropic.com>
New `transports/tait_ccdi.rs` — an Embassy task owning a SECOND UART (UART0 GP0/GP1, distinct from the NinoTNC KISS link on UART1) that drives the core `radio::tait::driver::TaitCcdiRadio` CCDI driver. Mirrors kiss_serial's structure precisely (reuses its `UartByteStream` byte-source seam and the same `configure_uart` / `bind_interrupts` shape; a UART0 interrupt binding). The drive loop: - enables unsolicited PROGRESS output at boot (FUNCTION 0/4) — required before carrier-sense (DCD) / PTT edges are reported; - optionally retunes to a configured channel at boot (GO_TO_CHANNEL); - polls RSSI (integer tenths-of-dBm) on a ticker, then drains the carrier-sense / PTT / SDM PROGRESS edges the driver demuxes out of each transaction (and maintains `channel_busy` from). Self-quiets on NoResponse so an absent radio doesn't spam warnings. Wiring: `transports/mod.rs` registers the module; `config.rs` adds `TaitConfig` (baud default = core `tait::DEFAULT_BAUD` 28800, optional boot channel, RSSI poll cadence) via `TAIT_BAUD` / `TAIT_CHANNEL`; `main.rs` spawns it on UART0/GP0/GP1. COMPILE-VALIDATED ONLY (no hardware): `cargo build --release --locked` + `cargo test --release --locked --no-run` both green; no new deps (Cargo.lock unchanged). Needs bench validation with a Tait TM8100/TM8200 on GP0/GP1: the CCDI transact/demux under a live radio, PROGRESS-enable acknowledgement, RSSI values, carrier-sense/PTT edge timing, and the UART0 pin/baud choice against the real wiring. Note the driver's documented parity caveat — a command ERROR split into a later read than its prompt is not awaited (deferred grace window). Co-Authored-By: Claude Code <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Also fixes a red
main: #58's newNinoTncInboundEvent::{StatusReport,RssiReading}variants made the fw crate's classify match non-exhaustive (the core-only test gate doesn't build the excluded fw crate, so it slipped past local checks — thefw-buildCI job catches it).fw wave (all compile-validated only — no hardware run this session; both
cargo build --release --lockedandcargo test --release --locked --no-runpass, Cargo.lock unchanged):NetRomService::sweep()existed in core but no fw caller drove it (routes never aged); now wired into the NODES-interval tick before origination.transports/tait_ccdi.rsdriving coreradio::taitover a second UART (RSSI poll + carrier/PTT/SDM PROGRESS demux).Each item's bench-validation checklist is in the branch commits. Compile-only; behaviour needs the hardware-bringup session.
🤖 Generated with Claude Code