From 4bcd9c8aaac1869bec6a308403bd3909b4f30b53 Mon Sep 17 00:00:00 2001 From: Manolis Surligas Date: Sun, 2 Dec 2018 00:47:56 +0200 Subject: [PATCH] Start the implementation of a generic frame synchronizer The generic frame synchronizer will be able to adapt in a variety of common framing schemes used by popular modems. --- .gitlab-ci.yml | 2 +- grc/CMakeLists.txt | 1 + grc/satnogs_block_tree.xml | 1 + grc/satnogs_whitening.xml | 35 ++++ include/satnogs/frame_acquisition.h | 48 +++-- include/satnogs/whitening.h | 63 +++--- lib/frame_acquisition_impl.cc | 312 ++++++++++++++++++++++++++-- lib/frame_acquisition_impl.h | 58 +++++- lib/whitening.cc | 27 ++- 9 files changed, 492 insertions(+), 55 deletions(-) create mode 100644 grc/satnogs_whitening.xml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b396aae..ff00ce5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,7 @@ image: debian:latest before_script: - - apt-get update -qq && apt-get install -y -qq gnuradio-dev libcppunit-dev libpng++-dev libvorbis-dev libnova-dev cmake swig pkg-config build-essential + - apt-get update -qq && apt-get install -y -qq gnuradio-dev libcppunit-dev libpng++-dev libvorbis-dev cmake swig pkg-config build-essential git test: script: diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt index 2001a8e..bdd10a2 100644 --- a/grc/CMakeLists.txt +++ b/grc/CMakeLists.txt @@ -51,6 +51,7 @@ list(APPEND enabled_blocks satnogs_quad_demod_filter_ff.xml satnogs_ogg_source.xml satnogs_noaa_apt_sink.xml + satnogs_whitening.xml ) if(${INCLUDE_DEBUG_BLOCKS}) diff --git a/grc/satnogs_block_tree.xml b/grc/satnogs_block_tree.xml index 5288ed1..05efb7d 100644 --- a/grc/satnogs_block_tree.xml +++ b/grc/satnogs_block_tree.xml @@ -41,4 +41,5 @@ satnogs_quad_demod_filter_ff satnogs_ccsds_rs_decoder_mm satnogs_decoder_8b10b + variable_whitening \ No newline at end of file diff --git a/grc/satnogs_whitening.xml b/grc/satnogs_whitening.xml new file mode 100644 index 0000000..d166aea --- /dev/null +++ b/grc/satnogs_whitening.xml @@ -0,0 +1,35 @@ + + + Whitening + variable_whitening + import satnogs + self.$(id) = $(id) = satnogs.whitening_make($mask, $seed, $order) + satnogs.whitening_make($mask, $seed, $order) + + + Ignore Me + value + 'ok' + raw + all + + + + Mask + mask + int + + + + Seed + seed + int + + + + Order + order + int + + + diff --git a/include/satnogs/frame_acquisition.h b/include/satnogs/frame_acquisition.h index c1c7368..7a7b57f 100644 --- a/include/satnogs/frame_acquisition.h +++ b/include/satnogs/frame_acquisition.h @@ -22,6 +22,7 @@ #define INCLUDED_SATNOGS_FRAME_ACQUISITION_H #include +#include #include namespace gr @@ -39,19 +40,42 @@ class SATNOGS_API frame_acquisition : virtual public gr::sync_block public: typedef boost::shared_ptr sptr; - /*! - * \brief Return a shared_ptr to a new instance of satnogs::frame_acquisition. - * - * To avoid accidental use of raw pointers, satnogs::frame_acquisition's - * constructor is in a private implementation - * class. satnogs::frame_acquisition::make is the public interface for - * creating new instances. - */ + typedef enum { + CRC_NONE = 0, + CRC16, + CRC32 + } checksum_t; + + static sptr - make (const std::vector& preamble, - size_t preamble_threshold, - const std::vector& sync, - size_t sync_threshold); + make_generic_var_len (const std::vector& preamble, + size_t preamble_threshold, + const std::vector& sync, + size_t sync_threshold, + size_t frame_size_len = 1, + checksum_t crc = CRC_NONE, + whitening::sptr descrambler = nullptr, + size_t max_frame_len = 2048); + + static sptr + make_generic_const_len(const std::vector& preamble, + size_t preamble_threshold, + const std::vector& sync, + size_t sync_threshold, + size_t frame_len = 1, + checksum_t crc = CRC_NONE, + whitening::sptr descrambler = nullptr, + size_t max_frame_len = 2048); + + static sptr + make_golay24_var_len (const std::vector& preamble, + size_t preamble_threshold, + const std::vector& sync, + size_t sync_threshold, + checksum_t crc = CRC_NONE, + whitening::sptr descrambler = nullptr, + size_t max_frame_len = 2048); + }; } // namespace satnogs diff --git a/include/satnogs/whitening.h b/include/satnogs/whitening.h index 296679c..f54c109 100644 --- a/include/satnogs/whitening.h +++ b/include/satnogs/whitening.h @@ -23,41 +23,52 @@ #include #include +#include namespace gr { - namespace satnogs - { +namespace satnogs +{ - /*! - * \brief Performs data whitening and de-whitening - * - */ - class SATNOGS_API whitening - { - public: - whitening (uint32_t mask, uint32_t seed, uint32_t order); +/*! + * \brief Performs data whitening and de-whitening + * + */ +class SATNOGS_API whitening +{ +public: + static int base_unique_id; - void - reset(); + int + unique_id (); - 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); + typedef boost::shared_ptr sptr; - void - scramble_one_bit_per_byte (uint8_t *out, const uint8_t *in, - size_t bits_num); - void - descramble_one_bit_per_byte (uint8_t *out, const uint8_t *in, - size_t bits_num); + static sptr + make(uint32_t mask, uint32_t seed, uint32_t order); - private: - digital::lfsr d_lfsr; - }; + whitening (uint32_t mask, uint32_t seed, uint32_t order); - } // namespace satnogs + void + reset (); + + 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, size_t bits_num); + void + descramble_one_bit_per_byte (uint8_t *out, const uint8_t *in, + size_t bits_num); + +private: + digital::lfsr d_lfsr; +int d_id; +}; + +} // namespace satnogs } // namespace gr #endif /* INCLUDED_SATNOGS_WHITENING_H */ diff --git a/lib/frame_acquisition_impl.cc b/lib/frame_acquisition_impl.cc index 10d3505..1383ed9 100644 --- a/lib/frame_acquisition_impl.cc +++ b/lib/frame_acquisition_impl.cc @@ -23,36 +23,96 @@ #endif #include + #include "frame_acquisition_impl.h" +#include + namespace gr { namespace satnogs { frame_acquisition::sptr -frame_acquisition::make (const std::vector& preamble, - size_t preamble_threshold, - const std::vector& sync, - size_t sync_threshold) +frame_acquisition::make_generic_var_len (const std::vector& preamble, + size_t preamble_threshold, + const std::vector& sync, + size_t sync_threshold, + size_t frame_size_len, + checksum_t crc, + whitening::sptr descrambler, + size_t max_frame_len) { return gnuradio::get_initial_sptr ( - new frame_acquisition_impl (preamble, preamble_threshold, sync, - sync_threshold)); + new frame_acquisition_impl (frame_acquisition_impl::GENERIC_VAR_FRAME_LEN, + preamble, + preamble_threshold, sync, sync_threshold, + frame_size_len, 0, crc, descrambler, + max_frame_len)); } -frame_acquisition_impl::frame_acquisition_impl ( - const std::vector& preamble, size_t preamble_threshold, - const std::vector& sync, size_t sync_threshold) : +frame_acquisition::sptr +frame_acquisition::make_generic_const_len (const std::vector& preamble, + size_t preamble_threshold, + const std::vector& sync, + size_t sync_threshold, + size_t frame_len, checksum_t crc, + whitening::sptr descrambler, + size_t max_frame_len) +{ + return gnuradio::get_initial_sptr ( + new frame_acquisition_impl (frame_acquisition_impl::GENERIC_CONSTANT_FRAME_LEN, + preamble, + preamble_threshold, sync, sync_threshold, + 0, frame_len, crc, descrambler, + max_frame_len)); +} + +frame_acquisition::sptr +frame_acquisition::make_golay24_var_len (const std::vector& preamble, + size_t preamble_threshold, + const std::vector& sync, + size_t sync_threshold, checksum_t crc, + whitening::sptr descrambler, + size_t max_frame_len) +{ + return gnuradio::get_initial_sptr ( + new frame_acquisition_impl (frame_acquisition_impl::GOLAY24_CODED_FRAME_LEN, + preamble, + preamble_threshold, sync, sync_threshold, + 0, 0, crc, descrambler, + max_frame_len)); +} + +frame_acquisition_impl::frame_acquisition_impl (variant_t variant, + const std::vector& preamble, + size_t preamble_threshold, + const std::vector& sync, + size_t sync_threshold, + size_t frame_size_len, + size_t frame_len, + checksum_t crc, + whitening::sptr descrambler, + size_t max_frame_len) : gr::sync_block ("frame_acquisition", gr::io_signature::make (1, 1, sizeof(uint8_t)), gr::io_signature::make (0, 0, 0)), + d_variant(variant), d_preamble(preamble.size() * 8), + d_preamble_shift_reg(preamble.size() * 8), d_preamble_len(preamble.size() * 8), d_preamble_thrsh(preamble_threshold), d_sync(sync.size() * 8), + d_sync_shift_reg(sync.size() * 8), d_sync_len(sync.size() * 8), - d_sync_thrsh(sync_threshold) + d_sync_thrsh(sync_threshold), + d_state(SEARCHING), + d_cnt(0), + d_frame_size_field_len(frame_size_len), + d_frame_len(frame_len), + d_max_frame_len(max_frame_len), + d_crc(crc), + d_whitening(descrambler) { set_output_multiple(8); for(uint8_t b : preamble) { @@ -77,6 +137,45 @@ frame_acquisition_impl::frame_acquisition_impl ( d_sync <<= ((b >> 1) & 0x1); d_sync <<= (b & 0x1); } + + /* Parameters checking */ + if (max_frame_len == 0) { + throw std::invalid_argument ( + "The maximum frame size should be at least 1 byte"); + } + + if(d_sync_len < 8) { + throw std::invalid_argument("SYNC word should be at least 8 bits"); + } + + if(d_preamble_len < 8) { + throw std::invalid_argument("Preamble should be at least 8 bits"); + } + + if (d_preamble_len < 2 * d_preamble_thrsh) { + throw std::invalid_argument ( + "Too many error bits are allowed for the preamble." + "Consider lowering the threshold"); + } + + if (d_sync_len < 2 * d_sync_thrsh) { + throw std::invalid_argument ( + "Too many error bits are allowed for the sync word. " + "Consider lowering the threshold"); + } + + if (d_frame_size_field_len > 4) { + throw std::invalid_argument ("Frame length field can be up to 4 bytes"); + } + + if (d_frame_size_field_len == 0) { + throw std::invalid_argument ("Frame length field cannot be 0"); + } + + d_pdu = new uint8_t[max_frame_len]; + + /* Register the PMT message queue */ + message_port_register_out(pmt::mp("out")); } /* @@ -84,6 +183,7 @@ frame_acquisition_impl::frame_acquisition_impl ( */ frame_acquisition_impl::~frame_acquisition_impl () { + delete [] d_pdu; } @@ -95,10 +195,196 @@ frame_acquisition_impl::work (int noutput_items, { const uint8_t *in = (const uint8_t *) input_items[0]; - // Do <+signal processing+> + switch(d_state) + { + case SEARCHING: + return searching_preamble(in, noutput_items); + case SEARCHING_SYNC: + return searching_sync(in, noutput_items); + case DECODING_GENERIC_FRAME_LEN: + return dec_generic_frame_len(in, noutput_items); + case DECODING_GOLAY24_FRAME_LEN: + return dec_golay24_frame_len(in, noutput_items); + case DECODING_PAYLOAD: + return decoding(in, noutput_items); + default: + return noutput_items; + } +} - // Tell runtime system how many output items we produced. - return noutput_items; +int +frame_acquisition_impl::searching_preamble (const uint8_t* in, int len) +{ + for(int i = 0; i < len; i++) { + d_preamble_shift_reg <<= in[i]; + shift_reg tmp = d_preamble_shift_reg ^ d_preamble; + if(tmp.count() <= d_preamble_thrsh) { + d_state = SEARCHING_SYNC; + d_cnt = 0; + return i+1; + } + } + return len; +} + +int +frame_acquisition_impl::searching_sync (const uint8_t* in, int len) +{ + for (int i = 0; i < len; i++) { + d_sync_shift_reg <<= in[i]; + shift_reg tmp = d_sync_shift_reg ^ d_sync; + d_cnt++; + if (tmp.count () <= d_sync_thrsh) { + switch(d_variant) { + case GENERIC_VAR_FRAME_LEN: + d_state = DECODING_GENERIC_FRAME_LEN; + break; + case GENERIC_CONSTANT_FRAME_LEN: + d_state = DECODING_GENERIC_FRAME_LEN; + break; + } + d_cnt = 0; + return i + 1; + } + + /* The sync word should be available by now */ + if(d_cnt > d_preamble_len * 2 + d_sync_len) { + reset(); + return i + 1; + } + } + return len; +} + +int +frame_acquisition_impl::dec_generic_frame_len (const uint8_t* in, int len) +{ + const int s = std::min(len / 8, (int) d_frame_size_field_len); + for(int i = 0; i < s; i++) { + uint8_t b = 0x0; + b |= in[i * 8] << 7; + b |= in[i * 8 + 1] << 6; + b |= in[i * 8 + 2] << 5; + b |= in[i * 8 + 3] << 4; + b |= in[i * 8 + 4] << 3; + b |= in[i * 8 + 5] << 2; + b |= in[i * 8 + 6] << 1; + b |= in[i * 8 + 7]; + d_frame_len <<= 8; + d_frame_len |= b; + d_cnt++; + if(d_cnt == d_frame_size_field_len) { + + /* Most of the available modems apply whitening on the frame length too */ + if(d_whitening) { + uint32_t descrambled = 0x0; + d_whitening->descramble((uint8_t *)&descrambled, + (const uint8_t *)&d_frame_len, + d_frame_size_field_len, true); + d_frame_len = descrambled; + } + if(d_frame_len < d_max_frame_len) { + d_state = DECODING_PAYLOAD; + } + else{ + reset(); + return (i + 1) * 8; + } + d_cnt = 0; + return (i + 1) * 8; + } + } + return s * 8; +} + +int +frame_acquisition_impl::dec_golay24_frame_len (const uint8_t* in, int len) +{ + /* Golay24 needs 3 bytes to decode */ + const int s = std::min(len / 8, 3); + for(int i = 0; i < s; i++) { + uint8_t b = 0x0; + b |= in[i * 8] << 7; + b |= in[i * 8 + 1] << 6; + b |= in[i * 8 + 2] << 5; + b |= in[i * 8 + 3] << 4; + b |= in[i * 8 + 4] << 3; + b |= in[i * 8 + 5] << 2; + b |= in[i * 8 + 6] << 1; + b |= in[i * 8 + 7]; + d_frame_len <<= 8; + d_frame_len |= b; + d_cnt++; + + /* Try to decode the frame length */ + if (d_cnt == 3) { + if(d_whitening) { + uint32_t descrambled = 0x0; + d_whitening->descramble((uint8_t *)&descrambled, + (const uint8_t *)&d_frame_len, 3, true); + d_frame_len = descrambled; + } + golay24 g = golay24 (); + uint16_t tmp = 0; + if (g.decode24 (&tmp, d_frame_len)) { + d_frame_len = tmp; + /* Check if the payload can fit in the buffer */ + if(d_frame_len > d_max_frame_len) { + reset(); + return (i + 1) * 8; + } + else{ + d_state = DECODING_PAYLOAD; + } + } + else { + reset (); + return (i + 1) * 8; + } + d_cnt = 0; + return (i + 1) * 8; + } + } + return s * 8; +} + +int +frame_acquisition_impl::decoding (const uint8_t* in, int len) +{ + const int s = len / 8; + for(int i = 0; i < s; i++) { + uint8_t b = 0x0; + b |= in[i * 8] << 7; + b |= in[i * 8 + 1] << 6; + b |= in[i * 8 + 2] << 5; + b |= in[i * 8 + 3] << 4; + b |= in[i * 8 + 4] << 3; + b |= in[i * 8 + 5] << 2; + b |= in[i * 8 + 6] << 1; + b |= in[i * 8 + 7]; + d_pdu[d_cnt++] = b; + if(d_cnt == d_frame_len) { + if(d_whitening) { + d_whitening->descramble(d_pdu, d_pdu, d_frame_len, true); + } + /* TODO */ + reset(); + return (i+1) * 8; + } + } + return len; +} + +void +frame_acquisition_impl::reset () +{ + if(d_whitening) { + d_whitening->reset(); + } + d_cnt = 0; + d_state = SEARCHING; + d_preamble_shift_reg.reset(); + d_sync_shift_reg.reset(); } } /* namespace satnogs */ diff --git a/lib/frame_acquisition_impl.h b/lib/frame_acquisition_impl.h index afe1f71..40aca9c 100644 --- a/lib/frame_acquisition_impl.h +++ b/lib/frame_acquisition_impl.h @@ -33,10 +33,23 @@ class frame_acquisition_impl : public frame_acquisition { public: - frame_acquisition_impl (const std::vector& preamble, + typedef enum { + GENERIC_VAR_FRAME_LEN = 0, + GENERIC_CONSTANT_FRAME_LEN, + GOLAY24_CODED_FRAME_LEN + } variant_t; + + frame_acquisition_impl (variant_t variant, + const std::vector& preamble, size_t preamble_threshold, const std::vector& sync, - size_t sync_threshold); + size_t sync_threshold, + size_t frame_size_len, + size_t frame_len, + checksum_t crc, + whitening::sptr descrambler, + size_t max_frame_len); + ~frame_acquisition_impl (); // Where all the action really happens @@ -45,12 +58,53 @@ public: gr_vector_void_star &output_items); private: + /** + * Decoding FSM + */ + typedef enum + { + SEARCHING, //!< when searching for the start of the preamble + SEARCHING_SYNC, + DECODING_GENERIC_FRAME_LEN, + DECODING_GOLAY24_FRAME_LEN, + DECODING_PAYLOAD + } decoding_state_t; + + const variant_t d_variant; shift_reg d_preamble; + shift_reg d_preamble_shift_reg; const size_t d_preamble_len; const size_t d_preamble_thrsh; shift_reg d_sync; + shift_reg d_sync_shift_reg; const size_t d_sync_len; const size_t d_sync_thrsh; + decoding_state_t d_state; + uint32_t d_cnt; + const uint32_t d_frame_size_field_len; + uint32_t d_frame_len; + const uint32_t d_max_frame_len; + const checksum_t d_crc; + whitening::sptr d_whitening; + uint8_t *d_pdu; + + + int + searching_preamble(const uint8_t *in, int len); + + int + searching_sync(const uint8_t *in, int len); + + int dec_generic_frame_len(const uint8_t *in, int len); + + + int dec_golay24_frame_len(const uint8_t *in, int len); + + int + decoding(const uint8_t *in, int len); + + void + reset(); }; } // namespace satnogs diff --git a/lib/whitening.cc b/lib/whitening.cc index b1f7733..88488f8 100644 --- a/lib/whitening.cc +++ b/lib/whitening.cc @@ -31,6 +31,23 @@ namespace gr namespace satnogs { +int whitening::base_unique_id = 1; + + +/** + * 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::sptr +whitening::make (uint32_t mask, uint32_t seed, uint32_t order) +{ + return whitening::sptr(new whitening(mask, seed, order)); +} + + /** * Data whitening and de-whitening class * @param mask the polynomial mask @@ -39,8 +56,16 @@ namespace satnogs * number of memory stages. */ whitening::whitening (uint32_t mask, uint32_t seed, uint32_t order) : - d_lfsr (mask, seed, order) + d_lfsr (mask, seed, order), + d_id(0) { + d_id = base_unique_id++; +} + +int +whitening::unique_id () +{ + return d_id; } /**