Add LRPT decoding flowgraph

This commit is contained in:
Manolis Surligas 2018-08-09 19:29:47 +03:00
parent 854becb15d
commit 63218b157b
8 changed files with 238 additions and 124 deletions

View File

@ -17,11 +17,15 @@
<name>AMSAT FOX</name>
<block>satnogs_fox_telem_mm</block>
</cat>
<cat>
<name>METOP</name>
<block>satnogs_lrpt_sync</block>
<block>satnogs_lrpt_decoder</block>
</cat>
</cat>
<block>satnogs_cw_matched_filter_ff</block>
<block>satnogs_morse_decoder</block>
<block>satnogs_multi_format_msg_sink</block>
<block>satnogs_lrpt_sync</block>
<block>satnogs_iq_sink</block>
<block>satnogs_ogg_encoder</block>
<block>satnogs_ogg_source</block>

View File

@ -23,12 +23,16 @@
#include <cstdint>
#include <cmath>
#include <arpa/inet.h>
namespace gr
{
namespace satnogs
{
#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
#define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32))
/**
* Computes the Mean Absolute Percentage Error
* @param ref the reference value

View File

@ -41,8 +41,10 @@ namespace gr
void
reset();
void scramble(uint8_t *out, const uint8_t *in, size_t len);
void descramble(uint8_t *out, const uint8_t *in, size_t len);
void scramble(uint8_t *out, const uint8_t *in, size_t len,
bool msb = false);
void descramble(uint8_t *out, const uint8_t *in, size_t len,
bool msb = false);
void
scramble_one_bit_per_byte (uint8_t *out, const uint8_t *in,

View File

@ -25,6 +25,9 @@
#include <gnuradio/io_signature.h>
#include "lrpt_decoder_impl.h"
#include <satnogs/log.h>
#include <satnogs/utils.h>
extern "C" {
#include <fec.h>
}
@ -47,11 +50,18 @@ lrpt_decoder_impl::lrpt_decoder_impl()
: gr::block("lrpt_decoder",
gr::io_signature::make(0, 0, 0),
gr::io_signature::make(0, 0, 0)),
d_cadu_len(1020 + 4),
d_coded_cadu_len(1020 * 2 + 4*2),
d_conv_deinterl(36, 2048)
/*
* Metop violates the standard as many times as possible...
* The frame should contain 128 RS check symbols at the end.
* For some unknown reasons, it seems that the RS encoding is not performed.
* Thus, they dropped the check symbols at the end of the frame.
*/
d_cadu_len(1020 + 4 - 128),
d_coded_cadu_len(1020 * 2 + 4*2 - 128 * 2),
d_mpdu_max_len(59400),
d_scrambler(0x2A9, 0xFF, 7),
d_have_mpdu(false)
{
message_port_register_in(pmt::mp("cadu"));
message_port_register_out(pmt::mp("frame"));
@ -63,11 +73,13 @@ lrpt_decoder_impl::lrpt_decoder_impl()
if(!d_vt) {
throw std::runtime_error("lrpt_decoder: Failed to init Viterbi decoder");
}
int polys[2] = {0x79, 0x5b};
int polys[2] = {0x4f, 0x6d};
set_viterbi27_polynomial(polys);
d_cadu = new uint8_t[d_cadu_len];
d_coded_cadu_syms = new uint8_t[d_coded_cadu_len * 8];
d_mpdu = new uint8_t[d_mpdu_max_len];
}
@ -79,6 +91,7 @@ lrpt_decoder_impl::~lrpt_decoder_impl ()
delete [] d_cadu;
delete [] d_coded_cadu_syms;
delete [] d_mpdu;
}
void
@ -93,21 +106,68 @@ lrpt_decoder_impl::decode (pmt::pmt_t m)
init_viterbi27(d_vt, 0);
for(size_t i = 0; i < d_coded_cadu_len; i++) {
d_coded_cadu_syms[i * 8] = ~(255 + (coded_cadu[i] >> 7));
d_coded_cadu_syms[i * 8 + 1] = ~(255 + (coded_cadu[i] >> 6) & 0x1);
d_coded_cadu_syms[i * 8 + 2] = ~(255 + (coded_cadu[i] >> 5) & 0x1);
d_coded_cadu_syms[i * 8 + 3] = ~(255 + (coded_cadu[i] >> 4) & 0x1);
d_coded_cadu_syms[i * 8 + 4] = ~(255 + (coded_cadu[i] >> 3) & 0x1);
d_coded_cadu_syms[i * 8 + 5] = ~(255 + (coded_cadu[i] >> 2) & 0x1);
d_coded_cadu_syms[i * 8 + 6] = ~(255 + (coded_cadu[i] >> 1) & 0x1);
d_coded_cadu_syms[i * 8 + 7] = ~(255 + (coded_cadu[i] & 0x1));
d_coded_cadu_syms[i * 8] = 0xFF * (coded_cadu[i] >> 7);
d_coded_cadu_syms[i * 8 + 1] = 0xFF * ((coded_cadu[i] >> 6) & 0x1);
d_coded_cadu_syms[i * 8 + 2] = 0xFF * ((coded_cadu[i] >> 5) & 0x1);
d_coded_cadu_syms[i * 8 + 3] = 0xFF * ((coded_cadu[i] >> 4) & 0x1);
d_coded_cadu_syms[i * 8 + 4] = 0xFF * ((coded_cadu[i] >> 3) & 0x1);
d_coded_cadu_syms[i * 8 + 5] = 0xFF * ((coded_cadu[i] >> 2) & 0x1);
d_coded_cadu_syms[i * 8 + 6] = 0xFF * ((coded_cadu[i] >> 1) & 0x1);
d_coded_cadu_syms[i * 8 + 7] = 0xFF * ((coded_cadu[i] & 0x1));
}
/* Convolutional decoding */
update_viterbi27_blk(d_vt, d_coded_cadu_syms, d_cadu_len * 8);
chainback_viterbi27(d_vt, d_cadu, d_cadu_len * 8, 0);
message_port_pub(pmt::mp("frame"), pmt::make_blob(d_cadu, d_cadu_len));
/* Descrambling */
d_scrambler.reset();
d_scrambler.descramble(d_cadu + 4, d_cadu + 4, d_cadu_len - 4, true);
decode_ccsds_packet(d_cadu + 4);
}
void
lrpt_decoder_impl::decode_ccsds_packet(const uint8_t *cvcdu)
{
/* Check first the VCDU version and if encryption is off */
if( (cvcdu[0] >> 6) != 0x1) {
return;
}
if(cvcdu[6] != 0x0 || cvcdu[7] != 0x0) {
return;
}
/* Check if the VCDU contans data */
//if((cvcdu[8] >> 3) != 0x0 && (cvcdu[8] >> 3) != 0x1f) {
// return;
//}
const uint8_t *mpdu = cvcdu + 10;
/* Check CCSDS packet version and type */
//if( (mpdu[0] >> 5) != 0x0) {
// return;
// }
uint32_t vcdu_seq = 0;
vcdu_seq = cvcdu[2];
vcdu_seq = (vcdu_seq << 8) | cvcdu[3];
vcdu_seq = (vcdu_seq << 8) | cvcdu[4];
uint16_t hdr_ptr = 0;
hdr_ptr = cvcdu[8] & 0x7;
hdr_ptr = (hdr_ptr << 8) | cvcdu[9];
/* Try to find the start of a MPDU */
if(!d_have_mpdu) {
if(hdr_ptr != 0) {
return;
}
d_have_mpdu = true;
}
message_port_pub(pmt::mp("frame"), pmt::make_blob(cvcdu, d_cadu_len - 4));
}
} /* namespace satnogs */
} /* namespace gr */

