time_scale, FM-real collision noise, squelch-open delay, opt-in scheduling priority + samoyed echo/flush pin - #17
Merged
Merged
Conversation
…ll clock The only real-time pacing in the router is a handful of wall-clock tickers (rxFeeder block ticker, composite recorder ticker, txWatchdog tick + silence window); everything else is sample-count-driven. Dividing those intervals by a new top-level time_scale knob (config, or -time-scale on sim-router which overrides it) runs the whole simulation N x faster — a 60 s protocol exchange completes in 60/N s. The txSilenceWindow must scale too: silence detection has to track the (faster) audio rate or tx_end events fire mid-transmission. Documented caveat (README + config comment + startup warning): the TNC children's own wall-clock behaviours (CSMA persist/slottime, internal timeouts) do NOT scale, so time_scale > 1 is an accelerated-testing mode, not a calibrated CSMA simulation, and KISS hosts must scale their own T1/T2 to match. Motivated by packet.net's Packet.LinkBench rung-2 campaign (long soak exchanges dominated by wall-clock waiting). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ead of digital silence A collision inside the capture margin used to produce pure digital silence — unrealistically clean. A real FM discriminator outputs loud garble (the heterodyne beat between two comparable carriers plus wideband noise) at an amplitude comparable to the signals themselves, and that garble is exactly what hammers a receiving modem's DCD and false-sync paths during real collisions. The already-config-accepted "noise" mode now renders collisions as gaussian noise whose RMS matches the strongest signal's post-loss level (hot collision = loud garble, weak distant one = quiet garble), routed through the existing AddNoise machinery so RNG locking and clamping stay in one place. "silence" remains the default for backwards compatibility; "sum" stays a stub. From packet.net's Packet.LinkBench rung-2 campaign (collision-recovery results were implausibly good against the silence model). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pening delay Real FM receivers mute audio until squelch opens after a carrier appears (tens of ms, plus discriminator settle) — the on-air reason KISS TXDELAY exists. net-sim used to deliver a transmission's first samples instantly, making tiny TXDELAY values look fine when they wouldn't be on air. New optional per-link squelch_open_ms (0..500, default 0 = previous behaviour, strictly validated): the linkQueue — the per-directed-link seam squelch belongs to — delivers the first squelch_open_ms worth of blocks of each transmission as silence instead of audio. Silence, not nothing: the carrier stays visible to the mixer for capture/collision decisions; only the receiver's audio is muted while the squelch opens. Transmission boundary: push tags a block start-of-transmission after a silenceWindow-sized idle gap — the txWatchdog's exact idle→active rule (time-scaled txSilenceWindow), so inter-frame pauses inside one keyup don't re-mute and the two boundary definitions can't drift apart. The tag rides on the block, so the pop-side mute is counted in blocks (sim-time audio), independent of drain timing and of time_scale. From packet.net's Packet.LinkBench rung-2 campaign (TXDELAY sweep bottomed out unrealistically at ~0). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ren for jitter-free pacing The 10 ms pacing tickers and the TNC children's software demodulators glitch under shared host load: a lost tick gaps a receiver's audio and produces decode failures that look like RF problems. New opt-in -rt-priority flag on sim-router renices the router's own process and (via the new tnc.Child.Pid()) every spawned TNC child to -10. Plain niceness only, deliberately not SCHED_FIFO — a real-time policy risks starving the host, and niceness is enough for ticker jitter. Best-effort by design: without CAP_SYS_NICE the kernel returns EPERM, which earns one clear warning (pointing at --cap-add SYS_NICE / cap_add: [SYS_NICE], snippet added to the README docker section) and the simulation continues at normal priority. Default behaviour is untouched. Uses syscall directly (golang.org/x/sys is not a dependency of this module). From packet.net's Packet.LinkBench rung-2 campaign (bench results were noisy whenever CI neighbours were busy). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ket-net/samoyed#1) The previous pin echoed the ACKMODE 0x0C acknowledgement when the frame's audio was rendered (which runs much faster than real time), not when it had been played out — hosts saw TX-complete echoes milliseconds after queueing on an idle channel. It also kept transmitting frames a disconnected KISS TCP client had queued, so successive test sessions against the same port heard each other's leftovers. Both fixed in packet-net/samoyed#1; this pin picks up that merge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…im-router) The renice option landed on cmd/sim-router only, but the container entrypoint runs sim-web, which embeds the router as a library — so the flag was unreachable in the shipped image. Thread it through sim-web's flag set and app state into router.Options. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sim user docker --cap-add SYS_NICE only populates the container's bounding set; the non-root `sim` user's processes don't inherit it, so the renice EPERM'd even with the cap granted. setcap cap_sys_nice+ep on sim-web/sim-router gives the processes the capability directly — still gated on the container being granted the cap AND -rt-priority being passed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…C denies negative nice) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ed (wall-clock TX pacing) — measured, tracked upstream Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…everted The disconnect flush (packet-net/samoyed#1, second commit) wedged the channel under repeated mid-transfer KISS disconnects — bisected with packet.net's LinkBench stress sequence; full evidence in #19. Reverted upstream (packet-net/samoyed#3); this pin carries the validated set: ACKMODE echo at PTT release (the 7 ms-echo fix) plus the tq lost-wakeup fix (packet-net/samoyed#2). Co-Authored-By: Claude Fable 5 <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.
Four simulator improvements plus a samoyed pin bump, all driven by packet.net's
Packet.LinkBenchrung-2 campaign (sustained AX.25 bulk-transfer testing over this sim). Each lands as its own commit with tests;make test(vet + full suite) green throughout, race-detector pass clean.time_scale(config +-time-scaleflag, default 1.0) — run the whole simulation N× faster than wall clock. The sim's only real-time dependencies are the rxFeeder pacing ticker, the txWatchdog tick/silence-window, and the composite-recorder ticker (everything else is sample-count-driven), so scaling those three runs the full audio pipeline — modem included — proportionally faster. A 10-minute sustained-transfer experiment becomes ~1 minute at ×10. Documented caveat (README + startup warning): the TNC children's own wall-clock behaviours (CSMA persist/slottime) don't scale, and hosts must scale their protocol timers to match — an accelerated-testing mode, not a calibrated CSMA sim. Test proves block sequences are byte-identical across scales.collision_mode: noiseimplemented (was an accepted-but-stub mode) — a collision inside the capture margin now renders gaussian garble at an amplitude tracking the strongest signal, instead of pure digital silence. A real FM discriminator outputs loud heterodyne garble when comparable carriers beat — silence is unrealistically clean (perfectly detectable, can never false-sync a modem or hold DCD open).silencestays the default; capture/single-source behaviour byte-identical.Per-link
squelch_open_ms(default 0 = today) — mutes the first N ms of each transmission as heard by the receiving port, modelling RX squelch/carrier-detect opening — the thing KISS TXDELAY exists for, which the sim previously made invisible (instant audio delivery makes a too-short TXDELAY look fine when it wouldn't be on air). Silence is substituted rather than blocks dropped, so the mixer still sees the carrier for capture/collision decisions. Transmission boundary reuses the txWatchdog's exact idle→active rule so the two definitions can't drift.-rt-priority(opt-in) — renices the router and its TNC children to −10 so the 10 ms pacing ticker doesn't glitch under shared host load (these tests run on a box that also builds/tests the host stack). EPERM logs a one-liner pointing atcap_add: [SYS_NICE]and continues. Deliberately niceness, not SCHED_FIFO (won't starve the host). README compose snippet included.samoyed pin → packet-net/samoyed#1 — the previous build echoed the ACKMODE TX-complete acknowledgement at audio render time (~25× faster than real time; hosts measured 7 ms echoes for 350 ms-minimum frames), and kept transmitting frames a disconnected KISS client had queued (successive test sessions heard each other's leftovers). Both fixed upstream in the fork; this picks up the merge.
🤖 Generated with Claude Code