Redesign of the AX.25 decoder

This commits introduces a significant redesign of the AX.25 decoding
block. Due to the poor AX.25 sync flag, the decoders exhibited too many
false alarms. To deal with this problem, we introduced the quadrature
demod filter block, that tried to measure the SNR based on the running
variance of the signal. The problem with that was that the user should
have to fine tune two parameters, the one related with the amplitude of
the incoming signal and the second one related with the window that the
calculations should take place. This solution worked until now, but we
can always increase the performance.

The new AX.25 decoder stores the bitstream in a queue and always tries
to exlpoit possible valid frames in the queue. If now sync flags have
been encountered, the queue is flushed. After a valid frame extraction,
bits corresponding to this frame are also deleted from the queue.

This technique requires more memory and CPU, but it increases a lot the
decoding performance.
This commit is contained in:
Manolis Surligas 2019-07-18 16:39:26 +03:00
parent 23b819feb9
commit 89d4184393
3 changed files with 285 additions and 388 deletions

View File

@ -197,7 +197,10 @@ namespace gr
out[i++] = fcs & 0xFF; out[i++] = fcs & 0xFF;
out[i++] = (fcs >> 8) & 0xFF; out[i++] = (fcs >> 8) & 0xFF;
memset (out + i, AX25_SYNC_FLAG, postamble_len); memset (out + i, AX25_SYNC_FLAG, postamble_len);
for(size_t j = preamble_len; j < i; j++) {
printf("0x%02x ", out[j]);
}
printf("\n");
return i + postamble_len; return i + postamble_len;
} }

View File

