Skip to content

feat(app): give auto-discover a duty floor from the airtime the last cycle spent - #581

Merged
efiten merged 2 commits into
efiten:masterfrom
khagele:feat/381-autoping-floor
Sep 8, 2026
Merged

feat(app): give auto-discover a duty floor from the airtime the last cycle spent#581
efiten merged 2 commits into
efiten:masterfrom
khagele:feat/381-autoping-floor

Conversation

@khagele

@khagele khagele commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Closes #381

The problem

shouldAutoFire fires on the 10 s interval or on 50 m of movement, whichever comes first, with no lower bound on the period: at 90 km/h the distance gate fires every 2 s. The module's budget note, "10 s alone is ~0.46% duty cycle", describes one 46 ms frame, where a cycle is the Discover plus a trace-ping per target, five frames for a standing sweep (#479). With the firmware's own numbers a standing sweep at SF8 was 12.9% of airtime, and 21.6% at 90 km/h. The refinement, with sources, is in the issue thread.

My changes in this PR

  • app/src/airtime.js: RadioLib's getTimeOnAir, the function the firmware's getEstAirtimeFor calls, ported in the same integer arithmetic. Firmware facts in the header, each with its file: default preset 869.618 MHz, BW 62.5, SF8, CR 4/5; 32-symbol preamble at SF ≤ 8 and 16 above; Discover 8 bytes and trace-ping 12 on air; 10% duty cycle for the 869.400 to 869.650 MHz sub-band (ERC 70-03). The SF comes from PACKET_SELF_INFO; the bandwidth and the budget are stated assumptions, not settings, and an unknown SF reads as 8.
  • The floor (shouldAutoFire, minPeriodMs parameter): the airtime the previous cycle spent divided by the budget, and no gate fires inside it. The interval stays as the lower bound, so a hunt inside the budget is unchanged.
  • Frames are counted as they leave (app/src/app.js, state.autoPing.sentBytes): the Discover when the transport took it, each trace-ping when it went out. A ping BLE dropped spent no airtime and buys no silence. feat(app): add Share my node name, off by default, sending a zero-hop advert each cycle a companion is the target #577 and feat(app): ask a selected companion for its telemetry each cycle, zero-hop, and keep what it answers per node #578 join the count by pushing their byte size; the gate does not change.
  • The Status tab shows the cadence: "Auto-discover: On, every 13 s", from the last cycle. A suppressed cycle is readable, not a silent cap (AGENTS.md §5.4).
  • Header comment of autoping.js now matches what the code guarantees. Decision log docs/2026-09-05-autoping-duty-floor.md. Changelog entry in both copies.
preset Discover trace-ping standing sweep of 4 floor
SF8 / 62.5 242 ms 263 ms 1294 ms 12.9 s
SF7 / 62.5 121 ms 131 ms 645 ms 6.5 s

Verification

Suite Result
app vitest 907 passed, build and eslint clean
web vitest 509 passed
web e2e whatsnew.spec.js, --fail-on-flaky-tests 13 passed (the changelog copies)

New tests, each mutation-checked: the RadioLib worked example for a 12-byte frame at SF8 (263 ms, red with the preamble set to 16), the per-SF preamble, the SF fallback (red without it), the floor binding standing still for a sweep of four at SF8 and not for one target at SF7 (red with the budget at 100%), and in autoping.test.js the three cases the issue asked for: floor binds at speed with no target, floor binds on target count standing still, interval gate untouched when the floor is shorter (all red with the floor line removed), plus the first cycle never waiting and the Status text.

Browser, transport stubbed and SF set by hand: Status reads "Off"; switching on fires one Discover (8 bytes, floor 2.4 s at SF8) and reads "On, every 10 s"; at SF12 the same Discover makes a 25.1 s floor, a tick at 11 s does not fire, a tick at 26 s does, and Status reads "On, every 26 s".

Not in this PR

🤖 Generated with Claude Code

khagele and others added 2 commits September 5, 2026 11:17
…cycle spent

The gate fired on the interval or on 50 m of movement with no lower bound,
and its budget note described one 46 ms frame where a cycle is five at a
standing sweep. With the firmware's default preset (SF8, BW 62.5, 32-symbol
preamble) a standing sweep was 12.9% of airtime and 21.6% at 90 km/h.

A cycle is now followed by the airtime it actually spent divided by the 10%
budget of the default sub-band, per-SF from PACKET_SELF_INFO, and no gate
fires inside it. Frames are counted as they leave. The Status tab shows the
cadence, so a suppressed cycle is readable.

Closes efiten#381

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The conflict was the changelog arrays only: master gained efiten#574's and
efiten#580's entries while this branch added its own. Both kept, newest first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEhmD6grc51qBBDgTq89sQ
@efiten
efiten merged commit 6450c3c into efiten:master Sep 8, 2026
14 of 16 checks passed
@github-actions github-actions Bot mentioned this pull request Sep 8, 2026
This was referenced Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

app: autoping has no floor on how often a cycle may fire, and is over its own duty budget standing still

2 participants