kopia lustrzana https://github.com/rpp0/gr-lora
Starting decoder rework
~ Changed indentation layout ~ Replaced some functions from utilities.h with more performant ones (tested in gr-lora-benchmarks) + Added message if spreading factor is set outside of compatible range + Added this keyword for class members + Added NDEBUG macro to be able to completely remove debugging (for performance improvement) ~ Extracted multiplications from loops to a precalculation instead ~ Reworked flow of most of the functions * TODO: Change find_preamble algorithm for better detection * TODO: Change detect_downchirp algorithm for better syncpull/27/head
rodzic
447487380b
commit
dbadd1aacb
Plik diff jest za duży
Load Diff
|
@ -21,36 +21,40 @@
|
||||||
#ifndef INCLUDED_LORA_DECODER_IMPL_H
|
#ifndef INCLUDED_LORA_DECODER_IMPL_H
|
||||||
#define INCLUDED_LORA_DECODER_IMPL_H
|
#define INCLUDED_LORA_DECODER_IMPL_H
|
||||||
|
|
||||||
#include <lora/decoder.h>
|
#include <liquid/liquid.h>
|
||||||
|
#include "lora/decoder.h"
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <deque>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#define DECIMATOR_FILTER_SIZE 2*8*1+1 // 2*decim_factor*delay+1
|
#define DECIMATOR_FILTER_SIZE (2*8*1 + 1) // 2*decim_factor*delay+1
|
||||||
|
|
||||||
namespace gr {
|
namespace gr {
|
||||||
namespace lora {
|
namespace lora {
|
||||||
|
|
||||||
typedef enum decoder_state {
|
enum class DecoderState {
|
||||||
DETECT,
|
DETECT,
|
||||||
SYNC,
|
SYNC,
|
||||||
PAUSE,
|
PAUSE,
|
||||||
DECODE_HEADER,
|
DECODE_HEADER,
|
||||||
DECODE_PAYLOAD,
|
DECODE_PAYLOAD,
|
||||||
STOP
|
STOP
|
||||||
} decoder_state;
|
};
|
||||||
|
|
||||||
class decoder_impl : public decoder
|
class decoder_impl : public decoder {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
decoder_state d_state;
|
DecoderState d_state;
|
||||||
|
|
||||||
|
/// using std::complex = gr_complex
|
||||||
std::vector<gr_complex> d_downchirp;
|
std::vector<gr_complex> d_downchirp;
|
||||||
std::vector<gr_complex> d_upchirp;
|
std::vector<gr_complex> d_upchirp;
|
||||||
std::vector<float> d_downchirp_ifreq;
|
std::vector<float> d_downchirp_ifreq;
|
||||||
std::vector<float> d_upchirp_ifreq;
|
std::vector<float> d_upchirp_ifreq;
|
||||||
std::vector<gr_complex> d_fft;
|
std::vector<gr_complex> d_fft;
|
||||||
std::vector<gr_complex> d_mult;
|
std::vector<gr_complex> d_mult;
|
||||||
|
|
||||||
uint8_t d_sf;
|
uint8_t d_sf;
|
||||||
uint32_t d_bw;
|
uint32_t d_bw;
|
||||||
uint8_t d_cr;
|
uint8_t d_cr;
|
||||||
|
@ -66,66 +70,74 @@ namespace gr {
|
||||||
uint32_t d_payload_symbols;
|
uint32_t d_payload_symbols;
|
||||||
uint32_t d_payload_length;
|
uint32_t d_payload_length;
|
||||||
uint32_t d_corr_fails;
|
uint32_t d_corr_fails;
|
||||||
|
|
||||||
std::vector<unsigned int> d_words;
|
std::vector<unsigned int> d_words;
|
||||||
std::vector<uint8_t> d_demodulated;
|
std::vector<uint8_t> d_demodulated;
|
||||||
std::vector<uint8_t> d_words_deshuffled;
|
std::vector<uint8_t> d_words_deshuffled;
|
||||||
std::vector<uint8_t> d_words_dewhitened;
|
std::vector<uint8_t> d_words_dewhitened;
|
||||||
std::vector<uint8_t> d_data;
|
std::vector<uint8_t> d_data;
|
||||||
|
|
||||||
std::ofstream d_debug_samples;
|
std::ofstream d_debug_samples;
|
||||||
std::ofstream d_debug;
|
std::ofstream d_debug;
|
||||||
|
|
||||||
fftplan d_q;
|
fftplan d_q;
|
||||||
|
|
||||||
float d_decim_h[DECIMATOR_FILTER_SIZE];
|
float d_decim_h[DECIMATOR_FILTER_SIZE];
|
||||||
uint32_t d_corr_decim_factor;
|
uint32_t d_corr_decim_factor;
|
||||||
int d_decim_factor;
|
int d_decim_factor;
|
||||||
firdecim_crcf d_decim;
|
firdecim_crcf d_decim = nullptr;
|
||||||
float d_cfo_estimation;
|
float d_cfo_estimation;
|
||||||
int d_cfo_step;
|
int d_cfo_step;
|
||||||
double d_dt;
|
double d_dt;
|
||||||
|
|
||||||
bool calc_energy_threshold(gr_complex *samples, int window_size, float threshold);
|
bool calc_energy_threshold(gr_complex *samples, int window_size, float threshold);
|
||||||
void build_ideal_chirps(void);
|
void build_ideal_chirps(void);
|
||||||
void samples_to_file(const std::string path, const gr_complex* v, int length, int elem_size);
|
void samples_to_file(const std::string path, const gr_complex *v, uint32_t length, uint32_t elem_size);
|
||||||
void samples_debug(const gr_complex* v, int length);
|
void samples_debug(const gr_complex *v, uint32_t length);
|
||||||
float sliding_norm_cross_correlate(const float *samples_1, const float *samples_2, uint32_t window, uint32_t slide, int32_t *index);
|
float sliding_norm_cross_correlate(const float *samples_1, const float *samples_2, uint32_t window, uint32_t slide, int32_t *index);
|
||||||
float norm_cross_correlate(const float *samples_1, const float *samples_2, uint32_t window);
|
float norm_cross_correlate(const float *samples_1, const float *samples_2, uint32_t window);
|
||||||
float detect_downchirp(const gr_complex *samples, uint32_t window);
|
float detect_downchirp(const gr_complex *samples, uint32_t window);
|
||||||
float detect_upchirp(const gr_complex *samples_1, uint32_t window, uint32_t slide, int32_t *index);
|
float detect_upchirp(const gr_complex *samples_1, uint32_t window, uint32_t slide, int32_t *index);
|
||||||
float cross_correlate(const gr_complex *samples_1, const gr_complex *samples_2, int window);
|
float cross_correlate(const gr_complex *samples_1, const gr_complex *samples_2, int window);
|
||||||
|
|
||||||
unsigned int get_shift_fft(gr_complex *samples);
|
unsigned int get_shift_fft(gr_complex *samples);
|
||||||
|
|
||||||
void determine_cfo(const gr_complex *samples);
|
void determine_cfo(const gr_complex *samples);
|
||||||
void correct_cfo(gr_complex* samples, int num_samples);
|
void correct_cfo(gr_complex *samples, uint32_t num_samples);
|
||||||
int find_preamble_start(gr_complex *samples);
|
int find_preamble_start(gr_complex *samples);
|
||||||
int find_preamble_start_fast(gr_complex *samples, uint32_t len);
|
int find_preamble_start_fast(gr_complex *samples, uint32_t len);
|
||||||
|
|
||||||
unsigned int max_frequency_gradient_idx(gr_complex *samples);
|
unsigned int max_frequency_gradient_idx(gr_complex *samples);
|
||||||
|
|
||||||
bool demodulate(gr_complex *samples, bool is_header);
|
bool demodulate(gr_complex *samples, bool is_header);
|
||||||
void deinterleave(int ppm);
|
void deinterleave(uint32_t ppm);
|
||||||
int decode(uint8_t *out_data, bool is_header);
|
int decode(uint8_t *out_data, bool is_header);
|
||||||
void deshuffle(const uint8_t *shuffle_pattern, bool is_header);
|
void deshuffle(const uint8_t *shuffle_pattern, bool is_header);
|
||||||
void dewhiten(const uint8_t *prng);
|
void dewhiten(const uint8_t *prng);
|
||||||
void hamming_decode(uint8_t *out_data);
|
void hamming_decode(uint8_t *out_data);
|
||||||
void nibble_reverse(uint8_t *out_data, int len);
|
void nibble_reverse(uint8_t *out_data, int len);
|
||||||
float stddev(const float *values, int len, float mean);
|
float stddev(const float *values, uint32_t len, float mean);
|
||||||
|
|
||||||
inline void instantaneous_phase(const gr_complex *in_samples, float *out_iphase, uint32_t window);
|
inline void instantaneous_phase(const gr_complex *in_samples, float *out_iphase, uint32_t window);
|
||||||
void instantaneous_frequency(const gr_complex *in_samples, float *out_ifreq, uint32_t window);
|
void instantaneous_frequency(const gr_complex *in_samples, float *out_ifreq, uint32_t window);
|
||||||
|
|
||||||
uint8_t lookup_cr(uint8_t bytevalue);
|
uint8_t lookup_cr(uint8_t bytevalue);
|
||||||
void msg_raw_chirp_debug(const gr_complex *raw_samples, uint32_t num_samples);
|
void msg_raw_chirp_debug(const gr_complex *raw_samples, uint32_t num_samples);
|
||||||
void msg_lora_frame(const uint8_t *frame_bytes, uint32_t frame_len);
|
void msg_lora_frame(const uint8_t *frame_bytes, uint32_t frame_len);
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
decoder_impl(float samp_rate, int sf);
|
decoder_impl(float samp_rate, uint8_t sf);
|
||||||
~decoder_impl();
|
~decoder_impl();
|
||||||
|
|
||||||
// Where all the action really happens
|
/// Where all the action really happens
|
||||||
int work(int noutput_items,
|
int work(int noutput_items,
|
||||||
gr_vector_const_void_star& input_items,
|
gr_vector_const_void_star& input_items,
|
||||||
gr_vector_void_star& output_items);
|
gr_vector_void_star& output_items);
|
||||||
|
|
||||||
// GRC interfaces
|
/// GRC interfaces
|
||||||
virtual void set_sf(uint8_t sf);
|
virtual void set_sf(uint8_t sf);
|
||||||
virtual void set_samp_rate(float samp_rate);
|
virtual void set_samp_rate(float samp_rate);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace lora
|
} // namespace lora
|
||||||
} // namespace gr
|
} // namespace gr
|
||||||
|
|
||||||
|
|
102
lib/utilities.h
102
lib/utilities.h
|
@ -1,24 +1,20 @@
|
||||||
#ifndef UTILITIES_H
|
#ifndef UTILITIES_H
|
||||||
#define UTILITIES_H
|
#define UTILITIES_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
namespace gr {
|
namespace gr {
|
||||||
namespace lora {
|
namespace lora {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::string to_bin(T v, int element_len_bits) {
|
std::string to_bin(T v, uint32_t bitwidth) {
|
||||||
T mask = 0;
|
unsigned long long maxpow = bitwidth ? (1ull << (bitwidth - 1)) : 0,
|
||||||
unsigned int maxpow = element_len_bits;
|
mask;
|
||||||
|
|
||||||
std::string result = "";
|
std::string result = "";
|
||||||
|
|
||||||
for(int i = 0; i < maxpow; i++) {
|
for (mask = 0x1; mask <= maxpow; mask <<= 1) {
|
||||||
mask = pow(2, i);
|
result += (v & mask) ? "1" : "0";
|
||||||
//std::cout << (unsigned int)v << " AND " << mask << " is " << (v & mask) << std::endl;
|
|
||||||
|
|
||||||
if((v & mask) > 0) {
|
|
||||||
result += "1";
|
|
||||||
} else {
|
|
||||||
result += "0";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -27,61 +23,48 @@ namespace gr {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline void print_vector(std::ostream& out, std::vector<T>& v, std::string prefix, int element_len_bits) {
|
inline void print_vector(std::ostream& out, std::vector<T>& v, std::string prefix, int element_len_bits) {
|
||||||
out << prefix << ": ";
|
out << prefix << ": ";
|
||||||
for(int i = 0; i < v.size(); i++) {
|
|
||||||
out << to_bin(v[i], element_len_bits) << ", ";
|
for (T x : v)
|
||||||
}
|
out << to_bin(x, element_len_bits) << ", ";
|
||||||
|
|
||||||
out << std::endl << std::flush;
|
out << std::endl << std::flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline void print_vector_raw(std::ostream& out, std::vector<T>& v, int element_len_bits) {
|
inline void print_vector_raw(std::ostream& out, std::vector<T>& v, int element_len_bits) {
|
||||||
for(int i = 0; i < v.size(); i++) {
|
|
||||||
out << to_bin(v[i], element_len_bits);
|
for (T x : v)
|
||||||
}
|
out << to_bin(x, element_len_bits);
|
||||||
|
|
||||||
out << std::flush;
|
out << std::flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool check_parity(std::string word, bool even) {
|
bool check_parity(std::string& word, bool even) {
|
||||||
int count = 0;
|
size_t count = 0, i = 0;
|
||||||
|
|
||||||
for(int i = 0; i < 7; i++) {
|
while(i < 7) {
|
||||||
if(word[i] == '1')
|
if (word[i++] == '1')
|
||||||
count += 1;
|
++count;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(even)
|
return (count & 0x1) == (even ? 0 : 1);
|
||||||
return ((count % 2) == 0);
|
|
||||||
else
|
|
||||||
return (((count+1) % 2) == 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t select_bits(uint32_t data, uint8_t *indices, uint8_t n) {
|
uint32_t select_bits(uint32_t data, uint8_t *indices, uint8_t n) {
|
||||||
uint32_t result = 0;
|
uint32_t r = 0;
|
||||||
|
|
||||||
for(uint32_t j = 0; j < n; j++) {
|
for(uint8_t i = 0; i < n; ++i)
|
||||||
uint32_t power = pow(2, indices[j]);
|
r |= (data & (1 << indices[i])) ? (1 << i) : 0;
|
||||||
if((data & power) > 0) {
|
|
||||||
result += pow(2, j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fec_extract_data_only(uint8_t *in_data, uint32_t len, uint8_t *indices, uint8_t n, uint8_t *out_data) {
|
void fec_extract_data_only(uint8_t *in_data, uint32_t len, uint8_t *indices, uint8_t n, uint8_t *out_data) {
|
||||||
uint8_t out_index = 0;
|
for (uint32_t i = 0, out_index = 0; i < len; i += 2) {
|
||||||
|
uint8_t d1 = (select_bits(in_data[i], indices, n) & 0xff) << 4;
|
||||||
|
d1 |= (i + 1 < len) ? select_bits(in_data[i + 1], indices, n) & 0xff : 0;
|
||||||
|
|
||||||
for(uint32_t i = 0; i < len; i+=2) {
|
out_data[out_index++] = d1;
|
||||||
uint8_t d1 = 0;
|
|
||||||
d1 = select_bits(in_data[i], indices, n) & 0xff;
|
|
||||||
|
|
||||||
|
|
||||||
uint8_t d2 = 0;
|
|
||||||
if(i+1 < len)
|
|
||||||
d2 = select_bits(in_data[i+1], indices, n) & 0xff;
|
|
||||||
|
|
||||||
out_data[out_index] = (d1 << 4) | d2;
|
|
||||||
out_index++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,11 +110,7 @@ namespace gr {
|
||||||
// p4 01010101
|
// p4 01010101
|
||||||
|
|
||||||
// Syndrome matrix = columns of "cover bits" above
|
// Syndrome matrix = columns of "cover bits" above
|
||||||
uint8_t H[16];
|
uint8_t H[16] = { 0 };
|
||||||
|
|
||||||
for(uint8_t i = 0; i < 16; i++) {
|
|
||||||
H[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t i0 = pack_nibble(1, 0, 0, 0);
|
uint8_t i0 = pack_nibble(1, 0, 0, 0);
|
||||||
uint8_t i1 = pack_nibble(0, 1, 1, 1);
|
uint8_t i1 = pack_nibble(0, 1, 1, 1);
|
||||||
|
@ -164,9 +143,8 @@ namespace gr {
|
||||||
|
|
||||||
uint8_t syndrome = pack_nibble((uint8_t)(p1 != p1c), (uint8_t)(p2 != p2c), (uint8_t)(p3 != p3c), (uint8_t)(p4 != p4c));
|
uint8_t syndrome = pack_nibble((uint8_t)(p1 != p1c), (uint8_t)(p2 != p2c), (uint8_t)(p3 != p3c), (uint8_t)(p4 != p4c));
|
||||||
|
|
||||||
if(syndrome != 0) {
|
if (syndrome) {
|
||||||
uint8_t index = H[syndrome];
|
v ^= pow2[ H[syndrome] ];
|
||||||
v = v ^ pow2[index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t d1 = bit(v, 1);
|
uint8_t d1 = bit(v, 1);
|
||||||
|
@ -179,17 +157,11 @@ namespace gr {
|
||||||
|
|
||||||
// Manual Hamming
|
// Manual Hamming
|
||||||
void hamming_decode_soft(uint8_t *words, uint32_t len, uint8_t *out_data) {
|
void hamming_decode_soft(uint8_t *words, uint32_t len, uint8_t *out_data) {
|
||||||
uint32_t out_index = 0;
|
for (uint32_t i = 0, out_index = 0; i < len; i += 2) {
|
||||||
for(int i = 0; i < len; i+=2) {
|
uint8_t d1 = hamming_decode_soft_byte(words[i]) << 4;
|
||||||
uint8_t d1 = 0;
|
d1 |= (i + 1 < len) ? hamming_decode_soft_byte(words[i + 1]) : 0;
|
||||||
d1 = hamming_decode_soft_byte(words[i]);
|
|
||||||
|
|
||||||
uint8_t d2 = 0;
|
out_data[out_index++] = d1;
|
||||||
if(i+1 < len)
|
|
||||||
d2 = hamming_decode_soft_byte(words[i+1]);
|
|
||||||
|
|
||||||
out_data[out_index] = (d1 << 4) | d2;
|
|
||||||
out_index++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue