From 63218b157ba468cac83ae8faa93a042b18c849a1 Mon Sep 17 00:00:00 2001 From: Manolis Surligas Date: Thu, 9 Aug 2018 19:29:47 +0300 Subject: [PATCH] Add LRPT decoding flowgraph --- grc/satnogs_block_tree.xml | 6 +- include/satnogs/utils.h | 4 + include/satnogs/whitening.h | 6 +- lib/lrpt_decoder_impl.cc | 88 ++++++++++++++--- lib/lrpt_decoder_impl.h | 10 +- lib/lrpt_sync_impl.cc | 53 ++++++---- lib/lrpt_sync_impl.h | 3 +- lib/whitening.cc | 192 ++++++++++++++++++++---------------- 8 files changed, 238 insertions(+), 124 deletions(-) diff --git a/grc/satnogs_block_tree.xml b/grc/satnogs_block_tree.xml index c7d35e8..5288ed1 100644 --- a/grc/satnogs_block_tree.xml +++ b/grc/satnogs_block_tree.xml @@ -17,11 +17,15 @@ AMSAT FOX satnogs_fox_telem_mm + + METOP + satnogs_lrpt_sync + satnogs_lrpt_decoder + satnogs_cw_matched_filter_ff satnogs_morse_decoder satnogs_multi_format_msg_sink - satnogs_lrpt_sync satnogs_iq_sink satnogs_ogg_encoder satnogs_ogg_source diff --git a/include/satnogs/utils.h b/include/satnogs/utils.h index 836c0cd..767ec17 100644 --- a/include/satnogs/utils.h +++ b/include/satnogs/utils.h @@ -23,12 +23,16 @@ #include #include +#include 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 diff --git a/include/satnogs/whitening.h b/include/satnogs/whitening.h index a450016..296679c 100644 --- a/include/satnogs/whitening.h +++ b/include/satnogs/whitening.h @@ -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, diff --git a/lib/lrpt_decoder_impl.cc b/lib/lrpt_decoder_impl.cc index 6c49d35..e7bb7bf 100644 --- a/lib/lrpt_decoder_impl.cc +++ b/lib/lrpt_decoder_impl.cc @@ -25,6 +25,9 @@ #include #include "lrpt_decoder_impl.h" #include +#include + + extern "C" { #include } @@ -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 */ diff --git a/lib/lrpt_decoder_impl.h b/lib/lrpt_decoder_impl.h index 010b0dc..378870f 100644 --- a/lib/lrpt_decoder_impl.h +++ b/lib/lrpt_decoder_impl.h @@ -23,6 +23,7 @@ #include #include +#include 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 diff --git a/lib/lrpt_sync_impl.cc b/lib/lrpt_sync_impl.cc index 66b72f0..4db4c2b 100644 --- a/lib/lrpt_sync_impl.cc +++ b/lib/lrpt_sync_impl.cc @@ -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"), diff --git a/lib/lrpt_sync_impl.h b/lib/lrpt_sync_impl.h index 6cd0f29..516c797 100644 --- a/lib/lrpt_sync_impl.h +++ b/lib/lrpt_sync_impl.h @@ -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; diff --git a/lib/whitening.cc b/lib/whitening.cc index 2fff50d..b1f7733 100644 --- a/lib/whitening.cc +++ b/lib/whitening.cc @@ -26,97 +26,117 @@ #include #include -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 */