mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-05-31 06:12: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