View File

@ -23,6 +23,7 @@
#include <satnogs/lrpt_decoder.h>
#include <satnogs/convolutional_deinterleaver.h>
#include <satnogs/whitening.h>
namespace gr
{
@ -39,13 +40,20 @@ public:
private:
const size_t d_cadu_len;
const size_t d_coded_cadu_len;
convolutional_deinterleaver d_conv_deinterl;
const size_t d_mpdu_max_len;
whitening d_scrambler;
bool d_have_mpdu;
uint8_t *d_coded_cadu_syms;
uint8_t *d_cadu;
uint8_t *d_mpdu;
void *d_vt;
void
decode(pmt::pmt_t m);
void
decode_ccsds_packet(const uint8_t *cvcdu);
};
} // namespace satnogs

View File

@ -58,12 +58,18 @@ lrpt_sync_impl::lrpt_sync_impl (size_t threshold) :
*/
d_window((72 + 8)/2),
/* Each CADU has the 4 byte ASM and a VCDU of 1020 bytes*/
d_coded_cadu_len(1020 * 2 + 4*2),
/*
* NOTE:
* Metop violates the standard as many times as possible...
* The frame should contain 128 RS check symbols at the end.
* For some unknown reasons, it seems that the RS encoding is not performed.
* Thus, they dropped the check symbols at the end of the frame.
*/
d_coded_cadu_len(1020 * 2 + 4*2 - 128 * 2),
d_frame_sync(false),
d_received(0),
d_rotate(1, 0),
d_rotate(1.0, 0.0),
d_qpsk(digital::constellation_qpsk::make()),
d_conv_deinter(36, 2048),
d_shift_reg0(0x0),
d_shift_reg1(0x0),
d_shift_reg2(0x0),
@ -72,25 +78,29 @@ lrpt_sync_impl::lrpt_sync_impl (size_t threshold) :
set_output_multiple(d_window);
const int alignment_multiple = volk_get_alignment () / sizeof(gr_complex);
set_alignment (std::max (1, alignment_multiple));
d_rotate_pi2 = (gr_complex *)volk_malloc(d_window, volk_get_alignment ());
d_rotate_pi2 = (gr_complex *) volk_malloc (d_window * sizeof(gr_complex),
volk_get_alignment ());
if(!d_rotate_pi2) {
throw std::runtime_error("lrpt_sync: Could not allocate memory");
}
d_rotate_2pi2 = (gr_complex *)volk_malloc(d_window, volk_get_alignment ());
d_rotate_2pi2 = (gr_complex *) volk_malloc (d_window * sizeof(gr_complex),
volk_get_alignment ());
if(!d_rotate_2pi2) {
volk_free(d_rotate_pi2);
throw std::runtime_error("lrpt_sync: Could not allocate memory");
}
d_rotate_3pi2 = (gr_complex *)volk_malloc(d_window, volk_get_alignment ());
d_rotate_3pi2 = (gr_complex *) volk_malloc (d_window * sizeof(gr_complex),
volk_get_alignment ());
if(!d_rotate_3pi2) {
volk_free(d_rotate_pi2);
volk_free(d_rotate_2pi2);
throw std::runtime_error("lrpt_sync: Could not allocate memory");
}
d_corrected = (gr_complex *)volk_malloc(d_window, volk_get_alignment ());
d_corrected = (gr_complex *)volk_malloc(d_window * sizeof(gr_complex),
volk_get_alignment ());
if(!d_corrected) {
volk_free(d_rotate_pi2);
volk_free(d_rotate_2pi2);
@ -98,7 +108,7 @@ lrpt_sync_impl::lrpt_sync_impl (size_t threshold) :
throw std::runtime_error("lrpt_sync: Could not allocate memory");
}
uint64_t asm_coded = reverse_uint64_bytes(d_asm_coded);
uint64_t asm_coded = htonll(d_asm_coded);
d_coded_cadu = new uint8_t[d_coded_cadu_len];
memcpy(d_coded_cadu, &asm_coded, sizeof(uint64_t));
d_received = sizeof(uint64_t);
@ -133,11 +143,11 @@ lrpt_sync_impl::work_no_sync(const gr_complex *in, int noutput_items)
int multiple = noutput_items / d_window;
for(int i = 0; i < multiple; i++) {
volk_32fc_s32fc_multiply_32fc(d_rotate_pi2, in + i * d_window,
gr_complex(0, 1), d_window);
gr_complex(0.0, 1.0), d_window);
volk_32fc_s32fc_multiply_32fc(d_rotate_2pi2, in + i * d_window,
gr_complex(-1, 0), d_window);
gr_complex(-1.0, 0.0), d_window);
volk_32fc_s32fc_multiply_32fc(d_rotate_3pi2, in + i * d_window,
gr_complex(0, -1), d_window);
gr_complex(0.0, -1.0), d_window);
/*
* Search for the sync pattern, rotating the QPSK constellation on
* all possible positions
@ -147,9 +157,11 @@ lrpt_sync_impl::work_no_sync(const gr_complex *in, int noutput_items)
//bits = (d_conv_deinter.decode_bit(bits >> 1) << 1) | d_conv_deinter.decode_bit(bits & 0x1);
d_shift_reg0 = (d_shift_reg0 << 2) | bits;
if(found_sync(d_shift_reg0)) {
d_rotate = gr_complex(1.0, 0);
d_rotate = gr_complex(1.0, 0.0);
d_frame_sync = true;
return i * d_window + j;
uint64_t asm_coded = htonll(d_shift_reg0);
memcpy(d_coded_cadu, &asm_coded, sizeof(uint64_t));
return i * d_window + j + 1;
}
bits = d_qpsk->decision_maker(d_rotate_pi2 + j);
@ -158,16 +170,20 @@ lrpt_sync_impl::work_no_sync(const gr_complex *in, int noutput_items)
if(found_sync(d_shift_reg1)) {
d_rotate = gr_complex(0.0, 1.0);
d_frame_sync = true;
return i * d_window + j;
uint64_t asm_coded = htonll(d_shift_reg1);
memcpy(d_coded_cadu, &asm_coded, sizeof(uint64_t));
return i * d_window + j + 1;
}
bits = d_qpsk->decision_maker(d_rotate_2pi2 + j);
//bits = (d_conv_deinter.decode_bit(bits >> 1) << 1) | d_conv_deinter.decode_bit(bits & 0x1);
d_shift_reg2 = (d_shift_reg2 << 2) | bits;
if(found_sync(d_shift_reg2)) {
d_rotate = gr_complex(-1.0, 0);
d_rotate = gr_complex(-1.0, 0.0);
d_frame_sync = true;
return i * d_window + j;
uint64_t asm_coded = htonll(d_shift_reg2);
memcpy(d_coded_cadu, &asm_coded, sizeof(uint64_t));
return i * d_window + j + 1;
}
bits = d_qpsk->decision_maker(d_rotate_3pi2 + j);
@ -176,7 +192,9 @@ lrpt_sync_impl::work_no_sync(const gr_complex *in, int noutput_items)
if(found_sync(d_shift_reg3)) {
d_rotate = gr_complex(0.0, -1.0);
d_frame_sync = true;
return i * d_window + j;
uint64_t asm_coded = htonll(d_shift_reg3);
memcpy(d_coded_cadu, &asm_coded, sizeof(uint64_t));
return i * d_window + j + 1;
}
}
}
@ -200,7 +218,6 @@ lrpt_sync_impl::work_sync(const gr_complex *in, int noutput_items)
d_coded_cadu[d_received++] = b;
if(d_received == d_coded_cadu_len) {
LOG_ERROR("frame");
d_received = sizeof(uint64_t);
d_frame_sync = false;
message_port_pub (pmt::mp ("cadu"),

View File

@ -44,7 +44,7 @@ public:
private:
const size_t d_thresh;
const uint64_t d_asm_coded;
const uint64_t d_asm_coded_len;
const size_t d_asm_coded_len;
const uint64_t d_asm_coded_mask;
const int d_window;
const size_t d_coded_cadu_len;
@ -52,7 +52,6 @@ private:
size_t d_received;
gr_complex d_rotate;
digital::constellation_qpsk::sptr d_qpsk;
convolutional_deinterleaver d_conv_deinter;
uint64_t d_shift_reg0;
uint64_t d_shift_reg1;
uint64_t d_shift_reg2;

View File

@ -26,97 +26,117 @@
#include <satnogs/whitening.h>
#include <satnogs/utils.h>
namespace gr {
namespace satnogs {
namespace gr
{
namespace satnogs
{
/**
* Data whitening and de-whitening class
* @param mask the polynomial mask
* @param seed the initial seed
* @param order the order of the shift register. This is equal to the
* number of memory stages.
*/
whitening::whitening (uint32_t mask, uint32_t seed, uint32_t order) :
d_lfsr(mask, seed, order)
{
/**
* Data whitening and de-whitening class
* @param mask the polynomial mask
* @param seed the initial seed
* @param order the order of the shift register. This is equal to the
* number of memory stages.
*/
whitening::whitening (uint32_t mask, uint32_t seed, uint32_t order) :
d_lfsr (mask, seed, order)
{
}
/**
* Resets the scrambler (or the descrambler) to the initial stage and
* the initial seed.
*/
void
whitening::reset ()
{
d_lfsr.reset ();
}
/**
* Performs data scrambling
* @param out the output buffer
* @param in the input buffer
* @param len the number of the bytes to be scrambled
* @param msb if set to true, the descrambler starts from the msb
*/
void
whitening::scramble (uint8_t* out, const uint8_t* in, size_t len, bool msb)
{
size_t i;
uint8_t b;
if(msb) {
for (i = 0; i < len; i++) {
b = d_lfsr.next_bit () << 7;
b |= d_lfsr.next_bit () << 6;
b |= d_lfsr.next_bit () << 5;
b |= d_lfsr.next_bit () << 4;
b |= d_lfsr.next_bit () << 3;
b |= d_lfsr.next_bit () << 2;
b |= d_lfsr.next_bit () << 1;
b |= d_lfsr.next_bit ();
out[i] = in[i] ^ b;
}
/**
* Resets the scrambler (or the descrambler) to the initial stage and
* the initial seed.
*/
void
whitening::reset ()
{
d_lfsr.reset();
}
else{
for (i = 0; i < len; i++) {
b = d_lfsr.next_bit ();
b |= d_lfsr.next_bit () << 1;
b |= d_lfsr.next_bit () << 2;
b |= d_lfsr.next_bit () << 3;
b |= d_lfsr.next_bit () << 4;
b |= d_lfsr.next_bit () << 5;
b |= d_lfsr.next_bit () << 6;
b |= d_lfsr.next_bit () << 7;
out[i] = in[i] ^ b;
}
}
}
/**
* Performs data scrambling
* @param out the output buffer
* @param in the input buffer
* @param len the number of the bytes to be scrambled
*/
void
whitening::scramble (uint8_t* out, const uint8_t* in, size_t len)
{
size_t i;
uint8_t b;
for(i = 0; i < len; i++){
b = d_lfsr.next_bit();
b |= d_lfsr.next_bit() << 1;
b |= d_lfsr.next_bit() << 2;
b |= d_lfsr.next_bit() << 3;
b |= d_lfsr.next_bit() << 4;
b |= d_lfsr.next_bit() << 5;
b |= d_lfsr.next_bit() << 6;
b |= d_lfsr.next_bit() << 7;
out[i] = in[i] ^ b;
}
}
/**
* Performs data de-scrambling
* @param out the output buffer
* @param in the input buffer
* @param len the number of the bytes to be de-scrambled
* @param msb if set to true, the descrambler starts from the msb
*/
void
whitening::descramble (uint8_t* out, const uint8_t* in, size_t len,
bool msb)
{
scramble (out, in, len, msb);
}
/**
* Performs data de-scrambling
* @param out the output buffer
* @param in the input buffer
* @param len the number of the bytes to be de-scrambled
*/
void
whitening::descramble (uint8_t* out, const uint8_t* in, size_t len)
{
scramble(out, in, len);
}
/**
* Performs data scrambling. The input and output buffer
* contain one bit per byte
* @param out the output buffer
* @param in the input buffer
* @param bits_num the number of bits to be scrambled
*/
void
whitening::scramble_one_bit_per_byte (uint8_t* out, const uint8_t* in,
size_t bits_num)
{
size_t i;
for (i = 0; i < bits_num; i++) {
out[i] = in[i] ^ d_lfsr.next_bit ();
}
}
/**
* Performs data scrambling. The input and output buffer
* contain one bit per byte
* @param out the output buffer
* @param in the input buffer
* @param bits_num the number of bits to be scrambled
*/
void
whitening::scramble_one_bit_per_byte (uint8_t* out, const uint8_t* in,
size_t bits_num)
{
size_t i;
for(i = 0; i < bits_num; i++){
out[i] = in[i] ^ d_lfsr.next_bit();
}
}
/**
* Performs data descrambling. The input and output buffer
* contain one bit per byte
* @param out the output buffer
* @param in the input buffer
* @param bits_num the number of bits to be descrambled
*/
void
whitening::descramble_one_bit_per_byte (uint8_t* out, const uint8_t* in,
size_t bits_num)
{
scramble_one_bit_per_byte (out, in, bits_num);
}
/**
* Performs data descrambling. The input and output buffer
* contain one bit per byte
* @param out the output buffer
* @param in the input buffer
* @param bits_num the number of bits to be descrambled
*/
void
whitening::descramble_one_bit_per_byte (uint8_t* out, const uint8_t* in,
size_t bits_num)
{
scramble_one_bit_per_byte(out, in, bits_num);
}
} /* namespace satnogs */
} /* namespace satnogs */
} /* namespace gr */