From 14f72bae8a8dcea6d425b21075b1b33b287d42d3 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 11 Dec 2015 03:58:35 +0100 Subject: [PATCH] BFM demod: added RDS decoder class --- plugins/channel/bfm/CMakeLists.txt | 2 + plugins/channel/bfm/rdsdecoder.cpp | 254 +++++++++++++++++++++++++++++ plugins/channel/bfm/rdsdecoder.h | 59 +++++++ 3 files changed, 315 insertions(+) create mode 100644 plugins/channel/bfm/rdsdecoder.cpp create mode 100644 plugins/channel/bfm/rdsdecoder.h diff --git a/plugins/channel/bfm/CMakeLists.txt b/plugins/channel/bfm/CMakeLists.txt index d41a9a66b..5fa3c0f9c 100644 --- a/plugins/channel/bfm/CMakeLists.txt +++ b/plugins/channel/bfm/CMakeLists.txt @@ -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 diff --git a/plugins/channel/bfm/rdsdecoder.cpp b/plugins/channel/bfm/rdsdecoder.cpp new file mode 100644 index 000000000..2d4ea72c5 --- /dev/null +++ b/plugins/channel/bfm/rdsdecoder.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#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<. // +/////////////////////////////////////////////////////////////////////////////////// + +#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_ */