mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-30 12:30:20 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			1203 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1203 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| ///////////////////////////////////////////////////////////////////////////////////
 | ||
| // Copyright (C) 2015 F4EXB                                                      //
 | ||
| // written by Edouard Griffiths                                                  //
 | ||
| //                                                                               //
 | ||
| // 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 as 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 V3 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 "rdsparser.h"
 | ||
| #include "rdstmc.h"
 | ||
| 
 | ||
| #include <QDebug>
 | ||
| #include <string.h>
 | ||
| #include <sstream>
 | ||
| #include <iostream>
 | ||
| #include <cmath>
 | ||
| #include <cstring>
 | ||
| #include "boost/format.hpp"
 | ||
| 
 | ||
| 
 | ||
| const unsigned int RDSParser::offset_pos[5] = {0,1,2,3,2};
 | ||
| const unsigned int RDSParser::offset_word[5] = {252,408,360,436,848};
 | ||
| const unsigned int RDSParser::syndrome[5] = {383,14,303,663,748};
 | ||
| const char * const RDSParser::offset_name[] = {"A","B","C","D","C'"};
 | ||
| 
 | ||
| /* page 77, Annex F in the standard */
 | ||
| const std::string RDSParser::pty_table[32] = {
 | ||
| 	"None",
 | ||
| 	"News",
 | ||
| 	"Current Affairs",
 | ||
| 	"Information",
 | ||
| 	"Sport",
 | ||
| 	"Education",
 | ||
| 	"Drama",
 | ||
| 	"Cultures",
 | ||
| 	"Science",
 | ||
| 	"Varied Speech",
 | ||
| 	"Pop Music",
 | ||
| 	"Rock Music",
 | ||
| 	"Easy Listening",
 | ||
| 	"Light Classics M",
 | ||
| 	"Serious Classics",
 | ||
| 	"Other Music",
 | ||
| 	"Weather & Metr",
 | ||
| 	"Finance",
 | ||
| 	"Children’s Progs",
 | ||
| 	"Social Affairs",
 | ||
| 	"Religion",
 | ||
| 	"Phone In",
 | ||
| 	"Travel & Touring",
 | ||
| 	"Leisure & Hobby",
 | ||
| 	"Jazz Music",
 | ||
| 	"Country Music",
 | ||
| 	"National Music",
 | ||
| 	"Oldies Music",
 | ||
| 	"Folk Music",
 | ||
| 	"Documentary",
 | ||
| 	"Alarm Test",
 | ||
| 	"Alarm-Alarm!"
 | ||
| };
 | ||
| 
 | ||
| /* page 71, Annex D, table D.1 in the standard */
 | ||
| const std::string RDSParser::pi_country_codes[15][5] = {
 | ||
| 	{"DE","GR","MA","__","MD"},
 | ||
| 	{"DZ","CY","CZ","IE","EE"},
 | ||
| 	{"AD","SM","PL","TR","__"},
 | ||
| 	{"IL","CH","VA","MK","__"},
 | ||
| 	{"IT","JO","SK","__","__"},
 | ||
| 	{"BE","FI","SY","__","UA"},
 | ||
| 	{"RU","LU","TN","__","__"},
 | ||
| 	{"PS","BG","__","NL","PT"},
 | ||
| 	{"AL","DK","LI","LV","SI"},
 | ||
| 	{"AT","GI","IS","LB","__"},
 | ||
| 	{"HU","IQ","MC","__","__"},
 | ||
| 	{"MT","GB","LT","HR","__"},
 | ||
| 	{"DE","LY","YU","__","__"},
 | ||
| 	{"__","RO","ES","SE","__"},
 | ||
| 	{"EG","FR","NO","BY","BA"}
 | ||
| };
 | ||
| 
 | ||
| /* page 72, Annex D, table D.2 in the standard */
 | ||
| const std::string RDSParser::coverage_area_codes[16] = {
 | ||
| 	"Local",
 | ||
| 	"International",
 | ||
| 	"National",
 | ||
| 	"Supra-regional",
 | ||
| 	"Regional 1",
 | ||
| 	"Regional 2",
 | ||
| 	"Regional 3",
 | ||
| 	"Regional 4",
 | ||
| 	"Regional 5",
 | ||
| 	"Regional 6",
 | ||
| 	"Regional 7",
 | ||
| 	"Regional 8",
 | ||
| 	"Regional 9",
 | ||
| 	"Regional 10",
 | ||
| 	"Regional 11",
 | ||
| 	"Regional 12"
 | ||
| };
 | ||
| 
 | ||
| const std::string RDSParser::rds_group_acronyms[16] = {
 | ||
| 	"BASIC",
 | ||
| 	"PIN/SL",
 | ||
| 	"RT",
 | ||
| 	"AID",
 | ||
| 	"CT",
 | ||
| 	"TDC",
 | ||
| 	"IH",
 | ||
| 	"RP",
 | ||
| 	"TMC",
 | ||
| 	"EWS",
 | ||
| 	"___",
 | ||
| 	"___",
 | ||
| 	"___",
 | ||
| 	"___",
 | ||
| 	"EON",
 | ||
| 	"___"
 | ||
| };
 | ||
| 
 | ||
| const std::string RDSParser::rds_group_acronym_tags[16] = {
 | ||
| 	"BAS",
 | ||
| 	"PIN",
 | ||
| 	"TXT",
 | ||
| 	"AID",
 | ||
| 	"TIM",
 | ||
| 	"TDC",
 | ||
| 	"IH_",
 | ||
| 	"RP_",
 | ||
| 	"TMC",
 | ||
| 	"EWS",
 | ||
| 	"___",
 | ||
| 	"___",
 | ||
| 	"___",
 | ||
| 	"___",
 | ||
| 	"EON",
 | ||
| 	"___"
 | ||
| };
 | ||
| 
 | ||
| /* page 74, Annex E, table E.1 in the standard: that's the ASCII table!!! */
 | ||
| 
 | ||
| /* see page 84, Annex J in the standard */
 | ||
