diff --git a/src/components/configuration/constants.test.ts b/src/components/configuration/constants.test.ts new file mode 100644 index 000000000..e6b4b2fde --- /dev/null +++ b/src/components/configuration/constants.test.ts @@ -0,0 +1,33 @@ +import { describe, it, expect } from 'vitest'; +import { REGION_OPTIONS } from './constants'; + +// Guards against RegionCode drift from meshtastic/protobufs config.proto (#3927). +// When upstream adds a RegionCode value, extend REGION_OPTIONS and bump the max here. +describe('REGION_OPTIONS', () => { + const HIGHEST_REGION_CODE = 37; // ITU2_125CM — keep in sync with config.proto enum RegionCode + + it('covers contiguous RegionCode values 0..HIGHEST_REGION_CODE with no gaps or dupes', () => { + const values = REGION_OPTIONS.map((o) => o.value).sort((a, b) => a - b); + expect(values).toEqual(Array.from({ length: HIGHEST_REGION_CODE + 1 }, (_, i) => i)); + }); + + it('every option has a non-empty "NAME - description" label', () => { + for (const o of REGION_OPTIONS) { + expect(o.label).toMatch(/^\S+ - .+/); + } + }); + + it('uses the upstream enum name ITU2_2M for value 28 (not the old ITU23_2M)', () => { + const v28 = REGION_OPTIONS.find((o) => o.value === 28); + expect(v28?.label.startsWith('ITU2_2M -')).toBe(true); + }); + + it('includes the ITU amateur regions 33-37 added in #3927', () => { + const byValue = new Map(REGION_OPTIONS.map((o) => [o.value, o.label])); + expect(byValue.get(33)?.startsWith('ITU3_2M')).toBe(true); + expect(byValue.get(34)?.startsWith('ITU1_70CM')).toBe(true); + expect(byValue.get(35)?.startsWith('ITU2_70CM')).toBe(true); + expect(byValue.get(36)?.startsWith('ITU3_70CM')).toBe(true); + expect(byValue.get(37)?.startsWith('ITU2_125CM')).toBe(true); + }); +}); diff --git a/src/components/configuration/constants.ts b/src/components/configuration/constants.ts index 809fff71f..ed9c16f92 100644 --- a/src/components/configuration/constants.ts +++ b/src/components/configuration/constants.ts @@ -117,11 +117,16 @@ export const REGION_OPTIONS: RegionOption[] = [ { value: 25, label: 'NP_865 - Nepal 865MHz' }, { value: 26, label: 'BR_902 - Brazil 902MHz' }, { value: 27, label: 'ITU1_2M - ITU Region 1 Amateur 2m (144-146 MHz)' }, - { value: 28, label: 'ITU23_2M - ITU Region 2/3 Amateur 2m (144-148 MHz)' }, + { value: 28, label: 'ITU2_2M - ITU Region 2 Amateur 2m (144-148 MHz)' }, { value: 29, label: 'EU_866 - European Union 866MHz SRD' }, { value: 30, label: 'EU_874 - European Union 874MHz SRD' }, { value: 31, label: 'EU_917 - European Union 917MHz SRD' }, - { value: 32, label: 'EU_N_868 - European Union 868MHz Narrow' } + { value: 32, label: 'EU_N_868 - European Union 868MHz Narrow' }, + { value: 33, label: 'ITU3_2M - ITU Region 3 Amateur 2m (144-148 MHz)' }, + { value: 34, label: 'ITU1_70CM - ITU Region 1 Amateur 70cm (430-440 MHz)' }, + { value: 35, label: 'ITU2_70CM - ITU Region 2 Amateur 70cm (420-450 MHz, check local law)' }, + { value: 36, label: 'ITU3_70CM - ITU Region 3 Amateur 70cm (430-450 MHz, check local law)' }, + { value: 37, label: 'ITU2_125CM - ITU Region 2 Amateur 1.25m (220-225 MHz, check local law)' } ]; // Config.LoRaConfig.FEM_LNA_Mode (firmware >= v2.7.20, meshtastic/firmware#9809). diff --git a/src/server/services/deviceBackupService.ts b/src/server/services/deviceBackupService.ts index e93251a90..d1a654939 100644 --- a/src/server/services/deviceBackupService.ts +++ b/src/server/services/deviceBackupService.ts @@ -175,7 +175,18 @@ const EnumMappings: Record> = { 23: 'KZ_433', 24: 'KZ_863', 25: 'NP_865', - 26: 'BR_902' + 26: 'BR_902', + 27: 'ITU1_2M', + 28: 'ITU2_2M', + 29: 'EU_866', + 30: 'EU_874', + 31: 'EU_917', + 32: 'EU_N_868', + 33: 'ITU3_2M', + 34: 'ITU1_70CM', + 35: 'ITU2_70CM', + 36: 'ITU3_70CM', + 37: 'ITU2_125CM' }, DetectionTriggerType: { 0: 'NONE', diff --git a/src/utils/loraFrequency.test.ts b/src/utils/loraFrequency.test.ts index 4939934d2..8ca83d841 100644 --- a/src/utils/loraFrequency.test.ts +++ b/src/utils/loraFrequency.test.ts @@ -138,6 +138,26 @@ describe('calculateLoRaFrequency', () => { }); }); + describe('ITU amateur regions added in RegionCode 33-37 (#3927)', () => { + // freqStart from firmware RadioInterface.cpp (develop / firmware#10627). + // Slot 0 at 250 kHz = freqStart + 0.125 MHz. + it('ITU3_2M (region 33): freqStart 144.0 → slot 0 = 144.125 MHz', () => { + expect(calculateLoRaFrequency(33, 1, 0, 0)).toBe('144.125 MHz'); + }); + it('ITU1_70CM (region 34): freqStart 430.0 → slot 0 = 430.125 MHz', () => { + expect(calculateLoRaFrequency(34, 1, 0, 0)).toBe('430.125 MHz'); + }); + it('ITU2_70CM (region 35): freqStart 420.0 → slot 0 = 420.125 MHz', () => { + expect(calculateLoRaFrequency(35, 1, 0, 0)).toBe('420.125 MHz'); + }); + it('ITU3_70CM (region 36): freqStart 430.0 → slot 0 = 430.125 MHz', () => { + expect(calculateLoRaFrequency(36, 1, 0, 0)).toBe('430.125 MHz'); + }); + it('ITU2_125CM (region 37): freqStart 220.0 → slot 0 = 220.125 MHz', () => { + expect(calculateLoRaFrequency(37, 1, 0, 0)).toBe('220.125 MHz'); + }); + }); + describe('Override frequency', () => { it('should use override frequency when set', () => { const result = calculateLoRaFrequency(1, 21, 915.0, 0); diff --git a/src/utils/loraFrequency.ts b/src/utils/loraFrequency.ts index 2d10edf07..0f5f30218 100644 --- a/src/utils/loraFrequency.ts +++ b/src/utils/loraFrequency.ts @@ -73,11 +73,16 @@ export function calculateLoRaFrequency( 25: [865.0, 868.0], // NP_865: 865-868 MHz 26: [902.0, 907.5], // BR_902: 902-907.5 MHz 27: [144.0, 146.0], // ITU1_2M: ITU Region 1 Amateur 2m - 28: [144.0, 148.0], // ITU23_2M: ITU Region 2/3 Amateur 2m + 28: [144.0, 148.0], // ITU2_2M: ITU Region 2 Amateur 2m 29: [865.6, 867.6], // EU_866: EU 866MHz SRD (2.5% duty) 30: [873.4, 876.0], // EU_874: EU 874MHz SRD (Decision 2022/172 Band 1, awaiting firmware) 31: [917.4, 919.4], // EU_917: EU 917MHz SRD (Decision 2022/172 Band 4, awaiting firmware) - 32: [869.4, 869.65] // EU_N_868: EU 868MHz Narrow (mandates LITE/NARROW preset) + 32: [869.4, 869.65], // EU_N_868: EU 868MHz Narrow (mandates LITE/NARROW preset) + 33: [144.0, 148.0], // ITU3_2M: ITU Region 3 Amateur 2m (firmware#10627, on develop / awaiting release) + 34: [430.0, 440.0], // ITU1_70CM: ITU Region 1 Amateur 70cm (firmware#10627, on develop / awaiting release) + 35: [420.0, 450.0], // ITU2_70CM: ITU Region 2 Amateur 70cm (firmware#10627, on develop / awaiting release) + 36: [430.0, 450.0], // ITU3_70CM: ITU Region 3 Amateur 70cm (firmware#10627, on develop / awaiting release) + 37: [220.0, 225.0] // ITU2_125CM: ITU Region 2 Amateur 1.25m (on develop / awaiting release) }; if (!region || region === 0) {