mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-31 04:50:29 -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 | ||||
| 	bfmplugin.cpp | ||||
| 	rdsdemod.cpp | ||||
| 	rdsdecoder.cpp | ||||
| ) | ||||
| 
 | ||||
| set(bfm_HEADERS | ||||
| @ -12,6 +13,7 @@ set(bfm_HEADERS | ||||
| 	bfmdemodgui.h | ||||
| 	bfmplugin.h | ||||
| 	rdsdemod.h | ||||
| 	rdsdecoder.h | ||||
| ) | ||||
| 
 | ||||
| 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