| const std::string RDSParser::language_codes[44] = {
 | ||
| 	"N/A",
 | ||
| 	"Albanian",
 | ||
| 	"Breton",
 | ||
| 	"Catalan",
 | ||
| 	"Croatian",
 | ||
| 	"Welsh",
 | ||
| 	"Czech",
 | ||
| 	"Danish",
 | ||
| 	"German",
 | ||
| 	"English",
 | ||
| 	"Spanish",
 | ||
| 	"Esperanto",
 | ||
| 	"Estonian",
 | ||
| 	"Basque",
 | ||
| 	"Faroese",
 | ||
| 	"French",
 | ||
| 	"Frisian",
 | ||
| 	"Irish",
 | ||
| 	"Gaelic",
 | ||
| 	"Galician",
 | ||
| 	"Icelandic",
 | ||
| 	"Italian",
 | ||
| 	"Lappish",
 | ||
| 	"Latin",
 | ||
| 	"Latvian",
 | ||
| 	"Luxembourgian",
 | ||
| 	"Lithuanian",
 | ||
| 	"Hungarian",
 | ||
| 	"Maltese",
 | ||
| 	"Dutch",
 | ||
| 	"Norwegian",
 | ||
| 	"Occitan",
 | ||
| 	"Polish",
 | ||
| 	"Portuguese",
 | ||
| 	"Romanian",
 | ||
| 	"Romansh",
 | ||
| 	"Serbian",
 | ||
| 	"Slovak",
 | ||
| 	"Slovene",
 | ||
| 	"Finnish",
 | ||
| 	"Swedish",
 | ||
| 	"Turkish",
 | ||
| 	"Flemish",
 | ||
| 	"Walloon"
 | ||
| };
 | ||
| 
 | ||
| /* see page 12 in ISO 14819-1 */
 | ||
| const std::string RDSParser::tmc_duration[8][2] =
 | ||
| {
 | ||
| 	{"no duration given", "no duration given"},
 | ||
| 	{"15 minutes", "next few hours"},
 | ||
| 	{"30 minutes", "rest of the day"},
 | ||
| 	{"1 hour", "until tomorrow evening"},
 | ||
| 	{"2 hours", "rest of the week"},
 | ||
| 	{"3 hours", "end of next week"},
 | ||
| 	{"4 hours", "end of the month"},
 | ||
| 	{"rest of the day", "long period"}
 | ||
| };
 | ||
| 
 | ||
| /* optional message content, data field lengths and labels
 | ||
|  * see page 15 in ISO 14819-1 */
 | ||
| const int RDSParser::optional_content_lengths[16] = {3,3,5,5,5,8,8,8,8,11,16,16,16,16,0,0};
 | ||
| 
 | ||
| const std::string RDSParser::label_descriptions[16] = {
 | ||
| 	"Duration",
 | ||
| 	"Control code",
 | ||
| 	"Length of route affected",
 | ||
| 	"Speed limit advice",
 | ||
| 	"Quantifier",
 | ||
| 	"Quantifier",
 | ||
| 	"Supplementary information code",
 | ||
| 	"Explicit start time",
 | ||
| 	"Explicit stop time",
 | ||
| 	"Additional event",
 | ||
| 	"Detailed diversion instructions",
 | ||
| 	"Destination",
 | ||
| 	"RFU (Reserved for future use)",
 | ||
| 	"Cross linkage to source of problem, or another route",
 | ||
| 	"Separator",
 | ||
| 	"RFU (Reserved for future use)"
 | ||
| };
 | ||
| 
 | ||
| RDSParser::RDSParser()
 | ||
| {
 | ||
| 	clearAllFields();
 | ||
| }
 | ||
| 
 | ||
| RDSParser::~RDSParser()
 | ||
| {
 | ||
| }
 | ||
| 
 | ||
| void RDSParser::clearUpdateFlags()
 | ||
| {
 | ||
| 	m_pi_updated = false;
 | ||
| 	m_g0_updated = false;
 | ||
| 	m_g0_af_updated = false;
 | ||
| 	m_g1_updated = false;
 | ||
| 	m_g2_updated = false;
 | ||
| 	m_g3_updated = false;
 | ||
| 	m_g4_updated = false;
 | ||
| 	m_g5_updated = false;
 | ||
| 	m_g6_updated = false;
 | ||
| 	m_g7_updated = false;
 | ||
| 	m_g8_updated = false;
 | ||
| 	m_g9_updated = false;
 | ||
| 	m_g10_updated = false;
 | ||
| 	m_g11_updated = false;
 | ||
| 	m_g12_updated = false;
 | ||
| 	m_g13_updated = false;
 | ||
| 	m_g14_updated = false;
 | ||
| 	m_g14_data_available = false;
 | ||
| 	m_g15_updated = false;
 | ||
|     radiotext_AB_flag = false;
 | ||
|     debug = false;
 | ||
|     log = false;
 | ||
| }
 | ||
| 
 | ||
| void RDSParser::clearAllFields()
 | ||
