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_ */