Skip to main content

pamoja_lorawan/device/
channels.rs

1//! The channels a device may transmit on, and how a network changes them.
2//!
3//! In a dynamic channel plan a device starts with the region's default channels, and a
4//! network adds more, moves their downlink frequency, and enables or disables them. In a fixed
5//! plan every channel is numbered in advance, each answers on a downlink channel the plan
6//! names, and a network only enables and disables them. RP002-1.0.5 gives the dynamic plans up
7//! to 80 channels and the fixed ones 72, and the 96-channel CN470-510 plan of the LoRaWAN 1.0.3
8//! Regional Parameters, revision A, addresses 96, so that is the capacity here.
9
10use pamoja_lora::region::{ChannelPlan, MaskControl, PlanKind};
11
12use crate::cflist::{CfList, CfListKind};
13
14/// How many channels a device keeps.
15pub const MAX_CHANNELS: usize = 96;
16
17/// The mask groups `LinkADRReq` addresses, sixteen channels each.
18pub(crate) const MASK_GROUPS: usize = MAX_CHANNELS / 16;
19
20/// The most channels a dynamic plan defines: RP002-1.0.5 gives each "a maximum of 80
21/// channels", the most its `ChMaskCntl` values reach.
22pub(crate) const DYNAMIC_MAX_CHANNELS: usize = 80;
23
24/// The data rates a channel a join accept creates carries: RP002-1.0.5 section 3.3.1 makes
25/// them "usable for DR0 to DR5 125 kHz LoRa modulation".
26const CREATED_DATA_RATES: (u8, u8) = (0, 5);
27
28/// One channel a device can transmit on.
29#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
30pub struct Channel {
31    /// Where uplinks go out, in hertz.
32    pub uplink_hz: u32,
33    /// Where the first receive window listens, in hertz: the uplink frequency unless a
34    /// network moved it with `DlChannelReq`, or the downlink channel a fixed plan answers the
35    /// channel on.
36    pub downlink_hz: u32,
37    /// The slowest data rate the channel carries.
38    pub min_data_rate: u8,
39    /// The fastest data rate the channel carries.
40    pub max_data_rate: u8,
41}
42
43impl Channel {
44    /// A channel whose downlink is on its uplink frequency.
45    ///
46    /// # Arguments
47    ///
48    /// * `hz` - the frequency, in hertz.
49    /// * `min_data_rate` - the slowest data rate it carries.
50    /// * `max_data_rate` - the fastest data rate it carries.
51    ///
52    /// # Returns
53    ///
54    /// The channel.
55    pub const fn new(hz: u32, min_data_rate: u8, max_data_rate: u8) -> Channel {
56        Channel {
57            uplink_hz: hz,
58            downlink_hz: hz,
59            min_data_rate,
60            max_data_rate,
61        }
62    }
63
64    /// Reports whether the channel carries a data rate.
65    ///
66    /// # Arguments
67    ///
68    /// * `data_rate` - the data rate.
69    ///
70    /// # Returns
71    ///
72    /// Whether it falls in the channel's range.
73    pub const fn carries(&self, data_rate: u8) -> bool {
74        data_rate >= self.min_data_rate && data_rate <= self.max_data_rate
75    }
76}
77
78/// A device's channel table and which of them are enabled.
79#[derive(Clone, Copy, Debug, PartialEq, Eq)]
80pub(crate) struct Channels {
81    slots: [Option<Channel>; MAX_CHANNELS],
82    enabled: [u16; MASK_GROUPS],
83    defaults: usize,
84}
85
86impl Channels {
87    /// The region's default channels, every one enabled, each answering where the plan says.
88    pub(crate) fn defaults(plan: &ChannelPlan) -> Channels {
89        let mut channels = Channels {
90            slots: [None; MAX_CHANNELS],
91            enabled: [0; MASK_GROUPS],
92            defaults: 0,
93        };
94        let mut index = 0;
95        for block in plan.default_channels {
96            for offset in 0..block.count {
97                if index >= MAX_CHANNELS {
98                    break;
99                }
100                if let Some(hz) = block.frequency_hz(offset) {
101                    let mut channel = Channel::new(hz, block.min_data_rate, block.max_data_rate);
102                    channel.downlink_hz = plan.rx1_frequency_hz(index as u16, hz).unwrap_or(hz);
103                    channels.slots[index] = Some(channel);
104                    channels.set_bit(index, true);
105                }
106                index += 1;
107            }
108        }
109        channels.defaults = index;
110        channels
111    }
112
113    /// A table carried on from a saved state.
114    pub(crate) const fn from_parts(
115        slots: [Option<Channel>; MAX_CHANNELS],
116        enabled: [u16; MASK_GROUPS],
117        defaults: usize,
118    ) -> Channels {
119        Channels {
120            slots,
121            enabled,
122            defaults,
123        }
124    }
125
126    /// Every slot of the table, defined or not.
127    pub(crate) const fn slots(&self) -> &[Option<Channel>; MAX_CHANNELS] {
128        &self.slots
129    }
130
131    /// How many of the channels are the region's defaults, which lead the table.
132    pub(crate) const fn default_count(&self) -> usize {
133        self.defaults
134    }
135
136    pub(crate) fn get(&self, index: usize) -> Option<Channel> {
137        self.slots.get(index).copied().flatten()
138    }
139
140    pub(crate) fn is_enabled(&self, index: usize) -> bool {
141        index < MAX_CHANNELS && self.enabled[index / 16] & (1 << (index % 16)) != 0
142    }
143
144    /// The enabled channels, with their indexes.
145    pub(crate) fn enabled(&self) -> impl Iterator<Item = (usize, Channel)> + '_ {
146        (0..MAX_CHANNELS).filter_map(move |index| {
147            self.get(index)
148                .filter(|_| self.is_enabled(index))
149                .map(|channel| (index, channel))
150        })
151    }
152
153    /// The enabled-channel mask, sixteen channels a group.
154    pub(crate) const fn mask(&self) -> [u16; MASK_GROUPS] {
155        self.enabled
156    }
157
158    /// Reports whether any channel enabled under `mask` carries a data rate.
159    pub(crate) fn carries(&self, mask: &[u16; MASK_GROUPS], data_rate: u8) -> bool {
160        (0..MAX_CHANNELS).any(|index| {
161            mask[index / 16] & (1 << (index % 16)) != 0
162                && self
163                    .get(index)
164                    .is_some_and(|channel| channel.carries(data_rate))
165        })
166    }
167
168    /// Re-enables every default channel and leaves the others as they are.
169    ///
170    /// This is what TS001-1.0.4 section 4.3.1.1 has a device do at the end of its back-off:
171    /// in a dynamic plan "enable the region's default channels and make no change to the
172    /// configuration of the dynamically configured channels", and in a fixed plan, whose
173    /// channels are all defaults, "enable all channels".
174    pub(crate) fn enable_defaults(&mut self) {
175        for index in 0..self.defaults {
176            if self.get(index).is_some() {
177                self.set_bit(index, true);
178            }
179        }
180    }
181
182    /// Takes the channel list a join accept carried.
183    ///
184    /// In a dynamic plan, RP002-1.0.5 section 3.3.1: the list replaces every channel but the
185    /// defaults; a type 0 list defines the five after them, and a type 1 list creates one
186    /// after them for each bit set, at the frequency the region's numbering gives that bit. A
187    /// frequency the radio cannot use, a number the numbering does not define, and a reserved
188    /// list type are all ignored, as the section asks.
189    ///
190    /// In a fixed plan only a type 1 list applies, and its groups set which of the numbered
191    /// channels are enabled, a cleared bit disabling its channel (RP002-1.0.5 sections 3.5.4,
192    /// 3.8.4 and 3.9.4). Bits past the plan's channels are reserved and read as nothing.
193    pub(crate) fn apply_cflist(
194        &mut self,
195        plan: &ChannelPlan,
196        list: CfList,
197        usable: impl Fn(u32) -> bool,
198    ) {
199        if plan.kind == PlanKind::Fixed {
200            if let Some(groups) = list.channel_mask_groups() {
201                for index in 0..MAX_CHANNELS {
202                    let on = groups[index / 16] & (1 << (index % 16)) != 0;
203                    self.set_bit(index, on && self.get(index).is_some());
204                }
205            }
206            return;
207        }
208
209        for index in self.defaults..MAX_CHANNELS {
210            self.slots[index] = None;
211            self.set_bit(index, false);
212        }
213
214        let (low, high) = CREATED_DATA_RATES;
215        match list.kind() {
216            CfListKind::Frequencies => {
217                let Some(frequencies) = list.frequencies_hz() else {
218                    return;
219                };
220                for (slot, hz) in frequencies.into_iter().enumerate() {
221                    let index = self.defaults + slot;
222                    if hz != 0 && index < DYNAMIC_MAX_CHANNELS && usable(hz) {
223                        self.slots[index] = Some(Channel::new(hz, low, high));
224                        self.set_bit(index, true);
225                    }
226                }
227            }
228            CfListKind::ChannelMasks => {
229                let PlanKind::Dynamic {
230                    channel_list: Some(numbering),
231                } = plan.kind
232                else {
233                    return;
234                };
235                let mut index = self.defaults;
236                for number in list.enabled_channels() {
237                    if index >= DYNAMIC_MAX_CHANNELS {
238                        break;
239                    }
240                    if let Some(hz) = numbering.frequency_hz(number).filter(|hz| usable(*hz)) {
241                        self.slots[index] = Some(Channel::new(hz, low, high));
242                        self.set_bit(index, true);
243                        index += 1;
244                    }
245                }
246            }
247            CfListKind::Reserved(_) => {}
248        }
249    }
250
251    /// Works out the mask a contiguous block of `LinkADRReq` channel controls leaves.
252    ///
253    /// Each control means what the plan's table says, RP002-1.0.5 table 14 for the dynamic
254    /// plans, tables 23 and 43 for the 900 MHz plans, and tables 52 and 53 for CN470-510.
255    /// TS001-1.0.4 section 5.3 applies the block in order as one command.
256    ///
257    /// # Returns
258    ///
259    /// The mask, or `None` when the block must be refused: a reserved control, a bit that
260    /// enables a channel the device has not defined, or every channel left disabled.
261    pub(crate) fn mask_after(
262        &self,
263        block: &[(u16, u8)],
264        controls: &[MaskControl; 8],
265    ) -> Option<[u16; MASK_GROUPS]> {
266        let mut mask = self.enabled;
267        for &(bits, control) in block {
268            match controls[usize::from(control & 0x07)] {
269                MaskControl::Group(group) => self.set_group(&mut mask, group, bits)?,
270                MaskControl::Banks => {
271                    for bank in 0..10 {
272                        self.switch_bank(&mut mask, bank, bits & (1 << bank) != 0);
273                    }
274                }
275                MaskControl::PairedBanks => {
276                    for bank in 0..8 {
277                        let on = bits & (1 << bank) != 0;
278                        self.switch_bank(&mut mask, bank, on);
279                        self.switch(&mut mask, 64 + bank, on);
280                    }
281                    self.switch_bank(&mut mask, 9, bits & (1 << 9) != 0);
282                }
283                MaskControl::All { on, then_group } => {
284                    for index in 0..MAX_CHANNELS {
285                        self.switch(&mut mask, index, on);
286                    }
287                    if let Some(group) = then_group {
288                        self.set_group(&mut mask, group, bits)?;
289                    }
290                }
291                MaskControl::Reserved => return None,
292            }
293        }
294        if mask.iter().all(|group| *group == 0) {
295            return None;
296        }
297        Some(mask)
298    }
299
300    /// Sets one group of sixteen from a mask, refusing a bit for an undefined channel.
301    fn set_group(&self, mask: &mut [u16; MASK_GROUPS], group: u8, bits: u16) -> Option<()> {
302        let group = usize::from(group);
303        if group >= MASK_GROUPS {
304            return None;
305        }
306        for bit in 0..16 {
307            if bits & (1 << bit) != 0 && self.get(group * 16 + bit).is_none() {
308                return None;
309            }
310        }
311        mask[group] = bits;
312        Some(())
313    }
314
315    /// Switches the eight channels of a bank.
316    fn switch_bank(&self, mask: &mut [u16; MASK_GROUPS], bank: usize, on: bool) {
317        for index in bank * 8..bank * 8 + 8 {
318            self.switch(mask, index, on);
319        }
320    }
321
322    /// Switches one channel in a mask, leaving an undefined channel off.
323    fn switch(&self, mask: &mut [u16; MASK_GROUPS], index: usize, on: bool) {
324        if index >= MAX_CHANNELS {
325            return;
326        }
327        let bit = 1 << (index % 16);
328        if on && self.get(index).is_some() {
329            mask[index / 16] |= bit;
330        } else {
331            mask[index / 16] &= !bit;
332        }
333    }
334
335    pub(crate) fn set_mask(&mut self, mask: [u16; MASK_GROUPS]) {
336        self.enabled = mask;
337    }
338
339    /// Creates or replaces a channel, enabled.
340    pub(crate) fn create(&mut self, index: usize, channel: Channel) {
341        if index < MAX_CHANNELS {
342            self.slots[index] = Some(channel);
343            self.set_bit(index, true);
344        }
345    }
346
347    /// Removes a channel.
348    pub(crate) fn remove(&mut self, index: usize) {
349        if index < MAX_CHANNELS {
350            self.slots[index] = None;
351            self.set_bit(index, false);
352        }
353    }
354
355    /// Moves a channel's downlink frequency.
356    pub(crate) fn set_downlink(&mut self, index: usize, hz: u32) {
357        if let Some(Some(channel)) = self.slots.get_mut(index) {
358            channel.downlink_hz = hz;
359        }
360    }
361
362    fn set_bit(&mut self, index: usize, on: bool) {
363        let bit = 1 << (index % 16);
364        if on {
365            self.enabled[index / 16] |= bit;
366        } else {
367            self.enabled[index / 16] &= !bit;
368        }
369    }
370}
371
372#[cfg(test)]
373mod tests {
374    use super::*;
375    use pamoja_lora::region::{Cn470Plan, Region};
376
377    fn european() -> Channels {
378        Channels::defaults(Region::Eu868.plan())
379    }
380
381    fn american() -> Channels {
382        Channels::defaults(Region::Us915.plan())
383    }
384
385    fn enabled_numbers(channels: &Channels) -> Vec<usize> {
386        channels.enabled().map(|(index, _)| index).collect()
387    }
388
389    fn numbers(mask: [u16; MASK_GROUPS]) -> Vec<usize> {
390        (0..MAX_CHANNELS)
391            .filter(|index| mask[index / 16] & (1 << (index % 16)) != 0)
392            .collect()
393    }
394
395    #[test]
396    fn a_european_device_starts_on_its_three_default_channels() {
397        let channels = european();
398        let enabled: Vec<u32> = channels.enabled().map(|(_, c)| c.uplink_hz).collect();
399        assert_eq!(enabled, [868_100_000, 868_300_000, 868_500_000]);
400        assert_eq!(channels.default_count(), 3);
401        assert_eq!(
402            channels.get(0).map(|c| (c.min_data_rate, c.max_data_rate)),
403            Some((0, 5))
404        );
405        assert_eq!(channels.get(2).map(|c| c.downlink_hz), Some(868_500_000));
406    }
407
408    #[test]
409    fn an_american_device_starts_on_all_seventy_two_answered_on_eight() {
410        // RP002-1.0.5 sections 3.5.2 and 3.5.7.
411        let channels = american();
412        assert_eq!(channels.enabled().count(), 72);
413        assert_eq!(channels.default_count(), 72);
414        let channel = |index| channels.get(index).expect("defined");
415        assert_eq!(
416            (channel(0).uplink_hz, channel(0).downlink_hz),
417            (902_300_000, 923_300_000)
418        );
419        assert_eq!(
420            (channel(63).uplink_hz, channel(63).downlink_hz),
421            (914_900_000, 927_500_000)
422        );
423        assert_eq!(
424            (channel(64).uplink_hz, channel(64).downlink_hz),
425            (903_000_000, 923_300_000)
426        );
427        assert_eq!(
428            (channel(64).min_data_rate, channel(64).max_data_rate),
429            (4, 4)
430        );
431        assert_eq!(channels.get(72), None);
432    }
433
434    #[test]
435    fn the_ninety_six_channel_plan_fills_the_table() {
436        let channels = Channels::defaults(Cn470Plan::Channels96.plan());
437        assert_eq!(channels.enabled().count(), 96);
438        assert_eq!(
439            channels.get(49).map(|c| c.downlink_hz),
440            Some(500_500_000),
441            "channel 49 is answered on downlink channel 1"
442        );
443    }
444
445    #[test]
446    fn a_frequency_list_defines_the_five_channels_after_the_defaults() {
447        let mut channels = european();
448        let list = CfList::frequencies([867_100_000, 867_300_000, 0, 867_700_000, 867_900_000])
449            .expect("valid");
450        channels.apply_cflist(Region::Eu868.plan(), list, |_| true);
451
452        let enabled: Vec<(usize, u32)> =
453            channels.enabled().map(|(i, c)| (i, c.uplink_hz)).collect();
454        assert_eq!(
455            enabled,
456            [
457                (0, 868_100_000),
458                (1, 868_300_000),
459                (2, 868_500_000),
460                (3, 867_100_000),
461                (4, 867_300_000),
462                (6, 867_700_000),
463                (7, 867_900_000),
464            ],
465            "an unused slot leaves its channel undefined"
466        );
467        assert_eq!(
468            channels.get(3).map(|c| (c.min_data_rate, c.max_data_rate)),
469            Some((0, 5))
470        );
471    }
472
473    #[test]
474    fn a_mask_list_creates_channels_in_order_of_their_numbers() {
475        // RP002-1.0.5 section 3.3.1.1's example: an EU868 device creates ChIndex 3 at 863.1,
476        // 4 at 867.3, 5 at 867.7 and 6 at 868.7 MHz.
477        let mut channels = european();
478        let list = CfList::channel_masks([0x0001, 0x10A0, 0, 0, 0, 0]);
479        channels.apply_cflist(Region::Eu868.plan(), list, |_| true);
480
481        let created: Vec<(usize, u32)> = channels
482            .enabled()
483            .skip(3)
484            .map(|(i, c)| (i, c.uplink_hz))
485            .collect();
486        assert_eq!(
487            created,
488            [
489                (3, 863_100_000),
490                (4, 867_300_000),
491                (5, 867_700_000),
492                (6, 868_700_000)
493            ]
494        );
495    }
496
497    #[test]
498    fn a_fixed_plan_takes_a_mask_list_as_which_channels_are_on() {
499        // RP002-1.0.5 section 3.5.4: channels 8 to 15 and the second 500 kHz channel, and a
500        // bit past channel 71, which is reserved.
501        let mut channels = american();
502        let list = CfList::channel_masks([0xFF00, 0, 0, 0, 0x0102, 0]);
503        channels.apply_cflist(Region::Us915.plan(), list, |_| true);
504        assert_eq!(
505            enabled_numbers(&channels),
506            [8, 9, 10, 11, 12, 13, 14, 15, 65]
507        );
508
509        let mut channels = american();
510        let frequencies = CfList::frequencies([903_100_000, 0, 0, 0, 0]).expect("valid");
511        channels.apply_cflist(Region::Us915.plan(), frequencies, |_| true);
512        assert_eq!(
513            channels.enabled().count(),
514            72,
515            "a fixed plan ignores a list of frequencies"
516        );
517    }
518
519    #[test]
520    fn a_region_with_no_numbering_ignores_a_mask_list() {
521        let plan = Region::Eu433.plan();
522        let mut channels = Channels::defaults(plan);
523        channels.apply_cflist(plan, CfList::channel_masks([0xFFFF, 0, 0, 0, 0, 0]), |_| {
524            true
525        });
526        assert_eq!(channels.enabled().count(), 3);
527    }
528
529    #[test]
530    fn a_frequency_the_radio_cannot_use_is_left_out() {
531        let mut channels = european();
532        let list = CfList::frequencies([867_100_000, 902_300_000, 0, 0, 0]).expect("valid");
533        channels.apply_cflist(Region::Eu868.plan(), list, |hz| hz < 870_000_000);
534        assert!(channels.get(3).is_some());
535        assert!(channels.get(4).is_none());
536    }
537
538    #[test]
539    fn channel_controls_set_groups_banks_and_everything() {
540        let mut channels = european();
541        channels.apply_cflist(
542            Region::Eu868.plan(),
543            CfList::frequencies([
544                867_100_000,
545                867_300_000,
546                867_500_000,
547                867_700_000,
548                867_900_000,
549            ])
550            .expect("valid"),
551            |_| true,
552        );
553        let controls = &MaskControl::DYNAMIC;
554
555        assert_eq!(
556            channels.mask_after(&[(0b0000_0101, 0)], controls),
557            Some([0b0101, 0, 0, 0, 0, 0])
558        );
559        assert_eq!(
560            channels.mask_after(&[(0, 5)], controls),
561            None,
562            "switching every bank off leaves nothing enabled"
563        );
564        assert_eq!(
565            channels.mask_after(&[(1, 5)], controls),
566            Some([0xFF, 0, 0, 0, 0, 0])
567        );
568        assert_eq!(
569            channels.mask_after(&[(0b0001, 0), (0xFFFF, 6)], controls),
570            Some([0xFF, 0, 0, 0, 0, 0]),
571            "6 enables every defined channel, whatever came before it in the block"
572        );
573        assert_eq!(
574            channels.mask_after(&[(0xFFFF, 7)], controls),
575            None,
576            "7 is reserved"
577        );
578        assert_eq!(
579            channels.mask_after(&[(1 << 9, 0)], controls),
580            None,
581            "a bit for an undefined channel refuses the block"
582        );
583        assert_eq!(
584            channels.mask_after(&[(0, 0), (0b10, 0)], controls),
585            Some([0b10, 0, 0, 0, 0, 0]),
586            "the block is taken as a whole, so an empty group partway is fine"
587        );
588    }
589
590    #[test]
591    fn the_american_controls_narrow_a_device_to_one_sub_band() {
592        // RP002-1.0.5 section 3.5.5's note: from 64-channel operation to the first eight,
593        // either as 7 then 0, or as 5 alone, which also keeps the paired 500 kHz channel.
594        let channels = american();
595        let controls = &Region::Us915.plan().mask_controls;
596
597        let two = channels
598            .mask_after(&[(0x0000, 7), (0x00FF, 0)], controls)
599            .expect("accepted");
600        assert_eq!(numbers(two), [0, 1, 2, 3, 4, 5, 6, 7]);
601
602        let one = channels
603            .mask_after(&[(0x0001, 5)], controls)
604            .expect("accepted");
605        assert_eq!(numbers(one), [0, 1, 2, 3, 4, 5, 6, 7, 64]);
606
607        let second = channels
608            .mask_after(&[(0x0002, 5)], controls)
609            .expect("accepted");
610        assert_eq!(
611            numbers(second),
612            [8, 9, 10, 11, 12, 13, 14, 15, 65],
613            "the second sub-band that many networks run"
614        );
615
616        let wide_only = channels
617            .mask_after(&[(0x00F0, 7)], controls)
618            .expect("accepted");
619        assert_eq!(numbers(wide_only), [68, 69, 70, 71]);
620
621        let everything = channels
622            .mask_after(&[(0x0000, 7), (0x00FF, 6)], controls)
623            .expect("accepted");
624        assert_eq!(numbers(everything).len(), 72);
625
626        assert_eq!(
627            channels.mask_after(&[(0x0100, 6)], controls),
628            None,
629            "channel 72 is not defined"
630        );
631        assert_eq!(
632            channels.mask_after(&[(0x0000, 7)], controls),
633            None,
634            "7 with an empty mask leaves nothing on"
635        );
636    }
637
638    #[test]
639    fn the_chinese_antenna_plans_switch_everything_and_reserve_the_rest() {
640        // RP002-1.0.5 tables 52 and 53.
641        let twenty = Cn470Plan::Antenna20MhzA.plan();
642        let channels = Channels::defaults(twenty);
643        let controls = &twenty.mask_controls;
644        assert_eq!(
645            channels.mask_after(&[(0, 7), (0x000F, 3)], controls),
646            Some([0, 0, 0, 0x000F, 0, 0])
647        );
648        assert_eq!(channels.mask_after(&[(0xFFFF, 4)], controls), None);
649        assert_eq!(channels.mask_after(&[(0, 7)], controls), None);
650
651        let twenty_six = Cn470Plan::Antenna26MhzB.plan();
652        let channels = Channels::defaults(twenty_six);
653        let controls = &twenty_six.mask_controls;
654        assert_eq!(
655            channels.mask_after(&[(0, 4), (0x8000, 2)], controls),
656            Some([0, 0, 0x8000, 0, 0, 0])
657        );
658        assert_eq!(
659            channels.mask_after(&[(0x0001, 3)], controls),
660            Some([0xFFFF, 0xFFFF, 0xFFFF, 0, 0, 0])
661        );
662        assert_eq!(
663            channels.mask_after(&[(0x0001, 3), (0x0001, 3)], controls),
664            Some([0xFFFF, 0xFFFF, 0xFFFF, 0, 0, 0])
665        );
666        assert_eq!(channels.mask_after(&[(0x0001, 5)], controls), None);
667        assert_eq!(
668            channels.mask_after(&[(0x0001, 2), (0x0001, 3)], controls),
669            Some([0xFFFF, 0xFFFF, 0xFFFF, 0, 0, 0])
670        );
671    }
672
673    #[test]
674    fn the_ninety_six_channel_plan_reaches_its_sixth_group() {
675        let plan = Cn470Plan::Channels96.plan();
676        let channels = Channels::defaults(plan);
677        assert_eq!(
678            channels.mask_after(
679                &[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0x8000, 5)],
680                &plan.mask_controls
681            ),
682            Some([0, 0, 0, 0, 0, 0x8000])
683        );
684    }
685
686    #[test]
687    fn restoring_defaults_leaves_created_channels_as_they_were() {
688        let mut channels = european();
689        channels.create(3, Channel::new(867_100_000, 0, 5));
690        channels.set_mask([0b1000, 0, 0, 0, 0, 0]);
691        channels.enable_defaults();
692        assert_eq!(channels.mask(), [0b1111, 0, 0, 0, 0, 0]);
693
694        let mut channels = american();
695        channels.set_mask([0, 0x0100, 0, 0, 0, 0]);
696        channels.enable_defaults();
697        assert_eq!(
698            channels.enabled().count(),
699            72,
700            "a fixed plan turns every channel back on"
701        );
702    }
703
704    #[test]
705    fn a_channel_carries_the_rates_in_its_range() {
706        let channel = Channel::new(868_100_000, 0, 5);
707        assert!(channel.carries(0) && channel.carries(5));
708        assert!(!channel.carries(6));
709        let channels = european();
710        assert!(channels.carries(&channels.mask(), 5));
711        assert!(!channels.carries(&channels.mask(), 6));
712    }
713}