| {
 | ||
| 	// PI data
 | ||
| 	m_pi_count = 0;
 | ||
| 	m_pi_program_identification = 0;
 | ||
| 	m_pi_program_type = 0;
 | ||
| 	m_pi_area_coverage_index = 0;
 | ||
| 	m_pi_country_identification = 0;
 | ||
| 	m_pi_traffic_program = false;
 | ||
|     pi_country_identification = 0;
 | ||
|     pi_program_reference_number = 0;
 | ||
| 
 | ||
| 	// Group 00 data
 | ||
| 	m_g0_count = 0;
 | ||
| 	std::memset(m_g0_program_service_name, ' ', sizeof(m_g0_program_service_name));
 | ||
| 	m_g0_program_service_name[sizeof(m_g0_program_service_name) - 1] = '\0';
 | ||
| 	m_g0_psn_bitmap = 0;
 | ||
| 	m_g0_traffic_announcement = false;
 | ||
| 	m_g0_music_speech = false;
 | ||
| 	m_g0_mono_stereo = false;
 | ||
| 	m_g0_artificial_head = false;
 | ||
| 	m_g0_compressed = false;
 | ||
| 	m_g0_static_pty = false;
 | ||
| 	m_g0_alt_freq.clear();
 | ||
| 
 | ||
| 	// Group 01 data
 | ||
| 	m_g1_count = 0;
 | ||
| 	m_g1_country_page_index = -1;
 | ||
| 	m_g1_country_index = -1;
 | ||
| 	m_g1_language_index = -1;
 | ||
| 	m_g1_pin_day = 0;
 | ||
| 	m_g1_pin_hour = 0;
 | ||
| 	m_g1_pin_minute = 0;
 | ||
| 
 | ||
| 	// Group 02 data
 | ||
| 	m_g2_count = 0;
 | ||
| 	std::memset(m_g2_radiotext, ' ', sizeof(m_g2_radiotext));
 | ||
| 	m_g2_radiotext[sizeof(m_g2_radiotext) - 1] = '\0';
 | ||
| 
 | ||
| 	// Group 03 data
 | ||
| 	m_g3_count = 0;
 | ||
| 	m_g3_groupB = false;
 | ||
| 	m_g3_appGroup = 0;
 | ||
| 	m_g3_message = 0;
 | ||
| 	m_g3_aid = 0;
 | ||
| 
 | ||
| 
 | ||
| 	// Group 04 data
 | ||
| 	m_g4_count = 0;
 | ||
| 	m_g4_hours = 0;
 | ||
| 	m_g4_minutes = 0;
 | ||
| 	m_g4_seconds = 0;
 | ||
| 	m_g4_year = 0;
 | ||
| 	m_g4_month = 0;
 | ||
| 	m_g4_day = 0;
 | ||
| 	m_g4_local_time_offset = 0.0;
 | ||
| 
 | ||
| 	// Group 05..07 data
 | ||
| 	m_g5_count = 0;
 | ||
| 	m_g6_count = 0;
 | ||
| 	m_g7_count = 0;
 | ||
| 
 | ||
| 	// Group 08 data
 | ||
| 	m_g8_count = 0;
 | ||
| 	m_g8_diversion_recommended = false;
 | ||
| 	m_g8_dp_ci = 0;
 | ||
| 	m_g8_sign = false;
 | ||
| 	m_g8_extent = 0;
 | ||
| 	m_g8_event = 0;
 | ||
| 	m_g8_location = 0;
 | ||
| 	m_g8_label_index = -1;
 | ||
| 	m_g8_content = 0;
 | ||
| 
 | ||
| 	// Group 09 data
 | ||
| 	m_g9_varA = 0;
 | ||
| 	m_g9_cA = 0;
 | ||
| 	m_g9_dA = 0;
 | ||
| 	m_g9_varB = 0;
 | ||
| 	m_g9_dB = 0;
 | ||
| 
 | ||
| 	// Group 10..13 data
 | ||
| 	m_g9_count = 0;
 | ||
| 	m_g10_count = 0;
 | ||
| 	m_g11_count = 0;
 | ||
| 	m_g12_count = 0;
 | ||
| 	m_g13_count = 0;
 | ||
| 
 | ||
| 	// Group 14
 | ||
| 	m_g14_count = 0;
 | ||
| 	m_g14_program_service_names.clear();
 | ||
| 	m_g14_alt_freqs.clear();
 | ||
| 	m_g14_mapped_freqs.clear();
 | ||
| 	std::memset(m_g14_program_service_name, ' ', sizeof(m_g14_program_service_name));
 | ||
| 	m_g14_program_service_name[8] = '\0';
 | ||
| 	m_g14_alt_freq_set.clear();
 | ||
| 	m_g14_mapped_freq_set.clear();
 | ||
| 	m_g14_psn_counter = 0;
 | ||
| 
 | ||
| 	// Group 15
 | ||
| 	m_g15_count = 0;
 | ||
| 
 | ||
| 	clearUpdateFlags();
 | ||
| }
 | ||
| 
 | ||
| void RDSParser::parseGroup(unsigned int *group)
 | ||
| {
 | ||
| 	unsigned int group_type = (unsigned int)((group[1] >> 12) & 0xf);
 | ||
| 	bool ab = (group[1] >> 11 ) & 0x1;
 | ||
| 
 | ||
| 	/*
 | ||
| 	qDebug() << "RDSParser::parseGroup:"
 | ||
| 			<< " type: " << group_type << (ab ? 'B' :'A')
 | ||
| 			<< " (" << rds_group_acronyms[group_type].c_str() << ")";*/
 | ||
| 
 | ||
| 	m_pi_count++;
 | ||
| 	m_pi_updated = true;
 | ||
| 
 | ||
| 	m_pi_program_identification = group[0];                // "PI"
 | ||
| 	m_pi_traffic_program        = (group[1] >> 10) & 0x01; // "TP"
 | ||
| 	m_pi_program_type           = (group[1] >> 5) & 0x1f;  // "PTY"
 | ||
| 	m_pi_country_identification = (m_pi_program_identification >> 12) & 0xf;
 | ||
| 	m_pi_area_coverage_index = (m_pi_program_identification >> 8) & 0xf;
 | ||
| 
 | ||
| 	/*
 | ||
| 	std::string pistring = str(boost::format("%04X") % m_pi_program_identification);
 | ||
| 
 | ||
| 	qDebug() << "RDSParser::parseGroup:"
 | ||
| 			<< " PI:" << pistring.c_str()
 | ||
| 			<< " - " << "PTY:" << pty_table[m_pi_program_type].c_str()
 | ||
| 			<< " (country:" << (pi_country_codes[m_pi_country_identification - 1][0]).c_str()
 | ||
| 			<< "/" << (pi_country_codes[m_pi_country_identification - 1][1]).c_str()
 | ||
| 			<< "/" << (pi_country_codes[m_pi_country_identification - 1][2]).c_str()
 | ||
| 			<< "/" << (pi_country_codes[m_pi_country_identification - 1][3]).c_str()
 | ||
| 			<< "/" << (pi_country_codes[m_pi_country_identification - 1][4]).c_str()
 | ||
| 			<< ", area:" << coverage_area_codes[m_pi_area_coverage_index].c_str()
 | ||
| 			<< ", program:" << int(pi_program_reference_number) << ")";*/
 | ||
| 
 | ||
| 	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;*/
 | ||
| }
 | ||
| 
 | ||
| /* BASIC TUNING: see page 21 of the standard */
 | ||
| void RDSParser::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;
 | ||
| 
 | ||
| 	m_g0_count++;
 | ||
| 	m_g0_updated = true;
 | ||
| 
 | ||
| 	m_pi_traffic_program      = (group[1] >> 10) & 0x01;       // "TP"
 | ||
| 	m_g0_traffic_announcement = (group[1] >>  4) & 0x01;       // "TA"
 | ||
| 	m_g0_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"
 | ||
| 
 | ||
| 	if (segment_address == 0)
 | ||
| 	{
 | ||
| 		std::memset(m_g0_program_service_name, ' ', sizeof(m_g0_program_service_name));
 | ||
| 		m_g0_program_service_name[sizeof(m_g0_program_service_name) - 1] = '\0';
 | ||
| 		m_g0_psn_bitmap = 0;
 | ||
| 	}
 | ||
