Add a IEEE 802.15.4 compatible decoder
Add a IEEE 802.15.4 like decoder, which supports the IEEE 802.15.4 standard but also a large variety of ICs based on this framing scheme. Such framings are quite common in many Cubesat missions. The decoder has been tested an works well with at least the Reaktor Hello World satellite
This commit is contained in:
parent
142c995370
commit
c40b83211f
81
README.md
81
README.md
|
@ -98,6 +98,35 @@ Before submitting a new merge request, rebase the `master` branch and
|
||||||
confirm that the automated CI tests have successfully completed for all platforms
|
confirm that the automated CI tests have successfully completed for all platforms
|
||||||
mandated by the `.gitlab-ci.yml` recipe.
|
mandated by the `.gitlab-ci.yml` recipe.
|
||||||
|
|
||||||
|
### Coding style
|
||||||
|
For the C++ code, `gr-satnogs` uses a slightly modified version of the
|
||||||
|
**Stroustrup** style, which is a nicer adaptation of the well known K&R style.
|
||||||
|
In addition, we decided to decrease the indentation from 4 to 2 spaces.
|
||||||
|
This choice was made mainly to avoid braking statements with long namespaces.
|
||||||
|
We also found ourselves, that with smaller indentation we use more descriptive
|
||||||
|
variable names, avoiding frustrating abbreviations without phoenixes etc.
|
||||||
|
|
||||||
|
At the root directory of the project there is the `astyle` options
|
||||||
|
file `.astylerc` containing the proper configuration.
|
||||||
|
Developers can import this configuration to their favorite editor.
|
||||||
|
In addition the `hooks/pre-commit` file contains a Git hook,
|
||||||
|
that can be used to perform before every commit, code style formatting
|
||||||
|
with `astyle` and the `.astylerc` parameters.
|
||||||
|
To enable this hook developers should copy the hook at their `.git/hooks`
|
||||||
|
directory.
|
||||||
|
Failing to comply with the coding style described by the `.astylerc`
|
||||||
|
will result to failure of the automated tests running on our CI services.
|
||||||
|
So make sure that you either import on your editor the coding style rules
|
||||||
|
or use the `pre-commit` Git hook.
|
||||||
|
|
||||||
|
|
||||||
|
Regarding the naming of files and variables, we use the underscore naming
|
||||||
|
convention (`do_this`) instead of camel cases (`DoNotDoThis`).
|
||||||
|
Exception to this rule is the CMake module filenames. In addition,
|
||||||
|
all private variables of a C++ class, should start with the prefix
|
||||||
|
`d_` allowing the developers to spot easily private members of the object.
|
||||||
|
|
||||||
|
|
||||||
### Adding a new Satellite Demodulator
|
### Adding a new Satellite Demodulator
|
||||||
Demodulators are responsible for filtering, resampling and demodulating an
|
Demodulators are responsible for filtering, resampling and demodulating an
|
||||||
analog signal and converting it into suitable form, for a decoder to be able
|
analog signal and converting it into suitable form, for a decoder to be able
|
||||||
|
@ -125,15 +154,16 @@ The `satnogs::decoder` class, is a virtual class providing a generic API that
|
||||||
every derived decoder class should implement.
|
every derived decoder class should implement.
|
||||||
The core of this class is the
|
The core of this class is the
|
||||||
|
|
||||||
```
|
```cpp
|
||||||
decoder_status_t decode (const void *in, int len)
|
decoder_status_t
|
||||||
|
decode(const void *in, int len)
|
||||||
```
|
```
|
||||||
method. This method accepts an input buffer `in`. The type of the items depends
|
method. This method accepts an input buffer `in`. The type of the items depends
|
||||||
on the implementation. It also takes the `len` argument specifying the number
|
on the implementation. It also takes the `len` argument specifying the number
|
||||||
of items available in the `in` buffer.
|
of items available in the `in` buffer.
|
||||||
The method returns a `decoder_status_t` class object.
|
The method returns a `decoder_status_t` class object.
|
||||||
|
|
||||||
```
|
```cpp
|
||||||
class decoder_status
|
class decoder_status
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -164,32 +194,6 @@ decoded and its data are available on the `data` field.
|
||||||
information regarding it, using the `gr-satnogs` metadata format. More about them
|
information regarding it, using the `gr-satnogs` metadata format. More about them
|
||||||
in the [Metadata](#metadata) section
|
in the [Metadata](#metadata) section
|
||||||
|
|
||||||
### Coding style
|
|
||||||
For the C++ code, `gr-satnogs` uses a slightly modified version of the
|
|
||||||
**Stroustrup** style, which is a nicer adaptation of the well known K&R style.
|
|
||||||
In addition, we decided to decrease the indentation from 4 to 2 spaces.
|
|
||||||
This choice was made mainly to avoid braking statements with long namespaces.
|
|
||||||
We also found ourselves, that with smaller indentation we use more descriptive
|
|
||||||
variable names, avoiding frustrating abbreviations without phoenixes etc.
|
|
||||||
|
|
||||||
At the root directory of the project there is the `astyle` options
|
|
||||||
file `.astylerc` containing the proper configuration.
|
|
||||||
Developers can import this configuration to their favorite editor.
|
|
||||||
In addition the `hooks/pre-commit` file contains a Git hook,
|
|
||||||
that can be used to perform before every commit, code style formatting
|
|
||||||
with `astyle` and the `.astylerc` parameters.
|
|
||||||
To enable this hook developers should copy the hook at their `.git/hooks`
|
|
||||||
directory.
|
|
||||||
Failing to comply with the coding style described by the `.astylerc`
|
|
||||||
will result to failure of the automated tests running on our CI services.
|
|
||||||
So make sure that you either import on your editor the coding style rules
|
|
||||||
or use the `pre-commit` Git hook.
|
|
||||||
|
|
||||||
|
|
||||||
Regarding the naming of files and variables, we use the underscore naming convention (`do_this`) instead of
|
|
||||||
camel cases (`DoNotDoThis`).
|
|
||||||
Exception to this rule is the CMake module filenames. In addition, all private variables of a C++ class, should start with the prefix `d_` allowing the developers to spot easily private members of the object.
|
|
||||||
|
|
||||||
|
|
||||||
### Metadata
|
### Metadata
|
||||||
Each decoder generates a `pmt::pmt_t` dictionary containing the decoded data and
|
Each decoder generates a `pmt::pmt_t` dictionary containing the decoded data and
|
||||||
|
@ -216,7 +220,7 @@ metadata::keys()` static method which returns a list with the available
|
||||||
metadata keys. This method is also available in Python through the Swig interface.
|
metadata keys. This method is also available in Python through the Swig interface.
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```
|
```python
|
||||||
$ python
|
$ python
|
||||||
>>> import satnogs
|
>>> import satnogs
|
||||||
>>> satnogs.metadata.keys()
|
>>> satnogs.metadata.keys()
|
||||||
|
@ -232,6 +236,23 @@ The `json_converter` block accepts also a string that may be used to inject
|
||||||
an arbitrary number of additional information under the `extra` JSON field.
|
an arbitrary number of additional information under the `extra` JSON field.
|
||||||
Of course, this string should be in a JSON valid format.
|
Of course, this string should be in a JSON valid format.
|
||||||
|
|
||||||
|
For example, such a JSON string with information on the extra field could be like
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"corrected_bits" : 0,
|
||||||
|
"extra" :
|
||||||
|
{
|
||||||
|
"x" : 3,
|
||||||
|
"y" : "test"
|
||||||
|
},
|
||||||
|
"pdu" : "igAg7nRAOCAniUMAtIoAAAAAAAAAAAAAAABNJ4kfAFD4wwAfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||||
|
"symbol_erasures" : 0,
|
||||||
|
"time" : "2019-09-11T15:39:13.514138Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Release Policy
|
### Release Policy
|
||||||
The `gr-satnogs` OOT module uses the `major.api-compatibility.minor`
|
The `gr-satnogs` OOT module uses the `major.api-compatibility.minor`
|
||||||
versioning scheme. is used by the
|
versioning scheme. is used by the
|
||||||
|
|
|
@ -50,6 +50,7 @@ list(APPEND enabled_blocks
|
||||||
satnogs_ogg_source.xml
|
satnogs_ogg_source.xml
|
||||||
satnogs_noaa_apt_sink.xml
|
satnogs_noaa_apt_sink.xml
|
||||||
satnogs_whitening.xml
|
satnogs_whitening.xml
|
||||||
|
satnogs_ieee802_15_4_variant_decoder.xml
|
||||||
)
|
)
|
||||||
|
|
||||||
if(${INCLUDE_DEBUG_BLOCKS})
|
if(${INCLUDE_DEBUG_BLOCKS})
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
<name>Decoders</name>
|
<name>Decoders</name>
|
||||||
<block>variable_ax25_decoder</block>
|
<block>variable_ax25_decoder</block>
|
||||||
<block>variable_amsat_duv_decoder</block>
|
<block>variable_amsat_duv_decoder</block>
|
||||||
|
<block>variable_ieee802_15_4_variant_decoder</block>
|
||||||
</cat>
|
</cat>
|
||||||
<cat>
|
<cat>
|
||||||
<name>Satellites</name>
|
<name>Satellites</name>
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<block>
|
||||||
|
<name>IEEE 802.15.4 Decoder Definition</name>
|
||||||
|
<key>variable_ieee802_15_4_variant_decoder</key>
|
||||||
|
<import>import satnogs</import>
|
||||||
|
<var_make>self.$(id) = $(id) = satnogs.ieee802_15_4_variant_decoder_make($preamble, $preamble_thrsh, $sync_word, $sync_thrsh, $crc, $whitening, $var_len, $frame_len)</var_make>
|
||||||
|
<var_value>satnogs.ieee802_15_4_variant_decoder_make($preamble, $preamble_thrsh, $sync_word, $sync_thrsh, $crc, $whitening, $var_len, $frame_len)</var_value>
|
||||||
|
<make></make>
|
||||||
|
<param>
|
||||||
|
<name>Ignore Me</name>
|
||||||
|
<key>value</key>
|
||||||
|
<value>'ok'</value>
|
||||||
|
<type>raw</type>
|
||||||
|
<hide>all</hide>
|
||||||
|
</param>
|
||||||
|
|
||||||
|
<param>
|
||||||
|
<name>Variable Length</name>
|
||||||
|
<key>var_len</key>
|
||||||
|
<type>enum</type>
|
||||||
|
<option>
|
||||||
|
<name>Variable Frame Length</name>
|
||||||
|
<key>True</key>
|
||||||
|
</option>
|
||||||
|
<option>
|
||||||
|
<name>Constant Frame Length</name>
|
||||||
|
<key>False</key>
|
||||||
|
</option>
|
||||||
|
</param>
|
||||||
|
|
||||||
|
<param>
|
||||||
|
<name>Frame Preamble</name>
|
||||||
|
<key>preamble</key>
|
||||||
|
<value>[0x55, 0x55, 0x55, 0x55, 0x55]</value>
|
||||||
|
<type>raw</type>
|
||||||
|
</param>
|
||||||
|
|
||||||
|
<param>
|
||||||
|
<name>Preamble Threshold</name>
|
||||||
|
<key>preamble_thrsh</key>
|
||||||
|
<value>5</value>
|
||||||
|
<type>int</type>
|
||||||
|
</param>
|
||||||
|
|
||||||
|
<param>
|
||||||
|
<name>Synchronization Word</name>
|
||||||
|
<key>sync_word</key>
|
||||||
|
<value>[0x31, 0xe5]</value>
|
||||||
|
<type>raw</type>
|
||||||
|
</param>
|
||||||
|
|
||||||
|
<param>
|
||||||
|
<name>Synchronization Word Threshold</name>
|
||||||
|
<key>sync_thrsh</key>
|
||||||
|
<value>3</value>
|
||||||
|
<type>int</type>
|
||||||
|
</param>
|
||||||
|
|
||||||
|
<param>
|
||||||
|
<name>Maximum Frame Length</name>
|
||||||
|
<key>frame_len</key>
|
||||||
|
<value>254</value>
|
||||||
|
<type>int</type>
|
||||||
|
</param>
|
||||||
|
|
||||||
|
<param>
|
||||||
|
<name>CRC</name>
|
||||||
|
<key>crc</key>
|
||||||
|
<value>None</value>
|
||||||
|
<type>raw</type>
|
||||||
|
</param>
|
||||||
|
|
||||||
|
<param>
|
||||||
|
<name>Whitening</name>
|
||||||
|
<key>whitening</key>
|
||||||
|
<value>None</value>
|
||||||
|
<type>raw</type>
|
||||||
|
</param>
|
||||||
|
|
||||||
|
</block>
|
|
@ -33,15 +33,12 @@ namespace satnogs {
|
||||||
class SATNOGS_API crc {
|
class SATNOGS_API crc {
|
||||||
public:
|
public:
|
||||||
typedef enum crc_type {
|
typedef enum crc_type {
|
||||||
PDU = 0,
|
CRC_NONE = 0,
|
||||||
CRC_VALID,
|
CRC16_CCITT,
|
||||||
FREQ_OFFSET,
|
CRC16_CCITT_REVERSED,
|
||||||
CORRECTED_BITS,
|
CRC16_AX25,
|
||||||
TIME,
|
CRC16_IBM,
|
||||||
SAMPLE_START,
|
CRC_METHODS_NUM
|
||||||
SAMPLE_CNT,
|
|
||||||
SYMBOL_ERASURES,
|
|
||||||
KEYS_NUM
|
|
||||||
} crc_t;
|
} crc_t;
|
||||||
|
|
||||||
static uint16_t
|
static uint16_t
|
||||||
|
@ -56,6 +53,9 @@ public:
|
||||||
static uint16_t
|
static uint16_t
|
||||||
crc16_ibm(const uint8_t *data, size_t len);
|
crc16_ibm(const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
crc_size(crc_t t);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const uint16_t crc16_ccitt_table_reverse[256];
|
static const uint16_t crc16_ccitt_table_reverse[256];
|
||||||
static const uint16_t crc16_ccitt_table[256];
|
static const uint16_t crc16_ccitt_table[256];
|
||||||
|
|
|
@ -101,6 +101,9 @@ public:
|
||||||
virtual void
|
virtual void
|
||||||
reset() = 0;
|
reset() = 0;
|
||||||
|
|
||||||
|
virtual size_t
|
||||||
|
input_multiple() const;
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
max_frame_len() const;
|
max_frame_len() const;
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,11 @@
|
||||||
#define INCLUDED_SATNOGS_IEEE802_15_4_VARIANT_DECODER_H
|
#define INCLUDED_SATNOGS_IEEE802_15_4_VARIANT_DECODER_H
|
||||||
|
|
||||||
#include <satnogs/api.h>
|
#include <satnogs/api.h>
|
||||||
|
#include <satnogs/decoder.h>
|
||||||
#include <satnogs/whitening.h>
|
#include <satnogs/whitening.h>
|
||||||
#include <satnogs/crc.h>
|
#include <satnogs/crc.h>
|
||||||
|
#include <satnogs/shift_reg.h>
|
||||||
|
|
||||||
|
|
||||||
namespace gr {
|
namespace gr {
|
||||||
namespace satnogs {
|
namespace satnogs {
|
||||||
|
@ -38,15 +41,114 @@ namespace satnogs {
|
||||||
* scheme.
|
* scheme.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class SATNOGS_API ieee802_15_4_variant_decoder {
|
class SATNOGS_API ieee802_15_4_variant_decoder : public decoder {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param preamble the preamble should be a repeated word. Note that due to AGC
|
||||||
|
* settling, the receiver may not receive the whole preamble. If the preamble
|
||||||
|
* is indeed a repeated pattern, a portion of it can be given as parameter.
|
||||||
|
* The block should be able to deal with this. However, a quite small subset
|
||||||
|
* may lead to a larger number of false alarms
|
||||||
|
*
|
||||||
|
* @param preamble_threshold the maximum number of bits that are
|
||||||
|
* allowed to be wrong at the preamble
|
||||||
|
*
|
||||||
|
* @param sync the synchronization work following the preamble
|
||||||
|
*
|
||||||
|
* @param sync_threshold the maximum number of bits that are
|
||||||
|
* allowed to be wrong at the synchronization word
|
||||||
|
*
|
||||||
|
* @param crc the CRC scheme to use
|
||||||
|
*
|
||||||
|
* @param descrambler if set, data will be first descrambled by this descrambling
|
||||||
|
* method
|
||||||
|
*
|
||||||
|
* @param var_len if set to true, variable length decoding is used. Otherwise,
|
||||||
|
* the \p max_len parameter indicates the fixed frame size
|
||||||
|
*
|
||||||
|
* @param max_len the maximum allowed decode-able frame length
|
||||||
|
*
|
||||||
|
* @return shared pointer of the decoder
|
||||||
|
*/
|
||||||
|
static decoder::decoder_sptr
|
||||||
|
make(const std::vector<uint8_t> &preamble,
|
||||||
|
size_t preamble_threshold,
|
||||||
|
const std::vector<uint8_t> &sync,
|
||||||
|
size_t sync_threshold,
|
||||||
|
crc::crc_t crc,
|
||||||
|
whitening::whitening_sptr descrambler, bool var_len = true,
|
||||||
|
size_t max_len = 1024);
|
||||||
|
|
||||||
ieee802_15_4_variant_decoder(const std::vector<uint8_t> &preamble,
|
ieee802_15_4_variant_decoder(const std::vector<uint8_t> &preamble,
|
||||||
size_t preamble_threshold,
|
size_t preamble_threshold,
|
||||||
const std::vector<uint8_t> &sync,
|
const std::vector<uint8_t> &sync,
|
||||||
|
size_t sync_threshold,
|
||||||
crc::crc_t crc,
|
crc::crc_t crc,
|
||||||
whitening::whitening_sptr descrambler);
|
whitening::whitening_sptr descrambler,
|
||||||
|
bool var_len = true,
|
||||||
|
size_t max_len = 1024);
|
||||||
~ieee802_15_4_variant_decoder();
|
~ieee802_15_4_variant_decoder();
|
||||||
|
|
||||||
|
decoder_status_t
|
||||||
|
decode(const void *in, int len);
|
||||||
|
|
||||||
|
void
|
||||||
|
reset();
|
||||||
|
|
||||||
|
size_t
|
||||||
|
input_multiple() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* Decoding FSM
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SEARCHING, //!< when searching for the start of the preamble
|
||||||
|
SEARCHING_SYNC, //!< We have preamble, search for sync
|
||||||
|
DECODING_GENERIC_FRAME_LEN, //!< Decoding the frame length
|
||||||
|
DECODING_PAYLOAD //!< Decoding the payload
|
||||||
|
} decoding_state_t;
|
||||||
|
|
||||||
|
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;
|
||||||
|
crc::crc_t d_crc;
|
||||||
|
whitening::whitening_sptr d_descrambler;
|
||||||
|
const bool d_var_len;
|
||||||
|
size_t d_len;
|
||||||
|
size_t d_length_field_len;
|
||||||
|
decoding_state_t d_state;
|
||||||
|
size_t d_cnt;
|
||||||
|
uint8_t *d_pdu;
|
||||||
|
|
||||||
|
decoder_status_t
|
||||||
|
decode_var_len(const void *in, int len);
|
||||||
|
|
||||||
|
decoder_status_t
|
||||||
|
decode_const_len(const void *in, int len);
|
||||||
|
|
||||||
|
int
|
||||||
|
search_preamble(const uint8_t *in, int len);
|
||||||
|
|
||||||
|
int
|
||||||
|
search_sync(const uint8_t *in, int len);
|
||||||
|
|
||||||
|
int
|
||||||
|
decode_frame_len(const uint8_t *in);
|
||||||
|
|
||||||
|
void
|
||||||
|
decode_payload(decoder_status_t &status, const uint8_t *in, int len);
|
||||||
|
|
||||||
|
bool
|
||||||
|
check_crc();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace satnogs
|
} // namespace satnogs
|
||||||
|
|
21
lib/crc.cc
21
lib/crc.cc
|
@ -149,6 +149,27 @@ crc::crc16_ibm(const uint8_t *data, size_t len)
|
||||||
return crc;
|
return crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param t the CRC method
|
||||||
|
* @return the size of the specified CRC in bytes
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
crc::crc_size(crc_t t)
|
||||||
|
{
|
||||||
|
switch (t) {
|
||||||
|
case CRC_NONE:
|
||||||
|
return 0;
|
||||||
|
case CRC16_CCITT:
|
||||||
|
case CRC16_CCITT_REVERSED:
|
||||||
|
case CRC16_AX25:
|
||||||
|
case CRC16_IBM:
|
||||||
|
return sizeof(uint16_t);
|
||||||
|
default:
|
||||||
|
throw std::invalid_argument("crc: Invalid CRC method");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} /* namespace satnogs */
|
} /* namespace satnogs */
|
||||||
} /* namespace gr */
|
} /* namespace gr */
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,22 @@ decoder::~decoder()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, the number of input items can be arbitrary (multiple of 1).
|
||||||
|
* Decoders that require the number of input items to be a multiple of a
|
||||||
|
* specific number, should override this method.
|
||||||
|
*
|
||||||
|
* The frame_decoder block, will use the number returned from this method
|
||||||
|
* to properly alter the behavior of the GNU Radio scheduler
|
||||||
|
*
|
||||||
|
* @return the number that input items should be multiple
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
decoder::input_multiple() const
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @return the maximum allowed frame length
|
* @return the maximum allowed frame length
|
||||||
|
|
|
@ -50,6 +50,14 @@ frame_decoder_impl::frame_decoder_impl(decoder::decoder_sptr decoder_object,
|
||||||
"frame_decoder: Size mismatch between the block input and the decoder");
|
"frame_decoder: Size mismatch between the block input and the decoder");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Each decoder implementing the generic decoder API may have special
|
||||||
|
* requirements on the number of input items. If not, by default this will
|
||||||
|
* set a multiple of 1 item, which corresponds to an arbitrary number of
|
||||||
|
* samples
|
||||||
|
*/
|
||||||
|
set_output_multiple(d_decoder->input_multiple());
|
||||||
|
|
||||||
message_port_register_in(pmt::mp("reset"));
|
message_port_register_in(pmt::mp("reset"));
|
||||||
message_port_register_out(pmt::mp("out"));
|
message_port_register_out(pmt::mp("out"));
|
||||||
|
|
||||||
|
|
|
@ -23,21 +23,342 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <gnuradio/io_signature.h>
|
#include <gnuradio/io_signature.h>
|
||||||
|
#include <satnogs/log.h>
|
||||||
#include <satnogs/ieee802_15_4_variant_decoder.h>
|
#include <satnogs/ieee802_15_4_variant_decoder.h>
|
||||||
|
#include <satnogs/metadata.h>
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
|
||||||
namespace gr {
|
namespace gr {
|
||||||
namespace satnogs {
|
namespace satnogs {
|
||||||
|
|
||||||
ieee802_15_4_variant_decoder::ieee802_15_4_variant_decoder(
|
decoder::decoder_sptr
|
||||||
const std::vector<uint8_t> &preamble, size_t preamble_threshold,
|
ieee802_15_4_variant_decoder::make(const std::vector<uint8_t> &preamble,
|
||||||
const std::vector<uint8_t> &sync, crc::crc_t crc,
|
size_t preamble_threshold,
|
||||||
whitening::whitening_sptr descrambler)
|
const std::vector<uint8_t> &sync,
|
||||||
|
size_t sync_threshold,
|
||||||
|
crc::crc_t crc,
|
||||||
|
whitening::whitening_sptr descrambler,
|
||||||
|
bool var_len,
|
||||||
|
size_t max_len)
|
||||||
{
|
{
|
||||||
|
return decoder::decoder_sptr(
|
||||||
|
new ieee802_15_4_variant_decoder(preamble, preamble_threshold,
|
||||||
|
sync, sync_threshold, crc,
|
||||||
|
descrambler, var_len, max_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
ieee802_15_4_variant_decoder::ieee802_15_4_variant_decoder(
|
||||||
|
const std::vector<uint8_t> &preamble,
|
||||||
|
size_t preamble_threshold,
|
||||||
|
const std::vector<uint8_t> &sync,
|
||||||
|
size_t sync_threshold,
|
||||||
|
crc::crc_t crc,
|
||||||
|
whitening::whitening_sptr descrambler,
|
||||||
|
bool var_len, size_t max_len) :
|
||||||
|
decoder(sizeof(uint8_t), max_len),
|
||||||
|
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_descrambler(descrambler),
|
||||||
|
d_crc(crc),
|
||||||
|
d_var_len(var_len),
|
||||||
|
d_len(max_len + crc::crc_size(crc)),
|
||||||
|
d_length_field_len(0),
|
||||||
|
d_state(SEARCHING),
|
||||||
|
d_cnt(0),
|
||||||
|
d_pdu(new uint8_t[max_len])
|
||||||
|
{
|
||||||
|
for (uint8_t b : preamble) {
|
||||||
|
d_preamble <<= (b >> 7);
|
||||||
|
d_preamble <<= ((b >> 6) & 0x1);
|
||||||
|
d_preamble <<= ((b >> 5) & 0x1);
|
||||||
|
d_preamble <<= ((b >> 4) & 0x1);
|
||||||
|
d_preamble <<= ((b >> 3) & 0x1);
|
||||||
|
d_preamble <<= ((b >> 2) & 0x1);
|
||||||
|
d_preamble <<= ((b >> 1) & 0x1);
|
||||||
|
d_preamble <<= (b & 0x1);
|
||||||
|
}
|
||||||
|
for (uint8_t b : sync) {
|
||||||
|
d_sync <<= (b >> 7);
|
||||||
|
d_sync <<= ((b >> 6) & 0x1);
|
||||||
|
d_sync <<= ((b >> 5) & 0x1);
|
||||||
|
d_sync <<= ((b >> 4) & 0x1);
|
||||||
|
d_sync <<= ((b >> 3) & 0x1);
|
||||||
|
d_sync <<= ((b >> 2) & 0x1);
|
||||||
|
d_sync <<= ((b >> 1) & 0x1);
|
||||||
|
d_sync <<= (b & 0x1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parameters checking */
|
||||||
|
if (max_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 (var_len) {
|
||||||
|
d_length_field_len = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ieee802_15_4_variant_decoder::~ieee802_15_4_variant_decoder()
|
ieee802_15_4_variant_decoder::~ieee802_15_4_variant_decoder()
|
||||||
{
|
{
|
||||||
|
delete [] d_pdu;
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder_status_t
|
||||||
|
ieee802_15_4_variant_decoder::decode(const void *in, int len)
|
||||||
|
{
|
||||||
|
if (d_var_len) {
|
||||||
|
return decode_var_len(in, len);
|
||||||
|
}
|
||||||
|
return decode_const_len(in, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ieee802_15_4_variant_decoder::reset()
|
||||||
|
{
|
||||||
|
if (d_descrambler) {
|
||||||
|
d_descrambler->reset();
|
||||||
|
}
|
||||||
|
d_cnt = 0;
|
||||||
|
d_state = SEARCHING;
|
||||||
|
d_preamble_shift_reg.reset();
|
||||||
|
d_sync_shift_reg.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder_status_t
|
||||||
|
ieee802_15_4_variant_decoder::decode_var_len(const void *in, int len)
|
||||||
|
{
|
||||||
|
decoder_status_t status;
|
||||||
|
switch (d_state) {
|
||||||
|
case SEARCHING:
|
||||||
|
status.consumed = search_preamble((const uint8_t *) in, len);
|
||||||
|
break;
|
||||||
|
case SEARCHING_SYNC:
|
||||||
|
status.consumed = search_sync((const uint8_t *) in, len);
|
||||||
|
break;
|
||||||
|
case DECODING_GENERIC_FRAME_LEN:
|
||||||
|
status.consumed = decode_frame_len((const uint8_t *) in);
|
||||||
|
break;
|
||||||
|
case DECODING_PAYLOAD:
|
||||||
|
decode_payload(status, (const uint8_t *) in, len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("ieee802_15_4_variant_decoder: Invalid state");
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder_status_t
|
||||||
|
ieee802_15_4_variant_decoder::decode_const_len(const void *in, int len)
|
||||||
|
{
|
||||||
|
decoder_status_t status;
|
||||||
|
switch (d_state) {
|
||||||
|
case SEARCHING:
|
||||||
|
status.consumed = search_preamble((const uint8_t *) in, len);
|
||||||
|
break;
|
||||||
|
case SEARCHING_SYNC:
|
||||||
|
status.consumed = search_preamble((const uint8_t *) in, len);
|
||||||
|
break;
|
||||||
|
case DECODING_PAYLOAD:
|
||||||
|
decode_payload(status, (const uint8_t *) in, len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("ieee802_15_4_variant_decoder: Invalid state");
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To greatly simplify the logic, the decoder requests that the number of
|
||||||
|
* input items should be a multiple of 8
|
||||||
|
* @return 8
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
ieee802_15_4_variant_decoder::input_multiple() const
|
||||||
|
{
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ieee802_15_4_variant_decoder::search_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
|
||||||
|
ieee802_15_4_variant_decoder::search_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) {
|
||||||
|
if (d_var_len) {
|
||||||
|
d_state = DECODING_GENERIC_FRAME_LEN;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
d_state = DECODING_PAYLOAD;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
ieee802_15_4_variant_decoder::decode_frame_len(const uint8_t *in)
|
||||||
|
{
|
||||||
|
uint8_t b = 0x0;
|
||||||
|
b |= in[0] << 7;
|
||||||
|
b |= in[1] << 6;
|
||||||
|
b |= in[2] << 5;
|
||||||
|
b |= in[3] << 4;
|
||||||
|
b |= in[4] << 3;
|
||||||
|
b |= in[5] << 2;
|
||||||
|
b |= in[6] << 1;
|
||||||
|
b |= in[7];
|
||||||
|
|
||||||
|
if (d_descrambler) {
|
||||||
|
uint8_t descrambled = 0x0;
|
||||||
|
d_descrambler->descramble((uint8_t *)&descrambled,
|
||||||
|
(const uint8_t *)&b, 1, false);
|
||||||
|
d_len = descrambled;
|
||||||
|
d_pdu[0] = descrambled;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
d_len = b;
|
||||||
|
d_pdu[0] = b;
|
||||||
|
}
|
||||||
|
d_len += crc::crc_size(d_crc);
|
||||||
|
/* Length field is needed for the CRC calculation */
|
||||||
|
d_cnt = 1;
|
||||||
|
d_state = DECODING_PAYLOAD;
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ieee802_15_4_variant_decoder::decode_payload(decoder_status_t &status,
|
||||||
|
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_len + d_length_field_len) {
|
||||||
|
if (d_descrambler) {
|
||||||
|
d_descrambler->descramble(d_pdu + d_length_field_len,
|
||||||
|
d_pdu + d_length_field_len, d_len, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
status.decode_success = true;
|
||||||
|
status.consumed = (i + 1) * 8;
|
||||||
|
if (check_crc()) {
|
||||||
|
metadata::add_time_iso8601(status.data);
|
||||||
|
metadata::add_pdu(status.data, d_pdu + d_length_field_len,
|
||||||
|
d_len - crc::crc_size(d_crc));
|
||||||
|
metadata::add_crc_valid(status.data, true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
metadata::add_pdu(status.data, d_pdu + d_length_field_len, d_len);
|
||||||
|
metadata::add_crc_valid(status.data, false);
|
||||||
|
}
|
||||||
|
reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
status.consumed = s * 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ieee802_15_4_variant_decoder::check_crc()
|
||||||
|
{
|
||||||
|
uint16_t crc16_c;
|
||||||
|
uint16_t crc16_received;
|
||||||
|
switch (d_crc) {
|
||||||
|
case crc::CRC_NONE:
|
||||||
|
return true;
|
||||||
|
case crc::CRC16_CCITT:
|
||||||
|
crc16_c = crc::crc16_ccitt(d_pdu, d_len + d_length_field_len - 2);
|
||||||
|
memcpy(&crc16_received, d_pdu + d_length_field_len + d_len - 2, 2);
|
||||||
|
crc16_received = ntohs(crc16_received);
|
||||||
|
LOG_DEBUG("Received: 0x%02x Computed: 0x%02x", crc16_received, crc16_c);
|
||||||
|
if (crc16_c == crc16_received) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case crc::CRC16_CCITT_REVERSED:
|
||||||
|
crc16_c = crc::crc16_ccitt_reversed(d_pdu, d_len + d_length_field_len - 2);
|
||||||
|
memcpy(&crc16_received, d_pdu + d_length_field_len + d_len - 2, 2);
|
||||||
|
crc16_received = ntohs(crc16_received);
|
||||||
|
LOG_DEBUG("Received: 0x%02x Computed: 0x%02x", crc16_received, crc16_c);
|
||||||
|
if (crc16_c == crc16_received) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case crc::CRC16_IBM:
|
||||||
|
crc16_c = crc::crc16_ibm(d_pdu, d_len + d_length_field_len - 2);
|
||||||
|
memcpy(&crc16_received, d_pdu + d_length_field_len + d_len - 2, 2);
|
||||||
|
crc16_received = ntohs(crc16_received);
|
||||||
|
LOG_WARN("Received: 0x%02x Computed: 0x%02x", crc16_received, crc16_c);
|
||||||
|
if (crc16_c == crc16_received) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("ieee802_15_4_variant_decoder: Invalid CRC");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace satnogs */
|
} /* namespace satnogs */
|
||||||
|
|
|
@ -43,16 +43,18 @@
|
||||||
#include "satnogs/lrpt_sync.h"
|
#include "satnogs/lrpt_sync.h"
|
||||||
#include "satnogs/metadata.h"
|
#include "satnogs/metadata.h"
|
||||||
#include "satnogs/crc.h"
|
#include "satnogs/crc.h"
|
||||||
|
#include "satnogs/ieee802_15_4_variant_decoder.h"
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
|
||||||
|
%include "satnogs/whitening.h"
|
||||||
%include "satnogs/morse_tree.h"
|
%include "satnogs/morse_tree.h"
|
||||||
%include "satnogs/decoder.h"
|
%include "satnogs/decoder.h"
|
||||||
%include "satnogs/amsat_duv_decoder.h"
|
%include "satnogs/amsat_duv_decoder.h"
|
||||||
%include "satnogs/ax25_decoder.h"
|
%include "satnogs/ax25_decoder.h"
|
||||||
%include "satnogs/metadata.h"
|
%include "satnogs/metadata.h"
|
||||||
%include "satnogs/crc.h"
|
%include "satnogs/crc.h"
|
||||||
|
%include "satnogs/ieee802_15_4_variant_decoder.h"
|
||||||
|
|
||||||
%include "satnogs/morse_decoder.h"
|
%include "satnogs/morse_decoder.h"
|
||||||
GR_SWIG_BLOCK_MAGIC2(satnogs, morse_decoder);
|
GR_SWIG_BLOCK_MAGIC2(satnogs, morse_decoder);
|
||||||
|
@ -81,8 +83,6 @@ GR_SWIG_BLOCK_MAGIC2(satnogs, doppler_correction_cc);
|
||||||
%include "satnogs/upsat_fsk_frame_encoder.h"
|
%include "satnogs/upsat_fsk_frame_encoder.h"
|
||||||
GR_SWIG_BLOCK_MAGIC2(satnogs, upsat_fsk_frame_encoder);
|
GR_SWIG_BLOCK_MAGIC2(satnogs, upsat_fsk_frame_encoder);
|
||||||
|
|
||||||
%include "satnogs/whitening.h"
|
|
||||||
|
|
||||||
%include "satnogs/udp_msg_sink.h"
|
%include "satnogs/udp_msg_sink.h"
|
||||||
GR_SWIG_BLOCK_MAGIC2(satnogs, udp_msg_sink);
|
GR_SWIG_BLOCK_MAGIC2(satnogs, udp_msg_sink);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue