Lora settings expansion and validation logic improvement#9878
Lora settings expansion and validation logic improvement#9878thebentern merged 44 commits intomeshtastic:developfrom
Conversation
… clarity and consistency
…broken at this point
…ementing validateRegionConfig in RadioInterface
… method for invalid settings
…or reject invalid settings
There was a problem hiding this comment.
Pull request overview
This PR expands LoRa region/preset metadata and introduces new validation/clamping helpers intended to improve modem configuration safety and region-specific preset enforcement.
Changes:
- Extends
RegionInfowith additional regulatory/preset metadata and adds per-region “available presets” lists. - Adds
RadioInterface::{validateConfigRegion, validateConfigLora, clampConfigLora}and updates Admin/boot flows to use them. - Updates unit tests to target the new validation path (though the referenced API currently doesn’t exist).
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| test/test_radio/test_main.cpp | Renames tests to use a new validation helper (currently missing). |
| src/modules/AdminModule.h | Changes handleSetConfig signature to pass fromOthers. |
| src/modules/AdminModule.cpp | Applies new LoRa region/config validation and different behavior for remote vs local config updates. |
| src/mesh/RadioInterface.h | Deprecates old preset bootstrap helper and declares new validation/clamp APIs. |
| src/mesh/RadioInterface.cpp | Adds region preset lists, expands region definitions, implements validation/clamping, and modifies channel/frequency selection. |
| src/mesh/NodeDB.cpp | Switches boot-time preset field coercion from the old helper to clampConfigLora(). |
| src/mesh/MeshRadio.h | Expands RegionInfo struct and exposes preset lists. |
…tion and clamping methods; add tests for region and preset handling
…utstanding comments.
…ult frequency slot check in applyModemConfig
|
I need to think of some better tests, but I'm tired. Maybe tomorrow... |
…ault preset retrieval
…and channel calculations
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.
Comments suppressed due to low confidence (1)
src/modules/AdminModule.h:1
- This changes both the signature and the access level of
handleSetConfig(public → protected, plus a newfromOthersparameter). If anything outsideAdminModulepreviously invokedhandleSetConfig(const meshtastic_Config&), this becomes a breaking change. A safer approach is to keep a public overload with the old signature (forwarding to the new method with an explicit default likefromOthers=false), or keep the method public if it’s part of the module’s expected integration surface.
#pragma once
You can also share your feedback on Copilot code review. Take the survey.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 10 comments.
Comments suppressed due to low confidence (1)
src/graphics/draw/MenuHandler.cpp:294
- After the early-return
if (!myRegion) return;, the laterif (myRegion) { ... } else { ... }block (and duplicated warning) is unreachable on theelsebranch. Removing the redundant conditional will simplify control flow and reduce duplicated logging.
LOG_WARN("Region not set, cannot calculate number of channels");
return;
}
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
| uint32_t channel_num; | ||
| float freq; |
There was a problem hiding this comment.
channel_num is declared as uint32_t but is assigned -1 when override_frequency is set, which wraps to 0xFFFFFFFF. This later affects saveChannelNum(channel_num) and logging (channel_num + 1) and can result in invalid persisted state. Use a signed type for channel_num (e.g., int32_t) or represent “no channel” explicitly (e.g., a separate boolean/optional) and avoid writing a wrapped sentinel into storage.
| uint32_t channel_num; | |
| float freq; | |
| int32_t channel_num = -1; | |
| float freq = 0.0f; |
| // override if we have a verbatim frequency | ||
| if (loraConfig.override_frequency) { | ||
| freq = loraConfig.override_frequency; | ||
| channel_num = -1; | ||
| uses_default_frequency_slot = false; |
There was a problem hiding this comment.
channel_num is declared as uint32_t but is assigned -1 when override_frequency is set, which wraps to 0xFFFFFFFF. This later affects saveChannelNum(channel_num) and logging (channel_num + 1) and can result in invalid persisted state. Use a signed type for channel_num (e.g., int32_t) or represent “no channel” explicitly (e.g., a separate boolean/optional) and avoid writing a wrapped sentinel into storage.
| float freqSlotWidth = newRegion->profile->spacing + (newRegion->profile->padding * 2) + (check_bw / 1000); // in MHz | ||
| uint32_t numFreqSlots = round((newRegion->freqEnd - newRegion->freqStart + newRegion->profile->spacing) / freqSlotWidth); |
There was a problem hiding this comment.
freqSlotWidth can become 0 (e.g., spacing/padding are 0 and check_bw resolves to 0), which causes division by zero when computing numFreqSlots. If numFreqSlots ends up as 0, the subsequent modulo operations (% numFreqSlots) are also undefined behavior. Add an explicit guard: if freqSlotWidth <= 0 or computed numFreqSlots == 0, then return false (validation) or clamp to a safe default (clamp mode) before any division/modulo.
| const char *channelName = channels.getName(channels.getPrimaryIndex()); | ||
| const char *presetNameDisplay = | ||
| DisplayFormatters::getModemPresetDisplayName(loraConfig.modem_preset, false, loraConfig.use_preset); | ||
| uint32_t channelNameHashSlot = hash(channelName) % numFreqSlots; | ||
| uint32_t presetNameHashSlot = hash(presetNameDisplay) % numFreqSlots; |
There was a problem hiding this comment.
freqSlotWidth can become 0 (e.g., spacing/padding are 0 and check_bw resolves to 0), which causes division by zero when computing numFreqSlots. If numFreqSlots ends up as 0, the subsequent modulo operations (% numFreqSlots) are also undefined behavior. Add an explicit guard: if freqSlotWidth <= 0 or computed numFreqSlots == 0, then return false (validation) or clamp to a safe default (clamp mode) before any division/modulo.
| #include <assert.h> | ||
| #include <pb_decode.h> | ||
| #include <pb_encode.h> | ||
| #include <string.h> |
There was a problem hiding this comment.
round() is used but there is no clear include for it in this translation unit (typically <cmath>). Relying on transitive includes is fragile and can break builds on different toolchains/settings. Include <cmath> and prefer the float-specific variant (roundf) or a cast-safe integer conversion (lroundf) consistent with the desired rounding semantics.
| #include <string.h> | |
| #include <string.h> | |
| #include <cmath> |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@thebentern Kind of going in circles here... I think its time. |
|
Woohoo! |
@thebentern I think this has reached MVP stage. Please proceed to tear it to shreds.
🤝 Attestations