| 
 | ||
| 	m_g0_program_service_name[segment_address * 2]     = (group[3] >> 8) & 0xff;
 | ||
| 	m_g0_program_service_name[segment_address * 2 + 1] =  group[3]       & 0xff;
 | ||
| 	m_g0_psn_bitmap |= 1<<segment_address;
 | ||
| 
 | ||
| 	/* see page 41, table 9 of the standard */
 | ||
| 	switch (segment_address)
 | ||
| 	{
 | ||
| 		case 0:
 | ||
| 			m_g0_mono_stereo = decoder_control_bit;
 | ||
| 			break;
 | ||
| 		case 1:
 | ||
| 			m_g0_artificial_head = decoder_control_bit;
 | ||
| 			break;
 | ||
| 		case 2:
 | ||
| 			m_g0_compressed = decoder_control_bit;
 | ||
| 			break;
 | ||
| 		case 3:
 | ||
| 			m_g0_static_pty = decoder_control_bit;
 | ||
| 			break;
 | ||
| 		default:
 | ||
| 			break;
 | ||
| 	}
 | ||
| 
 | ||
| 	/* unused
 | ||
| 	flagstring[0] = m_pi_traffic_program        ? '1' : '0';
 | ||
| 	flagstring[1] = m_g0_traffic_announcement   ? '1' : '0';
 | ||
| 	flagstring[2] = m_g0_music_speech           ? '1' : '0';
 | ||
| 	flagstring[3] = m_g0_mono_stereo            ? '1' : '0';
 | ||
| 	flagstring[4] = m_g0_artificial_head        ? '1' : '0';
 | ||
| 	flagstring[5] = m_g0_compressed             ? '1' : '0';
 | ||
| 	flagstring[6] = m_g0_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)
 | ||
| 		{
 | ||
|             // @TODO: Find proper header or STL on OSX
 | ||
| 			auto res = m_g0_alt_freq.insert(af_1/1e3);
 | ||
| 			m_g0_af_updated = m_g0_af_updated || res.second;
 | ||
| 			no_af += 1;
 | ||
| 		}
 | ||
| 
 | ||
| 		if (af_2)
 | ||
| 		{
 | ||
|             // @TODO: Find proper header or STL on OSX
 | ||
|             auto res = m_g0_alt_freq.insert(af_2/1e3);
 | ||
|             m_g0_af_updated = m_g0_af_updated || res.second;
 | ||
| 			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);
 | ||
| 		}*/
 | ||
| 	}
 | ||
| 
 | ||
| 	/*
 | ||
| 	qDebug() << "RDSParser::decode_type0: "
 | ||
| 			<< "\"" << std::string(m_g0_program_service_name, 8).c_str()
 | ||
| 			<< "\" -" << (m_pi_traffic_program ? "TP" : "!TP")
 | ||
| 			<< '-' << (m_g0_traffic_announcement ? "TA" : "!TA")
 | ||
| 			<< '-' << (m_g0_music_speech ? "Music" : "Speech")
 | ||
| 			<< '-' << (m_g0_mono_stereo ? "MONO" : "STEREO")
 | ||
| 			<< " - AF:" << af_string.c_str();*/
 | ||
| }
 | ||
| 
 | ||
| double RDSParser::decode_af(unsigned int af_code)
 | ||
