Skip to main content

rds_rs/
decoder.rs

1#![allow(dead_code)]
2
3use crate::alt_freq_decoder::get_vhf_frequency;
4use crate::alt_freq_table::{Freq, FreqType};
5use crate::oda::{OdaEntry, decode_oda, is_oda_group_type_used, is_valid_oda_app_id};
6use crate::ptyn::decode_ptyn;
7use crate::radiotext::RtVariant;
8use crate::rds::RdsData;
9use crate::types::{
10    Content, Group, GroupType, GroupVersion, NUM_TDC, Pin, ProgramInformation, ProgramType,
11    SlcData, ValidFields,
12};
13use core::ops::BitOr;
14use modular_bitfield_msb::prelude::*;
15
16// See RBDS Standard section 3.1.5.3.
17#[bitfield(bits = 16)]
18struct GroupType2BlockB {
19    group_type: GroupType,     // Group type (code + version).
20    tp: bool,                  // TP bit.
21    program_type: ProgramType, // PTY: Program type.
22    text_flag: RtVariant,
23    text_segment_addr: B4,
24}
25
26// See RBDS Standard section 3.1.5.4.
27#[bitfield(bits = 16)]
28#[derive(Default, Clone, PartialEq, Eq)]
29struct GroupType3ABlockB {
30    group_type: GroupType,        // Group type (code + version).
31    traffic_program: bool,        // TP bit.
32    program_type: ProgramType,    // PTY: Program type.
33    application_group: GroupType, // See Annex M.
34}
35
36impl BitOr for ValidFields {
37    type Output = Self;
38
39    fn bitor(self, rhs: Self) -> Self {
40        const N: usize = core::mem::size_of::<ValidFields>();
41        let l = self.into_bytes();
42        let r = rhs.into_bytes();
43        let mut m = [0u8; N];
44        for i in 0..N {
45            m[i] = l[i] | r[i];
46        }
47        Self::from_bytes(m)
48    }
49}
50
51impl Group {
52    fn get_type(&self) -> GroupType {
53        GroupType2BlockB::from_bytes(self.b.unwrap().to_be_bytes()).group_type()
54    }
55}
56
57fn decode_block_b_common(block: &GroupType2BlockB, rds_data: &mut RdsData) -> ValidFields {
58    rds_data.tn.traffic.set_tp(block.tp());
59    rds_data.tn.program_type = block.program_type();
60    ValidFields::new().with_tp(true).with_pty(true)
61}
62
63// Type 0 groups: Basic tuning and switching information.
64fn decode_group_type_0(
65    group: &Group,
66    rds_data: &mut RdsData,
67    advanced_ps_decoding: bool,
68) -> ValidFields {
69    // See RBDS Standard section 3.1.5.1.
70    #[bitfield(bits = 16)]
71    struct BlockB {
72        group_type: GroupType,      // Group type (code + version).
73        traffic_program: bool,      // TP bit.
74        program_type: ProgramType,  // PTY: Program type.
75        traffic_announcement: bool, // TA bit: section 3.2.1.3.
76        ms: Content,                // M/S bit: section 3.2.1.4.
77        di_bit: bool,               // DI bit: section 3.2.1.5.
78        seg_addr: B2,               // Prog. service name and DI segment addr.
79    }
80
81    let mut valid = ValidFields::new();
82    let block_b = BlockB::from_bytes(group.b.unwrap().to_be_bytes());
83    if block_b.group_type().version() == GroupVersion::B && group.c.is_some() {
84        let _ = rds_data
85            .alt_freq_decoder
86            .decode_freq_block(group.c, &mut rds_data.alt_freqs);
87        valid.set_af(true);
88    }
89    // Decoder identification and Dynamic PTY indicator / DI codes.
90    // The d bits come MSB first.
91    match block_b.seg_addr() {
92        0 => rds_data.did_pty.set_dynamic_pty(block_b.di_bit()),
93        1 => rds_data.did_pty.set_compressed(block_b.di_bit()),
94        2 => rds_data.did_pty.set_artificial_head(block_b.di_bit()),
95        3 => rds_data.did_pty.set_stereo(block_b.di_bit()),
96        _ => return valid,
97    }
98    if group.d.is_none() {
99        return valid;
100    }
101    rds_data.tn.traffic.set_ta(block_b.traffic_announcement());
102    valid.set_ta(true);
103    rds_data.content = block_b.ms();
104    valid.set_ms(true);
105
106    let pair_idx = 2 * block_b.seg_addr();
107    let ps_bytes = group.d.unwrap().to_be_bytes();
108    if advanced_ps_decoding {
109        if rds_data
110            .tn
111            .ps
112            .update_advanced(pair_idx as usize, ps_bytes[0])
113        {
114            valid.set_ps(true);
115        }
116        if rds_data
117            .tn
118            .ps
119            .update_advanced((pair_idx + 1) as usize, ps_bytes[1])
120        {
121            valid.set_ps(true);
122        }
123    } else {
124        rds_data.tn.ps.update_simple(pair_idx as usize, ps_bytes);
125        valid.set_ps(true);
126    }
127    valid
128}
129
130// Type 1 groups: Program Item Number and slow labeling codes.
131fn decode_group_type_1(group: &Group, rds_data: &mut RdsData) -> ValidFields {
132    // See RBDS Standard section 3.1.5.2.
133    #[bitfield(bits = 16)]
134    struct GroupType1BlockB {
135        group_type: GroupType,     // Group type (code + version).
136        traffic_program: bool,     // TP bit.
137        program_type: ProgramType, // PTY: Program type.
138        radio_paging_codes: B5,    // See Annex M.
139    }
140
141    let mut valid = ValidFields::new();
142    let block_b = GroupType1BlockB::from_bytes(group.b.unwrap().to_be_bytes());
143    if block_b.group_type().version() == GroupVersion::A
144        && let Some(group_c) = group.c
145    {
146        rds_data.slc = SlcData::from_bytes(group_c.to_be_bytes());
147        valid.set_slc(true);
148    }
149
150    // Per spec (3.2.1.7): If a type 1 group is transmitted without a
151    // valid PIN, the day of the month shall be set to zero. In this
152    // case a receiver which evaluates PIN shall ignore the other
153    // information in block 4.
154    rds_data.tn.pin = group
155        .d
156        .map(|d| Pin::from_bytes(d.to_be_bytes()))
157        .unwrap_or_default();
158    if group.d.is_some() {
159        valid.set_pin(true);
160    }
161    valid
162}
163
164// Type 2 groups: Radiotext.
165// See RBDS Standard setion 3.1.5.3.
166fn decode_group_type_2a(group: &Group, rds_data: &mut RdsData) -> ValidFields {
167    let block_b = GroupType2BlockB::from_bytes(group.b.unwrap().to_be_bytes());
168    let chars: [Option<[u8; 2]>; 2] =
169        [group.c.map(u16::to_be_bytes), group.d.map(u16::to_be_bytes)];
170    let rt = match block_b.text_flag() {
171        RtVariant::A => &mut rds_data.rt.a,
172        RtVariant::B => &mut rds_data.rt.b,
173    };
174    let addr = (block_b.text_segment_addr() as usize) * 4;
175    rt.update_rt_simple(addr, &chars);
176    if rds_data.rt.decode_rt != block_b.text_flag() {
177        rt.bump_rt_validation_count();
178    }
179    rt.update_rt_advance(addr, &chars);
180    rds_data.rt.decode_rt = block_b.text_flag();
181    ValidFields::new().with_rt(true)
182}
183
184// Type 2 groups: Radiotext.
185// See RBDS Standard setion 3.1.5.3.
186fn decode_group_type_2b(group: &Group, rds_data: &mut RdsData) -> ValidFields {
187    if group.d.is_none() {
188        return ValidFields::new();
189    }
190    let block_b = GroupType2BlockB::from_bytes(group.b.unwrap().to_be_bytes());
191    let chars: [Option<[u8; 2]>; 1] = [group.d.map(u16::to_be_bytes)];
192    let rt = match block_b.text_flag() {
193        RtVariant::A => &mut rds_data.rt.a,
194        RtVariant::B => &mut rds_data.rt.b,
195    };
196    let addr = (block_b.text_segment_addr() as usize) * 2;
197    rt.update_rt_simple(addr, &chars);
198    if rds_data.rt.decode_rt != block_b.text_flag() {
199        rt.bump_rt_validation_count();
200    }
201    rt.update_rt_advance(addr, &chars);
202    rds_data.rt.decode_rt = block_b.text_flag();
203    ValidFields::new().with_rt(true)
204}
205
206// Type 3A groups: Application identification for Open data.
207fn decode_group_type_3a(group: &Group, rds_data: &mut RdsData) -> ValidFields {
208    let valid = ValidFields::new();
209    if group.d.is_none() {
210        return valid;
211    }
212    let block_b = GroupType3ABlockB::from_bytes(group.b.unwrap().to_be_bytes());
213    let app_id = group.d.unwrap();
214
215    // Per spec 3.1.5.4:
216    // > The AID code 0000 (Hex) may be used to indicate that the respective
217    // > group type is being used for the normal feature specified in this
218    // > standard. Application Identification codes 0001 to FFFF (Hex) indicate
219    // > applications as specified in the ODA Directory
220    if !is_valid_oda_app_id(app_id) {
221        return valid;
222    }
223
224    let entry = rds_data.oda.get_mut(&app_id);
225    if let Some(e) = entry {
226        e.group_type = block_b.group_type();
227    } else if !rds_data.oda.is_full() {
228        let _ = rds_data.oda.insert(
229            app_id,
230            OdaEntry {
231                group_type: block_b.group_type(),
232                packet_count: 0,
233            },
234        );
235    }
236    valid
237}
238
239// Type 3B groups: Open Data Application.
240fn decode_group_type_3b(group: &Group, rds_data: &mut RdsData) -> ValidFields {
241    // See RBDS Standard section 3.1.5.5.
242    decode_oda(group, group.get_type(), rds_data)
243}
244
245// Type 4A groups : Clock-time and date.
246fn decode_group_type_4a(group: &Group, rds_data: &mut RdsData) -> ValidFields {
247    // See RBDS Standard section 3.1.5.6.
248    #[bitfield(bits = 16)]
249    struct BlockB {
250        group_type: GroupType,     // Group type (code + version).
251        traffic_program: bool,     // TP bit.
252        program_type: ProgramType, // PTY: Program type.
253        spare: B3,                 // Unused.
254        date_msb: B2,              // Top two MSB bits of julian date.
255    }
256    #[bitfield(bits = 16)]
257    struct BlockC {
258        date: B15,
259        hour_msb: B1,
260    }
261    #[bitfield(bits = 16)]
262    struct BlockD {
263        hour: B4,
264        minute: B6,
265        local_offset_dir: B1, // Offset direction from UTC: 0=+, 1=-;
266        local_offset_val: B5, // Offset in half-hour increments
267    }
268
269    if group.c.is_none() || group.d.is_none() {
270        return ValidFields::new();
271    }
272    let block_b = BlockB::from_bytes(group.b.unwrap().to_be_bytes());
273    let block_c = BlockC::from_bytes(group.b.unwrap().to_be_bytes());
274    let block_d = BlockD::from_bytes(group.b.unwrap().to_be_bytes());
275
276    rds_data.clock.mjd = (u32::from(block_b.date_msb()) << 15) + u32::from(block_c.date());
277    rds_data.clock.hour = (block_c.hour_msb() << 4) + block_d.hour();
278    rds_data.clock.minute = block_d.minute();
279    rds_data.clock.utc_offset_half_hours = if block_d.local_offset_dir() == 0 {
280        block_d.local_offset_val().cast_signed()
281    } else {
282        -(block_d.local_offset_val().cast_signed())
283    };
284    ValidFields::new().with_clock(true)
285}
286
287// Type 4B groups: Open data application.
288fn decode_group_type_4b(group: &Group, rds_data: &mut RdsData) -> ValidFields {
289    // See RBDS Standard section 3.1.5.7.
290    decode_oda(group, group.get_type(), rds_data)
291}
292
293fn decode_tdc_block(block: u16, rds_data: &mut RdsData) {
294    // See RBDS Standard section 4.18.
295
296    let channel = rds_data.tdc.current_channel as usize;
297    // `channel` comes from a 5-bit value, so shouldn't be greater than 31.
298    assert!(channel < NUM_TDC);
299
300    rds_data.tdc.data[channel].write((block >> 8) as u8);
301    rds_data.tdc.data[channel].write((block & 0xff) as u8);
302}
303
304// Type 5 groups: Transparent data channels or ODA.
305fn decode_group_type_5a(group: &Group, rds_data: &mut RdsData) -> ValidFields {
306    // See RBDS Standard section 3.1.5.8.
307    #[bitfield(bits = 16)]
308    struct BlockB {
309        group_type: GroupType,     // Group type (code + version).
310        traffic_program: bool,     // TP bit.
311        program_type: ProgramType, // PTY: Program type.
312        // Address code identifies "channel number" (out of 32) to which the data are addressed.
313        address: B5,
314    }
315    let block_b = BlockB::from_bytes(group.b.unwrap().to_be_bytes());
316
317    if is_oda_group_type_used(&rds_data.oda, block_b.group_type()) {
318        return decode_oda(group, block_b.group_type(), rds_data);
319    }
320    let mut valid = ValidFields::new();
321    rds_data.tdc.current_channel = block_b.address();
322    if let Some(group_c) = group.c {
323        decode_tdc_block(group_c, rds_data);
324        valid.set_tdc(true);
325    }
326    if let Some(group_d) = group.d {
327        decode_tdc_block(group_d, rds_data);
328        valid.set_tdc(true);
329    }
330    valid
331}
332
333// Type 5 groups: ODA.
334fn decode_group_type_5b(group: &Group, rds_data: &mut RdsData) -> ValidFields {
335    // See RBDS Standard section 3.1.5.8.
336    const GROUP_TYPE: GroupType = GroupType::from_bytes([5 << (1 + GroupVersion::B as u8)]);
337    if is_oda_group_type_used(&rds_data.oda, GROUP_TYPE) {
338        return decode_oda(group, GROUP_TYPE, rds_data);
339    }
340    ValidFields::new()
341}
342
343// Type 6 groups: In-house applications or ODA/
344// See RBDS Standard section 3.1.5.9.
345fn decode_group_type_6(group: &Group, rds_data: &mut RdsData) -> ValidFields {
346    #[bitfield(bits = 16)]
347    struct BlockB {
348        group_type: GroupType,     // Group type (code + version).
349        traffic_program: bool,     // TP bit.
350        program_type: ProgramType, // PTY: Program type.
351        unused: B5,
352    }
353    let block_b = BlockB::from_bytes(group.b.unwrap().to_be_bytes());
354    if is_oda_group_type_used(&rds_data.oda, block_b.group_type()) {
355        return decode_oda(group, block_b.group_type(), rds_data);
356    }
357
358    // According to RBDS spec.: "Consumer receivers should ignore the in-house
359    // information coded in these groups".
360    ValidFields::new()
361}
362
363// Type 7A groups: Radio Paging or ODA.
364fn decode_group_type_7a(group: &Group, rds_data: &mut RdsData) -> ValidFields {
365    // See RBDS Standard section 3.1.5.10.
366    const GROUP_TYPE: GroupType = GroupType::from_bytes([7 << (1 + GroupVersion::A as u8)]);
367    if is_oda_group_type_used(&rds_data.oda, GROUP_TYPE) {
368        return decode_oda(group, GROUP_TYPE, rds_data);
369    }
370
371    // No stations seem to broadcast this data. Will implement if/when needed.
372    ValidFields::new()
373}
374
375// Type 7B groups: Open data application.
376fn decode_group_type_7b(group: &Group, rds_data: &mut RdsData) -> ValidFields {
377    // See RBDS Standard section 3.1.5.11.
378    decode_oda(group, group.get_type(), rds_data)
379}
380
381// Type 8 groups: Traffic Message Channel or ODA
382fn decode_group_type_8(group: &Group, rds_data: &mut RdsData) -> ValidFields {
383    // See RBDS Standard section 3.1.5.12.
384    let gt = group.get_type();
385    if is_oda_group_type_used(&rds_data.oda, gt) {
386        return decode_oda(group, gt, rds_data);
387    }
388    if gt.version() == GroupVersion::A {
389        // Decode TMC data. This requires obtaining a copy of EN ISO 14819-1:2013.
390    }
391    ValidFields::new()
392}
393
394// Type 9 groups: Emergency warning systems or ODA.
395fn decode_group_type_9(group: &Group, rds_data: &mut RdsData) -> ValidFields {
396    // See RBDS Standard section 3.1.5.13.
397    let gt = group.get_type();
398    if is_oda_group_type_used(&rds_data.oda, gt) {
399        return decode_oda(group, gt, rds_data);
400    }
401
402    let mut valid = ValidFields::new();
403    if gt.version() == GroupVersion::B {
404        return valid;
405    }
406
407    if group.c.is_none() || group.d.is_none() {
408        return valid;
409    }
410
411    rds_data
412        .ews
413        .set_block_b_lsb((group.b.unwrap() & 0b11111) as u8);
414    rds_data.ews.set_block_c(group.c.unwrap());
415    rds_data.ews.set_block_d(group.d.unwrap());
416    valid.set_ews(true);
417    valid
418}
419
420// Type 10 groups: Program Type Name.
421fn decode_group_type_10a(group: &Group, rds_data: &mut RdsData) -> ValidFields {
422    // See RBDS Standard section 3.1.5.14.
423    decode_ptyn(group, rds_data)
424}
425
426// Type 10 groups: Open data.
427fn decode_group_type_10b(group: &Group, rds_data: &mut RdsData) -> ValidFields {
428    // See RBDS Standard section 3.1.5.14.
429    match group.get_type().version() {
430        GroupVersion::A => decode_ptyn(group, rds_data),
431        GroupVersion::B => decode_oda(group, group.get_type(), rds_data),
432    }
433}
434
435// Type 11 groups: Open Data Application.
436fn decode_group_type_11(group: &Group, rds_data: &mut RdsData) -> ValidFields {
437    // See RBDS Standard section 3.1.5.15.
438    decode_oda(group, group.get_type(), rds_data)
439}
440
441// Type 12 groups: Open Data Application.
442fn decode_group_type_12(group: &Group, rds_data: &mut RdsData) -> ValidFields {
443    // See RBDS Standard section 3.1.5.16.
444    decode_oda(group, group.get_type(), rds_data)
445}
446
447// Type 13A groups: Enhanced Radio Paging or ODA.
448fn decode_group_type_13a(group: &Group, _rds_data: &mut RdsData) -> ValidFields {
449    // See RBDS Standard section 3.1.5.17.
450    #[bitfield(bits = 16)]
451    struct BlockB {
452        group_type: GroupType,     // Group type (code + version).
453        traffic_program: bool,     // TP bit.
454        program_type: ProgramType, // PTY: Program type.
455        information: B2,
456        sty: B3,
457    }
458    let _block_b = BlockB::from_bytes(group.b.unwrap().to_be_bytes());
459
460    // The type 13A group may be used for ODA when it is not used for Radio
461    // Paging, and its group structure is then as shown in 3.1.4.2
462
463    // TODO: How to determine if this is used for radio paging???
464    ValidFields::new()
465}
466
467// Type 13B groups: Open Data Application.
468fn decode_group_type_13b(group: &Group, rds_data: &mut RdsData) -> ValidFields {
469    // See RBDS Standard section 3.1.5.18.
470    decode_oda(group, group.get_type(), rds_data)
471}
472
473// Type 14 groups: Enhanced Other Networks information.
474fn decode_group_type_14a(group: &Group, rds_data: &mut RdsData) -> ValidFields {
475    // See RBDS Standard section 3.1.5.19.
476    #[bitfield(bits = 16)]
477    struct BlockB {
478        group_type: GroupType,     // Group type (code + version).
479        traffic_program: bool,     // TP bit.
480        program_type: ProgramType, // PTY: Program type.
481        tp_on: bool,               // TP (ON).
482        variant_code: B4,
483    }
484    let block_b = BlockB::from_bytes(group.b.unwrap().to_be_bytes());
485    let mut valid = ValidFields::new().with_tp_on(true);
486    rds_data.on.traffic.set_tp(block_b.tp_on());
487    match block_b.variant_code() {
488        0..=3 => {
489            let idx: usize = 2 * (block_b.variant_code() as usize);
490            if let Some(group_c) = group.c {
491                rds_data.tn.ps.update_simple(idx, group_c.to_be_bytes());
492                valid.set_ps_on(true);
493            }
494        }
495        4 => {
496            let _ = rds_data
497                .on_freq_decoder
498                .decode_freq_block(group.c, &mut rds_data.on_freqs);
499            valid.set_on_freqs(true);
500        }
501        5..=9 => {
502            if let Some(group_c) = group.c {
503                let freqs = group_c.to_be_bytes();
504                if freqs[1] != 0 {
505                    rds_data.map_freqs.add(Freq {
506                        frequency: get_vhf_frequency(freqs[1]),
507                        freq_type: FreqType::SameProgram,
508                    });
509                    valid.set_map_freqs(true);
510                }
511            }
512        }
513        13 => {
514            if let Some(group_c) = group.c {
515                rds_data.on.traffic.set_ta((group_c & 0b1) != 0);
516                valid.set_ta_on(true);
517            }
518        }
519        14 if group.c.is_some() => {
520            rds_data.on.pin = group
521                .c
522                .map(|c| Pin::from_bytes(c.to_be_bytes()))
523                .unwrap_or_default();
524            valid.set_pin_on(true);
525        }
526        _ => {}
527    }
528    valid
529}
530
531// Type 14 groups: Enhanced Other Networks information.
532fn decode_group_type_14b(group: &Group, rds_data: &mut RdsData) -> ValidFields {
533    // See RBDS Standard section 3.1.5.19.
534    #[bitfield(bits = 16)]
535    struct BlockB {
536        group_type: GroupType,     // Group type (code + version).
537        tp: bool,                  // TP bit.
538        program_type: ProgramType, // PTY: Program type.
539        tp_on: bool,               // TP (ON).
540        ta_on: bool,               // TA (ON).
541        unused: B3,
542    }
543    let block_b = BlockB::from_bytes(group.b.unwrap().to_be_bytes());
544    let valid = ValidFields::new()
545        .with_tp(true)
546        .with_ta_on(true)
547        .with_tp_on(true);
548    rds_data.tn.traffic.set_tp(block_b.tp());
549    rds_data.on.traffic.set_ta(block_b.ta_on());
550    rds_data.on.traffic.set_tp(block_b.tp_on());
551    // TODO: Parse PI code.
552    valid
553}
554
555// Type 15 groups: Fast basic tuning and switching information.
556fn decode_group_type_15(_group: &Group, _rds_data: &RdsData) -> ValidFields {
557    ValidFields::new()
558}
559
560pub struct Decoder {
561    advanced_ps_decoding: bool,
562}
563
564impl Decoder {
565    #[must_use]
566    pub fn new(advanced_ps_decoding: bool) -> Self {
567        Decoder {
568            advanced_ps_decoding,
569        }
570    }
571
572    /// Decode a group of RDS data and update the supplied RDS data object.
573    /// Will return a `ValidFields` bitfield which describes the updated RDS
574    /// data fields.
575    /// Note: `RdsData::valid` also describes the valid data fields, but those
576    /// are cumulative over all calls to `decode()` with the same RDS data
577    /// object.
578    pub fn decode(&mut self, group: &Group, rds_data: &mut RdsData) -> ValidFields {
579        let mut valid = ValidFields::default();
580
581        if let Some(group_a) = group.a {
582            rds_data.program_information = ProgramInformation::from_bytes(group_a.to_be_bytes());
583            valid.set_pi(true);
584        }
585
586        let Some(blk_b) = group.b else {
587            rds_data.valid = rds_data.valid | valid;
588            return valid;
589        };
590
591        // We don't yet know what block/version this is, but decode as 2B as all
592        // blocks share the first four common fields.
593        let block_b = GroupType2BlockB::from_bytes(blk_b.to_be_bytes());
594        valid = valid | decode_block_b_common(&block_b, rds_data);
595
596        let new_valid = match (block_b.group_type().code(), block_b.group_type().version()) {
597            (0, GroupVersion::A | GroupVersion::B) => {
598                decode_group_type_0(group, rds_data, self.advanced_ps_decoding)
599            }
600            (1, GroupVersion::A | GroupVersion::B) => decode_group_type_1(group, rds_data),
601            (2, GroupVersion::A) => decode_group_type_2a(group, rds_data),
602            (2, GroupVersion::B) => decode_group_type_2b(group, rds_data),
603            (3, GroupVersion::A) => decode_group_type_3a(group, rds_data),
604            (3, GroupVersion::B) => decode_group_type_3b(group, rds_data),
605            (4, GroupVersion::A) => decode_group_type_4a(group, rds_data),
606            (4, GroupVersion::B) => decode_group_type_4b(group, rds_data),
607            (5, GroupVersion::A) => decode_group_type_5a(group, rds_data),
608            (5, GroupVersion::B) => decode_group_type_5b(group, rds_data),
609            (6, GroupVersion::A | GroupVersion::B) => decode_group_type_6(group, rds_data),
610            (7, GroupVersion::A) => decode_group_type_7a(group, rds_data),
611            (7, GroupVersion::B) => decode_group_type_7b(group, rds_data),
612            (8, GroupVersion::A | GroupVersion::B) => decode_group_type_8(group, rds_data),
613            (9, GroupVersion::A | GroupVersion::B) => decode_group_type_9(group, rds_data),
614            (10, GroupVersion::A) => decode_group_type_10a(group, rds_data),
615            (10, GroupVersion::B) => decode_group_type_10b(group, rds_data),
616            (11, GroupVersion::A | GroupVersion::B) => decode_group_type_11(group, rds_data),
617            (12, GroupVersion::A | GroupVersion::B) => decode_group_type_12(group, rds_data),
618            (13, GroupVersion::A) => decode_group_type_13a(group, rds_data),
619            (13, GroupVersion::B) => decode_group_type_13b(group, rds_data),
620            (14, GroupVersion::A) => decode_group_type_14a(group, rds_data),
621            (14, GroupVersion::B) => decode_group_type_14b(group, rds_data),
622            (15, GroupVersion::A | GroupVersion::B) => decode_group_type_15(group, rds_data),
623            _ => {
624                // Other group types not implemented yet
625                ValidFields::new()
626            }
627        };
628        valid = valid | new_valid; // Merge in group decoding fields
629        rds_data.valid = rds_data.valid | valid; // And into RDS object.
630        valid
631    }
632}
633
634#[cfg(test)]
635mod tests;