@ -38,8 +38,7 @@ namespace gr
bool descramble, size_t max_frame_len) bool descramble, size_t max_frame_len)
{ {
return gnuradio::get_initial_sptr ( return gnuradio::get_initial_sptr (
new ax25_decoder_bm_impl (addr, ssid, promisc, descramble, new ax25_decoder_bm_impl (addr, ssid, promisc, descramble, max_frame_len));
max_frame_len));
} }
/* /*
@ -63,8 +62,9 @@ namespace gr
d_decoded_bits (0), d_decoded_bits (0),
d_lfsr (0x21, 0x0, 16), d_lfsr (0x21, 0x0, 16),
d_frame_buffer ( d_frame_buffer (
new uint8_t[max_frame_len + AX25_MAX_ADDR_LEN new uint8_t[max_frame_len + AX25_MAX_ADDR_LEN + AX25_MAX_CTRL_LEN
+ AX25_MAX_CTRL_LEN + sizeof(uint16_t)]) + sizeof(uint16_t)]),
d_start_idx(0)
{ {
/* Valid PDUs output message port */ /* Valid PDUs output message port */
message_port_register_out (pmt::mp ("pdu")); message_port_register_out (pmt::mp ("pdu"));
@ -75,48 +75,67 @@ namespace gr
message_port_register_out (pmt::mp ("failed_pdu")); message_port_register_out (pmt::mp ("failed_pdu"));
} }
size_t
ax25_decoder_bm_impl::descramble_and_decode (const uint8_t* in, void
size_t nitems) ax25_decoder_bm_impl::decode ()
{ {
size_t i; while (1) {
bool cont = false;
switch (d_state) switch (d_state)
{ {
case NO_SYNC: case NO_SYNC:
for (i = 0; i < nitems; i++) { for (size_t i = 0; i < d_bitstream.size (); i++) {
descramble_and_decode_1b (in[i]); decode_1b (d_bitstream[i]);
if (d_shift_reg == AX25_SYNC_FLAG) { if (d_shift_reg == AX25_SYNC_FLAG) {
d_bitstream.erase (d_bitstream.begin (),
d_bitstream.begin () + i + 1);
enter_sync_state (); enter_sync_state ();
return i + 1; d_start_idx = 0;
cont = true;
break;
} }
} }
return nitems; if(cont) {
continue;
}
d_bitstream.clear ();
return;
case IN_SYNC: case IN_SYNC:
/* /*
* Most of the transmitters repeat several times the AX.25 SYNC * Most of the transmitters repeat several times the AX.25 SYNC
* In case of G3RUH this is mandatory to allow the self synchronizing * In case of G3RUH this is mandatory to allow the self synchronizing
* scrambler to settle * scrambler to settle
*/ */
for (i = 0; i < nitems; i++) { for (size_t i = d_start_idx; i < d_bitstream.size (); i++) {
descramble_and_decode_1b (in[i]); decode_1b (d_bitstream[i]);
d_decoded_bits++; d_decoded_bits++;
if (d_decoded_bits == 8) { if (d_decoded_bits == 8) {
/* Perhaps we are in frame! */ /* Perhaps we are in frame! */
if (d_shift_reg != AX25_SYNC_FLAG) { if (d_shift_reg != AX25_SYNC_FLAG) {
d_start_idx = i + 1;
enter_decoding_state (); enter_decoding_state ();
return i+1; cont = true;
break;
} }
d_decoded_bits = 0; d_decoded_bits = 0;
} }
} }
return nitems; if(cont) {
continue;
}
d_start_idx = d_bitstream.size ();
return;
case DECODING: case DECODING:
for (i = 0; i < nitems; i++) { for (size_t i = d_start_idx; i < d_bitstream.size (); i++) {
descramble_and_decode_1b (in[i]); decode_1b (d_bitstream[i]);
if (d_shift_reg == AX25_SYNC_FLAG) { if (d_shift_reg == AX25_SYNC_FLAG) {
LOG_DEBUG("Found frame end"); LOG_DEBUG("Found frame end");
enter_frame_end(); if (enter_frame_end ()) {
return i+1; d_bitstream.erase (d_bitstream.begin (),
d_bitstream.begin () + i + 1);
}
cont = true;
break;
} }
else if ((d_shift_reg & 0xfc) == 0x7c) { else if ((d_shift_reg & 0xfc) == 0x7c) {
/*This was a stuffed bit */ /*This was a stuffed bit */
@ -125,138 +144,36 @@ namespace gr
else if ((d_shift_reg & 0xfe) == 0xfe) { else if ((d_shift_reg & 0xfe) == 0xfe) {
LOG_DEBUG("Invalid shift register value %u", d_received_bytes); LOG_DEBUG("Invalid shift register value %u", d_received_bytes);
reset_state (); reset_state ();
return i+1; cont = true;
break;
} }
else { else {
d_decoded_bits++; d_decoded_bits++;
if (d_decoded_bits == 8) { if (d_decoded_bits == 8) {
d_frame_buffer[d_received_bytes] = d_dec_b; d_frame_buffer[d_received_bytes++] = d_dec_b;
d_received_bytes++;
d_decoded_bits = 0; d_decoded_bits = 0;
/*Check if the frame limit was reached */ /*Check if the frame limit was reached */
if (d_received_bytes >= d_max_frame_len) { if (d_received_bytes >= d_max_frame_len) {
LOG_DEBUG("Wrong size"); LOG_DEBUG("Wrong size");
message_port_pub (
pmt::mp ("failed_pdu"),
pmt::make_blob (d_frame_buffer, d_max_frame_len));
reset_state (); reset_state ();
return i+1; cont = true;
break;
} }
} }
} }
} }
return nitems; if(cont) {
case FRAME_END: continue;
for (i = 0; i < nitems; i++) {
descramble_and_decode_1b (in[i]);
d_decoded_bits++;
if (d_decoded_bits == 8) {
/* Repetitions of the trailing SYNC flag finished */
if (d_shift_reg != AX25_SYNC_FLAG) {
reset_state();
return i + 1;
} }
d_decoded_bits = 0; d_start_idx = d_bitstream.size ();
} return;
}
return nitems;
default: default:
LOG_ERROR("Invalid decoding state"); LOG_ERROR("Invalid decoding state");
reset_state (); reset_state ();
return nitems; return;
} }
} }
size_t
ax25_decoder_bm_impl::decode (const uint8_t* in, size_t nitems)
{
size_t i;
switch (d_state)
{
case NO_SYNC:
for (i = 0; i < nitems; i++) {
decode_1b (in[i]);
if (d_shift_reg == AX25_SYNC_FLAG) {
enter_sync_state ();
return i + 1;
}
}
return nitems;
case IN_SYNC:
/*
* Most of the transmitters repeat several times the AX.25 SYNC
* In case of G3RUH this is mandatory to allow the self synchronizing
* scrambler to settle
*/
for (i = 0; i < nitems; i++) {
decode_1b (in[i]);
d_decoded_bits++;
if (d_decoded_bits == 8) {
/* Perhaps we are in frame! */
if (d_shift_reg != AX25_SYNC_FLAG) {
enter_decoding_state ();
return i + 1;
}
d_decoded_bits = 0;
}
}
return nitems;
case DECODING:
for (i = 0; i < nitems; i++) {
decode_1b (in[i]);
if (d_shift_reg == AX25_SYNC_FLAG) {
enter_frame_end ();
return i + 1;
}
else if ((d_shift_reg & 0xfc) == 0x7c) {
/*This was a stuffed bit */
d_dec_b <<= 1;
}
else if ((d_shift_reg & 0xfe) == 0xfe) {
LOG_DEBUG("Invalid shift register value");
reset_state ();
return i + 1;
}
else {
d_decoded_bits++;
if (d_decoded_bits == 8) {
d_frame_buffer[d_received_bytes] = d_dec_b;
d_received_bytes++;
d_decoded_bits = 0;
/*Check if the frame limit was reached */
if (d_received_bytes >= d_max_frame_len) {
LOG_DEBUG("Wrong size");
message_port_pub (
pmt::mp ("failed_pdu"),
pmt::make_blob (d_frame_buffer, d_max_frame_len));
reset_state ();
return i + 1;
}
}
}
}
return nitems;
case FRAME_END:
for (i = 0; i < nitems; i++) {
decode_1b (in[i]);
d_decoded_bits++;
if (d_decoded_bits == 8) {
/* Repetitions of the trailing SYNC flag finished */
if (d_shift_reg != AX25_SYNC_FLAG) {
reset_state ();
return i + 1;
}
d_decoded_bits = 0;
}
}
return nitems;
default:
LOG_ERROR("Invalid decoding state");
reset_state ();
return nitems;
}
} }
/* /*
@ -265,6 +182,7 @@ namespace gr
ax25_decoder_bm_impl::~ax25_decoder_bm_impl () ax25_decoder_bm_impl::~ax25_decoder_bm_impl ()
{ {
delete[] d_frame_buffer; delete[] d_frame_buffer;
LOG_DEBUG("Left over: %lu", d_bitstream.size());
} }
void void
@ -275,7 +193,6 @@ namespace gr
d_shift_reg = 0x0; d_shift_reg = 0x0;
d_decoded_bits = 0; d_decoded_bits = 0;
d_received_bytes = 0; d_received_bytes = 0;
d_prev_bit_nrzi = 0;
} }
void void
@ -294,38 +211,25 @@ namespace gr
uint8_t tmp; uint8_t tmp;
d_state = DECODING; d_state = DECODING;
d_decoded_bits = 0; d_decoded_bits = 0;
d_shift_reg = 0x0; d_received_bytes = 0;
/* /*
* Due to the possibility of bit stuffing on the first byte some special * Due to the possibility of bit stuffing on the first byte some special
* handling is necessary * handling is necessary
*/ */
tmp = d_dec_b;
d_dec_b = 0x0;
for (size_t i = 0; i < 8; i++) {
d_shift_reg = (d_shift_reg >> 1) | (((tmp >> i) & 0x1) << 7);
d_dec_b = (d_dec_b >> 1) | (((tmp >> i) & 0x1) << 7);
if ((d_shift_reg & 0xfc) == 0x7c) { if ((d_shift_reg & 0xfc) == 0x7c) {
/*This was a stuffed bit */ /*This was a stuffed bit */
d_dec_b <<= 1; d_dec_b <<= 1;
d_decoded_bits = 7;
} }
else { else {
d_decoded_bits++;
}
}
if(d_decoded_bits == 8) {
d_frame_buffer[0] = d_dec_b; d_frame_buffer[0] = d_dec_b;
d_decoded_bits = 0; d_decoded_bits = 0;
d_received_bytes = 1; d_received_bytes = 1;
} }
else {
d_received_bytes = 0;
} }
} bool
void
ax25_decoder_bm_impl::enter_frame_end () ax25_decoder_bm_impl::enter_frame_end ()
{ {
uint16_t fcs; uint16_t fcs;
@ -333,81 +237,56 @@ namespace gr
/* First check if the size of the frame is valid */ /* First check if the size of the frame is valid */
if (d_received_bytes < AX25_MIN_ADDR_LEN + sizeof(uint16_t)) { if (d_received_bytes < AX25_MIN_ADDR_LEN + sizeof(uint16_t)) {
d_dec_b = 0x0; reset_state ();
d_shift_reg = 0x0; return false;
d_decoded_bits = 0;
d_received_bytes = 0;
d_state = FRAME_END;
return;
} }
/*
* Check if the frame is correct using the FCS field
* Using this field also try to correct up to 2 error bits
*/
if (frame_check ()) {
message_port_pub (
pmt::mp ("pdu"),
pmt::make_blob (d_frame_buffer, d_received_bytes - sizeof(uint16_t)));
reset_state ();
return true;
}
else {
message_port_pub (
pmt::mp ("failed_pdu"),
pmt::make_blob (d_frame_buffer, d_received_bytes - sizeof(uint16_t)));
LOG_DEBUG("Wrong crc");
reset_state ();
return false;
}
}
inline void
ax25_decoder_bm_impl::decode_1b (uint8_t in)
{
/* In AX.25 the LS bit is sent first */
d_shift_reg = (d_shift_reg >> 1) | (in << 7);
d_dec_b = (d_dec_b >> 1) | (in << 7);
}
bool
ax25_decoder_bm_impl::frame_check ()
{
uint16_t fcs;
uint16_t recv_fcs = 0x0;
uint8_t orig_byte;
/* Check if the frame is correct using the FCS field */ /* Check if the frame is correct using the FCS field */
fcs = ax25_fcs (d_frame_buffer, d_received_bytes - sizeof(uint16_t)); fcs = ax25_fcs (d_frame_buffer, d_received_bytes - sizeof(uint16_t));
recv_fcs = (((uint16_t) d_frame_buffer[d_received_bytes - 1]) << 8) recv_fcs = (((uint16_t) d_frame_buffer[d_received_bytes - 1]) << 8)
| d_frame_buffer[d_received_bytes - 2]; | d_frame_buffer[d_received_bytes - 2];
if (fcs == recv_fcs) { if (fcs == recv_fcs) {
message_port_pub ( return true;
pmt::mp ("pdu"),
pmt::make_blob (d_frame_buffer,
d_received_bytes - sizeof(uint16_t)));
} }
else { return false;
message_port_pub (
pmt::mp ("failed_pdu"),
pmt::make_blob (d_frame_buffer,
d_received_bytes - sizeof(uint16_t)));
LOG_DEBUG("Wrong crc");
}
d_dec_b = 0x0;
d_shift_reg = 0x0;
d_decoded_bits = 0;
d_received_bytes = 0;
d_state = FRAME_END;
}
/**
* Performs descrambling and NRZI decoding of an input bit.
* The decoded bit is then shifted in front of the d_shift_reg and the d_dec_b
* variables. This shift in front is due to the LS bit first transmission
* of the Ax.25 protocol.
*
* @param in input bit
*/
inline void
ax25_decoder_bm_impl::descramble_and_decode_1b (uint8_t in)
{
uint8_t descr_bit;
uint8_t dec_bit;
in &= 0x1;
/* Perform NRZI decoding */
dec_bit = (~((in - d_prev_bit_nrzi) % 2)) & 0x1;
d_prev_bit_nrzi = in;
/* Descramble the input bit */
descr_bit = d_lfsr.next_bit_descramble (dec_bit);
/* In AX.25 the LS bit is sent first */
d_shift_reg = (d_shift_reg >> 1) | (descr_bit << 7);
d_dec_b = (d_dec_b >> 1) | (descr_bit << 7);
}
inline void
ax25_decoder_bm_impl::decode_1b (uint8_t in)
{
uint8_t dec_bit;
in &= 0x1;
/* Perform NRZI decoding */
dec_bit = (~((in - d_prev_bit_nrzi) % 2)) & 0x1;
d_prev_bit_nrzi = in;
/* In AX.25 the LS bit is sent first */
d_shift_reg = (d_shift_reg >> 1) | (dec_bit << 7);
d_dec_b = (d_dec_b >> 1) | (dec_bit << 7);
} }
int int
@ -421,13 +300,27 @@ namespace gr
if (noutput_items < 1) { if (noutput_items < 1) {
return noutput_items; return noutput_items;
} }
if (d_descramble) { if (d_descramble) {
ret = descramble_and_decode (in, noutput_items); for (int i = 0; i < noutput_items; i++) {
/* Perform NRZI decoding */
uint8_t b = (~((in[i] - d_prev_bit_nrzi) % 2)) & 0x1;
d_prev_bit_nrzi = in[i];
b = d_lfsr.next_bit_descramble (b);
d_bitstream.push_back (b);
}
} }
else { else {
ret = decode (in, noutput_items); for (int i = 0; i < noutput_items; i++) {
/* Perform NRZI decoding */
uint8_t b = (~((in[i] - d_prev_bit_nrzi) % 2)) & 0x1;
d_prev_bit_nrzi = in[i];
d_bitstream.push_back (b);
} }
return ret; }
decode();
return noutput_items;
} }
} /* namespace satnogs */ } /* namespace satnogs */

