@@ -16,14 +16,48 @@ import Foundation
1616/// Moved out of `Channels.swift` (was `private struct`) so the Mesh Beacons join
1717/// enhancement can reuse the exact same math instead of hand-rolling a second hash.
1818struct LoRaChannelCalculator {
19- let config : LoRaConfigEntity ?
19+ private let regionCode : Int
20+ private let usePreset : Bool
21+ private let modemPreset : Int
22+ private let channelNum : Int
23+ private let bandwidth : Int
24+ private let overrideFrequency : Double
25+ private let frequencyOffset : Double
26+
27+ init ( config: LoRaConfigEntity ? ) {
28+ regionCode = Int ( config? . regionCode ?? 0 )
29+ usePreset = config? . usePreset ?? false
30+ modemPreset = Int ( config? . modemPreset ?? 0 )
31+ channelNum = Int ( config? . channelNum ?? 0 )
32+ bandwidth = Int ( config? . bandwidth ?? 0 )
33+ overrideFrequency = Double ( config? . overrideFrequency ?? 0 )
34+ frequencyOffset = Double ( config? . frequencyOffset ?? 0 )
35+ }
36+
37+ init (
38+ regionCode: Int ,
39+ usePreset: Bool ,
40+ modemPreset: Int ,
41+ channelNum: Int ,
42+ bandwidth: Int ,
43+ overrideFrequency: Double ,
44+ frequencyOffset: Double = 0
45+ ) {
46+ self . regionCode = regionCode
47+ self . usePreset = usePreset
48+ self . modemPreset = modemPreset
49+ self . channelNum = channelNum
50+ self . bandwidth = bandwidth
51+ self . overrideFrequency = overrideFrequency
52+ self . frequencyOffset = frequencyOffset
53+ }
2054
2155 private var region : RegionInfo ? {
22- RegionInfo ( regionCode: Int ( config ? . regionCode ?? 0 ) )
56+ RegionInfo ( regionCode: regionCode)
2357 }
2458
2559 var regionName : String {
26- guard let regionCode = RegionCodes ( rawValue: Int ( config ? . regionCode ?? 0 ) ) else {
60+ guard let regionCode = RegionCodes ( rawValue: regionCode) else {
2761 return " Unknown region "
2862 }
2963 return regionCode. description
@@ -33,8 +67,11 @@ struct LoRaChannelCalculator {
3367 /// (the firmware pins the slot when set); otherwise derives it from the primary
3468 /// channel name.
3569 func effectiveChannelSlot( primaryName: String ) -> Int {
36- if let channelNum = config? . channelNum, channelNum != 0 {
37- return Int ( channelNum)
70+ if channelNum != 0 {
71+ return channelNum
72+ }
73+ if let defaultSlot = region? . defaultSlot, defaultSlot > 0 {
74+ return defaultSlot
3875 }
3976 let numChannels = numChannels ( )
4077 guard numChannels > 0 else { return 0 }
@@ -51,30 +88,30 @@ struct LoRaChannelCalculator {
5188 }
5289
5390 func radioFrequencyMHz( slot: Int ) -> Double {
54- guard let config else { return 0 }
55- if config. overrideFrequency != 0 {
56- return Double ( config. overrideFrequency)
91+ if overrideFrequency != 0 {
92+ return overrideFrequency + frequencyOffset
5793 }
5894 guard let region else { return 0 }
59- let bandwidth = bandwidthMHz ( region: region)
60- guard bandwidth > 0 , slot > 0 else { return 0 }
61- return region. freqStart + bandwidth / 2 + Double( slot - 1 ) * bandwidth
95+ let bandwidthMHz = bandwidthMHz ( region: region)
96+ guard bandwidthMHz > 0 , slot > 0 else { return 0 }
97+ let slotWidth = region. spacing + 2 * region. padding + bandwidthMHz
98+ return region. freqStart + bandwidthMHz / 2 + region. padding + Double( slot - 1 ) * slotWidth + frequencyOffset
6299 }
63100
64101 private func numChannels( ) -> Int {
65102 guard let region else { return 0 }
66- let bandwidth = bandwidthMHz ( region: region)
67- guard bandwidth > 0 else { return 1 }
68- return max ( Int ( floor ( ( region. freqEnd - region. freqStart) / bandwidth) ) , 1 )
103+ let bandwidthMHz = bandwidthMHz ( region: region)
104+ guard bandwidthMHz > 0 else { return 1 }
105+ let slotWidth = region. spacing + 2 * region. padding + bandwidthMHz
106+ return max ( Int ( ( ( region. freqEnd - region. freqStart + region. spacing) / slotWidth) . rounded ( ) ) , 1 )
69107 }
70108
71109 private func bandwidthMHz( region: RegionInfo ) -> Double {
72- guard let config else { return 0 }
73- if config. usePreset {
74- let presetBandwidth = ModemPresets ( rawValue: Int ( config. modemPreset) ) ? . bandwidthMHz ?? 0
110+ if usePreset {
111+ let presetBandwidth = ModemPresets ( rawValue: modemPreset) ? . bandwidthMHz ?? 0
75112 return presetBandwidth * ( region. wideLoRa ? 3.25 : 1 )
76113 }
77- switch config . bandwidth {
114+ switch bandwidth {
78115 case 31 :
79116 return 0.03125
80117 case 62 :
@@ -88,7 +125,7 @@ struct LoRaChannelCalculator {
88125 case 1600 :
89126 return 1.625
90127 default :
91- return Double ( config . bandwidth) / 1000
128+ return Double ( bandwidth) / 1000
92129 }
93130 }
94131
@@ -185,6 +222,9 @@ struct RegionInfo {
185222 let freqStart : Double
186223 let freqEnd : Double
187224 let wideLoRa : Bool
225+ let spacing : Double
226+ let padding : Double
227+ let defaultSlot : Int
188228
189229 init ? ( regionCode: Int ) {
190230 guard let region = RegionCodes ( rawValue: regionCode) else { return nil }
@@ -241,38 +281,45 @@ struct RegionInfo {
241281 self . init ( freqStart: 865.0 , freqEnd: 868.0 )
242282 case . br902:
243283 self . init ( freqStart: 902.0 , freqEnd: 907.5 )
244- case . itu12M, . itu22M:
245- self . init ( freqStart: 144.0 , freqEnd: 148.0 )
284+ case . itu12M:
285+ self . init ( freqStart: 144.0 , freqEnd: 146.0 , padding: 0.0022 , defaultSlot: 26 )
286+ case . itu22M:
287+ self . init ( freqStart: 144.0 , freqEnd: 148.0 , padding: 0.0022 , defaultSlot: 51 )
246288 case . eu866:
247289 self . init ( freqStart: 866.0 , freqEnd: 866.5 )
248290 case . eu874:
249291 self . init ( freqStart: 873.0 , freqEnd: 876.0 )
250292 case . eu917:
251293 self . init ( freqStart: 917.0 , freqEnd: 921.0 )
252294 case . euN868:
253- self . init ( freqStart: 869.4 , freqEnd: 869.65 )
295+ self . init ( freqStart: 869.4 , freqEnd: 869.65 , padding : 0.0104 , defaultSlot : 1 )
254296 case . itu32M:
255- // ITU Region 3 Amateur Radio 2m band.
256- self . init ( freqStart: 144.0 , freqEnd: 148.0 )
297+ self . init ( freqStart: 144.0 , freqEnd: 148.0 , padding: 0.0022 , defaultSlot: 33 )
257298 case . itu170Cm:
258- // ITU Region 1 Amateur Radio 70cm band.
259- self . init ( freqStart: 430.0 , freqEnd: 440.0 )
299+ self . init ( freqStart: 430.0 , freqEnd: 440.0 , padding: 0.01875 , defaultSlot: 37 )
260300 case . itu270Cm:
261- // ITU Region 2 Amateur Radio 70cm band.
262- self . init ( freqStart: 420.0 , freqEnd: 450.0 )
301+ self . init ( freqStart: 420.0 , freqEnd: 450.0 , padding: 0.01875 , defaultSlot: 137 )
263302 case . itu370Cm:
264- // ITU Region 3 Amateur Radio 70cm band.
265- self . init ( freqStart: 430.0 , freqEnd: 450.0 )
303+ self . init ( freqStart: 430.0 , freqEnd: 450.0 , padding: 0.01875 , defaultSlot: 37 )
266304 case . itu2125Cm:
267- // ITU Region 2 Amateur Radio 1.25m (125cm) band.
268- self . init ( freqStart: 220.0 , freqEnd: 225.0 )
305+ self . init ( freqStart: 220.0 , freqEnd: 225.0 , padding: 0.01875 , defaultSlot: 37 )
269306 }
270307 }
271308
272- private init ( freqStart: Double , freqEnd: Double , wideLoRa: Bool = false ) {
309+ private init (
310+ freqStart: Double ,
311+ freqEnd: Double ,
312+ wideLoRa: Bool = false ,
313+ spacing: Double = 0 ,
314+ padding: Double = 0 ,
315+ defaultSlot: Int = 0
316+ ) {
273317 self . freqStart = freqStart
274318 self . freqEnd = freqEnd
275319 self . wideLoRa = wideLoRa
320+ self . spacing = spacing
321+ self . padding = padding
322+ self . defaultSlot = defaultSlot
276323 }
277324}
278325
@@ -299,7 +346,7 @@ extension ModemPresets {
299346 case . narrowFast, . narrowSlow:
300347 return 0.0625
301348 case . tinyFast, . tinySlow:
302- return 0.020
349+ return 0.0156
303350 }
304351 }
305352}
0 commit comments