Skip to content

Add MEDIUM_TURBO modem preset - #10988

Merged
thebentern merged 3 commits into
developfrom
add-medium-turbo-preset
Jul 11, 2026
Merged

Add MEDIUM_TURBO modem preset#10988
thebentern merged 3 commits into
developfrom
add-medium-turbo-preset

Conversation

@thebentern

@thebentern thebentern commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Add the MEDIUM_TURBO modem preset

MEDIUM_TURBO (enum value 16) already existed in the protobufs but was never wired into firmware — selecting it silently fell through to the LONG_FAST default and rendered an Invalid display name (which also feeds channel-frequency-slot hashing). This PR wires it up.

Per the proto definition, it "performs similarly to MEDIUM_FAST, but with 500kHz bandwidth. It is not legal to use in all regions due to this wider bandwidth."

Spec

500 kHz bandwidth (1625 kHz on 2.4 GHz wideLora regions), SF9, CR 4/5 — i.e. MEDIUM_FAST's spreading factor and coding rate at double the bandwidth.

Where it lands among the sub-GHz presets by on-air throughput:

Preset BW kHz SF CR ~Data rate
Short Turbo 500 7 4/5 21.9 kbps
Short Fast 250 7 4/5 10.9 kbps
★ Medium Turbo (new) 500 9 4/5 7.0 kbps
Short Slow 250 8 4/5 6.25 kbps
Medium Fast 250 9 4/5 3.5 kbps
Medium Slow 250 10 4/5 2.0 kbps
Long Turbo 500 11 4/8 1.3 kbps
Long Fast (default) 250 11 4/5 1.1 kbps

3rd-fastest preset — ~2× the throughput of Medium Fast (same SF9, double bandwidth), true medium range.

500 kHz "wide" handling

It's added to PRESETS_STD (US and other wide-capable regions) but deliberately not to PRESETS_EU_868 — EU868's band is only 250 kHz wide. The two existing backstops enforce this automatically: preset-list membership (supportsPreset) and the region-span physical-fit check. Verified: it resolves to 500/SF9/CR4:5 in US and is rejected + clamped to LONG_FAST in EU_868 and UNSET.

Changes

  • ParamsmodemPresetToParams() in MeshRadio.h
  • Display nameMediumTurbo / MedT in DisplayFormatters.cpp
  • Availability — appended to PRESETS_STD (turbo cluster) in RadioInterface.cpp
  • SNR grading — MEDIUM bucket in UIRenderer.cpp and CannedMessageModule.cpp
  • Tests — new positive coverage in test_radio; EU868-reject + US-accept in test_admin_radio and test_mesh_beacon; preset count 9→10; fuzz range extended to include the new value
  • Docslora_region_preset_compatibility_client_spec.md

The InkHUD preset menu picks up the new preset automatically via its dynamic region-list path (same as LONG_TURBO), so no InkHUD change is needed.

Testing

Native test suite green: 578/578 test cases, 0 failures, 0 ignored (all 30 suites, via the Docker native-test path).

Summary by CodeRabbit

  • New Features
    • Added support for the Medium Turbo LoRa modem preset, including parameter mapping.
    • Updated signal-quality labeling and preset display naming for the new preset.
  • Documentation
    • Updated LoRa region→preset compatibility docs to reflect a 10-preset standard group, including MEDIUM_TURBO.
  • Bug Fixes
    • Improved preset validation/clamping so MEDIUM_TURBO is accepted in US configurations and rejected/cleared in EU 868 configurations.
  • Tests
    • Added/updated unit tests and expanded fuzz coverage for MEDIUM_TURBO.

MEDIUM_TURBO (500 kHz, SF9, CR 4/5) already existed in the protobuf enum but
was never wired into firmware, so selecting it silently fell through to the
LONG_FAST default and rendered an "Invalid" display name.

Add its bw/sf/cr mapping (modemPresetToParams), display name (MediumTurbo/MedT),
PRESETS_STD membership (standard regions only — 500 kHz does not fit EU868's
250 kHz band, so it stays out of PRESETS_EU_868 and is rejected/clamped there),
and the MEDIUM SNR-grading bucket. Includes positive coverage in test_radio,
EU868-reject + US-accept coverage in test_admin_radio and test_mesh_beacon,
the STD preset count 9->10, an extended fuzz range, and the client-spec doc.
@github-actions

github-actions Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

⚡ Try this PR in the Web Flasher

Flash this PR in the Web Flasher

firmware commit boards expires

Warning

