Default transmit duty cycle to 10% instead of 50% - #3351
Conversation
The shipped default was airtime factor 1.0, which CommonCLI reports as a 50% duty cycle. The default frequency is 869.618 MHz (platformio.ini), which sits in the 869.4 to 869.65 MHz sub-band. ETSI EN 300 220-2 V3.2.1 caps that sub-band at 10%, so the out-of-the-box configuration exceeds the limit that applies to its own default frequency. Introduce MAX_DUTY_CYCLE (percent, default 10) in Dispatcher.h and derive DEFAULT_AIRTIME_FACTOR from it using the same conversion as 'set dutycycle'. Apply it to the five node types that hardcoded 1.0 and to the Dispatcher base class fallback. Regions without a duty cycle limit can restore the old behaviour with -D MAX_DUTY_CYCLE=100. Only the default changes. The stored preference is still airtime_factor, and 'set dutycycle' and 'set af' keep working as before. Refs meshcore-dev#2047 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EaVu6vC5LziX1zYZeiu49X
|
It should probably be linked with the preset as quite a lot of regions do not have duty cycle limits, and a 10% default only applies to regions that do |
|
You are right, and #3351 cannot be fixed in place. There is no region concept in the firmware: Replaced by #3361, which derives the limit from the sub-band of the configured frequency instead. Outside 863 to 870 MHz it returns no limit, so the channel plans without a duty cycle regulation keep the behaviour they have now, and it re-derives on Closing this one in favour of that. |
Refs #2047.
Problem
The shipped default is
airtime_factor = 1.0, whichget dutycyclereports as50.0%.The default frequency is
-D LORA_FREQ=869.618(platformio.ini:29). That falls in the 869.4 to 869.65 MHz sub-band, which ETSI EN 300 220-2 V3.2.1 caps at 10%. Per-sub-band limits for EU868 are 0.1% (863-865), 1% (865-868), 1% (868-868.6), 0.1% (868.7-869.2), 10% (869.4-869.65) and 1% (869.7-870); see https://www.actility.com/understanding-duty-cycle-lorawan/ for the table and the standard reference. The same 10% figure applies in the UK.So the default configuration allows five times the duty cycle that applies to its own default frequency. Every EU and UK operator has to change this by hand on each node, and the value is not discoverable unless you already know that
afmeans duty cycle.Change
src/Dispatcher.h: newMAX_DUTY_CYCLE(percent, default 10) andDEFAULT_AIRTIME_FACTOR, derived with the same conversionset dutycyclealready uses (CommonCLI.cpp:455)._prefs.airtime_factor = 1.0now useDEFAULT_AIRTIME_FACTOR: companion_radio, simple_repeater, simple_room_server, simple_secure_chat, simple_sensor.Dispatcher::getAirtimeBudgetFactor()returned a hardcoded1.0as the base class fallback; it now uses the same constant.docs/cli_commands.mddocumented 50% as the default.Builds for regions without a duty cycle limit can keep the old behaviour with
-D MAX_DUTY_CYCLE=100.What this does not change
Only the default for a node with no stored preferences. The persisted field is still
airtime_factor(keyafin the config serializer, offset 0 in the legacy prefs file), andset dutycycleandset afbehave exactly as before. Existing nodes keep whatever they have configured.Scope left out deliberately
Two things I noticed while reading the duty cycle code and did not touch here, to keep this to one change:
Dispatcher::updateTxBudget()starts full and its ceiling is a full window's allowance. In the worst case a node can spend the full bucket plus a window of refill inside one observation window, so roughly twice the nominal duty cycle in that window. ETSI measures over a one hour observation period, so this can overshoot even when configured correctly.constrain(airtime_factor, 0, 9.0f)sanitisation only exists inloadPrefsInt(), the legacy/com_prefsloader. The/prefs.jsonpath vialoadSerial()does not clamp, soset dutycycle 1(af 99) persists there but is reset to 10% for a node migrating from the legacy format.Happy to open separate issues for either if useful.
Testing
Not covered by a unit test. The native test environment (
platformio.ini,[env:native]) does not compileDispatcher.cppand uses a mockMesh.h, so testing this would mean adding a mock radio to that environment, which is a larger change than the default itself. Verified instead thatDEFAULT_AIRTIME_FACTORresolves in all five translation units: each includes<Mesh.h>, which includes<Dispatcher.h>atsrc/Mesh.h:3. The CI build matrix covers the compile.