View File

@ -24,6 +24,7 @@
#include <satnogs/ax25_decoder_bm.h> #include <satnogs/ax25_decoder_bm.h>
#include <gnuradio/digital/lfsr.h> #include <gnuradio/digital/lfsr.h>
#include <deque>
namespace gr namespace gr
{ {
@ -35,7 +36,7 @@ namespace gr
private: private:
typedef enum typedef enum
{ {
NO_SYNC, IN_SYNC, DECODING, FRAME_END NO_SYNC, IN_SYNC, DECODING
} decoding_state_t; } decoding_state_t;
/** /**
@ -53,6 +54,8 @@ namespace gr
size_t d_decoded_bits; size_t d_decoded_bits;
digital::lfsr d_lfsr; digital::lfsr d_lfsr;
uint8_t *d_frame_buffer; uint8_t *d_frame_buffer;
std::deque<uint8_t> d_bitstream;
size_t d_start_idx;
void void
reset_state (); reset_state ();
@ -60,18 +63,16 @@ namespace gr
enter_sync_state (); enter_sync_state ();
void void
enter_decoding_state (); enter_decoding_state ();
void bool
enter_frame_end (); enter_frame_end ();
size_t void
descramble_and_decode (const uint8_t *in, size_t nitems); decode ();
size_t
decode (const uint8_t *in, size_t nitems);
inline void
descramble_and_decode_1b (uint8_t in);
inline void inline void
decode_1b (uint8_t in); decode_1b (uint8_t in);
bool
frame_check();
public: public: