feat(config): filter LoRa modem presets by region legality (#3924 Part 1)#3936
Conversation
Part 1 of #3924 (Part 2, the amateur-radio advisory warning, merged in #3930). Mirrors the official Meshtastic mobile apps by filtering the modem- preset picker to presets that are legal for the selected LoRa region. There is NO protobuf wire field carrying a region -> preset legality map; it never landed at any 2.8.x tag (verified against meshtastic/protobufs config.proto — Config.LoRaConfig only exposes region + modem_preset). The apps replicate a firmware-side computation locally: a preset is legal for a region iff (freqEnd - freqStart) >= presetBandwidthKHz/1000. This replicates that table + math as a local constant (no protobuf submodule bump needed). - constants.ts: add REGION_FREQ_INFO (from firmware RadioInterface.cpp regions[]), PRESET_BANDWIDTH_KHZ (from MeshRadio.h modemPresetToParams), and getPresetBandwidthKHz / isPresetLegalForRegion / getLegalPresetOptions helpers. Correct the stale comment that claimed a 2.8 wire map exists. - LoRaConfigSection.tsx + RadioConfigurationSection.tsx: filter the preset picker via getLegalPresetOptions(region, modemPreset), always retaining the current selection, plus a note when presets are hidden. - en.json: add lora_config.preset_filtered_note. - constants.test.ts: cover bandwidth lookup, per-region legality (EU_868 rejects the 500 kHz presets, RU allows them, wide bands allow all, unknown regions permissive), and option filtering. In practice only EU_868 (0.25 MHz span) filters anything today, rejecting the two 500 kHz presets (SHORT_TURBO, LONG_TURBO). Complements the #3930 warning. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AphLfgUJWaFNAgU5UXdJ4n
|
Claude finished @Yeraze's task —— View job PR Review: feat(config): filter LoRa modem presets by region legalityTasks:
Overall AssessmentThis is a well-researched and carefully implemented PR. The feasibility investigation is thorough, the firmware-mirroring math is correct, and the implementation is conservative where it needs to be (permissive fallback for unknown regions). A few observations below. Code Quality
The
// RadioConfigurationSection.tsx line 101-102
const legalPresetOptions = getLegalPresetOptions(region, modemPreset);
const hasFilteredPresets = legalPresetOptions.length < MODEM_PRESET_OPTIONS.length;
Several labels in this component are still hardcoded English strings that pre-date this PR (e.g. "Coding Rate", "Frequency Offset", "Override Frequency (Hz)", "Region", "TX Power", etc.). This is pre-existing debt, not introduced here, but worth noting. Potential Bugs / Edge Cases
The legality check on line 279: return spanMHz >= bandwidthMHz;
The comment on line 205 says Test CoverageCoverage is solid for the new helpers:
Gap: no test for the Gap: no render/integration test for the UI components. The PerformanceNo concerns. The filter runs over 13 static options, and the expensive path ( SecurityNo concerns. This is entirely client-side UI filtering with no auth surface. The note in the PR description correctly explains that the firmware will silently rewrite an illegal preset anyway — so even if the filter were bypassed, no unsafe config would persist. Minor Nits
SummaryThe core logic is correct, well-tested, and correctly mirrors firmware behavior. The two actionable items for this PR are:
The |
Address review on #3936: use `spanMHz >= bandwidthMHz - 1e-9` so IEEE-754 subtraction (e.g. 869.2 - 868.7) can't mis-exclude a preset whose bandwidth lands exactly on a region's span. No behavior change for current region values. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AphLfgUJWaFNAgU5UXdJ4n
|
Addressed in 7eca6bd: added an epsilon guard ( |
|
Claude finished @Yeraze's task —— View job PR Review: feat(config): filter LoRa modem presets by region legalityTasks:
Updated Review (post
|
Summary
Part 1 of #3924 — per-region modem-preset filtering, the follow-up to the amateur-radio advisory warning (Part 2) merged in #3930. Mirrors the official Meshtastic mobile apps / firmware 2.8 by filtering the LoRa modem-preset picker to only the presets that are legal for the currently selected region. The #3930 warning is untouched; filtering complements it.
Feasibility finding: the "2.8 wire map" does not exist
The original blocker note in
constants.tsclaimed firmware 2.8 introduces a "region → preset legality map (sent once by the device)" that MeshMonitor couldn't consume because the vendored protobufs (v2.7.26) predate it. That is not accurate.meshtastic/protobufsconfig.proto:Config.LoRaConfigonly exposesregion(field 7) andmodem_preset(field 2).(freqEnd − freqStart) ≥ presetBandwidthKHz / 1000(spacing is 0 for every current region, so it reduces tospan ≥ bandwidth). If the check fails, firmware silently rewrites the preset toLONG_FAST.src/mesh/RadioInterface.cppregions[]table (freqStart/freqEnd),src/mesh/MeshRadio.hmodemPresetToParams()(preset→bandwidth), and theapplyModemConfig/bootstrapLoRaConfigFromPresetfit-check.So no protobuf submodule bump is possible or needed. This PR replicates the firmware table + math as a local constant (path "b").
Changes
constants.ts: addREGION_FREQ_INFO(region→band, transcribed from firmwareregions[]),PRESET_BANDWIDTH_KHZ(frommodemPresetToParams), and helpersgetPresetBandwidthKHz,isPresetLegalForRegion,getLegalPresetOptions. Correct the stale "2.8 wire map" comment.LoRaConfigSection.tsx(device config) andRadioConfigurationSection.tsx(remote admin): filter the preset picker viagetLegalPresetOptions(region, modemPreset)— same two components where feat(lora): warn when selecting a licensed amateur-radio region (#3924) #3930 put the warning. The currently-selected preset is always retained so the picker never renders blank for a device already on an out-of-band preset. A small note appears when presets are hidden.en.json: addlora_config.preset_filtered_note.constants.test.ts: cover bandwidth lookup (incl. wide-LoRa and unimplemented-preset fallback), per-region legality, and option filtering.Practical effect
Only EU_868 (0.25 MHz span) filters anything today — it rejects the two 500 kHz presets (
SHORT_TURBO,LONG_TURBO). RU (exactly 0.5 MHz) keeps them. Regions whose exact bounds aren't in the firmware master table (ITU amateur bands, newer EU narrow variants) are treated permissively (all presets legal), since their spans comfortably exceed every preset bandwidth.Testing
success: true, 7995 passed, 0 failed (2639 files).tsc -p tsconfig.server.json --noEmit: no new errors (5 pre-existingTelemetryChart.tsxerrors on main).vite build: success.🤖 Generated with Claude Code
https://claude.ai/code/session_01AphLfgUJWaFNAgU5UXdJ4n