Skip to content

fix: align channel frequency display with firmware#2088

Merged
garthvh merged 2 commits into
meshtastic:mainfrom
RCGV1:codex/fix-channel-frequency
Jul 16, 2026
Merged

fix: align channel frequency display with firmware#2088
garthvh merged 2 commits into
meshtastic:mainfrom
RCGV1:codex/fix-channel-frequency

Conversation

@RCGV1

@RCGV1 RCGV1 commented Jul 15, 2026

Copy link
Copy Markdown
Member

Summary

Fixes the iOS Channels frequency summary for firmware-managed amateur-band channel plans.

Fixes #2087.

  • Mirrors the firmware slot-width calculation: modem bandwidth + per-side padding + spacing.
  • Uses firmware-defined regional default slots when the device leaves channel_num at its default.
  • Applies the configured frequency_offset after calculating an automatic or override frequency.
  • Corrects TinyFast/TinySlow RF bandwidth from 20 kHz to 15.6 kHz.

Root cause

The iOS calculator used a simplified freqStart + bandwidth / 2 + (slot - 1) * bandwidth model. For ITU Region 2 Amateur 2 m / TinyFast, that meant:

Source Slot Displayed / selected frequency
Current iOS calculation 144 (hash) 146.870 MHz
Firmware default 51 145.010 MHz

Firmware defines TinyFast as 15.6 kHz RF bandwidth and turns it into 20 kHz slot spacing with 2.2 kHz padding on each side. ITU Region 2 Amateur 2 m also has an explicit default slot of 51. Firmware then applies frequency_offset to the final result.

Relevant firmware evidence:

Captured evidence

The supplied captures show the original mismatch:

  1. iOS Channels showed 146.870 MHz / Slot 144 for ITU Region 2 / Amateur 2m.
  2. The T-Beam BPF device showed ITU2_2M TinyFast, 145.010 MHz.
  3. Firmware serial output selected channel_num: 51 and 145.009995 MHz.

Tests

xcodebuild test -quiet -project Meshtastic.xcodeproj -scheme Meshtastic -destination 'platform=iOS Simulator,name=iPhone 17 Pro' -only-testing:MeshtasticTests/LoraDeviceEnumTests

The new focused cases verify:

  • ITU Region 2 TinyFast defaults to slot 51 / 145.010 MHz.
  • Explicit TinyFast slots advance by 20 kHz.
  • A +0.010 MHz frequency offset is applied after the slot calculation.
  • The 1.25 m 100 kHz amateur plan remains aligned with firmware.
  • US LongFast channel centers remain unchanged.

Summary by CodeRabbit

  • Bug Fixes

    • Improved LoRa channel slot selection and frequency computations across supported regions.
    • Added region-specific channel spacing, padding, and default-slot behavior.
    • Updated frequency-offset handling, including override-frequency scenarios.
    • Adjusted TinyFast/TinySlow preset bandwidth for more accurate radio behavior.
  • Tests

    • Added a LoRa channel frequency calculation test suite covering default/explicit slots, regional channel plans, frequency offsets, and standard channel center frequencies.

@coderabbitai

coderabbitai Bot commented Jul 15, 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d545655e-9f3a-4402-8667-099fad1184b8

📥 Commits

Reviewing files that changed from the base of the PR and between 57ca94e and 2541196.

📒 Files selected for processing (1)
  • MeshtasticTests/LoraDeviceEnumTests.swift
🚧 Files skipped from review as they are similar to previous changes (1)
  • MeshtasticTests/LoraDeviceEnumTests.swift

📝 Walkthrough

Walkthrough

LoRaChannelCalculator now uses extracted configuration, region-specific slot and frequency parameters, frequency offsets, updated TinyFast bandwidth, and expanded calculation tests.

Changes

LoRa channel calculation

Layer / File(s) Summary
Region calculation contract
Meshtastic/Helpers/LoRaChannelCalculator.swift
The calculator stores extracted configuration values, and RegionInfo defines spacing, padding, and default-slot metadata for regional calculations.
Slot and frequency calculations
Meshtastic/Helpers/LoRaChannelCalculator.swift, MeshtasticTests/LoraDeviceEnumTests.swift
Slot selection and frequency derivation apply regional parameters and offsets, numeric bandwidth uses the supplied value, TinyFast bandwidth is set to 0.0156 MHz, and regional calculation cases are tested.

Estimated code review effort: 4 (Complex) | ~45 minutes

Poem

A rabbit hopped through slots of air,
And tuned each channel with care.
With spacing and padding,
Frequencies came gliding,
TinyFast found its share.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% 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 clearly summarizes the main change: aligning channel frequency display with firmware behavior.
Description check ✅ Passed The description covers what changed, why, testing, and relevant evidence, matching the template well.
Linked Issues check ✅ Passed The changes address #2087 by correcting slot and frequency calculations to match firmware-managed channel plans.
Out of Scope Changes check ✅ Passed The added region logic, bandwidth tweak, and tests all support the reported frequency/slot bug fix.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@RCGV1
RCGV1 force-pushed the codex/fix-channel-frequency branch from e9e90ee to 57ca94e Compare July 15, 2026 19:28
@RCGV1
RCGV1 marked this pull request as ready for review July 15, 2026 19:30

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Meshtastic/Helpers/LoRaChannelCalculator.swift (1)

