mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-31 13:00:26 -04:00 
			
		
		
		
	BFM demod: added RDS decoder class
This commit is contained in:
		
							parent
							
								
									5761330365
								
							
						
					
					
						commit
						14f72bae8a
					
				| @ -5,6 +5,7 @@ set(bfm_SOURCES | |||||||
| 	bfmdemodgui.cpp | 	bfmdemodgui.cpp | ||||||
| 	bfmplugin.cpp | 	bfmplugin.cpp | ||||||
| 	rdsdemod.cpp | 	rdsdemod.cpp | ||||||
|  | 	rdsdecoder.cpp | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| set(bfm_HEADERS | set(bfm_HEADERS | ||||||
| @ -12,6 +13,7 @@ set(bfm_HEADERS | |||||||
| 	bfmdemodgui.h | 	bfmdemodgui.h | ||||||
| 	bfmplugin.h | 	bfmplugin.h | ||||||
| 	rdsdemod.h | 	rdsdemod.h | ||||||
|  | 	rdsdecoder.h | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| set(bfm_FORMS | set(bfm_FORMS | ||||||
|  | |||||||
							
								
								
									
										254
									
								
								plugins/channel/bfm/rdsdecoder.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								plugins/channel/bfm/rdsdecoder.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,254 @@ | |||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // 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                  //
 | ||||||
|  | //                                                                               //
 | ||||||
|  | // 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 <QDebug> | ||||||
|  | #include "rdsdecoder.h" | ||||||
|  | 
 | ||||||
|  | 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_sync = NO_SYNC; | ||||||
|  | 	m_presync = false; | ||||||
|  | 	m_lastseenOffsetCounter = 0; | ||||||
|  | 	m_bitCounter = 0; | ||||||
|  | 	m_lastseenOffset = 0; | ||||||
|  | 	m_wrongBlocksCounter   = 0; | ||||||
|  | 	m_blocksCounter        = 0; | ||||||
|  | 	m_blockBitCounter      = 0; | ||||||
|  | 	m_blockNumber          = 0; | ||||||
|  | 	m_groupAssemblyStarted = false; | ||||||
|  | 	m_sync                 = SYNC; | ||||||
|  | 	m_groupGoodBlocksCounter = 0; | ||||||
|  | 	m_wrongBlocksCounter   = 0; | ||||||
|  | 	m_goodBlock            = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | RDSDecoder::~RDSDecoder() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RDSDecoder::frameSync(bool bit) | ||||||
|  | { | ||||||
|  | 	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 | ||||||
|  | 					{ | ||||||
|  | 						qDebug("RDSDecoder::frameSync: Sync State Detected"); | ||||||
|  | 						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 | ||||||
|  | 					{ | ||||||
|  | 						m_wrongBlocksCounter++; | ||||||
|  | 						m_goodBlock = false; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | 				block_received_crc = checkword ^ offset_word[m_blockNumber]; | ||||||
|  | 
 | ||||||
|  | 				if (block_received_crc==block_calculated_crc) | ||||||
|  | 				{ | ||||||
|  | 					m_goodBlock = true; | ||||||
|  | 				} | ||||||
|  | 				else | ||||||
|  | 				{ | ||||||
|  | 					m_wrongBlocksCounter++; | ||||||
|  | 					m_goodBlock = false; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			//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) | ||||||
|  | 				{ | ||||||
|  | 					//decode_group(group); TODO: pass on to the group parser
 | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			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) | ||||||
|  | 				{ | ||||||
|  | 					qDebug() << "RDSDecoder::frameSync: Lost Sync (Got " << m_wrongBlocksCounter | ||||||
|  | 						<< " bad blocks on " << m_blocksCounter | ||||||
|  | 						<< " total)"; | ||||||
|  | 					enter_no_sync(); | ||||||
|  | 				} | ||||||
|  | 				else | ||||||
|  | 				{ | ||||||
|  | 					qDebug() << "RDSDecoder::frameSync: Still Sync-ed (Got " << m_wrongBlocksCounter | ||||||
|  | 						<< " bad blocks on " << m_blocksCounter | ||||||
|  | 						<< " total)"; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				m_blocksCounter = 0; | ||||||
|  | 				m_wrongBlocksCounter = 0; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	m_bitCounter++; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ////////////////////////// HELPER FUNTIONS /////////////////////////
 | ||||||
|  | 
 | ||||||
|  | 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
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
							
								
								
									
										59
									
								
								plugins/channel/bfm/rdsdecoder.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								plugins/channel/bfm/rdsdecoder.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | |||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // 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                  //
 | ||||||
|  | //                                                                               //
 | ||||||
|  | // 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/>.          //
 | ||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | 
 | ||||||
|  | #ifndef PLUGINS_CHANNEL_BFM_RDSDECODER_H_ | ||||||
|  | #define PLUGINS_CHANNEL_BFM_RDSDECODER_H_ | ||||||
|  | 
 | ||||||
|  | class RDSDecoder | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	RDSDecoder(); | ||||||
|  | 	~RDSDecoder(); | ||||||
|  | 
 | ||||||
|  | 	void frameSync(bool bit); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	unsigned int calc_syndrome(unsigned long message, unsigned char mlen); | ||||||
|  | 	void enter_sync(unsigned int sync_block_number); | ||||||
|  | 	void enter_no_sync(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	unsigned long m_reg; | ||||||
|  | 	enum { NO_SYNC, SYNC } m_sync; | ||||||
|  | 	bool m_presync; | ||||||
|  | 	unsigned long  m_lastseenOffsetCounter; | ||||||
|  | 	unsigned long  m_bitCounter; | ||||||
|  | 	unsigned char  m_lastseenOffset; | ||||||
|  | 	unsigned int   m_blockBitCounter; | ||||||
|  | 	unsigned int   m_wrongBlocksCounter; | ||||||
|  | 	unsigned int   m_blocksCounter; | ||||||
|  | 	unsigned int   m_groupGoodBlocksCounter; | ||||||
|  | 	unsigned char  m_blockNumber; | ||||||
|  | 	bool           m_groupAssemblyStarted; | ||||||
|  | 	bool           m_goodBlock; | ||||||
|  | 	unsigned int   m_group[4]; | ||||||
|  | 
 | ||||||
|  | 	/* see page 59, Annex C, table C.1 in the standard
 | ||||||
|  | 	 * offset word C' has been put at the end */ | ||||||
|  | 	static const unsigned int offset_pos[5]; | ||||||
|  | 	static const unsigned int offset_word[5]; | ||||||
|  | 	static const unsigned int syndrome[5]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #endif /* PLUGINS_CHANNEL_BFM_RDSDECODER_H_ */ | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user