[Protocol delta] No rolling-window duty-cycle enforcement — still using deprecated per-TX silent-period throttling #9

Closed
opened 2026-10-04 23:05:58 +02:00 by adam · 3 comments
Owner

Same interop-tracking effort as #19 — comparing this fork (built on
MeshCore v1.11 core) against current mainline meshcore-dev/MeshCore.

Delta found

Mainline replaced its old per-transmission silent-period throttling with
proper rolling-window duty-cycle enforcement in PR #1297 ("Implement
token bucket duty cycle enforcement", resolving
meshcore-dev/MeshCore#817): Dispatcher now tracks a tx_budget_ms
against a duty_cycle_window_ms (default 1 hour, src/Dispatcher.h:126-
128,152) that refills proportionally to elapsed idle time and is spent
on each transmission (src/Dispatcher.cpp:19-50, :93-102, :280-283).

This fork's Dispatcher.cpp still uses the old approach that PR replaced:
a single silent period after each transmission, proportional to that
one transmission's airtime (next_tx_time = futureMillis(t * getAirtimeBudgetFactor()), Dispatcher.cpp:86). No
token bucket, no rolling window — confirmed by grep, no
duty/token/bucket/budget symbols anywhere in this fork's
Dispatcher.

Why it's worth tracking

The old approach was specifically replaced because it doesn't correctly
enforce a true regulatory duty cycle over time — see
meshcore-dev/MeshCore#817 for the motivating writeup. A per-TX silent
period only looks at the immediately preceding transmission in
isolation; it doesn't prevent many small transmissions in sequence from
cumulatively exceeding the actual regulatory limit (e.g. EU 868MHz 10%,
or the stricter 1% band) measured over a real rolling window (an hour,
in mainline's implementation).

Two concrete consequences if this fork is run somewhere with legal
duty-cycle limits:

  • Possible non-compliance with regional spectrum regulations under
    bursty traffic, even with airtime_factor/dutycycle configured
    "correctly" under the old math.
  • Airtime fairness: a node using the old throttling could end up
    transmitting more than neighboring mainline-firmware nodes that are
    now properly rolling-window-limited — especially relevant for
    repeater roles carrying sustained relay traffic.

What this is not

Not a wire-format or parsing issue — purely local transmit-scheduling
behavior. Doesn't cause any corruption or interop break in message
delivery between devices. The same airtime_factor preference
(CommonCLI.h:22) already present in this fork is the right input value
to reuse if a token-bucket implementation gets ported — mainline's
getAirtimeBudgetFactor() virtual still feeds the same preference into
the new mechanism (src/Dispatcher.cpp:26,42,100,281), so this would be
an internal Dispatcher change, not a new CLI surface.

Scope

Affects any role that transmits on a schedule/relay basis (repeater,
room server, companion radio with flood relay) under sustained traffic.
Low impact for occasional, lightly-loaded personal companion use. Not
urgent, filing for tracking alongside #19.

Same interop-tracking effort as #19 — comparing this fork (built on MeshCore v1.11 core) against current mainline meshcore-dev/MeshCore. ## Delta found Mainline replaced its old per-transmission silent-period throttling with proper rolling-window duty-cycle enforcement in PR #1297 ("Implement token bucket duty cycle enforcement", resolving meshcore-dev/MeshCore#817): Dispatcher now tracks a `tx_budget_ms` against a `duty_cycle_window_ms` (default 1 hour, src/Dispatcher.h:126- 128,152) that refills proportionally to elapsed idle time and is spent on each transmission (src/Dispatcher.cpp:19-50, :93-102, :280-283). This fork's Dispatcher.cpp still uses the old approach that PR replaced: a single silent period after each transmission, proportional to that one transmission's airtime (`next_tx_time = futureMillis(t * getAirtimeBudgetFactor())`, Dispatcher.cpp:86). No token bucket, no rolling window — confirmed by grep, no `duty`/`token`/`bucket`/`budget` symbols anywhere in this fork's Dispatcher. ## Why it's worth tracking The old approach was specifically replaced because it doesn't correctly enforce a true regulatory duty cycle over time — see meshcore-dev/MeshCore#817 for the motivating writeup. A per-TX silent period only looks at the immediately preceding transmission in isolation; it doesn't prevent many small transmissions in sequence from cumulatively exceeding the actual regulatory limit (e.g. EU 868MHz 10%, or the stricter 1% band) measured over a real rolling window (an hour, in mainline's implementation). Two concrete consequences if this fork is run somewhere with legal duty-cycle limits: - Possible non-compliance with regional spectrum regulations under bursty traffic, even with `airtime_factor`/`dutycycle` configured "correctly" under the old math. - Airtime fairness: a node using the old throttling could end up transmitting more than neighboring mainline-firmware nodes that are now properly rolling-window-limited — especially relevant for repeater roles carrying sustained relay traffic. ## What this is not Not a wire-format or parsing issue — purely local transmit-scheduling behavior. Doesn't cause any corruption or interop break in message delivery between devices. The same `airtime_factor` preference (`CommonCLI.h:22`) already present in this fork is the right input value to reuse if a token-bucket implementation gets ported — mainline's `getAirtimeBudgetFactor()` virtual still feeds the same preference into the new mechanism (src/Dispatcher.cpp:26,42,100,281), so this would be an internal Dispatcher change, not a new CLI surface. ## Scope Affects any role that transmits on a schedule/relay basis (repeater, room server, companion radio with flood relay) under sustained traffic. Low impact for occasional, lightly-loaded personal companion use. Not urgent, filing for tracking alongside #19. <!-- github-sync-id:4680085009 -->
Author
Owner

These are some high-quality submissions, thank you!

Just to let you know I've seen both issue 19 and 20 and am looking at incorporating (probably starting with 20 first).

Might be a while before that gets done as I'm working full-time in an entirely unrelated role, but will tag you once that's completed.

These are some high-quality submissions, thank you! Just to let you know I've seen both issue 19 and 20 and am looking at incorporating (probably starting with 20 first). Might be a while before that gets done as I'm working full-time in an entirely unrelated role, but will tag you once that's completed. <!-- github-comment-id:4747046022 -->
Author
Owner

Just making a note that this has been incorporated into v1.12.4, but as I haven't had as much time as I'd like for thorough testing lately I'm leaving this one open for me to review again later. Will likely close it once I've got #19 rolled in as well.

Just making a note that this has been incorporated into v1.12.4, but as I haven't had as much time as I'd like for thorough testing lately I'm leaving this one open for me to review again later. Will likely close it once I've got #19 rolled in as well. <!-- github-comment-id:4963033980 -->
adam closed this issue 2026-10-27 09:37:25 +01:00
Author
Owner

Closing as 19 has been added to https://github.com/pelgraine/Meck/releases/tag/v1.15-max with Pro soon to follow.

Closing as 19 has been added to https://github.com/pelgraine/Meck/releases/tag/v1.15-max with Pro soon to follow. <!-- github-comment-id:6092907808 -->
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: adam/Meck#9