221-323: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Set the narrow-region spacing explicitly
Meshtastic/Helpers/LoRaChannelCalculator.swift:229-306spacing still defaults to 0 in every RegionInfo(regionCode:) case, but the firmware’s narrow profile uses 0.0104 MHz spacing. That makes the slot/frequency math for that region diverge from firmware; if spacing is only a placeholder, drop it instead.

🤖 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 `@Meshtastic/Helpers/LoRaChannelCalculator.swift` around lines 221 - 323,
Update the RegionInfo(regionCode:) narrow-region cases to use the firmware’s
0.0104 MHz spacing by passing it through the private RegionInfo initializer,
especially the EU narrow profile; ensure slot/frequency calculations consume
this value instead of the default zero. If spacing is not intended to be
configurable, remove the unused spacing property and initializer parameter
rather than retaining a zero placeholder.
🧹 Nitpick comments (1)
MeshtasticTests/LoraDeviceEnumTests.swift (1)

843-923: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Missing test for overrideFrequency combined with frequencyOffset. The new radioFrequencyMHz branch that returns overrideFrequency + frequencyOffset is untested; all five added tests use overrideFrequency: 0.

  • MeshtasticTests/LoraDeviceEnumTests.swift#L843-L923: add a @Test constructing LoRaChannelCalculator with a non-zero overrideFrequency and a non-zero frequencyOffset, asserting the sum is returned regardless of slot.
  • Meshtastic/Helpers/LoRaChannelCalculator.swift#L90-L99: no code change needed here; this is the logic the new test should cover.
🤖 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 `@MeshtasticTests/LoraDeviceEnumTests.swift` around lines 843 - 923, Add a
`@Test` alongside LoRaChannelCalculatorTests that constructs LoRaChannelCalculator
with non-zero overrideFrequency and frequencyOffset, then asserts
radioFrequencyMHz returns their sum for an arbitrary slot. Update
MeshtasticTests/LoraDeviceEnumTests.swift lines 843-923;
Meshtastic/Helpers/LoRaChannelCalculator.swift lines 90-99 requires no direct
change.
🤖 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.

Outside diff comments:
In `@Meshtastic/Helpers/LoRaChannelCalculator.swift`:
- Around line 221-323: Update the RegionInfo(regionCode:) narrow-region cases to
use the firmware’s 0.0104 MHz spacing by passing it through the private
RegionInfo initializer, especially the EU narrow profile; ensure slot/frequency
calculations consume this value instead of the default zero. If spacing is not
intended to be configurable, remove the unused spacing property and initializer
parameter rather than retaining a zero placeholder.

---

Nitpick comments:
In `@MeshtasticTests/LoraDeviceEnumTests.swift`:
- Around line 843-923: Add a `@Test` alongside LoRaChannelCalculatorTests that
constructs LoRaChannelCalculator with non-zero overrideFrequency and
frequencyOffset, then asserts radioFrequencyMHz returns their sum for an
arbitrary slot. Update MeshtasticTests/LoraDeviceEnumTests.swift lines 843-923;
Meshtastic/Helpers/LoRaChannelCalculator.swift lines 90-99 requires no direct
change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 20172ada-5e88-4b30-a991-478cb9310b62

📥 Commits

Reviewing files that changed from the base of the PR and between c819aa2 and 57ca94e.

📒 Files selected for processing (2)
  • Meshtastic/Helpers/LoRaChannelCalculator.swift
  • MeshtasticTests/LoraDeviceEnumTests.swift

@garthvh garthvh left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Verified this end-to-end before merging:

  • Firmware fidelity: checked every formula against the cited RadioInterface.cpp @ 5b1c1a9 — slot width (spacing + 2·padding + bw), round()-based slot count, first-channel centering with padding, offset-applied-last (including on override frequency), the 15.6 kHz Tiny bandwidth, and all the ham default slots / band bounds in the RDEF table. All match exactly.
  • Regression safety: ran the existing suites that depend on this calculator — ModemPresetFrequencySlot, BeaconAddVsSwitch, DiscoveryScanEngine (31 tests) — plus the new suite. All pass.

Two notes for the record:

  1. The floor → round slot-count change also (correctly) fixes five non-ham regions where iOS disagreed with firmware: UA_433/PH_433/KZ_433 (6.8 → 7 slots), ANZ_433 (6.96 → 7), and LORA_24 (102.8 → 103). Users in those regions will see their hash-derived slot move to match their radios — right thing, just noting it since the PR body only mentions US LongFast.
  2. Heads-up on the test command in the description: -only-testing:MeshtasticTests/LoraDeviceEnumTests doesn't run the new suite — the struct is LoRaChannelCalculatorTests, so that filter executes 0 of the new tests. Worth knowing for anyone re-verifying.

Two small follow-ups from the same review are filed separately and assigned your way (EU_866 band alignment, and a Mesh Beacons default-slot interaction).

Nice work tracing this through the firmware — the captured serial-output evidence made it easy to trust. How's class going, by the way?

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.

🐞 [Bug]: Frequency and Slot Calculation Incorrect

2 participants