aboutsummaryrefslogtreecommitdiffstats
path: root/rdsparse/parser_impl.cc
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2016-03-18 16:02:25 +0100
committerMatthias P. Braendli <matthias.braendli@mpb.li>2016-03-18 16:02:25 +0100
commit78a5d1945922e9d4b9932c9d39d61a9f192bcd56 (patch)
treece99822cb4b1f143b32ddf3d9b6de83f6b6f4160 /rdsparse/parser_impl.cc
parent626c6d11cba32fac3bef312c8791df15f5bbf65a (diff)
downloadmmbtools-aux-78a5d1945922e9d4b9932c9d39d61a9f192bcd56.tar.gz
mmbtools-aux-78a5d1945922e9d4b9932c9d39d61a9f192bcd56.tar.bz2
mmbtools-aux-78a5d1945922e9d4b9932c9d39d61a9f192bcd56.zip
Add rdparse tool in development
Diffstat (limited to 'rdsparse/parser_impl.cc')
-rw-r--r--rdsparse/parser_impl.cc622
1 files changed, 622 insertions, 0 deletions
diff --git a/rdsparse/parser_impl.cc b/rdsparse/parser_impl.cc
new file mode 100644
index 0000000..f4a4f9a
--- /dev/null
+++ b/rdsparse/parser_impl.cc
@@ -0,0 +1,622 @@
+/*
+ * Copyright (C) 2014 Bastian Bloessl <bloessl@ccs-labs.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include <iomanip>
+#define dout debug && std::cout
+#define lout log && std::cout
+
+#include "parser_impl.h"
+#include "constants.h"
+#include "tmc_events.h"
+#include <math.h>
+#include <boost/format.hpp>
+
+using namespace rds;
+
+parser_impl::parser_impl(bool log, bool debug, unsigned char pty_locale)
+ : log(log),
+ debug(debug),
+ pty_locale(pty_locale)
+{
+ reset();
+}
+
+void parser_impl::reset() {
+ memset(radiotext, ' ', sizeof(radiotext));
+ memset(program_service_name, '.', sizeof(program_service_name));
+
+ radiotext_AB_flag = 0;
+ traffic_program = false;
+ traffic_announcement = false;
+ music_speech = false;
+ program_type = 0;
+ pi_country_identification = 0;
+ pi_area_coverage = 0;
+ pi_program_reference_number = 0;
+ mono_stereo = false;
+ artificial_head = false;
+ compressed = false;
+ static_pty = false;
+}
+
+/* type 0 = PI
+ * type 1 = PS
+ * type 2 = PTY
+ * type 3 = flagstring: TP, TA, MuSp, MoSt, AH, CMP, stPTY
+ * type 4 = RadioText
+ * type 5 = ClockTime
+ * type 6 = Alternative Frequencies */
+void parser_impl::send_message(long id, std::string message)
+{
+ std::cout << "Message " << message << std::endl;
+}
+
+
+/* BASIC TUNING: see page 21 of the standard */
+void parser_impl::decode_type0(unsigned int *group, bool B) {
+ unsigned int af_code_1 = 0;
+ unsigned int af_code_2 = 0;
+ unsigned int no_af = 0;
+ double af_1 = 0;
+ double af_2 = 0;
+ char flagstring[8] = "0000000";
+
+ traffic_program = (group[1] >> 10) & 0x01; // "TP"
+ traffic_announcement = (group[1] >> 4) & 0x01; // "TA"
+ music_speech = (group[1] >> 3) & 0x01; // "MuSp"
+
+ bool decoder_control_bit = (group[1] >> 2) & 0x01; // "DI"
+ unsigned char segment_address = group[1] & 0x03; // "DI segment"
+
+ program_service_name[segment_address * 2] = (group[3] >> 8) & 0xff;
+ program_service_name[segment_address * 2 + 1] = group[3] & 0xff;
+
+ /* see page 41, table 9 of the standard */
+ switch (segment_address) {
+ case 0:
+ mono_stereo=decoder_control_bit;
+ break;
+ case 1:
+ artificial_head=decoder_control_bit;
+ break;
+ case 2:
+ compressed=decoder_control_bit;
+ break;
+ case 3:
+ static_pty=decoder_control_bit;
+ break;
+ default:
+ break;
+ }
+ flagstring[0] = traffic_program ? '1' : '0';
+ flagstring[1] = traffic_announcement ? '1' : '0';
+ flagstring[2] = music_speech ? '1' : '0';
+ flagstring[3] = mono_stereo ? '1' : '0';
+ flagstring[4] = artificial_head ? '1' : '0';
+ flagstring[5] = compressed ? '1' : '0';
+ flagstring[6] = static_pty ? '1' : '0';
+ static std::string af_string;
+
+ if(!B) { // type 0A
+ af_code_1 = int(group[2] >> 8) & 0xff;
+ af_code_2 = int(group[2]) & 0xff;
+ af_1 = decode_af(af_code_1);
+ af_2 = decode_af(af_code_2);
+
+ if(af_1) {
+ no_af += 1;
+ }
+ if(af_2) {
+ no_af += 2;
+ }
+
+ std::string af1_string;
+ std::string af2_string;
+ /* only AF1 => no_af==1, only AF2 => no_af==2, both AF1 and AF2 => no_af==3 */
+ if(no_af) {
+ if(af_1 > 80e3) {
+ af1_string = str(boost::format("%2.2fMHz") % (af_1/1e3));
+ } else if((af_1<2e3)&&(af_1>100)) {
+ af1_string = str(boost::format("%ikHz") % int(af_1));
+ }
+ if(af_2 > 80e3) {
+ af2_string = str(boost::format("%2.2fMHz") % (af_2/1e3));
+ } else if ((af_2 < 2e3) && (af_2 > 100)) {
+ af2_string = str(boost::format("%ikHz") % int(af_2));
+ }
+ }
+ if(no_af == 1) {
+ af_string = af1_string;
+ } else if(no_af == 2) {
+ af_string = af2_string;
+ } else if(no_af == 3) {
+ af_string = str(boost::format("%s, %s") % af1_string %af2_string);
+ }
+ }
+
+ lout << "==>" << std::string(program_service_name, 8)
+ << "<== -" << (traffic_program ? "TP" : " ")
+ << '-' << (traffic_announcement ? "TA" : " ")
+ << '-' << (music_speech ? "Music" : "Speech")
+ << '-' << (mono_stereo ? "MONO" : "STEREO")
+ << " - AF:" << af_string << std::endl;
+
+ send_message(1, std::string(program_service_name, 8));
+ send_message(3, flagstring);
+ send_message(6, af_string);
+}
+
+double parser_impl::decode_af(unsigned int af_code) {
+ static unsigned int number_of_freqs = 0;
+ static bool vhf_or_lfmf = 0; // 0 = vhf, 1 = lf/mf
+ double alt_frequency = 0; // in kHz
+
+ if((af_code == 0) || // not to be used
+ ( af_code == 205) || // filler code
+ ((af_code >= 206) && (af_code <= 223)) || // not assigned
+ ( af_code == 224) || // No AF exists
+ ( af_code >= 251)) { // not assigned
+ number_of_freqs = 0;
+ alt_frequency = 0;
+ }
+ if((af_code >= 225) && (af_code <= 249)) { // VHF frequencies follow
+ number_of_freqs = af_code - 224;
+ alt_frequency = 0;
+ vhf_or_lfmf = 1;
+ }
+ if(af_code == 250) { // an LF/MF frequency follows
+ number_of_freqs = 1;
+ alt_frequency = 0;
+ vhf_or_lfmf = 0;
+ }
+
+ if((af_code > 0) && (af_code < 205) && vhf_or_lfmf)
+ alt_frequency = 100.0 * (af_code + 875); // VHF (87.6-107.9MHz)
+ else if((af_code > 0) && (af_code < 16) && !vhf_or_lfmf)
+ alt_frequency = 153.0 + (af_code - 1) * 9; // LF (153-279kHz)
+ else if((af_code > 15) && (af_code < 136) && !vhf_or_lfmf)
+ alt_frequency = 531.0 + (af_code - 16) * 9 + 531; // MF (531-1602kHz)
+
+ return alt_frequency;
+}
+
+void parser_impl::decode_type1(unsigned int *group, bool B){
+ int ecc = 0;
+ int paging = 0;
+ char country_code = (group[0] >> 12) & 0x0f;
+ char radio_paging_codes = group[1] & 0x1f;
+ int variant_code = (group[2] >> 12) & 0x7;
+ unsigned int slow_labelling = group[2] & 0xfff;
+ int day = (int)((group[3] >> 11) & 0x1f);
+ int hour = (int)((group[3] >> 6) & 0x1f);
+ int minute = (int) (group[3] & 0x3f);
+
+ if(radio_paging_codes) {
+ lout << "paging codes: " << int(radio_paging_codes) << " ";
+ }
+ if(day || hour || minute) {
+ lout << boost::format("program item: %id, %i, %i ") % day % hour % minute;
+ }
+
+ if(!B){
+ switch(variant_code){
+ case 0: // paging + ecc
+ paging = (slow_labelling >> 8) & 0x0f;
+ ecc = slow_labelling & 0xff;
+ if(paging) {
+ lout << "paging: " << paging << " ";
+ }
+ if((ecc > 223) && (ecc < 229)) {
+ lout << "extended country code: "
+ << pi_country_codes[country_code-1][ecc-224]
+ << std::endl;
+ } else {
+ lout << "invalid extended country code: " << ecc << std::endl;
+ }
+ break;
+ case 1: // TMC identification
+ lout << "TMC identification code received" << std::endl;
+ break;
+ case 2: // Paging identification
+ lout << "Paging identification code received" << std::endl;
+ break;
+ case 3: // language codes
+ if(slow_labelling < 44) {
+ lout << "language: " << language_codes[slow_labelling]
+ << std::endl;
+ } else {
+ lout << "language: invalid language code " << slow_labelling
+ << std::endl;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void parser_impl::decode_type2(unsigned int *group, bool B){
+ unsigned char text_segment_address_code = group[1] & 0x0f;
+
+ // when the A/B flag is toggled, flush your current radiotext
+ if(radiotext_AB_flag != ((group[1] >> 4) & 0x01)) {
+ std::memset(radiotext, ' ', sizeof(radiotext));
+ }
+ radiotext_AB_flag = (group[1] >> 4) & 0x01;
+
+ if(!B) {
+ radiotext[text_segment_address_code *4 ] = (group[2] >> 8) & 0xff;
+ radiotext[text_segment_address_code * 4 + 1] = group[2] & 0xff;
+ radiotext[text_segment_address_code * 4 + 2] = (group[3] >> 8) & 0xff;
+ radiotext[text_segment_address_code * 4 + 3] = group[3] & 0xff;
+ } else {
+ radiotext[text_segment_address_code * 2 ] = (group[3] >> 8) & 0xff;
+ radiotext[text_segment_address_code * 2 + 1] = group[3] & 0xff;
+ }
+ lout << "Radio Text " << (radiotext_AB_flag ? 'B' : 'A')
+ << ": " << std::string(radiotext, sizeof(radiotext))
+ << std::endl;
+ send_message(4,std::string(radiotext, sizeof(radiotext)));
+}
+
+void parser_impl::decode_type3(unsigned int *group, bool B){
+ if(B) {
+ dout << "type 3B not implemented yet" << std::endl;
+ return;
+ }
+
+ int application_group = (group[1] >> 1) & 0xf;
+ int group_type = group[1] & 0x1;
+ int message = group[2];
+ int aid = group[3];
+
+ lout << "aid group: " << application_group
+ << " " << (group_type ? 'B' : 'A');
+ if((application_group == 8) && (group_type == false)) { // 8A
+ int variant_code = (message >> 14) & 0x3;
+ if(variant_code == 0) {
+ int ltn = (message >> 6) & 0x3f; // location table number
+ bool afi = (message >> 5) & 0x1; // alternative freq. indicator
+ bool M = (message >> 4) & 0x1; // mode of transmission
+ bool I = (message >> 3) & 0x1; // international
+ bool N = (message >> 2) & 0x1; // national
+ bool R = (message >> 1) & 0x1; // regional
+ bool U = message & 0x1; // urban
+ lout << "location table: " << ltn << " - "
+ << (afi ? "AFI-ON" : "AFI-OFF") << " - "
+ << (M ? "enhanced mode" : "basic mode") << " - "
+ << (I ? "international " : "")
+ << (N ? "national " : "")
+ << (R ? "regional " : "")
+ << (U ? "urban" : "")
+ << " aid: " << aid << std::endl;
+
+ } else if(variant_code==1) {
+ int G = (message >> 12) & 0x3; // gap
+ int sid = (message >> 6) & 0x3f; // service identifier
+ int gap_no[4] = {3, 5, 8, 11};
+ lout << "gap: " << gap_no[G] << " groups, SID: "
+ << sid << " ";
+ }
+ }
+ lout << "message: " << message << " - aid: " << aid << std::endl;;
+}
+
+void parser_impl::decode_type4(unsigned int *group, bool B){
+ if(B) {
+ dout << "type 4B not implemented yet" << std::endl;
+ return;
+ }
+
+ unsigned int hours = ((group[2] & 0x1) << 4) | ((group[3] >> 12) & 0x0f);
+ unsigned int minutes = (group[3] >> 6) & 0x3f;
+ double local_time_offset = .5 * (group[3] & 0x1f);
+
+ if((group[3] >> 5) & 0x1) {
+ local_time_offset *= -1;
+ }
+ double modified_julian_date = ((group[1] & 0x03) << 15) | ((group[2] >> 1) & 0x7fff);
+
+ unsigned int year = int((modified_julian_date - 15078.2) / 365.25);
+ unsigned int month = int((modified_julian_date - 14956.1 - int(year * 365.25)) / 30.6001);
+ unsigned int day = modified_julian_date - 14956 - int(year * 365.25) - int(month * 30.6001);
+ bool K = ((month == 14) || (month == 15)) ? 1 : 0;
+ year += K;
+ month -= 1 + K * 12;
+
+ std::string time = str(boost::format("%02i.%02i.%4i, %02i:%02i (%+.1fh)")\
+ % day % month % (1900 + year) % hours % minutes % local_time_offset);
+ lout << "Clocktime: " << time << std::endl;
+
+ send_message(5,time);
+}
+
+void parser_impl::decode_type5(unsigned int *group, bool B){
+ dout << "type 5 not implemented yet" << std::endl;
+}
+
+void parser_impl::decode_type6(unsigned int *group, bool B){
+ dout << "type 6 not implemented yet" << std::endl;
+}
+
+void parser_impl::decode_type7(unsigned int *group, bool B){
+ dout << "type 7 not implemented yet" << std::endl;
+}
+
+void parser_impl::decode_type8(unsigned int *group, bool B){
+ if(B) {
+ dout << "type 8B not implemented yet" << std::endl;
+ return;
+ }
+ bool T = (group[1] >> 4) & 0x1; // 0 = user message, 1 = tuning info
+ bool F = (group[1] >> 3) & 0x1; // 0 = multi-group, 1 = single-group
+ bool D = (group[2] > 15) & 0x1; // 1 = diversion recommended
+ static unsigned long int free_format[4];
+ static int no_groups = 0;
+
+ if(T) { // tuning info
+ lout << "#tuning info# ";
+ int variant = group[1] & 0xf;
+ if((variant > 3) && (variant < 10)) {
+ lout << "variant: " << variant << " - "
+ << group[2] << " " << group[3] << std::endl;
+ } else {
+ lout << "invalid variant: " << variant << std::endl;
+ }
+
+ } else if(F || D) { // single-group or 1st of multi-group
+ unsigned int dp_ci = group[1] & 0x7; // duration & persistence or continuity index
+ bool sign = (group[2] >> 14) & 0x1; // event direction, 0 = +, 1 = -
+ unsigned int extent = (group[2] >> 11) & 0x7; // number of segments affected
+ unsigned int event = group[2] & 0x7ff; // event code, defined in ISO 14819-2
+ unsigned int location = group[3]; // location code, defined in ISO 14819-3
+ lout << "#user msg# " << (D ? "diversion recommended, " : "");
+ if(F) {
+ lout << "single-grp, duration:" << tmc_duration[dp_ci][0];
+ } else {
+ lout << "multi-grp, continuity index:" << dp_ci;
+ }
+ int event_line = tmc_event_code_index[event][1];
+ lout << ", extent:" << (sign ? "-" : "") << extent + 1 << " segments"
+ << ", event" << event << ":" << tmc_events[event_line][1]
+ << ", location:" << location << std::endl;
+
+ } else { // 2nd or more of multi-group
+ unsigned int ci = group[1] & 0x7; // countinuity index
+ bool sg = (group[2] >> 14) & 0x1; // second group
+ unsigned int gsi = (group[2] >> 12) & 0x3; // group sequence
+ lout << "#user msg# multi-grp, continuity index:" << ci
+ << (sg ? ", second group" : "") << ", gsi:" << gsi;
+ lout << ", free format: " << (group[2] & 0xfff) << " "
+ << group[3] << std::endl;
+ // it's not clear if gsi=N-2 when gs=true
+ if(sg) {
+ no_groups = gsi;
+ }
+ free_format[gsi] = ((group[2] & 0xfff) << 12) | group[3];
+ if(gsi == 0) {
+ decode_optional_content(no_groups, free_format);
+ }
+ }
+}
+
+void parser_impl::decode_optional_content(int no_groups, unsigned long int *free_format){
+ int label = 0;
+ int content = 0;
+ int content_length = 0;
+ int ff_pointer = 0;
+
+ for (int i = no_groups; i == 0; i--){
+ ff_pointer = 12 + 16;
+ while(ff_pointer > 0){
+ ff_pointer -= 4;
+ label = (free_format[i] && (0xf << ff_pointer));
+ content_length = optional_content_lengths[label];
+ ff_pointer -= content_length;
+ content = (free_format[i] && (int(pow(2, content_length) - 1) << ff_pointer));
+ lout << "TMC optional content (" << label_descriptions[label]
+ << "):" << content << std::endl;
+ }
+ }
+}
+
+void parser_impl::decode_type9(unsigned int *group, bool B){
+ dout << "type 9 not implemented yet" << std::endl;
+}
+
+void parser_impl::decode_type10(unsigned int *group, bool B){
+ dout << "type 10 not implemented yet" << std::endl;
+}
+
+void parser_impl::decode_type11(unsigned int *group, bool B){
+ dout << "type 11 not implemented yet" << std::endl;
+}
+
+void parser_impl::decode_type12(unsigned int *group, bool B){
+ dout << "type 12 not implemented yet" << std::endl;
+}
+
+void parser_impl::decode_type13(unsigned int *group, bool B){
+ dout << "type 13 not implemented yet" << std::endl;
+}
+
+void parser_impl::decode_type14(unsigned int *group, bool B){
+
+ bool tp_on = (group[1] >> 4) & 0x01;
+ char variant_code = group[1] & 0x0f;
+ unsigned int information = group[2];
+ unsigned int pi_on = group[3];
+
+ char pty_on = 0;
+ bool ta_on = 0;
+ static char ps_on[8] = {' ',' ',' ',' ',' ',' ',' ',' '};
+ double af_1 = 0;
+ double af_2 = 0;
+
+ if (!B){
+ switch (variant_code){
+ case 0: // PS(ON)
+ case 1: // PS(ON)
+ case 2: // PS(ON)
+ case 3: // PS(ON)
+ ps_on[variant_code * 2 ] = (information >> 8) & 0xff;
+ ps_on[variant_code * 2 + 1] = information & 0xff;
+ lout << "PS(ON): ==>" << std::string(ps_on, 8) << "<==";
+ break;
+ case 4: // AF
+ af_1 = 100.0 * (((information >> 8) & 0xff) + 875);
+ af_2 = 100.0 * ((information & 0xff) + 875);
+ lout << boost::format("AF:%3.2fMHz %3.2fMHz") % (af_1/1000) % (af_2/1000);
+ break;
+ case 5: // mapped frequencies
+ case 6: // mapped frequencies
+ case 7: // mapped frequencies
+ case 8: // mapped frequencies
+ af_1 = 100.0 * (((information >> 8) & 0xff) + 875);
+ af_2 = 100.0 * ((information & 0xff) + 875);
+ lout << boost::format("TN:%3.2fMHz - ON:%3.2fMHz") % (af_1/1000) % (af_2/1000);
+ break;
+ case 9: // mapped frequencies (AM)
+ af_1 = 100.0 * (((information >> 8) & 0xff) + 875);
+ af_2 = 9.0 * ((information & 0xff) - 16) + 531;
+ lout << boost::format("TN:%3.2fMHz - ON:%ikHz") % (af_1/1000) % int(af_2);
+ break;
+ case 10: // unallocated
+ break;
+ case 11: // unallocated
+ break;
+ case 12: // linkage information
+ lout << boost::format("Linkage information: %x%x")
+ % ((information >> 8) & 0xff) % (information & 0xff);
+ break;
+ case 13: // PTY(ON), TA(ON)
+ ta_on = information & 0x01;
+ pty_on = (information >> 11) & 0x1f;
+ lout << "PTY(ON):" << pty_table[int(pty_on)][pty_locale];
+ if(ta_on) {
+ lout << " - TA";
+ }
+ break;
+ case 14: // PIN(ON)
+ lout << boost::format("PIN(ON):%x%x")
+ % ((information >> 8) & 0xff) % (information & 0xff);
+ break;
+ case 15: // Reserved for broadcasters use
+ break;
+ default:
+ dout << "invalid variant code:" << variant_code;
+ break;
+ }
+ }
+ if (pi_on){
+ lout << " PI(ON):" << pi_on;
+ if (tp_on) {
+ lout << "-TP-";
+ }
+ }
+ lout << std::endl;
+}
+
+void parser_impl::decode_type15(unsigned int *group, bool B){
+ dout << "type 15 not implemented yet" << std::endl;
+}
+
+void parser_impl::parse(unsigned int* group) {
+
+ unsigned int group_type = (unsigned int)((group[1] >> 12) & 0xf);
+ bool ab = (group[1] >> 11 ) & 0x1;
+
+ lout << boost::format("%02i%c ") % group_type % (ab ? 'B' :'A');
+ lout << "(" << rds_group_acronyms[group_type] << ")";
+
+ program_identification = group[0]; // "PI"
+ program_type = (group[1] >> 5) & 0x1f; // "PTY"
+ int pi_country_identification = (program_identification >> 12) & 0xf;
+ int pi_area_coverage = (program_identification >> 8) & 0xf;
+ unsigned char pi_program_reference_number = program_identification & 0xff;
+ std::string pistring = str(boost::format("%04X") % program_identification);
+ send_message(0, pistring);
+ send_message(2, pty_table[program_type][pty_locale]);
+
+ lout << " - PI:" << pistring << " - " << "PTY:" << pty_table[program_type][pty_locale];
+ lout << " (country:" << pi_country_codes[pi_country_identification - 1][0];
+ lout << "/" << pi_country_codes[pi_country_identification - 1][1];
+ lout << "/" << pi_country_codes[pi_country_identification - 1][2];
+ lout << "/" << pi_country_codes[pi_country_identification - 1][3];
+ lout << "/" << pi_country_codes[pi_country_identification - 1][4];
+ lout << ", area:" << coverage_area_codes[pi_area_coverage];
+ lout << ", program:" << int(pi_program_reference_number) << ")" << std::endl;
+
+ switch (group_type) {
+ case 0:
+ decode_type0(group, ab);
+ break;
+ case 1:
+ decode_type1(group, ab);
+ break;
+ case 2:
+ decode_type2(group, ab);
+ break;
+ case 3:
+ decode_type3(group, ab);
+ break;
+ case 4:
+ decode_type4(group, ab);
+ break;
+ case 5:
+ decode_type5(group, ab);
+ break;
+ case 6:
+ decode_type6(group, ab);
+ break;
+ case 7:
+ decode_type7(group, ab);
+ break;
+ case 8:
+ decode_type8(group, ab);
+ break;
+ case 9:
+ decode_type9(group, ab);
+ break;
+ case 10:
+ decode_type10(group, ab);
+ break;
+ case 11:
+ decode_type11(group, ab);
+ break;
+ case 12:
+ decode_type12(group, ab);
+ break;
+ case 13:
+ decode_type13(group, ab);
+ break;
+ case 14:
+ decode_type14(group, ab);
+ break;
+ case 15:
+ decode_type15(group, ab);
+ break;
+ }
+
+ #define HEX(a) std::hex << std::setfill('0') << std::setw(4) << long(a) << std::dec
+ for(int i = 0; i < 4; i++) {
+ dout << " " << HEX(group[i]);
+ }
+ dout << std::endl;
+}
+