11 changed files with 608 additions and 45 deletions
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0"?> |
||||
<block> |
||||
<name>RDS Decoder (redsea)</name> |
||||
<key>crfa_rds_decoder_redsea</key> |
||||
<category>[crfa]</category> |
||||
<import>import crfa</import> |
||||
<make>crfa.rds_decoder($log, $debug)</make> |
||||
<param> |
||||
<name>Log</name> |
||||
<key>log</key> |
||||
<value>False</value> |
||||
<type>bool</type> |
||||
<option> |
||||
<name>Enable</name> |
||||
<key>True</key> |
||||
</option> |
||||
<option> |
||||
<name>Disable</name> |
||||
<key>False</key> |
||||
</option> |
||||
</param> |
||||
<param> |
||||
<name>Debug</name> |
||||
<key>debug</key> |
||||
<value>False</value> |
||||
<type>bool</type> |
||||
<option> |
||||
<name>Enable</name> |
||||
<key>True</key> |
||||
</option> |
||||
<option> |
||||
<name>Disable</name> |
||||
<key>False</key> |
||||
</option> |
||||
</param> |
||||
<sink> |
||||
<name>in</name> |
||||
<type>byte</type> |
||||
</sink> |
||||
<source> |
||||
<name>out</name> |
||||
<type>message</type> |
||||
<optional>1</optional> |
||||
</source> |
||||
</block> |
||||
@ -0,0 +1,56 @@
|
||||
/* -*- c++ -*- */ |
||||
/*
|
||||
* Copyright 2017 <+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. |
||||
*/ |
||||
|
||||
|
||||
#ifndef INCLUDED_CRFA_RDS_DECODER_REDSEA_H |
||||
#define INCLUDED_CRFA_RDS_DECODER_REDSEA_H |
||||
|
||||
#include <crfa/api.h> |
||||
#include <gnuradio/sync_block.h> |
||||
|
||||
namespace gr { |
||||
namespace crfa { |
||||
|
||||
/*!
|
||||
* \brief <+description of block+> |
||||
* \ingroup crfa |
||||
* |
||||
*/ |
||||
class CRFA_API rds_decoder_redsea : virtual public gr::sync_block |
||||
{ |
||||
public: |
||||
typedef boost::shared_ptr<rds_decoder_redsea> sptr; |
||||
|
||||
/*!
|
||||
* \brief Return a shared_ptr to a new instance of crfa::rds_decoder_redsea. |
||||
* |
||||
* To avoid accidental use of raw pointers, crfa::rds_decoder_redsea's |
||||
* constructor is in a private implementation |
||||
* class. crfa::rds_decoder_redsea::make is the public interface for |
||||
* creating new instances. |
||||
*/ |
||||
static sptr make(bool log,bool debug); |
||||
}; |
||||
|
||||
} // namespace crfa
|
||||
} // namespace gr
|
||||
|
||||
#endif /* INCLUDED_CRFA_RDS_DECODER_REDSEA_H */ |
||||
|
||||
@ -0,0 +1,334 @@
|
||||
/* -*- 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_redsea_impl.h" |
||||
#include <map> |
||||
#include <vector> |
||||
|
||||
namespace gr { |
||||
namespace crfa { |
||||
|
||||
rds_decoder_redsea::sptr |
||||
rds_decoder_redsea::make(bool log, bool debug) |
||||
{ |
||||
return gnuradio::get_initial_sptr |
||||
(new rds_decoder_redsea_impl(log, debug)); |
||||
} |
||||
|
||||
/*
|
||||
* The private constructor |
||||
*/ |
||||
rds_decoder_redsea_impl::rds_decoder_redsea_impl(bool log, bool debug) |
||||
: gr::sync_block("rds_decoder_redsea", |
||||
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_redsea_impl::~rds_decoder_redsea_impl() |
||||
{ |
||||
} |
||||
|
||||
////////////////////////// HELPER FUNTIONS /////////////////////////
|
||||
|
||||
void rds_decoder_redsea_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_redsea_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_redsea_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_redsea_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_redsea_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 { |
||||
/*uint32_t block = reg;
|
||||
uint16_t message = block >> 10; |
||||
|
||||
received_offset_ = offsetForSyndrome(calcSyndrome(block)); |
||||
|
||||
if (!acquireSync()) |
||||
continue; |
||||
|
||||
block_counter_++; |
||||
bool was_valid_word = true; |
||||
|
||||
if (expected_offset_ == OFFSET_C && received_offset_ == OFFSET_CI) |
||||
expected_offset_ = OFFSET_CI; |
||||
|
||||
block_has_errors_[block_counter_ % block_has_errors_.size()] = false; |
||||
|
||||
if (received_offset_ != expected_offset_) { |
||||
block_has_errors_[block_counter_ % block_has_errors_.size()] = true; |
||||
|
||||
was_valid_word = false; |
||||
|
||||
// Detect & correct clock slips (Section C.1.2)
|
||||
if (expected_offset_ == OFFSET_A && pi_ != 0 && |
||||
((wideblock_ >> 12) & kBitmask16) == pi_) { |
||||
message = pi_; |
||||
wideblock_ >>= 1; |
||||
received_offset_ = OFFSET_A; |
||||
} else if (expected_offset_ == OFFSET_A && pi_ != 0 && |
||||
((wideblock_ >> 10) & kBitmask16) == pi_) { |
||||
message = pi_; |
||||
wideblock_ = (wideblock_ << 1) + getNextBit(); |
||||
received_offset_ = OFFSET_A; |
||||
left_to_read_ = 25; |
||||
} else { |
||||
uint32_t corrected_block = correctBurstErrors(block, expected_offset_); |
||||
if (corrected_block != block) { |
||||
message = corrected_block >> 10; |
||||
received_offset_ = expected_offset_; |
||||
} |
||||
} |
||||
|
||||
// Still no valid syndrome
|
||||
if (received_offset_ != expected_offset_) |
||||
uncorrectable(); |
||||
} |
||||
|
||||
// Error-free block received
|
||||
|
||||
if (received_offset_ == expected_offset_) { |
||||
if (expected_offset_ == OFFSET_CI) |
||||
group.setCI(message); |
||||
else |
||||
group.set(block_number_for_offset[expected_offset_], message); |
||||
|
||||
if (group.hasPi()) |
||||
pi_ = group.pi(); |
||||
} |
||||
|
||||
expected_offset_ = nextOffsetFor(expected_offset_); |
||||
|
||||
if (expected_offset_ == OFFSET_A) { |
||||
break; |
||||
}*/ |
||||
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; |
||||
}/*end of work function*/ |
||||
|
||||
|
||||
} /* namespace crfa */ |
||||
} /* namespace gr */ |
||||
|
||||
@ -0,0 +1,79 @@
|
||||
/* -*- 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. |
||||
*/ |
||||
|
||||
#ifndef INCLUDED_CRFA_RDS_DECODER_IMPL_H |
||||
#define INCLUDED_CRFA_RDS_DECODER_IMPL_H |
||||
|
||||
#include <crfa/rds_decoder_redsea.h> |
||||
|
||||
namespace gr { |
||||
namespace crfa { |
||||
|
||||
class rds_decoder_redsea_impl : public rds_decoder_redsea |
||||
{ |
||||
public: |
||||
rds_decoder_redsea_impl(bool log, bool debug); |
||||
|
||||
private: |
||||
~rds_decoder_redsea_impl(); |
||||
|
||||
// Where all the action really happens
|
||||
int work(int noutput_items, |
||||
gr_vector_const_void_star &input_items, |
||||
gr_vector_void_star &output_items); |
||||
void enter_no_sync(); |
||||
void enter_sync(unsigned int); |
||||
unsigned int calc_syndrome(unsigned long, unsigned char); |
||||
void decode_group(unsigned int*); |
||||
|
||||
unsigned long bit_counter; |
||||
unsigned long lastseen_offset_counter, reg; |
||||
unsigned int block_bit_counter; |
||||
unsigned int wrong_blocks_counter; |
||||
unsigned int blocks_counter; |
||||
unsigned int group_good_blocks_counter; |
||||
unsigned int group[4]; |
||||
unsigned char offset_chars[4]; // [ABCcDEx] (x=error)
|
||||
bool debug; |
||||
bool log; |
||||
bool presync; |
||||
bool good_block; |
||||
bool group_assembly_started; |
||||
unsigned char last_wrong_blocks_counter; |
||||
unsigned char lastseen_offset; |
||||
unsigned char block_number; |
||||
enum { NO_SYNC, SYNC } d_state; |
||||
//below copied from redsea
|
||||
enum eOffset { |
||||
OFFSET_A, OFFSET_B, OFFSET_C, OFFSET_CI, OFFSET_D, OFFSET_INVALID |
||||
} ; |
||||
uint32_t calcSyndrome(uint32_t vec); |
||||
eOffset offsetForSyndrome(uint16_t syndrome); |
||||
eOffset nextOffsetFor(eOffset o); |
||||
uint32_t correctBurstErrors(uint32_t block, eOffset offset); |
||||
|
||||
|
||||
}; |
||||
|
||||
} // namespace crfa
|
||||
} // namespace gr
|
||||
|
||||
#endif /* INCLUDED_CRFA_RDS_DECODER_IMPL_H */ |
||||
|
||||
Loading…
Reference in new issue