Skip to content

fix(dc.gate): measure true TX duty on dmc-dev, default threshold 35 - #25

Merged
Elektr0Vodka merged 3 commits into
dmc-devfrom
fix/dmc-dev-true-tx-duty
Oct 19, 2026
Merged

Elektr0Vodka merged 3 commits into
dmc-devfrom
fix/dmc-dev-true-tx-duty

Conversation

@Elektr0Vodka

Copy link
Copy Markdown

Why

dc.gate.thresh meant different things on the two branches:

  • dmc-dev: % of the duty-cycle budget used, which is what the docs said too.
  • observer: since 76072aa (08-27), the true TX duty cycle: the share of wall-clock time spent transmitting over 60 s. Its docs, merged from dmc-dev, still described the budget.

We're standardising on true TX duty on both branches.

Changes

  1. fix(dispatcher): cherry-pick of 76072aa (-x). getTxDutyCyclePercent() reads a rolling 60 s TxDutyWindow (6 × 10 s slots). The tx_budget_ms pacing is unchanged. The only conflict was the observer-only watchdog fields in the Dispatcher constructor.
  2. feat(dc.gate): default threshold 70 → 35, hysteresis stays 10, so regions re-open below 25%.
    • Why 35: on the EU 10% sub-band a repeater can sustain about 10%, so normal traffic stays well clear. At 35%, the 360 s/h airtime reserve lasts about 24 minutes, so gating steps in before pacing has to.
    • The prefs.json key changes from dc_gate_thr to dc_gate_duty. Values saved under the old key were budget percentages, so they are deliberately left behind and upgraded nodes start at 35.
    • Docs rewritten: cli_commands.md, dmc-docs nl/en/de, DMC_DEVSYNC.md. They now cover what is measured, how it differs from set dutycycle, suggested thresholds per sub-band, and an upgrade note.
  3. ci: run-unit-tests.yml now also runs native_region_gating. Until now the region-gating tests (including TxDutyWindow) never ran in CI.

Test

  • pio test -e native -e native_region_gating: 272/272 pass, including the 6 TxDutyWindow cases.
  • pio run -e Heltec_v3_repeater builds.

Before merging

  • Operators who enabled dc.gate will see the threshold reset to 35 after upgrading; this needs a line in the release notes.
  • The toolbox help strings (descriptions.nl/de.ts, firmwareRegistry.ts) still say "budget, default 70" and need a matching change in the toolbox repo.
  • For the observer: 76072aa is already there, so this mainly brings the default, the key and the docs.

🤖 Generated with Claude Code

getTxDutyCyclePercent() reported how depleted the tx_budget_ms token
bucket was ("% of the airtime budget consumed"). On DMC repeaters that
budget is loose (airtime_factor 1.0 = 50% allowance over a 1h window),
so real traffic never dents it and the reading sat at ~0: the observer
duty-cycle chart was flat and the region gate never fired.

Report the real duty cycle instead: TX airtime / wall-clock over a
rolling 60s window (TxDutyWindow, 6x10s slots, idle-safe decay). The
tx_budget_ms compliance limiter and all TX pacing are untouched; only
the metric (and thus the gate input and observer telemetry) changes.

dc_gate_threshold now means a true TX duty %, not "% of allowance".
Default left at 70; operators with gating enabled should re-set it to a
realistic duty (~35) or it will effectively never fire.

Tests: 6 new TxDutyWindow cases plus the existing 20 gating cases green;
heltec_v4_repeater_observer_mqtt builds and links.

(cherry picked from commit 76072aa)
dc.gate.thresh now compares against the true TX duty cycle over 60 s
(previous commit), not the share of the duty-cycle budget used. The old
default of 70 belonged to the budget meaning and would rarely trip:
on the EU 10% sub-band a repeater can only sustain about 10%.

New default 35 (hysteresis 10, so regions re-open below 25%). That is
well above what normal traffic sustains, while a repeater transmitting
35% of the time empties its 360 s/h airtime reserve in about 24 minutes,
so gating steps in before pacing has to.

The threshold is now stored as "dc_gate_duty" instead of "dc_gate_thr".
Values saved under the old key were budget percentages; they are left
behind so upgraded nodes start from the new default rather than a
budget value that means something else now.

Docs (cli_commands.md, dmc-docs nl/en/de, DMC_DEVSYNC.md) describe the
true-duty meaning, suggested thresholds per sub-band, and the upgrade.
test_region_gating, including the TxDutyWindow cases, has its own env
because it needs RegionMap and TransportKeyStore. The unit-test workflow
only ran native and native_kiss_modem, so these tests never ran in CI.
@Elektr0Vodka
Elektr0Vodka merged commit 94038a3 into dmc-dev Oct 19, 2026
1 check passed
Elektr0Vodka added a commit that referenced this pull request Oct 19, 2026
…filter limiters, true TX duty, SenseCAP GPS) into dmc-dev-packetlog
Elektr0Vodka added a commit that referenced this pull request Oct 19, 2026
…filter limiters, true TX duty, SenseCAP GPS) into dmc-observer-dev

Conflict resolutions:
- Dispatcher.h, build.sh: kept observer (radio watchdog; observer build.sh already
  emits partsig for every ESP32 env).
- CommonCLI.cpp: kept observer's /mqtt.json-gated legacy migration, then the
  GPS_DEFAULT_ON apply; dropped the obsolete "old default 70" comment now that
  the prefs key is dc_gate_duty.
- variants/sensecap_solar: took dmc-dev (ENV_INCLUDE_GPS + GPS_DEFAULT_ON). The
  observer's -UENV_INCLUDE_GPS came from the old bulk revert 22eb9b8, not a
  deliberate choice.
- platformio.ini native env, docs/cli_commands.md: took dmc-dev additions.
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.

1 participant