This is an automated, unreviewed CI test build. Back up your device configuration
before flashing, and only flash devices you are able to recover.

Supported boards built by this PR (27)
Device Board Platform
Crowpanel Adv 3.5 TFT elecrow-adv-35-tft esp32-s3
Heltec HT62 heltec-ht62-esp32c3-sx1262 esp32-c3
Heltec Mesh Node 096 heltec-mesh-node-t096 nrf52840
Heltec Mesh Node T1 heltec-mesh-node-t1 nrf52840
Heltec Mesh Node T114 heltec-mesh-node-t114 nrf52840
Heltec V3 heltec-v3 esp32-s3
Heltec V4 heltec-v4 esp32-s3
Meshnology W10 meshnology_w10 esp32-s3
Raspberry Pi Pico pico rp2040
Raspberry Pi Pico W picow rp2040
RAK WisMesh Tag rak_wismeshtag nrf52840
RAK WisBlock 11200 rak11200 esp32
RAK WisBlock 11310 rak11310 rp2040
RAK3312 rak3312 esp32-s3
RAK WisBlock 4631 rak4631 nrf52840
Seeed SenseCAP Mesh-Tracker-X1 seeed_mesh_tracker_X1 nrf52840
Seeed Wio Tracker L1 seeed_wio_tracker_L1 nrf52840
Seeed Xiao NRF52840 Kit seeed_xiao_nrf52840_kit nrf52840
Seeed Xiao ESP32-S3 seeed-xiao-s3 esp32-s3
Station G2 station-g2 esp32-s3
Station G3 station-g3 esp32-s3
LILYGO T-Deck t-deck-tft esp32-s3
LILYGO T-Echo t-echo nrf52840
LILYGO T-Echo Plus t-echo-plus nrf52840
LILYGO T-Impulse Plus t-impulse-plus nrf52840
LilyGo T3-C6 tlora-c6 esp32-c6
Seeed SenseCAP T1000-E tracker-t1000-e nrf52840

Build artifacts expire on 2026-08-10. Updated for 3614294.

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f6c5ff7-a821-4df5-9e7b-9a4117f98a29

📥 Commits

Reviewing files that changed from the base of the PR and between 23a78bd and 3614294.

📒 Files selected for processing (2)
  • test/test_fuzz_packets/test_main.cpp
  • test/test_mesh_beacon/test_main.cpp
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/test_fuzz_packets/test_main.cpp
  • test/test_mesh_beacon/test_main.cpp

📝 Walkthrough

Walkthrough

The standard LoRa preset set now includes MEDIUM_TURBO. Runtime mappings, display names, SNR thresholds, region validation, fuzz ranges, documentation, and tests were updated accordingly.

Changes

MEDIUM_TURBO preset support

Layer / File(s) Summary
Standard preset contract and metadata
.gitignore, docs/..., protobufs, src/mesh/RadioInterface.cpp
The standard preset documentation and references now define 10 presets, the protobuf submodule pointer was updated, and .pio-docker is ignored.
Runtime MEDIUM_TURBO handling
src/mesh/MeshRadio.h, src/DisplayFormatters.cpp, src/graphics/draw/UIRenderer.cpp, src/modules/CannedMessageModule.cpp
MEDIUM_TURBO now has explicit radio parameters, display names, and signal-quality SNR handling.
Validation and fuzz coverage
test/test_admin_radio/test_main.cpp, test/test_mesh_beacon/test_main.cpp, test/test_radio/test_main.cpp, test/test_fuzz_packets/test_main.cpp
Tests and fuzz inputs cover the expanded preset count, region-specific acceptance or clearing, radio configuration, and the new enum value.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding MEDIUM_TURBO modem preset support.
Description check ✅ Passed The description is detailed and covers the spec, changes, and testing, though it omits the template's attestation checklist.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-medium-turbo-preset

Comment @coderabbitai help to get the list of available commands.

@thebentern
thebentern requested a review from Copilot July 11, 2026 12:27

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
test/test_mesh_beacon/test_main.cpp (1)

254-257: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Condense the new comments to the repository’s C++ style.

Each added Doxygen block spans four lines. Keep the rationale to one concise line; the test names and assertions already explain the behavior. As per coding guidelines, C++ comments should be at most one or two lines and should not restate obvious code.

Proposed comment simplification
-/**
- * Verify MEDIUM_TURBO is also cleared for EU_868. Like SHORT_TURBO/LONG_TURBO it is a 500 kHz preset
- * that does not fit EU_868's 250 kHz band, so it must not survive admin validation there.
- */
+// MEDIUM_TURBO must be cleared for EU_868 because its 500 kHz bandwidth is unsupported.
 
