mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-02-03 09:44:01 -05: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…
Reference in New Issue
Block a user