| {
 | ||
| 	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
 | ||
| 	{
 | ||
| 		alt_frequency   = 0;
 | ||
| 	}
 | ||
| 
 | ||
| 	if ((af_code >= 225) && (af_code <= 249))      // VHF frequencies follow
 | ||
| 	{
 | ||
| 		alt_frequency   = 0;
 | ||
| 		vhf_or_lfmf     = 1;
 | ||
| 	}
 | ||
| 
 | ||
| 	if (af_code == 250)                            // an LF/MF frequency follows
 | ||
| 	{
 | ||
| 		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 RDSParser::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;
 | ||
| 	m_g1_pin_day    = (unsigned int)((group[3] >> 11) & 0x1f);
 | ||
| 	m_g1_pin_hour   = (unsigned int)((group[3] >>  6) & 0x1f);
 | ||
| 	m_g1_pin_minute = (unsigned int) (group[3]        & 0x3f);
 | ||
| 
 | ||
| 	m_g1_count++;
 | ||
| 
 | ||
| 	if (radio_paging_codes) {
 | ||
| 		//qDebug() << "RDSParser::decode_type1: paging codes: " << int(radio_paging_codes) << " ";
 | ||
| 	}
 | ||
| 
 | ||
| 	if (m_g1_pin_day || m_g1_pin_hour || m_g1_pin_minute) {
 | ||
| 		m_g1_updated = true;
 | ||
| 		/*
 | ||
| 		std::string s = str(boost::format("program item: %id, %i, %i ") % day % hour % minute);
 | ||
| 		qDebug() << "RDSParser::decode_type1: " << s.c_str();*/
 | ||
| 	}
 | ||
| 
 | ||
| 	if (!B)
 | ||
| 	{
 | ||
| 		switch (variant_code)
 | ||
| 		{
 | ||
| 			case 0: // paging + ecc
 | ||
| 				paging = (slow_labelling >> 8) & 0x0f;
 | ||
| 				ecc    =  slow_labelling       & 0xff;
 | ||
| 				if (paging) {
 | ||
| 					//qDebug() << "RDSParser::decode_type1: " << "paging: " << paging << " ";
 | ||
| 				}
 | ||
| 				if ((ecc > 223) && (ecc < 229)) {
 | ||
| 					m_g1_updated = true;
 | ||
| 					m_g1_country_page_index = country_code - 1;
 | ||
| 					m_g1_country_index = ecc - 224;
 | ||
| 					/*
 | ||
| 					qDebug() << "RDSParser::decode_type1: " << "extended country code: "
 | ||
| 						<< (pi_country_codes[country_code-1][ecc-224]).c_str();*/
 | ||
| 				} else {
 | ||
| 					qDebug() << "RDSParser::decode_type1: " << "invalid extended country code: " << ecc;
 | ||
| 				}
 | ||
| 				break;
 | ||
| 			case 1: // TMC identification
 | ||
| 				//qDebug() << "RDSParser::decode_type1: TMC identification code received";
 | ||
| 				break;
 | ||
| 			case 2: // Paging identification
 | ||
| 				//qDebug() << "RDSParser::decode_type1: Paging identification code received";
 | ||
| 				break;
 | ||
| 			case 3: // language codes
 | ||
| 				if (slow_labelling < 44) {
 | ||
| 					m_g1_updated = true;
 | ||
| 					m_g1_language_index = slow_labelling;
 | ||
| 					//qDebug() << "RDSParser::decode_type1: " << "language: " << language_codes[slow_labelling].c_str();
 | ||
| 				} else {
 | ||
| 					qDebug() << "RDSParser::decode_type1: " << "language: invalid language code " << slow_labelling;
 | ||
| 				}
 | ||
| 				break;
 | ||
| 			default:
 | ||
| 				break;
 | ||
| 		}
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| void RDSParser::decode_type2(unsigned int *group, bool B)
 | ||
| {
 | ||
| 	unsigned char text_segment_address_code = group[1] & 0x0f;
 | ||
| 
 | ||
| 	m_g2_updated = true;
 | ||
| 	m_g2_count++;
 | ||
| 
 | ||
| 	// when the A/B flag is toggled, flush your current radiotext
 | ||
| 	if (radiotext_AB_flag != ((group[1] >> 4) & 0x01))
 | ||
| 	{
 | ||
| 		std::memset(m_g2_radiotext, ' ', sizeof(m_g2_radiotext));
 | ||
| 		m_g2_radiotext[sizeof(m_g2_radiotext) - 1] = '\0';
 | ||
| 	}
 | ||
| 
 | ||
| 	radiotext_AB_flag = (group[1] >> 4) & 0x01;
 | ||
| 
 | ||
| 	if (!B)
 | ||
| 	{
 | ||
| 		m_g2_radiotext[text_segment_address_code * 4    ] = (group[2] >> 8) & 0xff;
 | ||
| 		m_g2_radiotext[text_segment_address_code * 4 + 1] =  group[2]       & 0xff;
 | ||
| 		m_g2_radiotext[text_segment_address_code * 4 + 2] = (group[3] >> 8) & 0xff;
 | ||
| 		m_g2_radiotext[text_segment_address_code * 4 + 3] =  group[3]       & 0xff;
 | ||
| 	}
 | ||
| 	else
 | ||
| 	{
 | ||
| 		m_g2_radiotext[text_segment_address_code * 2    ] = (group[3] >> 8) & 0xff;
 | ||
| 		m_g2_radiotext[text_segment_address_code * 2 + 1] =  group[3]       & 0xff;
 | ||
| 	}
 | ||
| 
 | ||
| 	/*
 | ||
| 	qDebug() << "RDSParser::decode_type2: " << "Radio Text " << (radiotext_AB_flag ? 'B' : 'A')
 | ||
| 		<< ": " << std::string(m_g2_radiotext, sizeof(m_g2_radiotext)).c_str();*/
 | ||
| }
 | ||
| 
 | ||
| void RDSParser::decode_type3(unsigned int *group, bool B)
 | ||
| {
 | ||
| 	if (B) {
 | ||
| 		qDebug() << "RDSParser::decode_type3: type 3B not implemented yet";
 | ||
| 		return;
 | ||
| 	}
 | ||
| 
 | ||
| 	int application_group = (group[1] >> 1) & 0xf;
 | ||
| 	int group_type        =  group[1] & 0x1;
 | ||
| 	int message           =  group[2];
 | ||
| 	int aid               =  group[3];
 | ||
| 
 | ||
| 
 | ||
| 	m_g3_updated = true;
 | ||
| 	m_g3_count++;
 | ||
| 
 | ||
| 	/*
 | ||
| 	qDebug() << "RDSParser::decode_type3: aid group: " << application_group
 | ||
| 		<< " " << (group_type ? 'B' : 'A');*/
 | ||
| 
 | ||
| 	if ((application_group == 8) && (group_type == 0))
 | ||
| 	{ // 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
 | ||
| 
 | ||
| 			/*
 | ||
| 			qDebug() << "RDSParser::decode_type3: location table: " << ltn << " - "
 | ||
| 				<< (afi ? "AFI-ON" : "AFI-OFF") << " - "
 | ||
| 				<< (M   ? "enhanced mode" : "basic mode") << " - "
 | ||
| 				<< (I   ? "international " : "")
 | ||
| 				<< (N   ? "national " : "")
 | ||
| 				<< (R   ? "regional " : "")
 | ||
| 				<< (U   ? "urban" : "")
 | ||
| 				<< " aid: " << aid;*/
 | ||
| 		}
 | ||
| 		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};
 | ||
| 			/*
 | ||
| 			qDebug() << "RDSParser::decode_type3: gap: " << gap_no[G] << " groups, SID: "
 | ||
| 				<< sid << " ";*/
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	m_g3_groupB = group_type;
 | ||
| 	m_g3_appGroup = application_group;
 | ||
| 	m_g3_message = message;
 | ||
| 	m_g3_aid = aid;
 | ||
| 
 | ||
| 	//qDebug() << "RDSParser::decode_type3: message: " << message << " - aid: " << aid;
 | ||
| }
 | ||
| 
 | ||
| void RDSParser::decode_type4(unsigned int *group, bool B)
 | ||
| {
 | ||
| 	if (B)
 | ||
| 	{
 | ||
| 		qDebug() << "RDSParser::decode_type4: type 4B not implemented yet";
 | ||
| 		return;
 | ||
| 	}
 | ||
| 
 | ||
| 	m_g4_updated = true;
 | ||
| 	m_g4_count++;
 | ||
| 
 | ||
| 	m_g4_hours   = ((group[2] & 0x1) << 4) | ((group[3] >> 12) & 0x0f);
 | ||
| 	m_g4_minutes =  (group[3] >> 6) & 0x3f;
 | ||
| 	m_g4_local_time_offset = .5 * (group[3] & 0x1f);
 | ||
| 
 | ||
| 	if ((group[3] >> 5) & 0x1) {
 | ||
| 		m_g4_local_time_offset *= -1;
 | ||
| 	}
 | ||
| 
 | ||
| 	double modified_julian_date = ((group[1] & 0x03) << 15) | ((group[2] >> 1) & 0x7fff);
 | ||
| 
 | ||
| 	m_g4_year  = int((modified_julian_date - 15078.2) / 365.25);
 | ||
| 	m_g4_month = int((modified_julian_date - 14956.1 - int(m_g4_year * 365.25)) / 30.6001);
 | ||
| 	m_g4_day   =               modified_julian_date - 14956 - int(m_g4_year * 365.25) - int(m_g4_month * 30.6001);
 | ||
| 	bool K = ((m_g4_month == 14) || (m_g4_month == 15)) ? 1 : 0;
 | ||
| 	m_g4_year += K;
 | ||
| 	m_g4_month -= 1 + K * 12;
 | ||
| 
 | ||
| 	/*
 | ||
| 	std::string time = str(boost::format("%02i.%02i.%4i, %02i:%02i (%+.1fh)")\
 | ||
| 		% m_g4_day % m_g4_month % (1900 + m_g4_year) % m_g4_hours % m_g4_minutes % m_g4_local_time_offset);
 | ||
| 
 | ||
| 	qDebug() << "RDSParser::decode_type4: Clocktime: " << time.c_str();*/
 | ||
| 
 | ||
| 	//send_message(5,time);
 | ||
| }
 | ||
| 
 | ||
| void RDSParser::decode_type5(unsigned int *group, bool B) {
 | ||
|     (void) group;
 | ||
|     (void) B;
 | ||
| 	qDebug() << "RDSParser::decode_type5: type5 not implemented yet";
 | ||
| 	m_g5_updated = true;
 | ||
| 	m_g5_count++;
 | ||
| }
 | ||
| 
 | ||
| void RDSParser::decode_type6(unsigned int *group, bool B) {
 | ||
|     (void) group;
 | ||
|     (void) B;
 | ||
| 	qDebug() << "RDSParser::decode_type6: type 6 not implemented yet";
 | ||
| 	m_g6_updated = true;
 | ||
| 	m_g6_count++;
 | ||
| }
 | ||
| 
 | ||
| void RDSParser::decode_type7(unsigned int *group, bool B) {
 | ||
|     (void) group;
 | ||
|     (void) B;
 | ||
| 	qDebug() << "RDSParser::decode_type7: type 7 not implemented yet";
 | ||
| 	m_g7_updated = true;
 | ||
| 	m_g7_count++;
 | ||
| }
 | ||
| 
 | ||
| void RDSParser::decode_type8(unsigned int *group, bool B)
 | ||
| {
 | ||
| 	if (B)
 | ||
| 	{
 | ||
| 		qDebug() << "RDSParser::decode_type8: type 8B not implemented yet";
 | ||
| 		return;
 | ||
| 	}
 | ||
| 
 | ||
| 	m_g8_updated = true;
 | ||
| 	m_g8_count++;
 | ||
| 
 | ||
| 	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
 | ||
| 	m_g8_diversion_recommended = D;
 | ||
| 
 | ||
| 	static unsigned long int free_format[4];
 | ||
| 	static int no_groups = 0;
 | ||
| 
 | ||
| 	if (T)
 | ||
| 	{ // tuning info
 | ||
| 		qDebug() << "RDSParser::decode_type8: #tuning info# ";
 | ||
| 		int variant = group[1] & 0xf;
 | ||
| 
 | ||
| 		if((variant > 3) && (variant < 10)) {
 | ||
| 			qDebug() << "RDSParser::decode_type8: variant: " << variant << " - "
 | ||
| 				<< group[2] << " " << group[3];
 | ||
| 		} else {
 | ||
| 			qDebug() << "RDSParser::decode_type8: invalid variant: " << variant;
 | ||
| 		}
 | ||
| 
 | ||
| 	}
 | ||
| 	else if (F || D)
 | ||
| 	{ // single-group or 1st of multi-group
 | ||
| 		m_g8_dp_ci            =  group[1]        & 0x7;   // duration & persistence or continuity index
 | ||
| 		m_g8_sign             = (group[2] >> 14) & 0x1;   // event direction, 0 = +, 1 = -
 | ||
| 		m_g8_extent           = (group[2] >> 11) & 0x7;   // number of segments affected
 | ||
| 		m_g8_event            =  group[2]        & 0x7ff; // event code, defined in ISO 14819-2
 | ||
| 		m_g8_location         =  group[3];                // location code, defined in ISO 14819-3
 | ||
| 
 | ||
| 		qDebug() << "RDSParser::decode_type8: #user msg# " << (D ? "diversion recommended, " : "");
 | ||
| 
 | ||
| 		if (F) {
 | ||
| 			qDebug() << "RDSParser::decode_type8: single-grp, duration:" << (tmc_duration[m_g8_dp_ci][0]).c_str();
 | ||
| 		} else {
 | ||
| 			qDebug() << "RDSParser::decode_type8: multi-grp, continuity index:" << m_g8_dp_ci;
 | ||
| 		}
 | ||
| 
 | ||
| 		int event_line = RDSTMC::get_tmc_event_code_index(m_g8_event, 1);
 | ||
| 
 | ||
| 		qDebug() << "RDSParser::decode_type8: extent:" << (m_g8_sign ? "-" : "") << m_g8_extent + 1 << " segments"
 | ||
| 			<< ", event" << m_g8_event << ":" << RDSTMC::get_tmc_events(event_line, 1).c_str()
 | ||
| 			<< ", location:" << m_g8_location;
 | ||
| 
 | ||
| 	}
 | ||
| 	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
 | ||
| 
 | ||
| 		qDebug() << "RDSParser::decode_type8: #user msg# multi-grp, continuity index:" << ci
 | ||
| 			<< (sg ? ", second group" : "") << ", gsi:" << gsi;
 | ||
| 
 | ||
| 		qDebug() << "RDSParser::decode_type8: free format: " << (group[2] & 0xfff) << " "
 | ||
| 			<< group[3];
 | ||
| 		// 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 RDSParser::decode_optional_content(int no_groups, unsigned long int *free_format)
 | ||
| {
 | ||
| 	int content_length = 0;
 | ||
| 	int ff_pointer     = 0;
 | ||
| 
 | ||
| 	if (no_groups == 0)
 | ||
| 	{
 | ||
|         int i = 0;
 | ||
| //	for (int i = no_groups; i == 0; i--) i is always 0 if no_groups is 0 and exit else skip
 | ||
| //	{
 | ||
| 		ff_pointer = 12 + 16;
 | ||
| 
 | ||
| 		while(ff_pointer >= 7) // ff_pointer must be >= 0 and is decreased by 7 in the loop
 | ||
| 		{
 | ||
| 			ff_pointer -= 4;
 | ||
| 			m_g8_label_index = (free_format[i] && ((0xf << ff_pointer) != 0)) ? 1 : 0;
 | ||
| 			content_length = 3; // optional_content_lengths[m_g8_label_index]; // always 3
 | ||
| 			ff_pointer -= content_length;
 | ||
| 			m_g8_content = (free_format[i] && ((int(std::pow(2, content_length) - 1) << ff_pointer) != 0)) ? 1 : 0;
 | ||
| 
 | ||
| 			qDebug() << "RDSParser::decode_optional_content:"
 | ||
| 			        << " TMC optional content (" << label_descriptions[m_g8_label_index].c_str() << ")"
 | ||
| 			        << ": " << m_g8_content;
 | ||
| 		}
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| void RDSParser::decode_type9(unsigned int *group, bool B){
 | ||
| 
 | ||
| 	if (B)
 | ||
| 	{
 | ||
| 		m_g9_varB = group[1] & 0x1f;
 | ||
| 		m_g9_dB   = group[3];
 | ||
| 	}
 | ||
| 	else
 | ||
| 	{
 | ||
| 		m_g9_varA = group[1] & 0x1f;
 | ||
| 		m_g9_cA   = group[2];
 | ||
| 		m_g9_dA   = group[3];
 | ||
| 	}
 | ||
| 
 | ||
| 	m_g9_updated = true;
 | ||
| 	m_g9_count++;
 | ||
| }
 | ||
| 
 | ||
| void RDSParser::decode_type10(unsigned int *group, bool B)
 | ||
| {
 | ||
|     (void) group;
 | ||
|     (void) B;
 | ||
| 	qDebug() << "RDSParser::decode_type10: type 10 not implemented yet";
 | ||
| 	m_g10_updated = true;
 | ||
| 	m_g10_count++;
 | ||
| }
 | ||
| 
 | ||
| void RDSParser::decode_type11(unsigned int *group, bool B)
 | ||
| {
 | ||
|     (void) group;
 | ||
|     (void) B;
 | ||
| 	qDebug() << "RDSParser::decode_type11: type 11 not implemented yet";
 | ||
| 	m_g11_updated = true;
 | ||
| 	m_g11_count++;
 | ||
| }
 | ||
| 
 | ||
| void RDSParser::decode_type12(unsigned int *group, bool B)
 | ||
| {
 | ||
|     (void) group;
 | ||
|     (void) B;
 | ||
| 	qDebug() << "RDSParser::decode_type12: type 12 not implemented yet";
 | ||
| 	m_g12_updated = true;
 | ||
| 	m_g12_count++;
 | ||
| }
 | ||
| 
 | ||
| void RDSParser::decode_type13(unsigned int *group, bool B)
 | ||
| {
 | ||
|     (void) group;
 | ||
|     (void) B;
 | ||
| 	qDebug() << "RDSParser::decode_type13: type 13 not implemented yet";
 | ||
| 	m_g13_updated = true;
 | ||
| 	m_g13_count++;
 | ||
| }
 | ||
| 
 | ||
| void RDSParser::decode_type14(unsigned int *group, bool B)
 | ||
| {
 | ||
| 	char variant_code        = group[1] & 0x0f;
 | ||
| 	unsigned int information = group[2];
 | ||
| 	unsigned int pi_on       = group[3];
 | ||
| 
 | ||
| 	bool ta_on = 0;
 | ||
| 	double af_1 = 0;
 | ||
| 	double af_2 = 0;
 | ||
| 
 | ||
| 	m_g14_updated = true;
 | ||
| 	m_g14_count++;
 | ||
| 
 | ||
| 	if (!B)
 | ||
| 	{
 | ||
| 		switch (variant_code)
 | ||
| 		{
 | ||
| 			case 0: // PS(ON)
 | ||
| 			case 1: // PS(ON)
 | ||
| 			case 2: // PS(ON)
 | ||
| 			case 3: // PS(ON)
 | ||
| 			{
 | ||
| 				m_g14_program_service_name[variant_code * 2    ] = (information >> 8) & 0xff;
 | ||
| 				m_g14_program_service_name[variant_code * 2 + 1] =  information       & 0xff;
 | ||
| 				m_g14_psn_counter++;
 | ||
| 				//qDebug() << "RDSParser::decode_type14: PS(ON): \"" << std::string(m_g14_program_service_name, 8).c_str() << "\"";
 | ||
| 				break;
 | ||
| 			}
 | ||
| 			case 4: // AF
 | ||
| 			{
 | ||
| 				af_1 = 100.0 * (((information >> 8) & 0xff) + 875);
 | ||
| 				af_2 = 100.0 * ((information & 0xff) + 875);
 | ||
| 				m_g14_alt_freq_set.insert(af_1/1000.0);
 | ||
| 				m_g14_alt_freq_set.insert(af_2/1000.0);
 | ||
| 				/*
 | ||
| 				std::string s = str(boost::format("AF:%3.2fMHz %3.2fMHz") % (af_1/1000) % (af_2/1000));
 | ||
| 				qDebug() << "RDSParser::decode_type14: " << s.c_str();*/
 | ||
| 				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);
 | ||
| 				m_g14_mapped_freq_set.insert(af_2/1000.0);
 | ||
| 				/*
 | ||
| 				std::string s = str(boost::format("TN:%3.2fMHz - ON:%3.2fMHz") % (af_1/1000) % (af_2/1000));
 | ||
| 				qDebug() << "RDSParser::decode_type14: " << s.c_str();*/
 | ||
| 				break;
 | ||
| 			}
 | ||
| 			case 9: // mapped frequencies (AM)
 | ||
| 			{
 | ||
| 				af_1 = 100.0 * (((information >> 8) & 0xff) + 875);
 | ||
| 				af_2 = 9.0 * ((information & 0xff) - 16) + 531;
 | ||
| 				m_g14_mapped_freq_set.insert(af_2/1000.0);
 | ||
| 				/*
 | ||
| 				std::string s = str(boost::format("TN:%3.2fMHz - ON:%ikHz") % (af_1/1000) % int(af_2));
 | ||
| 				qDebug() << "RDSParser::decode_type14: " << s.c_str();*/
 | ||
| 				break;
 | ||
| 			}
 | ||
| 			case 10: // unallocated
 | ||
| 				break;
 | ||
| 			case 11: // unallocated
 | ||
| 				break;
 | ||
| 			case 12: // linkage information
 | ||
| 			{
 | ||
| 				if (m_g14_psn_counter == 4)
 | ||
| 				{
 | ||
| 					//qDebug("RDSParser::decode_type14: m_g14_psn_updated: %d", m_g14_psn_counter);
 | ||
| 					std::pair<psns_map_t::iterator, bool> ret = m_g14_program_service_names.insert(psns_map_kv_t(pi_on, std::string(m_g14_program_service_name)));
 | ||
| 					std::memset(m_g14_program_service_name, ' ', sizeof(m_g14_program_service_name));
 | ||
| 					m_g14_program_service_name[8] = '\0';
 | ||
| 					m_g14_psn_counter = 0;
 | ||
| 					m_g14_data_available = ret.second;
 | ||
| 				}
 | ||
| 
 | ||
| 				if (m_g14_alt_freq_set.size() > 0)
 | ||
| 				{
 | ||
| 					//qDebug("RDSParser::decode_type14: m_g14_alt_freq_set updated");
 | ||
| 
 | ||
| 					std::pair<freqs_map_t::iterator, bool> retMap;
 | ||
| 					std::pair<freqs_set_t::iterator, bool> retSet;
 | ||
| 					bool updated = false;
 | ||
| 
 | ||
| 					freqs_map_t::iterator mIt = m_g14_alt_freqs.find(pi_on);
 | ||
| 
 | ||
| 					if (mIt == m_g14_alt_freqs.end()) // key does not exist yet => insert the whole set
 | ||
| 					{
 | ||
| 						retMap = m_g14_alt_freqs.insert(freqs_map_kv_t(pi_on, m_g14_alt_freq_set));
 | ||
| 						updated |= retMap.second;
 | ||
| 					}
 | ||
| 					else // merge sets
 | ||
| 					{
 | ||
| 						freqs_set_t::iterator sIt = m_g14_alt_freq_set.begin();
 | ||
| 						const freqs_set_t::iterator sItEnd = m_g14_alt_freq_set.end();
 | ||
| 
 | ||
| 						for (; sIt != sItEnd; ++sIt)
 | ||
| 						{
 | ||
| 							retSet = (mIt->second).insert(*sIt);
 | ||
| 							updated |= retSet.second;
 | ||
| 						}
 | ||
| 					}
 | ||
| 
 | ||
| 					m_g14_alt_freq_set.clear();
 | ||
| 					m_g14_data_available |= updated;
 | ||
| 				}
 | ||
| 
 | ||
| 				if (m_g14_mapped_freq_set.size() > 0)
 | ||
| 				{
 | ||
| 					//qDebug("RDSParser::decode_type14: m_g14_mapped_freq_set updated");
 | ||
| 
 | ||
| 					std::pair<freqs_map_t::iterator, bool> retMap;
 | ||
| 					std::pair<freqs_set_t::iterator, bool> retSet;
 | ||
| 					bool updated = false;
 | ||
| 
 | ||
| 					freqs_map_t::iterator mIt = m_g14_mapped_freqs.find(pi_on);
 | ||
| 
 | ||
| 					if (mIt == m_g14_mapped_freqs.end()) // key does not exist yet => insert the whole set
 | ||
| 					{
 | ||
| 						retMap = m_g14_mapped_freqs.insert(freqs_map_kv_t(pi_on, m_g14_mapped_freq_set));
 | ||
| 						updated |= retMap.second;
 | ||
| 					}
 | ||
| 					else // merge sets
 | ||
| 					{
 | ||
| 						freqs_set_t::iterator sIt = m_g14_mapped_freq_set.begin();
 | ||
| 						const freqs_set_t::iterator sItEnd = m_g14_mapped_freq_set.end();
 | ||
| 
 | ||
| 						for (; sIt != sItEnd; ++sIt)
 | ||
| 						{
 | ||
| 							retSet = (mIt->second).insert(*sIt);
 | ||
| 							updated |= retSet.second;
 | ||
| 						}
 | ||
| 					}
 | ||
| 
 | ||
| 					m_g14_mapped_freq_set.clear();
 | ||
| 					m_g14_data_available |= updated;
 | ||
| 				}
 | ||
| 
 | ||
| 				/*
 | ||
| 				std::string s = str(boost::format("Linkage information: %x%x") % ((information >> 8) & 0xff) % (information & 0xff));
 | ||
| 				qDebug() << "RDSParser::decode_type14: " << s.c_str();*/
 | ||
| 				break;
 | ||
| 			}
 | ||
| 			case 13: // PTY(ON), TA(ON)
 | ||
| 			{
 | ||
| 				ta_on = information & 0x01;
 | ||
| 				//qDebug() << "RDSParser::decode_type14: PTY(ON):" << pty_table[int(pty_on)].c_str();
 | ||
| 				if(ta_on) {
 | ||
| 					qDebug() << "RDSParser::decode_type14:  - TA";
 | ||
| 				}
 | ||
| 				break;
 | ||
| 			}
 | ||
| 			case 14: // PIN(ON)
 | ||
| 			{
 | ||
| 				/*
 | ||
| 				std::string s = str(boost::format("PIN(ON):%x%x") % ((information >> 8) & 0xff) % (information & 0xff));
 | ||
| 				qDebug() << "RDSParser::decode_type14: " << s.c_str();*/
 | ||
| 				break;
 | ||
| 			}
 | ||
| 			case 15: // Reserved for broadcasters use
 | ||
| 				break;
 | ||
| 			default:
 | ||
| 				//qDebug() << "RDSParser::decode_type14: invalid variant code:" << variant_code;
 | ||
| 				break;
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	/*
 | ||
| 	if (pi_on)
 | ||
| 	{
 | ||
| 		std::string pistring = str(boost::format("%04X") % pi_on);
 | ||
| 		qDebug() << "RDSParser::decode_type14: PI(ON):" << pistring.c_str();
 | ||
| 
 | ||
| 		if (tp_on) {
 | ||
| 			qDebug() << "RDSParser::decode_type14: TP(ON)";
 | ||
| 		}
 | ||
| 	}*/
 | ||
| }
 | ||
| 
 | ||
| void RDSParser::decode_type15(unsigned int *group, bool B)
 | ||
| {
 | ||
|     (void) group;
 | ||
|     (void) B;
 | ||
| 	qDebug() << "RDSParser::decode_type5: type 15 not implemented yet";
 | ||
| 	m_g15_updated = true;
 | ||
| 	m_g15_count++;
 | ||
| }
 | ||
| 
 |