...
 
-/**
- * Verify MEDIUM_TURBO passes validation for US (PROFILE_STD allows the full turbo family).
- * The same 500 kHz preset that is illegal in EU_868 must be preserved in permissive regions.
- */
+// MEDIUM_TURBO is preserved for US because PROFILE_STD allows the full turbo family.

Also applies to: 271-274

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/test_mesh_beacon/test_main.cpp` around lines 254 - 257, Condense the new
Doxygen comments near the MEDIUM_TURBO and corresponding test cases to one
concise line each, retaining only the non-obvious rationale. Keep the existing
test names and assertions unchanged, and follow the repository’s one- or
two-line C++ comment style.

Source: Coding guidelines

test/test_radio/test_main.cpp (1)

198-210: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the wideLora branch for MEDIUM_TURBO.

This test exercises only US/non-wide behavior, so it cannot catch a regression in the required 1625 kHz LORA_24 mapping. Add a second case using RegionCode_LORA_24 and assert 1625.0f.

Possible extension
     TEST_ASSERT_FLOAT_WITHIN(0.01f, 500.0f, testRadio->getBw());
+
+    config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_LORA_24;
+    initRegion();
+    testRadio->reconfigure();
+    TEST_ASSERT_FLOAT_WITHIN(0.01f, 1625.0f, testRadio->getBw());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/test_radio/test_main.cpp` around lines 198 - 210, Add a second scenario
to test_applyModemConfig_mediumTurbo using RegionCode_LORA_24, reconfigure the
radio, and assert that getBw() returns 1625.0f while preserving the existing
MEDIUM_TURBO parameter assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/test_mesh_beacon/test_main.cpp`:
- Around line 262-269: Update the test around handleSetModuleConfig to assert
moduleConfig.has_mesh_beacon is true before asserting
mesh_beacon.has_broadcast_on_preset is false, confirming the sanitized
configuration was persisted rather than dropped. Match the assertion pattern
used by the existing SHORT_TURBO test.

---

Nitpick comments:
In `@test/test_mesh_beacon/test_main.cpp`:
- Around line 254-257: Condense the new Doxygen comments near the MEDIUM_TURBO
and corresponding test cases to one concise line each, retaining only the
non-obvious rationale. Keep the existing test names and assertions unchanged,
and follow the repository’s one- or two-line C++ comment style.

In `@test/test_radio/test_main.cpp`:
- Around line 198-210: Add a second scenario to
test_applyModemConfig_mediumTurbo using RegionCode_LORA_24, reconfigure the
radio, and assert that getBw() returns 1625.0f while preserving the existing
MEDIUM_TURBO parameter assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0b2db170-7e71-46a3-9698-d5d1b7f25037

📥 Commits

Reviewing files that changed from the base of the PR and between ca833d9 and 23a78bd.

⛔ Files ignored due to path filters (1)
  • src/mesh/generated/meshtastic/config.pb.h is excluded by !**/generated/**, !src/mesh/generated/**
📒 Files selected for processing (12)
  • .gitignore
  • docs/lora_region_preset_compatibility_client_spec.md
  • protobufs
  • src/DisplayFormatters.cpp
  • src/graphics/draw/UIRenderer.cpp
  • src/mesh/MeshRadio.h
  • src/mesh/RadioInterface.cpp
  • src/modules/CannedMessageModule.cpp
  • test/test_admin_radio/test_main.cpp
  • test/test_fuzz_packets/test_main.cpp
  • test/test_mesh_beacon/test_main.cpp
  • test/test_radio/test_main.cpp

Comment thread test/test_mesh_beacon/test_main.cpp

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR wires the MEDIUM_TURBO LoRa modem preset into the firmware so it no longer falls back to LONG_FAST, and ensures it is surfaced consistently across parameter mapping, preset availability per region, UI display names, and validation/tests.

Changes:

  • Add MEDIUM_TURBO modem-preset → parameter mapping (500 kHz / SF9 / CR 4/5; wideLoRa uses 1625 kHz).
  • Expose the preset in standard-region preset lists and UI/display/SNR-bucket logic.
  • Extend unit tests and fuzz coverage to include the new enum value and region allow/deny behavior.

Reviewed changes

Copilot reviewed 11 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
test/test_radio/test_main.cpp Adds unit coverage for applying MEDIUM_TURBO params and clamping it out for EU868.
test/test_mesh_beacon/test_main.cpp Extends admin validation tests for beacon preset handling of MEDIUM_TURBO (EU868 cleared, US accepted).
test/test_fuzz_packets/test_main.cpp Extends fuzz range to include the new modem preset enum value.
test/test_admin_radio/test_main.cpp Updates region/preset validation tests and expected PROFILE_STD preset count.
src/modules/CannedMessageModule.cpp Includes MEDIUM_TURBO in the “MEDIUM” SNR grading bucket.
src/mesh/RadioInterface.cpp Adds MEDIUM_TURBO to PRESETS_STD availability list.
src/mesh/MeshRadio.h Implements MEDIUM_TURBO mapping in modemPresetToParams().
src/mesh/generated/meshtastic/config.pb.h Updates generated enum to include MEDIUM_TURBO=16 and adjusts MAX/ARRAYSIZE.
src/graphics/draw/UIRenderer.cpp Includes MEDIUM_TURBO in the “MEDIUM” SNR grading bucket for UI.
src/DisplayFormatters.cpp Adds display names for MEDIUM_TURBO (MedT / MediumTurbo).
docs/lora_region_preset_compatibility_client_spec.md Updates client spec docs for the standard preset group (9 → 10 presets).
.gitignore Ignores .pio-docker build artifacts.

Comment thread test/test_fuzz_packets/test_main.cpp Outdated
Comment thread test/test_fuzz_packets/test_main.cpp Outdated
- test_mesh_beacon: assert has_mesh_beacon before checking the invalid preset was
  cleared, so the EU868-cleared test can't pass on a dropped message (matches the
  existing SHORT_TURBO test).
- test_fuzz_packets: draw modem presets from _ModemPreset_ARRAYSIZE instead of a
  hard-coded 17 so the fuzz range tracks future enum additions automatically.
@thebentern thebentern added the enhancement New feature or request label Jul 11, 2026
@thebentern
thebentern merged commit c535564 into develop Jul 11, 2026
97 of 98 checks passed
@shalberd

shalberd commented Jul 14, 2026

Copy link
Copy Markdown

Hi, is it ensured somehow that the 500khz TURBO presets are not selectable on EU_868?
Background https://www.nxp.com/docs/en/supporting-information/RDRKEBANDPL.pdf
250 Khz only

also, here on page 22, section P, band number 54 https://www.etsi.org/deliver/etsi_en/300200_300299/30022002/03.02.01_60/en_30022002v030201p.pdf
that is band from 869,400 MHz to
869,650 MHz -> 250 Khz difference, with the default value 869.525 Mhz used by Meshtastic being right at mid-band.

Frequency slot calculator also shows no frequency slot available.

On the Android App, I can select the TURBO presets from the dropdown list when selecting region EU_868.
It is only after SAVE that the preset is instead set to LONG_FAST.

Is it technically possible to remove some frequency presets based on the region?
In this case, for EU_868: no selectable TURBO presets?
Conditional UI rendering, I mean.

@caveman99
caveman99 deleted the add-medium-turbo-preset branch August 26, 2026 19:41
Itzdavid01 pushed a commit to Itzdavid01/firmware that referenced this pull request Sep 5, 2026
* Protobufs

* Wire up MEDIUM_TURBO modem preset

MEDIUM_TURBO (500 kHz, SF9, CR 4/5) already existed in the protobuf enum but
was never wired into firmware, so selecting it silently fell through to the
LONG_FAST default and rendered an "Invalid" display name.

Add its bw/sf/cr mapping (modemPresetToParams), display name (MediumTurbo/MedT),
PRESETS_STD membership (standard regions only — 500 kHz does not fit EU868's
250 kHz band, so it stays out of PRESETS_EU_868 and is rejected/clamped there),
and the MEDIUM SNR-grading bucket. Includes positive coverage in test_radio,
EU868-reject + US-accept coverage in test_admin_radio and test_mesh_beacon,
the STD preset count 9->10, an extended fuzz range, and the client-spec doc.

* Address review feedback on MEDIUM_TURBO tests

- test_mesh_beacon: assert has_mesh_beacon before checking the invalid preset was
  cleared, so the EU868-cleared test can't pass on a dropped message (matches the
  existing SHORT_TURBO test).
- test_fuzz_packets: draw modem presets from _ModemPreset_ARRAYSIZE instead of a
  hard-coded 17 so the fuzz range tracks future enum additions automatically.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants