| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:02:48 +01:00
										 |  |  | // Copyright (C) 2015-2019 Edouard Griffiths, F4EXB <f4exb06@gmail.com>          //
 | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // 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                  //
 | 
					
						
							| 
									
										
										
										
											2019-04-11 06:39:30 +02:00
										 |  |  | // (at your option) any later version.                                           //
 | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // 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/>.          //
 | 
					
						
							|  |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-29 03:22:04 +01:00
										 |  |  | #include "rdsdecoder.h"
 | 
					
						
							| 
									
										
										
										
											2016-10-02 13:18:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | #include <QDebug>
 | 
					
						
							| 
									
										
										
										
											2017-12-29 03:22:04 +01:00
										 |  |  | #include <string.h>
 | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | const unsigned int RDSDecoder::offset_pos[5] = {0,1,2,3,2}; | 
					
						
							|  |  |  | const unsigned int RDSDecoder::offset_word[5] = {252,408,360,436,848}; | 
					
						
							|  |  |  | const unsigned int RDSDecoder::syndrome[5] = {383,14,303,663,748}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RDSDecoder::RDSDecoder() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	m_reg = 0; | 
					
						
							|  |  |  | 	m_presync = false; | 
					
						
							|  |  |  | 	m_lastseenOffsetCounter = 0; | 
					
						
							|  |  |  | 	m_bitCounter = 0; | 
					
						
							|  |  |  | 	m_lastseenOffset = 0; | 
					
						
							|  |  |  | 	m_blocksCounter        = 0; | 
					
						
							|  |  |  | 	m_blockBitCounter      = 0; | 
					
						
							|  |  |  | 	m_blockNumber          = 0; | 
					
						
							|  |  |  | 	m_groupAssemblyStarted = false; | 
					
						
							|  |  |  | 	m_sync                 = SYNC; | 
					
						
							|  |  |  | 	m_groupGoodBlocksCounter = 0; | 
					
						
							| 
									
										
										
										
											2015-12-20 19:30:58 +01:00
										 |  |  | 	m_wrongBlocksCounter   = 0; | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | 	m_goodBlock            = false; | 
					
						
							| 
									
										
										
										
											2017-05-05 10:40:45 +02:00
										 |  |  | 	m_qua                  = 0.0f; | 
					
						
							| 
									
										
										
										
											2017-12-29 03:22:04 +01:00
										 |  |  | 	memset(m_group, 0, 4*sizeof(int)); | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RDSDecoder::~RDSDecoder() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-13 09:45:29 +01:00
										 |  |  | bool RDSDecoder::frameSync(bool bit) | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2015-12-13 09:45:29 +01:00
										 |  |  | 	bool group_ready = false; | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | 	unsigned int reg_syndrome; | 
					
						
							|  |  |  | 	unsigned long bit_distance, block_distance; | 
					
						
							|  |  |  | 	unsigned int block_calculated_crc, block_received_crc, checkword, dataword; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	m_reg = (m_reg<<1) | bit; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (m_sync) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 	case NO_SYNC: | 
					
						
							|  |  |  | 		reg_syndrome = calc_syndrome(m_reg,26); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for (int j = 0; j < 5; j++) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			if (reg_syndrome == syndrome[j]) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				if (!m_presync) | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					m_lastseenOffset = j; | 
					
						
							|  |  |  | 					m_lastseenOffsetCounter = m_bitCounter; | 
					
						
							|  |  |  | 					m_presync=true; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				else | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					bit_distance = m_bitCounter - m_lastseenOffsetCounter; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					if (offset_pos[m_lastseenOffset] >= offset_pos[j]) | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						block_distance = offset_pos[j] + 4 - offset_pos[m_lastseenOffset]; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					else | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						block_distance = offset_pos[j] - offset_pos[m_lastseenOffset]; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					if ((block_distance*26)!=bit_distance) | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						m_presync = false; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					else | 
					
						
							|  |  |  | 					{ | 
					
						
							| 
									
										
										
										
											2015-12-15 02:55:12 +01:00
										 |  |  | 						//qDebug("RDSDecoder::frameSync: Sync State Detected");
 | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | 						enter_sync(j); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				break; //syndrome found, no more cycles
 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case SYNC: | 
					
						
							|  |  |  | 		// wait until 26 bits enter the buffer
 | 
					
						
							|  |  |  | 		if (m_blockBitCounter < 25) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			m_blockBitCounter++; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			m_goodBlock = false; | 
					
						
							|  |  |  | 			dataword = (m_reg>>10) & 0xffff; | 
					
						
							|  |  |  | 			block_calculated_crc = calc_syndrome(dataword, 16); | 
					
						
							|  |  |  | 			checkword = m_reg & 0x3ff; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// manage special case of C or C' offset word
 | 
					
						
							|  |  |  | 			if (m_blockNumber == 2) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				block_received_crc = checkword ^ offset_word[m_blockNumber]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if (block_received_crc==block_calculated_crc) | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					m_goodBlock = true; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				else | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					block_received_crc=checkword^offset_word[4]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					if (block_received_crc==block_calculated_crc) | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						m_goodBlock = true; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					else | 
					
						
							|  |  |  | 					{ | 
					
						
							| 
									
										
										
										
											2015-12-20 19:30:58 +01:00
										 |  |  | 						m_wrongBlocksCounter++; | 
					
						
							|  |  |  | 						m_goodBlock = false; | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				block_received_crc = checkword ^ offset_word[m_blockNumber]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if (block_received_crc==block_calculated_crc) | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					m_goodBlock = true; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				else | 
					
						
							|  |  |  | 				{ | 
					
						
							| 
									
										
										
										
											2015-12-20 19:30:58 +01:00
										 |  |  | 					m_wrongBlocksCounter++; | 
					
						
							|  |  |  | 					m_goodBlock = false; | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			//done checking CRC
 | 
					
						
							|  |  |  | 			if (m_blockNumber == 0 && m_goodBlock) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				m_groupAssemblyStarted = true; | 
					
						
							|  |  |  | 				m_groupGoodBlocksCounter = 1; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (m_groupAssemblyStarted) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				if (!m_goodBlock) | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					m_groupAssemblyStarted = false; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				else | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					m_group[m_blockNumber] = dataword; | 
					
						
							|  |  |  | 					m_groupGoodBlocksCounter++; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if (m_groupGoodBlocksCounter == 5) | 
					
						
							|  |  |  | 				{ | 
					
						
							| 
									
										
										
										
											2015-12-13 09:45:29 +01:00
										 |  |  | 					group_ready = true; //decode_group(group); pass on to the group parser
 | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			m_blockBitCounter = 0; | 
					
						
							|  |  |  | 			m_blockNumber = (m_blockNumber + 1) % 4; | 
					
						
							|  |  |  | 			m_blocksCounter++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// 1187.5 bps / 104 bits = 11.4 groups/sec, or 45.7 blocks/sec
 | 
					
						
							|  |  |  | 			if (m_blocksCounter == 50) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				if (m_wrongBlocksCounter > 35) | 
					
						
							|  |  |  | 				{ | 
					
						
							| 
									
										
										
										
											2015-12-14 03:01:44 +01:00
										 |  |  | 					/*
 | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | 					qDebug() << "RDSDecoder::frameSync: Lost Sync (Got " << m_wrongBlocksCounter | 
					
						
							|  |  |  | 						<< " bad blocks on " << m_blocksCounter | 
					
						
							| 
									
										
										
										
											2015-12-14 03:01:44 +01:00
										 |  |  | 						<< " total)";*/ | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | 					enter_no_sync(); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				else | 
					
						
							|  |  |  | 				{ | 
					
						
							| 
									
										
										
										
											2015-12-14 03:01:44 +01:00
										 |  |  | 					/*
 | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | 					qDebug() << "RDSDecoder::frameSync: Still Sync-ed (Got " << m_wrongBlocksCounter | 
					
						
							|  |  |  | 						<< " bad blocks on " << m_blocksCounter | 
					
						
							| 
									
										
										
										
											2015-12-14 03:01:44 +01:00
										 |  |  | 						<< " total)";*/ | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-14 03:01:44 +01:00
										 |  |  | 				m_qua = 2.0 * (50 - m_wrongBlocksCounter); | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | 				m_blocksCounter = 0; | 
					
						
							|  |  |  | 				m_wrongBlocksCounter = 0; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	m_bitCounter++; | 
					
						
							| 
									
										
										
										
											2015-12-13 09:45:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return group_ready; | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-10 22:59:13 +02:00
										 |  |  | ////////////////////////// HELPER FUNCTIONS /////////////////////////
 | 
					
						
							| 
									
										
										
										
											2015-12-11 03:58:35 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | void RDSDecoder::enter_sync(unsigned int sync_block_number) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	m_wrongBlocksCounter   = 0; | 
					
						
							|  |  |  | 	m_blocksCounter        = 0; | 
					
						
							|  |  |  | 	m_blockBitCounter      = 0; | 
					
						
							|  |  |  | 	m_blockNumber          = (sync_block_number + 1) % 4; | 
					
						
							|  |  |  | 	m_groupAssemblyStarted = false; | 
					
						
							|  |  |  | 	m_sync                 = SYNC; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RDSDecoder::enter_no_sync() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	m_presync = false; | 
					
						
							|  |  |  | 	m_sync = NO_SYNC; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  *  see Annex B, page 64 of the standard | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | unsigned int RDSDecoder::calc_syndrome(unsigned long message, unsigned char mlen) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned long reg = 0; | 
					
						
							|  |  |  | 	unsigned int i; | 
					
						
							|  |  |  | 	const unsigned long poly = 0x5B9; | 
					
						
							|  |  |  | 	const unsigned char plen = 10; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = mlen; i > 0; i--) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		reg = (reg << 1) | ((message >> (i-1)) & 0x01); | 
					
						
							|  |  |  | 		if (reg & (1 << plen)) reg = reg ^ poly; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = plen; i > 0; i--) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		reg = reg << 1; | 
					
						
							|  |  |  | 		if (reg & (1<<plen)) { | 
					
						
							|  |  |  | 			reg = reg ^ poly; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return (reg & ((1<<plen)-1));	// select the bottom plen bits of reg
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 |