You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
266 lines
8.0 KiB
266 lines
8.0 KiB
/* -*- c++ -*- */ |
|
/* |
|
* Copyright 2016 <+YOU OR YOUR COMPANY+>. |
|
* |
|
* This 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; either version 3, or (at your option) |
|
* any later version. |
|
* |
|
* This software 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 for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this software; see the file COPYING. If not, write to |
|
* the Free Software Foundation, Inc., 51 Franklin Street, |
|
* Boston, MA 02110-1301, USA. |
|
*/ |
|
|
|
#ifdef HAVE_CONFIG_H |
|
#include "config.h" |
|
#endif |
|
|
|
#define dout debug && std::cout |
|
#define lout log && std::cout |
|
|
|
#include <gnuradio/io_signature.h> |
|
#include "constants.h" |
|
#include "rds_decoder_impl.h" |
|
|
|
namespace gr { |
|
namespace crfa { |
|
|
|
rds_decoder::sptr |
|
rds_decoder::make(bool log, bool debug) |
|
{ |
|
return gnuradio::get_initial_sptr |
|
(new rds_decoder_impl(log, debug)); |
|
} |
|
|
|
/* |
|
* The private constructor |
|
*/ |
|
rds_decoder_impl::rds_decoder_impl(bool log, bool debug) |
|
: gr::sync_block("rds_decoder", |
|
gr::io_signature::make (1, 1, sizeof(char)), |
|
gr::io_signature::make (0, 0, 0)), |
|
log(log), |
|
debug(debug) |
|
{ |
|
set_output_multiple(104); // 1 RDS datagroup = 104 bits |
|
message_port_register_out(pmt::mp("out")); |
|
enter_no_sync(); |
|
} |
|
|
|
/* |
|
* Our virtual destructor. |
|
*/ |
|
rds_decoder_impl::~rds_decoder_impl() |
|
{ |
|
} |
|
|
|
////////////////////////// HELPER FUNTIONS ///////////////////////// |
|
|
|
void rds_decoder_impl::enter_no_sync() { |
|
pmt::pmt_t data(pmt::PMT_F); |
|
//pmt::pmt_t meta(pmt::PMT_NIL); |
|
pmt::pmt_t meta(pmt::from_long(1)); |
|
pmt::pmt_t pdu(pmt::cons(meta, data)); // make PDU: (metadata, data) pair |
|
message_port_pub(pmt::mp("out"), pdu); |
|
presync = false; |
|
d_state = NO_SYNC; |
|
} |
|
|
|
void rds_decoder_impl::enter_sync(unsigned int sync_block_number) { |
|
pmt::pmt_t data(pmt::PMT_T); |
|
//pmt::pmt_t meta(pmt::PMT_NIL); |
|
pmt::pmt_t meta(pmt::from_long(1)); |
|
pmt::pmt_t pdu(pmt::cons(meta, data)); // make PDU: (metadata, data) pair |
|
message_port_pub(pmt::mp("out"), pdu); |
|
last_wrong_blocks_counter = 0; |
|
wrong_blocks_counter = 0; |
|
blocks_counter = 0; |
|
block_bit_counter = 0; |
|
block_number = (sync_block_number + 1) % 4; |
|
group_assembly_started = false; |
|
d_state = SYNC; |
|
} |
|
|
|
/* see Annex B, page 64 of the standard */ |
|
unsigned int rds_decoder_impl::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 |
|
} |
|
|
|
void rds_decoder_impl::decode_group(unsigned int *group) { |
|
// raw data bytes, as received from RDS. |
|
// 8 info bytes, followed by 4 RDS offset chars: ABCD/ABcD/EEEE (in US) |
|
unsigned char bytes[13]; |
|
|
|
// RDS information words |
|
bytes[0] = (group[0] >> 8U) & 0xffU; |
|
bytes[1] = (group[0] ) & 0xffU; |
|
bytes[2] = (group[1] >> 8U) & 0xffU; |
|
bytes[3] = (group[1] ) & 0xffU; |
|
bytes[4] = (group[2] >> 8U) & 0xffU; |
|
bytes[5] = (group[2] ) & 0xffU; |
|
bytes[6] = (group[3] >> 8U) & 0xffU; |
|
bytes[7] = (group[3] ) & 0xffU; |
|
|
|
// RDS offset words |
|
bytes[8] = offset_chars[0]; |
|
bytes[9] = offset_chars[1]; |
|
bytes[10] = offset_chars[2]; |
|
bytes[11] = offset_chars[3]; |
|
bytes[12]=last_wrong_blocks_counter; |
|
pmt::pmt_t data(pmt::make_blob(bytes, 13)); |
|
//pmt::pmt_t meta(pmt::PMT_NIL); |
|
pmt::pmt_t meta(pmt::from_long(0)); |
|
pmt::pmt_t pdu(pmt::cons(meta, data)); // make PDU: (metadata, data) pair |
|
message_port_pub(pmt::mp("out"), pdu); |
|
} |
|
//work function |
|
int rds_decoder_impl::work (int noutput_items, |
|
gr_vector_const_void_star &input_items, |
|
gr_vector_void_star &output_items) |
|
{ |
|
const bool *in = (const bool *) input_items[0]; |
|
|
|
dout << "RDS data decoder at work: input_items = " |
|
<< noutput_items << ", /104 = " |
|
<< noutput_items / 104 << std::endl; |
|
|
|
int i=0,j; |
|
unsigned long bit_distance, block_distance; |
|
unsigned int block_calculated_crc, block_received_crc, checkword,dataword; |
|
unsigned int reg_syndrome; |
|
unsigned char offset_char('x'); // x = error while decoding the word offset |
|
|
|
/* the synchronization process is described in Annex C, page 66 of the standard */ |
|
while (i<noutput_items) { |
|
reg=(reg<<1)|in[i]; // reg contains the last 26 rds bits |
|
switch (d_state) { |
|
case NO_SYNC: |
|
reg_syndrome = calc_syndrome(reg,26); |
|
for (j=0;j<5;j++) { |
|
if (reg_syndrome==syndrome[j]) { |
|
if (!presync) { |
|
lastseen_offset=j; |
|
lastseen_offset_counter=bit_counter; |
|
presync=true; |
|
} |
|
else { |
|
bit_distance=bit_counter-lastseen_offset_counter; |
|
if (offset_pos[lastseen_offset]>=offset_pos[j]) |
|
block_distance=offset_pos[j]+4-offset_pos[lastseen_offset]; |
|
else |
|
block_distance=offset_pos[j]-offset_pos[lastseen_offset]; |
|
if ((block_distance*26)!=bit_distance) presync=false; |
|
else { |
|
lout << "@@@@@ Sync State Detected" << std::endl; |
|
enter_sync(j); |
|
} |
|
} |
|
break; //syndrome found, no more cycles |
|
} |
|
} |
|
break; |
|
case SYNC: |
|
/* wait until 26 bits enter the buffer */ |
|
if (block_bit_counter<25) block_bit_counter++; |
|
else { |
|
good_block=false; |
|
dataword=(reg>>10) & 0xffff;//data part of received block (upper 16 bits) |
|
block_calculated_crc=calc_syndrome(dataword,16); |
|
checkword=reg & 0x3ff;//checkword part of received block (lower 10 bits) |
|
/* manage special case of C or C' offset word */ |
|
if (block_number==2) { |
|
block_received_crc=checkword^offset_word[block_number]; |
|
if (block_received_crc==block_calculated_crc) { |
|
good_block=true; |
|
offset_char = 'C'; |
|
} else { |
|
block_received_crc=checkword^offset_word[4]; |
|
if (block_received_crc==block_calculated_crc) { |
|
good_block=true; |
|
offset_char = 'c'; // C' (C-Tag) |
|
} else { |
|
wrong_blocks_counter++; |
|
good_block=false; |
|
} |
|
} |
|
} |
|
else { |
|
block_received_crc=checkword^offset_word[block_number]; |
|
if (block_received_crc==block_calculated_crc) { |
|
good_block=true; |
|
if (block_number==0) offset_char = 'A'; |
|
else if (block_number==1) offset_char = 'B'; |
|
else if (block_number==3) offset_char = 'D'; |
|
} else { |
|
wrong_blocks_counter++; |
|
good_block=false; |
|
} |
|
} |
|
/* done checking CRC */ |
|
if (block_number==0 && good_block) { |
|
group_assembly_started=true; |
|
group_good_blocks_counter=1; |
|
} |
|
if (group_assembly_started) { |
|
if (!good_block) group_assembly_started=false; |
|
else { |
|
group[block_number]=dataword; |
|
offset_chars[block_number] = offset_char; |
|
group_good_blocks_counter++; |
|
} |
|
if (group_good_blocks_counter==5) decode_group(group); |
|
} |
|
block_bit_counter=0; |
|
block_number=(block_number+1) % 4; |
|
blocks_counter++; |
|
/* 1187.5 bps / 104 bits = 11.4 groups/sec, or 45.7 blocks/sec */ |
|
if (blocks_counter==50) { |
|
last_wrong_blocks_counter=wrong_blocks_counter; |
|
if (wrong_blocks_counter>35) { |
|
lout << "@@@@@ Lost Sync (Got " << wrong_blocks_counter |
|
<< " bad blocks on " << blocks_counter |
|
<< " total)" << std::endl; |
|
enter_no_sync(); |
|
} else { |
|
lout << "@@@@@ Still Sync-ed (Got " << wrong_blocks_counter |
|
<< " bad blocks on " << blocks_counter |
|
<< " total)" << std::endl; |
|
} |
|
blocks_counter=0; |
|
wrong_blocks_counter=0; |
|
} |
|
} |
|
break; |
|
default: |
|
d_state=NO_SYNC; |
|
break; |
|
} |
|
i++; |
|
bit_counter++; |
|
} |
|
return noutput_items; |
|
}/*work function*/ |
|
} /* namespace crfa */ |
|
} /* namespace gr */ |
|
|
|
|