diff --git a/lib/decoder_impl.cc b/lib/decoder_impl.cc index fe30526..3dd895d 100644 --- a/lib/decoder_impl.cc +++ b/lib/decoder_impl.cc @@ -19,7 +19,7 @@ */ #ifdef HAVE_CONFIG_H -#include "config.h" + #include "config.h" #endif #include @@ -30,583 +30,552 @@ #include "tables.h" #include "utilities.h" -//#define NO_TMP_WRITES 1 -//#define CFO_CORRECT 1 +//#define NO_TMP_WRITES 1 /// Debug output file write +//#define CFO_CORRECT 1 /// Correct shift fft estimation + + +#undef NDEBUG /// Debug printing namespace gr { - namespace lora { + namespace lora { - decoder::sptr - decoder::make(float samp_rate, int sf) { - return gnuradio::get_initial_sptr - (new decoder_impl(samp_rate, sf)); - } - - /* - * The private constructor - */ - decoder_impl::decoder_impl(float samp_rate, int sf) : gr::sync_block("decoder", - gr::io_signature::make(1, -1, sizeof(gr_complex)), - gr::io_signature::make(0, 2, sizeof(float))) { - d_state = DETECT; - - d_debug_samples.open("/tmp/grlora_debug", std::ios::out | std::ios::binary); - d_debug.open("/tmp/grlora_debug_txt", std::ios::out); - d_sf = sf; // Only affects PHY send - d_bw = 125000; - d_cr = 4; - d_bits_per_second = (double)d_sf * (4.0f/4.0f+d_cr) / (pow(2.0f, d_sf) / d_bw); - d_samples_per_second = samp_rate; - d_symbols_per_second = (double)d_bw / pow(2.0f, d_sf); - d_bits_per_symbol = (uint32_t)(d_bits_per_second / d_symbols_per_second); - d_samples_per_symbol = (uint32_t)(d_samples_per_second / d_symbols_per_second); - d_delay_after_sync = d_samples_per_symbol / 4; - d_corr_decim_factor = 8; // samples_per_symbol / corr_decim_factor = correlation window. Also serves as preamble decimation factor - d_number_of_bins = (uint32_t)pow(2, d_sf); - d_number_of_bins_hdr = d_number_of_bins / 4; - d_payload_symbols = 0; - d_cfo_estimation = 0.0f; - d_dt = 1.0f / d_samples_per_second; - - // Some preparations - std::cout << "Bits per symbol: " << d_bits_per_symbol << std::endl; - std::cout << "Bins per symbol: " << d_number_of_bins << std::endl; - std::cout << "Header bins per symbol: " << d_number_of_bins_hdr << std::endl; - std::cout << "Samples per symbol: " << d_samples_per_symbol << std::endl; - std::cout << "Decimation: " << d_samples_per_symbol / d_number_of_bins << std::endl; - - build_ideal_chirps(); - - set_output_multiple(2*d_samples_per_symbol); - d_fft.resize(d_number_of_bins); - d_mult.resize(d_number_of_bins); - d_q = fft_create_plan(d_number_of_bins, &d_mult[0], &d_fft[0], LIQUID_FFT_FORWARD, 0); - - // Decimation filter - float g[DECIMATOR_FILTER_SIZE]; - liquid_firdes_rrcos(8, 1, 0.5f, 0.3f, g); // Filter for interpolating - for (uint32_t i = 0; i < DECIMATOR_FILTER_SIZE; i++) // Reverse it to get decimation filter - d_decim_h[i] = g[DECIMATOR_FILTER_SIZE-i-1]; - d_decim_factor = d_samples_per_symbol / d_number_of_bins; - - d_decim = firdecim_crcf_create(d_decim_factor, d_decim_h, DECIMATOR_FILTER_SIZE); - - // Register gnuradio ports - message_port_register_out(pmt::mp("frames")); - message_port_register_out(pmt::mp("debug")); - } - - /* - * Our virtual destructor. - */ - decoder_impl::~decoder_impl() { - if(d_debug_samples.is_open()) - d_debug_samples.close(); - if(d_debug.is_open()) - d_debug.close(); - - fft_destroy_plan(d_q); - firdecim_crcf_destroy(d_decim); - } - - void decoder_impl::build_ideal_chirps(void) { - d_downchirp.resize(d_samples_per_symbol); - d_upchirp.resize(d_samples_per_symbol); - d_downchirp_ifreq.resize(d_samples_per_symbol); - d_upchirp_ifreq.resize(d_samples_per_symbol); - - double dir; - double T = 1.0f / d_symbols_per_second; - double f0 = (d_bw / 2.0f); - double amplitude = 1.0f; - - // Store time domain signal - dir = 1.0f; - for(int i = 0; i < d_samples_per_symbol; i++) { // Width in number of samples = samples_per_symbol - // See https://en.wikipedia.org/wiki/Chirp#Linear - double t = d_dt * i; - d_downchirp[i] = gr_complex(amplitude, amplitude) * gr_expj(dir * 2.0f * M_PI * (f0 * t + (-1.0f * (0.5 * d_bw / T) * pow(t, 2)))); + decoder::sptr decoder::make(float samp_rate, int sf) { + return gnuradio::get_initial_sptr + (new decoder_impl(samp_rate, sf)); } - dir = -1.0f; - for(int i = 0; i < d_samples_per_symbol; i++) { - double t = d_dt * i; - d_upchirp[i] = gr_complex(amplitude, amplitude) * gr_expj(dir * 2.0f * M_PI * (f0 * t + (-1.0f * (0.5 * d_bw / T) * pow(t, 2)))); - } + /** + * The private constructor + */ + decoder_impl::decoder_impl(float samp_rate, uint8_t sf) + : gr::sync_block("decoder", + gr::io_signature::make(1, -1, sizeof(gr_complex)), + gr::io_signature::make(0, 2, sizeof(float))) { + this->d_state = gr::lora::DecoderState::DETECT; - // Store instant. frequency - instantaneous_frequency(&d_downchirp[0], &d_downchirp_ifreq[0], d_samples_per_symbol); - instantaneous_frequency(&d_upchirp[0], &d_upchirp_ifreq[0], d_samples_per_symbol); - - samples_to_file("/tmp/downchirp", &d_downchirp[0], d_downchirp.size(), sizeof(gr_complex)); - samples_to_file("/tmp/upchirp", &d_upchirp[0], d_upchirp.size(), sizeof(gr_complex)); - } - - void decoder_impl::samples_to_file(const std::string path, const gr_complex* v, int length, int elem_size) { - #ifndef NO_TMP_WRITES - std::ofstream out_file; - out_file.open(path.c_str(), std::ios::out | std::ios::binary); - //for(std::vector::const_iterator it = v.begin(); it != v.end(); ++it) { - for(uint32_t i = 0; i < length; i++) { - out_file.write(reinterpret_cast(&v[i]), elem_size); + if (sf < 6 || sf > 13) { + //throw std::invalid_argument("[LoRa Decoder] ERROR : Spreading factor should be between 6 and 12 (inclusive)!\n Other values are currently not supported."); + std::cerr << "[LoRa Decoder] ERROR : Spreading factor should be between 6 and 12 (inclusive)!\n Other values are currently not supported." << std::endl; + exit(1); } - out_file.close(); - #endif - } - void decoder_impl::samples_debug(const gr_complex* v, int length) { - gr_complex start_indicator(0.0f,32.0f); - d_debug_samples.write(reinterpret_cast(&start_indicator), sizeof(gr_complex)); - for(uint32_t i = 1; i < length; i++) { - d_debug_samples.write(reinterpret_cast(&v[i]), sizeof(gr_complex)); - } - } + #ifndef NDEBUG + this->d_debug_samples.open("/tmp/grlora_debug", std::ios::out | std::ios::binary); + this->d_debug.open("/tmp/grlora_debug_txt", std::ios::out); + #endif - bool decoder_impl::calc_energy_threshold(gr_complex* samples, int window_size, float threshold) { - float result = 0.0f; - for(int i = 0; i < window_size; i++) { - result += std::pow(abs(samples[i]), 2); - } - result /= (float)window_size; + this->d_bw = 125000; + this->d_cr = 4; + this->d_samples_per_second = samp_rate; + this->d_corr_decim_factor = 8; // samples_per_symbol / corr_decim_factor = correlation window. Also serves as preamble decimation factor + this->d_payload_symbols = 0; + this->d_cfo_estimation = 0.0f; + this->d_dt = 1.0f / this->d_samples_per_second; - //d_debug << "T: " << result << "\n"; + this->d_sf = sf; // Only affects PHY send + this->d_bits_per_second = (double)this->d_sf * (double)(1 + this->d_cr) / (1 << this->d_sf) * this->d_bw; + this->d_symbols_per_second = (double)this->d_bw / (1 << this->d_sf); + this->d_bits_per_symbol = (uint32_t)(this->d_bits_per_second / this->d_symbols_per_second); + this->d_samples_per_symbol = (uint32_t)(this->d_samples_per_second / this->d_symbols_per_second); + this->d_delay_after_sync = this->d_samples_per_symbol / 4; + this->d_number_of_bins = (uint32_t)(1 << this->d_sf); + this->d_number_of_bins_hdr = this->d_number_of_bins / 4; - if(result > threshold) { - return true; - } else { - return false; - } - } + // Some preparations + std::cout << "Bits per symbol: \t" << this->d_bits_per_symbol << std::endl; + std::cout << "Bins per symbol: \t" << this->d_number_of_bins << std::endl; + std::cout << "Header bins per symbol: " << this->d_number_of_bins_hdr << std::endl; + std::cout << "Samples per symbol: \t" << this->d_samples_per_symbol << std::endl; + std::cout << "Decimation: \t\t" << (this->d_samples_per_symbol / this->d_number_of_bins) << std::endl; - void decoder_impl::instantaneous_frequency(const gr_complex* in_samples, float* out_ifreq, uint32_t window) { - float iphase[window]; + this->build_ideal_chirps(); - if(window < 2) { - // TODO: throw warning here - return; + this->set_output_multiple(2 * this->d_samples_per_symbol); + this->d_fft.resize(this->d_number_of_bins); + this->d_mult.resize(this->d_number_of_bins); + this->d_q = fft_create_plan(this->d_number_of_bins, &this->d_mult[0], &this->d_fft[0], LIQUID_FFT_FORWARD, 0); + + // Decimation filter + float g[DECIMATOR_FILTER_SIZE]; + liquid_firdes_rrcos(8, 1, 0.5f, 0.3f, g); // Filter for interpolating + + for (uint32_t i = 0; i < DECIMATOR_FILTER_SIZE; i++) // Reverse it to get decimation filter + this->d_decim_h[i] = g[DECIMATOR_FILTER_SIZE - i - 1]; + + this->d_decim_factor = this->d_samples_per_symbol / this->d_number_of_bins; + + this->d_decim = firdecim_crcf_create(this->d_decim_factor, this->d_decim_h, DECIMATOR_FILTER_SIZE); + + // Register gnuradio ports + this->message_port_register_out(pmt::mp("frames")); + this->message_port_register_out(pmt::mp("debug")); } - instantaneous_phase(in_samples, iphase, window); + /** + * Our virtual destructor. + */ + decoder_impl::~decoder_impl() { + #ifndef NDEBUG + if (this->d_debug_samples.is_open()) + this->d_debug_samples.close(); - // Instant freq - for(uint32_t i = 1; i < window; i++) { - out_ifreq[i-1] = iphase[i] - iphase[i-1]; - } - out_ifreq[window-1] = out_ifreq[window-2]; // Make sure there is no strong gradient if this value is accessed by mistake - } + if (this->d_debug.is_open()) + this->d_debug.close(); + #endif - inline void decoder_impl::instantaneous_phase(const gr_complex* in_samples, float* out_iphase, uint32_t window) { - for(uint32_t i = 0; i < window; i++) { - out_iphase[i] = arg(in_samples[i]); // = the same as atan2(imag(in_samples[i]),real(in_samples[i])); + fft_destroy_plan(this->d_q); + firdecim_crcf_destroy(this->d_decim); } - liquid_unwrap_phase(out_iphase, window); - } + void decoder_impl::build_ideal_chirps(void) { + this->d_downchirp .resize(this->d_samples_per_symbol); + this->d_upchirp .resize(this->d_samples_per_symbol); + this->d_downchirp_ifreq .resize(this->d_samples_per_symbol); + this->d_upchirp_ifreq .resize(this->d_samples_per_symbol); - float decoder_impl::cross_correlate(const gr_complex *samples_1, const gr_complex *samples_2, int window) { - float result = 0.0f; + const double T = -0.5 * this->d_bw * this->d_symbols_per_second; + const double f0 = (this->d_bw / 2.0f); + const double pre_dir = 2.0f * M_PI; + double t; + gr_complex cmx = gr_complex(1.0f, 1.0f); - for (int i = 0; i < window; i++) { - result += real(samples_1[i] * conj(samples_2[i])); + for (uint32_t i = 0; i < this->d_samples_per_symbol; i++) { + // Width in number of samples = samples_per_symbol + // See https://en.wikipedia.org/wiki/Chirp#Linear + t = this->d_dt * i; + this->d_downchirp[i] = cmx * gr_expj(pre_dir * t * (f0 + T * t)); + this->d_upchirp[i] = cmx * gr_expj(pre_dir * t * (f0 + T * t) * -1.0f); + } + + // Store instant. frequency + instantaneous_frequency(&this->d_downchirp[0], &this->d_downchirp_ifreq[0], this->d_samples_per_symbol); + instantaneous_frequency(&this->d_upchirp[0], &this->d_upchirp_ifreq[0], this->d_samples_per_symbol); + + samples_to_file("/tmp/downchirp", &this->d_downchirp[0], this->d_downchirp.size(), sizeof(gr_complex)); + samples_to_file("/tmp/upchirp", &this->d_upchirp[0], this->d_upchirp.size(), sizeof(gr_complex)); } - result = result / window; - return result; - } + void decoder_impl::samples_to_file(const std::string path, const gr_complex *v, uint32_t length, uint32_t elem_size) { + #ifndef NO_TMP_WRITES + std::ofstream out_file; + out_file.open(path.c_str(), std::ios::out | std::ios::binary); - float decoder_impl::detect_downchirp(const gr_complex *samples, uint32_t window) { - float samples_ifreq[window]; + //for(std::vector::const_iterator it = v.begin(); it != v.end(); ++it) { + for (uint32_t i = 0; i < length; i++) { + out_file.write(reinterpret_cast(&v[i]), elem_size); + } - instantaneous_frequency(samples, samples_ifreq, window); - return norm_cross_correlate(samples_ifreq, &d_downchirp_ifreq[0], window); - } - - /** - * Calculate normalized cross correlation of real values. - * See https://en.wikipedia.org/wiki/Cross-correlation#Normalized_cross-correlation. - */ - float decoder_impl::norm_cross_correlate(const float *samples_1, const float *samples_2, uint32_t window) { - float result = 0.0f; - - double average_1 = std::accumulate(samples_1, samples_1 + window, 0.0) / window; - double average_2 = std::accumulate(samples_2, samples_2 + window, 0.0) / window; - double sd_1 = stddev(samples_1, window, average_1); - double sd_2 = stddev(samples_2, window, average_2); - - for (int i = 0; i < window-1; i++) { - result += (samples_1[i] - average_1) * (samples_2[i] - average_2) / (sd_1 * sd_2); - } - result = result / (window-1); - - return result; - } - - float decoder_impl::sliding_norm_cross_correlate(const float *samples_1, const float *samples_2, uint32_t window, uint32_t slide, int32_t* index) { - float correlations[slide*2]; - float samples_1_padded[window+slide*2]; - - double average_1 = std::accumulate(samples_1, samples_1 + window, 0.0) / window; - double average_2 = std::accumulate(samples_2, samples_2 + window, 0.0) / window; - double sd_1 = stddev(samples_1, window, average_1); - double sd_2 = stddev(samples_2, window, average_2); - - // Create padding on both sides of the samples - for(uint32_t i = 0; i < window+slide*2; i++) { - samples_1_padded[i] = 0.0f; - } - for(uint32_t i = 0; i < window; i++) { - samples_1_padded[i+slide-1] = samples_1[i]; + out_file.close(); + #else + (void) path; + (void) v; + (void) length; + (void) elem_size; + #endif } - // Slide and correlate - for(uint32_t i = 0; i < 2*slide; i++) { + void decoder_impl::samples_debug(const gr_complex *v, uint32_t length) { + #ifndef NDEBUG + gr_complex start_indicator(0.0f, 32.0f); + this->d_debug_samples.write(reinterpret_cast(&start_indicator), sizeof(gr_complex)); + + for (uint32_t i = 1; i < length; i++) { + this->d_debug_samples.write(reinterpret_cast(&v[i]), sizeof(gr_complex)); + } + #else + (void) v; + (void) length; + #endif + } + + bool decoder_impl::calc_energy_threshold(gr_complex *samples, int window_size, float threshold) { float result = 0.0f; - for (uint32_t j = 0; j < window; j++) { - result += (samples_1_padded[i+j] - average_1) * (samples_2[j] - average_2) / (sd_1 * sd_2); - } - correlations[i] = result / window; - } - uint32_t argmax = (std::max_element(correlations,correlations+slide*2) - correlations); // Determine best correlation - *index = argmax - slide; // Determine how much we have to slide before the best correlation is reached - - return correlations[argmax]; - } - - float decoder_impl::stddev(const float *values, int len, float mean) { - double variance = 0.0f; - - for (unsigned int i = 0; i < len; i++) { - variance += std::pow(values[i] - mean, 2); - } - - variance /= len; - return std::sqrt(variance); - } - - float decoder_impl::detect_upchirp(const gr_complex *samples, uint32_t window, uint32_t slide, int32_t* index) { - float samples_ifreq[window]; - - instantaneous_frequency(samples, samples_ifreq, window); - return sliding_norm_cross_correlate(samples_ifreq, &d_upchirp_ifreq[0], window, slide, index); - } - - unsigned int decoder_impl::get_shift_fft(gr_complex* samples) { - float fft_mag[d_number_of_bins]; - gr_complex mult_hf[d_samples_per_symbol]; - - #ifdef CFO_CORRECT - determine_cfo(&samples[0]); - d_debug << "CFO: " << d_cfo_estimation << std::endl; - correct_cfo(&samples[0], d_samples_per_symbol); - #endif - - samples_to_file("/tmp/data", &samples[0], d_samples_per_symbol, sizeof(gr_complex)); - - // Multiply with ideal downchirp - for(uint32_t i = 0; i < d_samples_per_symbol; i++) { - mult_hf[i] = conj(samples[i] * d_downchirp[i]); - } - - samples_to_file("/tmp/mult", &mult_hf[0], d_samples_per_symbol, sizeof(gr_complex)); - - // Perform decimation - for (uint32_t i = 0; i < d_number_of_bins; i++) { - firdecim_crcf_execute(d_decim, &mult_hf[d_decim_factor*i], &d_mult[i]); - } - - samples_to_file("/tmp/resampled", &d_mult[0], d_number_of_bins, sizeof(gr_complex)); - - // Perform FFT - fft_execute(d_q); - - // Get magnitude - for(int i = 0; i < d_number_of_bins; i++) { - fft_mag[i] = abs(d_fft[i]); - } - - samples_to_file("/tmp/fft", &d_fft[0], d_number_of_bins, sizeof(gr_complex)); - - // Return argmax here - return (std::max_element(fft_mag,fft_mag+d_number_of_bins) - fft_mag); - } - - unsigned int decoder_impl::max_frequency_gradient_idx(gr_complex* samples) { - float instantaneous_phase[d_samples_per_symbol]; - float instantaneous_freq[d_samples_per_symbol]; - float bins[d_number_of_bins]; - - samples_to_file("/tmp/data", &samples[0], d_samples_per_symbol, sizeof(gr_complex)); - - // Determine instant phase - for(unsigned int i = 0; i < d_samples_per_symbol; i++) { - instantaneous_phase[i] = arg(samples[i]); - } - liquid_unwrap_phase(instantaneous_phase, d_samples_per_symbol); - - float max_if_diff = 2000.0f; - unsigned int max_if_diff_idx = 0; - - for(unsigned int i = 1; i < d_samples_per_symbol; i++) { - float ifreq = (instantaneous_phase[i] - instantaneous_phase[i-1]) / (2.0f * M_PI) * d_samples_per_second; // TODO: constant multiplication can be removed - instantaneous_freq[i-1] = ifreq; - } - - int osr = d_samples_per_symbol / d_number_of_bins; - float last_avg = instantaneous_freq[0]; - for(unsigned int i = 0; i < d_number_of_bins; i++) { - float avg = 0.0f; - for(unsigned int j = 0; j < osr; j++) { - avg += instantaneous_freq[(osr*i) + j]; - } - avg /= osr; - - float diff = abs(last_avg - avg); - - if(diff > max_if_diff) { - max_if_diff = diff; - max_if_diff_idx = i; + for (int i = 0; i < window_size; i++) { + float magn = abs(samples[i]); + result += magn * magn; } - last_avg = avg; - } - //std::cout << "!!!" << max_if_diff << std::endl; + result /= (float)window_size; - return max_if_diff_idx; - } + #ifndef NDEBUG + this->d_debug << "T: " << result << "\n"; + #endif - bool decoder_impl::demodulate(gr_complex* samples, bool is_header) { - unsigned int bin_idx = max_frequency_gradient_idx(samples); - //unsigned int bin_idx = get_shift_fft(samples); - //unsigned int bin_idx_test = get_shift_fft(samples); - unsigned int bin_idx_test = 0; - - // Header has additional redundancy - if(is_header) { - bin_idx /= 4; - bin_idx_test /= 4; + return result > threshold; } - // Decode (actually gray encode) the bin to get the symbol value - unsigned int word = gray_encode(bin_idx); - d_debug << to_bin(word, is_header ? d_sf-2 : d_sf) << " " << bin_idx << std::endl; - d_words.push_back(word); + void decoder_impl::instantaneous_frequency(const gr_complex *in_samples, float *out_ifreq, uint32_t window) { + float iphase[window]; - // Look for 4+cr symbols and stop - if(d_words.size() == (4 + d_cr)) { - // Deinterleave - if(is_header) { - deinterleave(d_sf - 2); + if (window < 2) { + // TODO: throw warning here + std::cerr << "LoRa Decoder Warning: window size < 2 !" << std::endl; + return; + } + + this->instantaneous_phase(in_samples, iphase, window); + + // Instant freq + for (uint32_t i = 1; i < window; i++) { + out_ifreq[i - 1] = iphase[i] - iphase[i - 1]; + } + + // Make sure there is no strong gradient if this value is accessed by mistake + out_ifreq[window - 1] = out_ifreq[window - 2]; + } + + inline void decoder_impl::instantaneous_phase(const gr_complex *in_samples, float *out_iphase, uint32_t window) { + for (uint32_t i = 0; i < window; i++) { + out_iphase[i] = arg(in_samples[i]); + // = the same as atan2(imag(in_samples[i]),real(in_samples[i])); + } + + liquid_unwrap_phase(out_iphase, window); + } + + float decoder_impl::cross_correlate(const gr_complex *samples_1, const gr_complex *samples_2, int window) { + float result = 0.0f; + + for (int i = 0; i < window; i++) { + result += real(samples_1[i] * conj(samples_2[i])); + } + + result /= (float)window; + + return result; + } + + float decoder_impl::detect_downchirp(const gr_complex *samples, uint32_t window) { + float samples_ifreq[window]; + + instantaneous_frequency(samples, samples_ifreq, window); + return norm_cross_correlate(samples_ifreq, &this->d_downchirp_ifreq[0], window); + } + + /** + * Calculate normalized cross correlation of real values. + * See https://en.wikipedia.org/wiki/Cross-correlation#Normalized_cross-correlation. + */ + float decoder_impl::norm_cross_correlate(const float *samples_1, const float *samples_2, uint32_t window) { + float result = 0.0f; + + float average_1 = std::accumulate(samples_1, samples_1 + window, 0.0f) / window; + float average_2 = std::accumulate(samples_2, samples_2 + window, 0.0f) / window; + float sd_1 = stddev(samples_1, window, average_1); + float sd_2 = stddev(samples_2, window, average_2); + + for (uint32_t i = 0; i < window - 1; i++) { + result += (samples_1[i] - average_1) * (samples_2[i] - average_2) + / (sd_1 * sd_2); + } + + result /= (float)(window - 1); + + return result; + } + + float decoder_impl::sliding_norm_cross_correlate(const float *samples_1, const float *samples_2, uint32_t window, uint32_t slide, int32_t *index) { + float correlations[slide * 2]; + float samples_1_padded[window + slide * 2] = { 0.0f }; + + double average_1 = std::accumulate(samples_1, samples_1 + window, 0.0) / window; + double average_2 = std::accumulate(samples_2, samples_2 + window, 0.0) / window; + double sd_1 = stddev(samples_1, window, average_1); + double sd_2 = stddev(samples_2, window, average_2); + + uint32_t i, j; + float result; + + // Create padding on both sides of the samples + for (i = 0; i < window; i++) { + samples_1_padded[i + slide - 1] = samples_1[i]; + } + + // Slide and correlate + for (i = 0; i < 2 * slide; i++) { + result = 0.0f; + + for (j = 0; j < window; j++) { + result += (samples_1_padded[i + j] - average_1) * (samples_2[j] - average_2) + / (sd_1 * sd_2); + } + + correlations[i] = result / (float)window; + } + + // Determine best correlation + uint32_t argmax = (std::max_element(correlations, correlations + slide * 2) - correlations); + // Determine how much we have to slide before the best correlation is reached + *index = argmax - slide; + + return correlations[argmax]; + } + + float decoder_impl::stddev(const float *values, uint32_t len, float mean) { + float variance = 0.0f, temp; + + for (unsigned int i = 0; i < len; i++) { + temp = values[i] - mean; + variance += temp * temp; + } + + variance /= (float)len; + return std::sqrt(variance); + } + + float decoder_impl::detect_upchirp(const gr_complex *samples, uint32_t window, uint32_t slide, int32_t *index) { + float samples_ifreq[window]; + + instantaneous_frequency(samples, samples_ifreq, window); + return sliding_norm_cross_correlate(samples_ifreq, &this->d_upchirp_ifreq[0], window, slide, index); + } + + unsigned int decoder_impl::get_shift_fft(gr_complex *samples) { + float fft_mag[this->d_number_of_bins]; + gr_complex mult_hf[this->d_samples_per_symbol]; + + #ifdef CFO_CORRECT + determine_cfo(&samples[0]); + #ifndef NDEBUG + this->d_debug << "CFO: " << this->d_cfo_estimation << std::endl; + #endif + correct_cfo(&samples[0], this->d_samples_per_symbol); + #endif + + samples_to_file("/tmp/data", &samples[0], this->d_samples_per_symbol, sizeof(gr_complex)); + + // Multiply with ideal downchirp + for (uint32_t i = 0; i < this->d_samples_per_symbol; i++) { + mult_hf[i] = conj(samples[i] * this->d_downchirp[i]); + } + + samples_to_file("/tmp/mult", &mult_hf[0], this->d_samples_per_symbol, sizeof(gr_complex)); + + // Perform decimation + for (uint32_t i = 0; i < this->d_number_of_bins; i++) { + firdecim_crcf_execute(this->d_decim, &mult_hf[this->d_decim_factor * i], &d_mult[i]); + } + + samples_to_file("/tmp/resampled", &this->d_mult[0], this->d_number_of_bins, sizeof(gr_complex)); + + // Perform FFT + fft_execute(this->d_q); + + // Get magnitude + for (uint32_t i = 0; i < this->d_number_of_bins; i++) { + fft_mag[i] = abs(this->d_fft[i]); + } + + samples_to_file("/tmp/fft", &this->d_fft[0], this->d_number_of_bins, sizeof(gr_complex)); + + // Return argmax here + return (std::max_element(fft_mag, fft_mag + this->d_number_of_bins) - fft_mag); + } + + unsigned int decoder_impl::max_frequency_gradient_idx(gr_complex *samples) { + float instantaneous_phase[this->d_samples_per_symbol]; + float instantaneous_freq [this->d_samples_per_symbol]; + //float bins[this->d_number_of_bins]; + + samples_to_file("/tmp/data", &samples[0], this->d_samples_per_symbol, sizeof(gr_complex)); + + // Determine instant phase + for (unsigned int i = 0; i < this->d_samples_per_symbol; i++) { + instantaneous_phase[i] = arg(samples[i]); + } + + liquid_unwrap_phase(instantaneous_phase, this->d_samples_per_symbol); + + float max_if_diff = 2000.0f; + unsigned int max_if_diff_idx = 0; + const double div = (double)this->d_samples_per_second / (2.0f * M_PI); + + for (unsigned int i = 1; i < this->d_samples_per_symbol; i++) { + instantaneous_freq[i - 1] = (float)((instantaneous_phase[i] - instantaneous_phase[i - 1]) * div); + } + + uint32_t osr = this->d_samples_per_symbol / this->d_number_of_bins; + float last_avg = instantaneous_freq[0]; + + for (unsigned int i = 0; i < this->d_number_of_bins; i++) { + float avg = 0.0f; + + for (unsigned int j = 0; j < osr; j++) { + avg += instantaneous_freq[(osr * i) + j]; + } + + avg /= (float)osr; + + float diff = abs(last_avg - avg); + + if (diff > max_if_diff) { + max_if_diff = diff; + max_if_diff_idx = i; + } + + last_avg = avg; + } + + //std::cout << "!!!" << max_if_diff << std::endl; + + return max_if_diff_idx; + } + + bool decoder_impl::demodulate(gr_complex *samples, bool is_header) { + unsigned int bin_idx = this->max_frequency_gradient_idx(samples); + //unsigned int bin_idx = get_shift_fft(samples); + //unsigned int bin_idx_test = get_shift_fft(samples); + unsigned int bin_idx_test = 0; + + // Header has additional redundancy + if (is_header) { + bin_idx /= 4; + bin_idx_test /= 4; + } + + // Decode (actually gray encode) the bin to get the symbol value + unsigned int word = gray_encode(bin_idx); + #ifndef NDEBUG + this->d_debug << gr::lora::to_bin(word, is_header ? this->d_sf - 2 : this->d_sf) << " " << bin_idx << std::endl; + #endif + this->d_words.push_back(word); + + // Look for 4+cr symbols and stop + if (this->d_words.size() == (4u + this->d_cr)) { + // Deinterleave + this->deinterleave(is_header ? this->d_sf - 2 : this->d_sf); + + return true; // Signal that a block is ready for decoding + } + + return false; // We need more words in order to decode a block + } + + void decoder_impl::deinterleave(uint32_t ppm) { + unsigned int bits_per_word = this->d_words.size(); + + if (bits_per_word > 8) { + // Not sure if this can ever occur. It would imply coding rate high than 4/8 e.g. 4/9. + std::cerr << "More than 8 bits per word. uint8_t will not be sufficient! Bytes need to be stored in intermediate array and then packed into words_deinterleaved!" << std::endl; + } + + std::deque words_deinterleaved; + unsigned int offset_start = ppm - 1, offset_diag, i; + uint8_t d; + + for (i = 0; i < ppm; i++) { + d = 0; + offset_diag = offset_start; + + for (unsigned int j = 0; j < bits_per_word; j++) { + uint8_t power = 1 << j; + unsigned int power_check = 1 << offset_diag; + + if (this->d_words[j] & power_check) { // Mask triggers + d += power; + } + + if (offset_diag) offset_diag--; + else offset_diag = ppm - 1; + } + + offset_start--; + words_deinterleaved.push_front(d); + } + + #ifndef NDEBUG + std::vector wd(words_deinterleaved.begin(), words_deinterleaved.begin() + ppm-1); + print_vector(this->d_debug, wd, "D", sizeof(uint8_t) * 8); + #endif + + // Add to demodulated data + this->d_demodulated.insert(this->d_demodulated.end(), words_deinterleaved.begin(), words_deinterleaved.end()); + + // Cleanup + this->d_words.clear(); + } + + int decoder_impl::decode(uint8_t *out_data, bool is_header) { + const uint8_t *prng = NULL; + const uint8_t shuffle_pattern[] = {7, 6, 3, 4, 2, 1, 0, 5}; + + if (is_header) { + prng = gr::lora::prng_header; } else { - deinterleave(d_sf); - } - - return true; // Signal that a block is ready for decoding - } - - return false; // We need more words in order to decode a block - } - - void decoder_impl::deinterleave(int ppm) { - unsigned int bits_per_word = d_words.size(); - - if(bits_per_word > 8) { // Not sure if this can ever occur. It would imply coding rate high than 4/8 e.g. 4/9. - std::cout << "More than 8 bits per word. uint8_t will not be sufficient! Bytes need to be stored in intermediate array and then packed into words_deinterleaved!" << std::endl; - } - - unsigned int offset_start = ppm-1; - std::vector words_deinterleaved; - for(unsigned int i = 0; i < ppm; i++) { - uint8_t d = 0; - unsigned int offset_diag = offset_start; - - for(unsigned int j = 0; j < bits_per_word; j++) { - uint8_t power = pow(2, j); - unsigned int power_check = pow(2, offset_diag); - if((d_words[j] & power_check) > 0) { // Mask triggers - d += power; - } - - if(offset_diag == 0) - offset_diag = ppm-1; - else - offset_diag -= 1; - } - - offset_start -= 1; - words_deinterleaved.push_back(d); - } - - std::reverse(words_deinterleaved.begin(),words_deinterleaved.end()); - print_vector(d_debug, words_deinterleaved, "D", sizeof(uint8_t)*8); - - // Add to demodulated data - for(int i = 0; i < words_deinterleaved.size(); i++) { - d_demodulated.push_back(words_deinterleaved[i]); - } - - // Cleanup - d_words.clear(); - } - - int decoder_impl::decode(uint8_t* out_data, bool is_header) { - const uint8_t* prng = NULL; - const uint8_t shuffle_pattern[] = {7, 6, 3, 4, 2, 1, 0, 5}; - - if(is_header) { - prng = prng_header; - } else { - if(d_sf == 7) - prng = prng_payload_sf7; - else if(d_sf == 8) - prng = prng_payload_sf8; - else if(d_sf == 9) - prng = prng_payload_sf9; - else if(d_sf == 10) - prng = prng_payload_sf10; - else if(d_sf == 11) - prng = prng_payload_sf11; - else if(d_sf == 12) - prng = prng_payload_sf12; - else - prng = prng_payload_sf7; - } - - deshuffle(shuffle_pattern, is_header); - dewhiten(prng); - hamming_decode(out_data); - - // Nibbles are reversed TODO why is this? - nibble_reverse(out_data, d_payload_length); - - // Print result - std::stringstream result; - for (int i = 0; i < d_payload_length; i++) { - result << " " << std::hex << std::setw(2) << std::setfill('0') << (int)out_data[i]; - } - - if(!is_header) { - d_data.insert(d_data.end(), out_data, out_data + d_payload_length); - std::cout << result.str() << std::endl; - - pmt::pmt_t payload_blob = pmt::make_blob(&d_data[0], sizeof(uint8_t) * (d_payload_length + 3)); - message_port_pub(pmt::mp("frames"), payload_blob); - } else { - d_data.insert(d_data.end(), out_data, out_data + 3); - std::cout << result.str(); - } - - return 0; - } - - void decoder_impl::deshuffle(const uint8_t* shuffle_pattern, bool is_header) { - uint32_t to_decode = d_demodulated.size(); - - if(is_header) - to_decode = 5; - - for(uint32_t i = 0; i < to_decode; i++) { - uint8_t original = d_demodulated[i]; - uint8_t result = 0; - - for(int j = 0; j < sizeof(shuffle_pattern) / sizeof(uint8_t); j++) { - uint8_t mask = pow(2, shuffle_pattern[j]); - if((original & mask) > 0) { - result += pow(2, j); + switch(this->d_sf) { + case 7: prng = gr::lora::prng_payload_sf7; break; + case 8: prng = gr::lora::prng_payload_sf8; break; + case 9: prng = gr::lora::prng_payload_sf9; break; + case 10: prng = gr::lora::prng_payload_sf10; break; + case 11: prng = gr::lora::prng_payload_sf11; break; + case 12: prng = gr::lora::prng_payload_sf12; break; + default: prng = gr::lora::prng_payload_sf7; break; } } - d_words_deshuffled.push_back(result); + this->deshuffle(shuffle_pattern, is_header); + this->dewhiten(prng); + this->hamming_decode(out_data); + + // Nibbles are reversed TODO why is this? + this->nibble_reverse(out_data, this->d_payload_length); + + // Print result + std::stringstream result; + + for (uint32_t i = 0; i < this->d_payload_length; i++) { + result << " " << std::hex << std::setw(2) << std::setfill('0') << (int)out_data[i]; + } + + if (!is_header) { + this->d_data.insert(this->d_data.end(), out_data, out_data + this->d_payload_length); + std::cout << result.str() << std::endl; + + pmt::pmt_t payload_blob = pmt::make_blob(&this->d_data[0], + sizeof(uint8_t) * (this->d_payload_length + 3)); + this->message_port_pub(pmt::mp("frames"), payload_blob); + } else { + this->d_data.insert(this->d_data.end(), out_data, out_data + 3); + std::cout << result.str(); + } + + return 0; } - //print_vector(d_debug, d_words_deshuffled, "S", sizeof(uint8_t)*8); - print_vector_raw(d_debug, d_words_deshuffled, sizeof(uint8_t)*8); - d_debug << std::endl; + void decoder_impl::deshuffle(const uint8_t *shuffle_pattern, bool is_header) { + const uint32_t to_decode = is_header ? 5 : this->d_demodulated.size(); + const uint32_t len = sizeof(shuffle_pattern) / sizeof(uint8_t); + uint8_t original, result; - // We're done with these words - if(is_header) - d_demodulated.erase(d_demodulated.begin(), d_demodulated.begin()+5); - else - d_demodulated.clear(); - } + for (uint32_t i = 0; i < to_decode; i++) { + original = this->d_demodulated[i]; + result = 0; - void decoder_impl::dewhiten(const uint8_t* prng) { - for(int i = 0; i < d_words_deshuffled.size(); i++) { - uint8_t xor_b = d_words_deshuffled[i] ^ prng[i]; - xor_b = (xor_b & 0xF0) >> 4 | (xor_b & 0x0F) << 4; // TODO: reverse bit order is performed here, but is probably due to mistake in whitening or interleaving - xor_b = (xor_b & 0xCC) >> 2 | (xor_b & 0x33) << 2; - xor_b = (xor_b & 0xAA) >> 1 | (xor_b & 0x55) << 1; - d_words_dewhitened.push_back(xor_b); - } - - print_vector(d_debug, d_words_dewhitened, "W", sizeof(uint8_t)*8); - - d_words_deshuffled.clear(); - } - - void decoder_impl::hamming_decode(uint8_t* out_data) { - uint8_t data_indices[4] = {1, 2, 3, 5}; - unsigned int n = ceil(d_words_dewhitened.size() * 4.0f / (4.0f + d_cr)); - fec_scheme fs = LIQUID_FEC_HAMMING84; - - if(d_cr == 4) { - hamming_decode_soft(&d_words_dewhitened[0], d_words_dewhitened.size(), out_data); - d_words_dewhitened.clear(); - return; - } else if(d_cr == 3) { - hamming_decode_soft(&d_words_dewhitened[0], d_words_dewhitened.size(), out_data); - d_words_dewhitened.clear(); - return; - } else if(d_cr == 2) { - fec_extract_data_only(&d_words_dewhitened[0], d_words_dewhitened.size(), data_indices, 4, out_data); - d_words_dewhitened.clear(); - return; - } else if(d_cr == 1) { // TODO: Report parity error to the user - fec_extract_data_only(&d_words_dewhitened[0], d_words_dewhitened.size(), data_indices, 4, out_data); - d_words_dewhitened.clear(); - return; - } - - /*fs = LIQUID_FEC_HAMMING84; - - unsigned int k = fec_get_enc_msg_length(fs, n); - fec hamming = fec_create(fs, NULL); - - fec_decode(hamming, n, &d_words_dewhitened[0], out_data); - - d_words_dewhitened.clear(); - fec_destroy(hamming);*/ - } - - void decoder_impl::nibble_reverse(uint8_t* out_data, int len) { - for(int i = 0; i < len; i++) { - out_data[i] = ((out_data[i] & 0x0f) << 4) | ((out_data[i] & 0xf0) >> 4); - } - } - - void decoder_impl::determine_cfo(const gr_complex* samples) { - float instantaneous_phase[d_samples_per_symbol]; - float instantaneous_freq[d_samples_per_symbol]; - - // Determine instant phase - for(unsigned int i = 0; i < d_samples_per_symbol; i++) { - instantaneous_phase[i] = arg(samples[i]); - } - liquid_unwrap_phase(instantaneous_phase, d_samples_per_symbol); - - // Determine instant freq - for(unsigned int i = 1; i < d_samples_per_symbol; i++) { - float ifreq = (instantaneous_phase[i] - instantaneous_phase[i-1]) / (2.0f * M_PI) * d_samples_per_second; - instantaneous_freq[i-1] = ifreq; - } + for (uint32_t j = 0; j < len; j++) { + if (original & (1 << shuffle_pattern[j])) { + result |= 1 << j; + } + } float sum = 0.0f; for(int i = 0; i < d_samples_per_symbol-1; i++) { sum += instantaneous_freq[i]; - } - sum /= d_samples_per_symbol-1; + #ifndef NDEBUG + //print_vector(d_debug, d_words_deshuffled, "S", sizeof(uint8_t)*8); + print_vector_raw(this->d_debug, this->d_words_deshuffled, sizeof(uint8_t) * 8); + this->d_debug << std::endl; + #endif - d_cfo_estimation = sum; + // We're done with these words + if (is_header){ + this->d_demodulated.erase(this->d_demodulated.begin(), this->d_demodulated.begin() + 5); + this->d_demodulated.clear(); + } + } + + void decoder_impl::dewhiten(const uint8_t *prng) { + uint32_t i, len = this->d_words_deshuffled.size(); /*d_cfo_estimation = (*std::max_element(instantaneous_freq, instantaneous_freq+d_samples_per_symbol-1) + *std::min_element(instantaneous_freq, instantaneous_freq+d_samples_per_symbol-1)) / 2;*/ } @@ -614,81 +583,96 @@ namespace gr { void decoder_impl::correct_cfo(gr_complex* samples, int num_samples) { for(uint32_t i = 0; i < num_samples; i++) { samples[i] = samples[i] * gr_expj(2.0f * M_PI * -d_cfo_estimation * (d_dt * i)); - } - } - - int decoder_impl::find_preamble_start(gr_complex* samples) { - for(int i = 0; i < d_samples_per_symbol; i++) { - unsigned int c = get_shift_fft(&samples[i]); - if(c == 0) { - return i; - } - } - } - - int decoder_impl::find_preamble_start_fast(gr_complex* samples, uint32_t len) { - int decimation = d_corr_decim_factor; - int decim_size = d_samples_per_symbol / decimation; - float decim[decimation]; - float gradient[decimation]; - uint32_t rising = 0; - uint32_t rising_required = 2; - - gradient[0] = 0.0f; - - - for(int i = 0; i < decimation; i++) { - float s[2] = { - arg(samples[i*decim_size]), - arg(samples[(i+1)*decim_size]) - }; - liquid_unwrap_phase(s, 2); - - decim[i] = (s[1] - s[0]) / (2.0f * M_PI) * d_samples_per_second; + this->d_words_deshuffled.clear(); } - for(int i = 1; i < decimation; i++) { - gradient[i] = decim[i] - decim[i-1]; - if(gradient[i] > gradient[i-1]) - rising++; - if(rising >= rising_required && gradient[i] <= -20000) { // TODO: Make this a bit more logical, e.g. d_bw / decimation * 2 -> 2 steps down - return i*decim_size; + void decoder_impl::hamming_decode(uint8_t *out_data) { + uint8_t data_indices[4] = {1, 2, 3, 5}; + + switch(this->d_cr) { + case 4: case 3: + gr::lora::hamming_decode_soft(&this->d_words_dewhitened[0], this->d_words_dewhitened.size(), out_data); + break; + case 2: case 1: // TODO: Report parity error to the user + gr::lora::fec_extract_data_only(&this->d_words_dewhitened[0], this->d_words_dewhitened.size(), data_indices, 4, out_data); + break; } - //d_debug << "G:" << gradient[i] << std::endl; + + this->d_words_dewhitened.clear(); + + /* + fec_scheme fs = LIQUID_FEC_HAMMING84; + unsigned int n = ceil(this->d_words_dewhitened.size() * 4.0f / (4.0f + d_cr)); + + unsigned int k = fec_get_enc_msg_length(fs, n); + fec hamming = fec_create(fs, NULL); + + fec_decode(hamming, n, &d_words_dewhitened[0], out_data); + + d_words_dewhitened.clear(); + fec_destroy(hamming);*/ } - return -1; - } - - uint8_t decoder_impl::lookup_cr(uint8_t bytevalue) { - switch (bytevalue & 0x0f) { - case 0x01: { - return 4; - break; - } - case 0x0f: { - return 3; - break; - } - case 0x0d: { - return 2; - break; - } - case 0x0b: { - return 1; - break; - } - default: { - return 4; - break; + void decoder_impl::nibble_reverse(uint8_t *out_data, int len) { + for (int i = 0; i < len; i++) { + out_data[i] = ((out_data[i] & 0x0f) << 4) | ((out_data[i] & 0xf0) >> 4); } } - } - void decoder_impl::msg_raw_chirp_debug(const gr_complex* raw_samples, uint32_t num_samples) { - pmt::pmt_t chirp_blob = pmt::make_blob(raw_samples, sizeof(gr_complex) * num_samples); - message_port_pub(pmt::mp("debug"), chirp_blob); - } + void decoder_impl::determine_cfo(const gr_complex *samples) { + float instantaneous_phase[this->d_samples_per_symbol]; +// float instantaneous_freq [this->d_samples_per_symbol]; + double div = (double) this->d_samples_per_second / (2.0f * M_PI); + + // Determine instant phase + for (unsigned int i = 0; i < this->d_samples_per_symbol; i++) { + instantaneous_phase[i] = arg(samples[i]); + } + + liquid_unwrap_phase(instantaneous_phase, this->d_samples_per_symbol); + + // Determine instant freq +// for (unsigned int i = 1; i < this->d_samples_per_symbol; i++) { +// instantaneous_freq[i - 1] = (float)((instantaneous_phase[i] - instantaneous_phase[i - 1]) * div); +// } + + float sum = 0.0f; + + for (uint32_t i = 1; i < this->d_samples_per_symbol; i++) { + sum += (float)((instantaneous_phase[i] - instantaneous_phase[i - 1]) * div); + } + + this->d_cfo_estimation = sum / (float)(this->d_samples_per_symbol - 1); + + /*d_cfo_estimation = (*std::max_element(instantaneous_freq, instantaneous_freq+d_samples_per_symbol-1) + *std::min_element(instantaneous_freq, instantaneous_freq+d_samples_per_symbol-1)) / 2;*/ + } + + void decoder_impl::correct_cfo(gr_complex *samples, uint32_t num_samples) { + const float mul = 2.0f * M_PI * -this->d_cfo_estimation * this->d_dt; + + for (uint32_t i = 0; i < num_samples; i++) { + samples[i] *= gr_expj(mul * i); + } + } + + int decoder_impl::find_preamble_start(gr_complex *samples) { + for (uint32_t i = 0; i < this->d_samples_per_symbol; i++) { + if (!this->get_shift_fft(&samples[i])) + return i; + } + + return -1; + } + + int decoder_impl::find_preamble_start_fast(gr_complex *samples, uint32_t len) { + (void) len; + + const uint32_t decimation = this->d_corr_decim_factor; + const uint32_t decim_size = this->d_samples_per_symbol / decimation; + + const float mul = (float)this->d_samples_per_second / (2.0f * M_PI); + uint32_t rising = 0; + static const uint32_t rising_required = 2; void decoder_impl::msg_lora_frame(const uint8_t *frame_bytes, uint32_t frame_len) { @@ -716,107 +700,171 @@ namespace gr { d_state = SYNC; consume_each(i+index_correction); break; - } - } - consume_each(2*d_samples_per_symbol); - break; - } - case SYNC: { - double c = detect_downchirp(&input[0], d_samples_per_symbol); - d_debug << "Cd: " << c << std::endl; - - if(c > 0.98f) { - d_debug << "SYNC: " << c << std::endl; - // Debug stuff - samples_to_file("/tmp/sync", &input[0], d_samples_per_symbol, sizeof(gr_complex)); - - d_state = PAUSE; - consume_each(d_samples_per_symbol); - } else { - d_corr_fails++; - if(d_corr_fails > 32) { - d_state = DETECT; - d_debug << "Lost sync" << std::endl; - } - consume_each(d_samples_per_symbol); - } - break; - } - case PAUSE: { - d_state = DECODE_HEADER; - - //samples_debug(input, d_samples_per_symbol + d_delay_after_sync); - consume_each(d_samples_per_symbol + d_delay_after_sync); - break; - } - case DECODE_HEADER: { - d_cr = 4; - if(demodulate(input, true)) { - uint8_t decoded[3]; - d_payload_length = 3; // TODO: A bit messy. I think it's better to make an internal decoded std::vector - decode(decoded, true); - - nibble_reverse(decoded, 1); // TODO: Why? Endianess? - d_payload_length = decoded[0]; - d_cr = lookup_cr(decoded[1]); - - int symbols_per_block = d_cr + 4; - int bits_needed = ((d_payload_length * 8) + 16); - float symbols_needed = float(bits_needed) * (symbols_per_block / 4.0f) / float(d_sf); - int blocks_needed = ceil(symbols_needed / symbols_per_block); - d_payload_symbols = blocks_needed * symbols_per_block; - - d_debug << "LEN: " << d_payload_length << " (" << d_payload_symbols << " symbols)" << std::endl; - - d_state = DECODE_PAYLOAD; - } - - msg_raw_chirp_debug(raw_input, d_samples_per_symbol); - //samples_debug(input, d_samples_per_symbol); - consume_each(d_samples_per_symbol); - break; - } - case DECODE_PAYLOAD: { - if(demodulate(input, false)) { - d_payload_symbols -= (4 + d_cr); - - if(d_payload_symbols <= 0) { - uint8_t decoded[d_payload_length]; - memset(decoded, 0x00, d_payload_length); - decode(decoded, false); - - d_state = DETECT; - d_data.clear(); - } - } - - msg_raw_chirp_debug(raw_input, d_samples_per_symbol); - //samples_debug(input, d_samples_per_symbol); - consume_each(d_samples_per_symbol); - break; - } - case STOP: { - consume_each(d_samples_per_symbol); - break; - } - default: { - std::cout << "Shouldn't happen\n"; - break; - } + void decoder_impl::msg_raw_chirp_debug(const gr_complex *raw_samples, uint32_t num_samples) { + pmt::pmt_t chirp_blob = pmt::make_blob(raw_samples, sizeof(gr_complex) * num_samples); + message_port_pub(pmt::mp("debug"), chirp_blob); } - // Tell runtime system how many output items we produced. - return 0; - } + void decoder_impl::msg_lora_frame(const uint8_t *frame_bytes, uint32_t frame_len) { + // ?? No implementation + } - void decoder_impl::set_sf(uint8_t sf) { - if(sf >= 7 && sf <= 13) - d_sf = sf; - } + int decoder_impl::work(int noutput_items, + gr_vector_const_void_star& input_items, + gr_vector_void_star& output_items) { + (void) noutput_items; + (void) output_items; - void decoder_impl::set_samp_rate(float samp_rate) { - d_samples_per_second = samp_rate; - } + gr_complex *input = (gr_complex *) input_items[0]; + gr_complex *raw_input = (gr_complex *) input_items[1]; +// float *out = (float *)output_items[0]; - } /* namespace lora */ + switch (this->d_state) { + case gr::lora::DecoderState::DETECT: { + int i = this->find_preamble_start_fast(&input[0], 2 * this->d_samples_per_symbol); + + if (i != -1) { + uint32_t c_window = std::min(2 * this->d_samples_per_symbol - i, + this->d_samples_per_symbol); + int32_t index_correction = 0; + float c = this->detect_upchirp(&input[i], + c_window, + this->d_samples_per_symbol / this->d_corr_decim_factor, + &index_correction); + + if (c > 0.8f) { + #ifndef NDEBUG + this->d_debug << "Cu: " << c << std::endl; + #endif + this->samples_to_file("/tmp/detectb", &input[i], this->d_samples_per_symbol, sizeof(gr_complex)); + this->samples_to_file("/tmp/detect", &input[i + index_correction], this->d_samples_per_symbol, sizeof(gr_complex)); + this->d_corr_fails = 0; + this->d_state = gr::lora::DecoderState::SYNC; + this->consume_each(i + index_correction); + break; + } + } + + this->consume_each(2 * this->d_samples_per_symbol); + break; + } + + case gr::lora::DecoderState::SYNC: { + double c = this->detect_downchirp(&input[0], this->d_samples_per_symbol); + #ifndef NDEBUG + this->d_debug << "Cd: " << c << std::endl; + #endif + + if (c > 0.98f) { + #ifndef NDEBUG + this->d_debug << "SYNC: " << c << std::endl; + #endif + // Debug stuff + this->samples_to_file("/tmp/sync", &input[0], this->d_samples_per_symbol, sizeof(gr_complex)); + + this->d_state = gr::lora::DecoderState::PAUSE; + } else { + this->d_corr_fails++; + + if (this->d_corr_fails > 32) { + this->d_state = gr::lora::DecoderState::DETECT; + #ifndef NDEBUG + this->d_debug << "Lost sync" << std::endl; + #endif + } + } + + this->consume_each(this->d_samples_per_symbol); + break; + } + + case gr::lora::DecoderState::PAUSE: { + this->d_state = gr::lora::DecoderState::DECODE_HEADER; + + //samples_debug(input, d_samples_per_symbol + d_delay_after_sync); + this->consume_each(this->d_samples_per_symbol + this->d_delay_after_sync); + break; + } + + case gr::lora::DecoderState::DECODE_HEADER: { + this->d_cr = 4; + + if (this->demodulate(input, true)) { + uint8_t decoded[3]; + // TODO: A bit messy. I think it's better to make an internal decoded std::vector + this->d_payload_length = 3; + + this->decode(decoded, true); + + this->nibble_reverse(decoded, 1); // TODO: Why? Endianess? + this->d_payload_length = decoded[0]; + this->d_cr = this->lookup_cr(decoded[1]); + + int symbols_per_block = this->d_cr + 4; + int bits_needed = this->d_payload_length * 8 + 16; + float symbols_needed = float(bits_needed) * (symbols_per_block / 4.0f) / float(this->d_sf); + int blocks_needed = ceil(symbols_needed / symbols_per_block); + this->d_payload_symbols = blocks_needed * symbols_per_block; + + #ifndef NDEBUG + this->d_debug << "LEN: " << this->d_payload_length << " (" << this->d_payload_symbols << " symbols)" << std::endl; + #endif + + this->d_state = gr::lora::DecoderState::DECODE_PAYLOAD; + } + + this->msg_raw_chirp_debug(raw_input, this->d_samples_per_symbol); + //samples_debug(input, d_samples_per_symbol); + this->consume_each(this->d_samples_per_symbol); + break; + } + + case gr::lora::DecoderState::DECODE_PAYLOAD: { + if (this->demodulate(input, false)) { + this->d_payload_symbols -= (4 + this->d_cr); + + if (this->d_payload_symbols <= 0) { + uint8_t decoded[this->d_payload_length] = { 0 }; + + this->decode(decoded, false); + + this->d_state = gr::lora::DecoderState::DETECT; + this->d_data.clear(); + } + } + + this->msg_raw_chirp_debug(raw_input, this->d_samples_per_symbol); + //samples_debug(input, d_samples_per_symbol); + this->consume_each(this->d_samples_per_symbol); + break; + } + + case gr::lora::DecoderState::STOP: { + this->consume_each(this->d_samples_per_symbol); + break; + } + + default: { + std::cerr << "LoRa Decoder: No state! Shouldn't happen\n"; + break; + } + } + + // Tell runtime system how many output items we produced. + return 0; + } + + void decoder_impl::set_sf(uint8_t sf) { + (void) sf; + std::cerr << "[LoRa Decoder] WARNING : Setting the spreading factor during execution is currently not supported." << std::endl + << "Nothing set, kept SF of " << this->d_sf << "." << std::endl; + } + + void decoder_impl::set_samp_rate(float samp_rate) { + (void) samp_rate; + std::cerr << "[LoRa Decoder] WARNING : Setting the sample rate during execution is currently not supported." << std::endl + << "Nothing set, kept SR of " << this->d_samples_per_second << "." << std::endl; + } + + } /* namespace lora */ } /* namespace gr */ diff --git a/lib/decoder_impl.h b/lib/decoder_impl.h index 8297205..f9be2c8 100644 --- a/lib/decoder_impl.h +++ b/lib/decoder_impl.h @@ -21,112 +21,124 @@ #ifndef INCLUDED_LORA_DECODER_IMPL_H #define INCLUDED_LORA_DECODER_IMPL_H -#include +#include +#include "lora/decoder.h" #include #include #include +#include #include -#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 lora { + namespace lora { - typedef enum decoder_state { - DETECT, - SYNC, - PAUSE, - DECODE_HEADER, - DECODE_PAYLOAD, - STOP - } decoder_state; + enum class DecoderState { + DETECT, + SYNC, + PAUSE, + DECODE_HEADER, + DECODE_PAYLOAD, + STOP + }; - class decoder_impl : public decoder - { - private: - decoder_state d_state; - std::vector d_downchirp; - std::vector d_upchirp; - std::vector d_downchirp_ifreq; - std::vector d_upchirp_ifreq; - std::vector d_fft; - std::vector d_mult; - uint8_t d_sf; - uint32_t d_bw; - uint8_t d_cr; - double d_bits_per_second; - uint32_t d_delay_after_sync; - uint32_t d_samples_per_second; - double d_symbols_per_second; - uint32_t d_bits_per_symbol; - uint32_t d_samples_per_symbol; - uint32_t d_number_of_bins; - uint32_t d_number_of_bins_hdr; - uint32_t d_compression; - uint32_t d_payload_symbols; - uint32_t d_payload_length; - uint32_t d_corr_fails; - std::vector d_words; - std::vector d_demodulated; - std::vector d_words_deshuffled; - std::vector d_words_dewhitened; - std::vector d_data; - std::ofstream d_debug_samples; - std::ofstream d_debug; - fftplan d_q; - float d_decim_h[DECIMATOR_FILTER_SIZE]; - uint32_t d_corr_decim_factor; - int d_decim_factor; - firdecim_crcf d_decim; - float d_cfo_estimation; - int d_cfo_step; - double d_dt; + class decoder_impl : public decoder { + private: + DecoderState d_state; - bool calc_energy_threshold(gr_complex* samples, int window_size, float threshold); - void build_ideal_chirps(void); - void samples_to_file(const std::string path, const gr_complex* v, int length, int elem_size); - void samples_debug(const gr_complex* v, int length); - 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 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 cross_correlate(const gr_complex *samples_1, const gr_complex *samples_2, int window); - unsigned int get_shift_fft(gr_complex* samples); - void determine_cfo(const gr_complex* samples); - void correct_cfo(gr_complex* samples, int num_samples); - int find_preamble_start(gr_complex* samples); - int find_preamble_start_fast(gr_complex* samples, uint32_t len); - unsigned int max_frequency_gradient_idx(gr_complex* samples); - bool demodulate(gr_complex* samples, bool is_header); - void deinterleave(int ppm); - int decode(uint8_t* out_data, bool is_header); - void deshuffle(const uint8_t* shuffle_pattern, bool is_header); - void dewhiten(const uint8_t* prng); - void hamming_decode(uint8_t* out_data); - void nibble_reverse(uint8_t* out_data, int len); - float stddev(const float *values, int len, float mean); - 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); - uint8_t lookup_cr(uint8_t bytevalue); - 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); + /// using std::complex = gr_complex + std::vector d_downchirp; + std::vector d_upchirp; + std::vector d_downchirp_ifreq; + std::vector d_upchirp_ifreq; + std::vector d_fft; + std::vector d_mult; + uint8_t d_sf; + uint32_t d_bw; + uint8_t d_cr; + double d_bits_per_second; + uint32_t d_delay_after_sync; + uint32_t d_samples_per_second; + double d_symbols_per_second; + uint32_t d_bits_per_symbol; + uint32_t d_samples_per_symbol; + uint32_t d_number_of_bins; + uint32_t d_number_of_bins_hdr; + uint32_t d_compression; + uint32_t d_payload_symbols; + uint32_t d_payload_length; + uint32_t d_corr_fails; - public: - decoder_impl(float samp_rate, int sf); - ~decoder_impl(); + std::vector d_words; + std::vector d_demodulated; + std::vector d_words_deshuffled; + std::vector d_words_dewhitened; + std::vector d_data; - // Where all the action really happens - int work(int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); + std::ofstream d_debug_samples; + std::ofstream d_debug; - // GRC interfaces - virtual void set_sf(uint8_t sf); - virtual void set_samp_rate(float samp_rate); - }; + fftplan d_q; - } // namespace lora + float d_decim_h[DECIMATOR_FILTER_SIZE]; + uint32_t d_corr_decim_factor; + int d_decim_factor; + firdecim_crcf d_decim = nullptr; + float d_cfo_estimation; + int d_cfo_step; + double d_dt; + + bool calc_energy_threshold(gr_complex *samples, int window_size, float threshold); + void build_ideal_chirps(void); + 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, 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 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_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); + + unsigned int get_shift_fft(gr_complex *samples); + + void determine_cfo(const gr_complex *samples); + void correct_cfo(gr_complex *samples, uint32_t num_samples); + int find_preamble_start(gr_complex *samples); + int find_preamble_start_fast(gr_complex *samples, uint32_t len); + + unsigned int max_frequency_gradient_idx(gr_complex *samples); + + bool demodulate(gr_complex *samples, bool is_header); + void deinterleave(uint32_t ppm); + int decode(uint8_t *out_data, bool is_header); + void deshuffle(const uint8_t *shuffle_pattern, bool is_header); + void dewhiten(const uint8_t *prng); + void hamming_decode(uint8_t *out_data); + void nibble_reverse(uint8_t *out_data, int len); + 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); + void instantaneous_frequency(const gr_complex *in_samples, float *out_ifreq, uint32_t window); + + uint8_t lookup_cr(uint8_t bytevalue); + 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); + + public: + decoder_impl(float samp_rate, uint8_t sf); + ~decoder_impl(); + + /// Where all the action really happens + int work(int noutput_items, + gr_vector_const_void_star& input_items, + gr_vector_void_star& output_items); + + /// GRC interfaces + virtual void set_sf(uint8_t sf); + virtual void set_samp_rate(float samp_rate); + }; + } // namespace lora } // namespace gr #endif /* INCLUDED_LORA_DECODER_IMPL_H */ diff --git a/lib/tables.h b/lib/tables.h index 5648ea4..8f2a6ff 100644 --- a/lib/tables.h +++ b/lib/tables.h @@ -2,35 +2,35 @@ #define TABLES_H namespace gr { - namespace lora { - const uint8_t prng_header[] = { - 0x22, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; + namespace lora { + const uint8_t prng_header[] = { + 0x22, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; - const uint8_t prng_payload_sf7[] = { // OK - 0xdc, 0xec, 0xb0, 0xf4, 0x9c, 0xfc, 0xc4, 0xdc, 0x10, 0xf8, 0x40, 0x34, 0xa8, 0x5c, 0xf0, 0x94, 0x60, 0x8, 0xf8, 0x48, 0xbc, 0x88, 0xa4, 0xd4, 0x14, 0xe4, 0x84, 0x38, 0x68, 0xec, 0xe4, 0xbc, 0xb0, 0x1c, 0x14, 0xa4, 0x3c, 0x4c, 0x90, 0x60, 0x84, 0x70, 0x20, 0x44, 0x4, 0x24, 0x80, 0x98, 0x40, 0xa4, 0x58, 0x4, 0xa0, 0x80, 0x98, 0x40, 0xa4, 0x10, 0x4c, 0x40, 0x60, 0xa8, 0x38, 0xb8, 0xa4, 0x80, 0x14, 0xc8, 0x84, 0xa0, 0x68, 0x68, 0xac, 0xbc, 0x18, 0x1c, 0x8c, 0xa4, 0xb8, 0x4c, 0xd8, 0x28, 0x64, 0xd8, 0x58, 0xdc, 0xb0, 0xa0, 0x9c, 0xd0, 0xc4, 0x44, 0x10, 0x7c, 0x8, 0xb4, 0x0, 0x5c, 0x68, 0x94, 0xe4, 0x8, 0xb0, 0x0, 0x5c, 0x20, 0xdc, 0x4c, 0xa0, 0x60, 0x98, 0x70, 0xec, 0xc, 0xac, 0xc4, 0x18, 0xa8, 0x8c, 0xb8, 0xf0, 0xc8, 0x38, 0x28, 0x54, 0xd8, 0x44, 0xdc, 0x7c, 0xe8, 0x34, 0x30, 0x5c, 0x74, 0xdc, 0x60, 0xa0, 0xf8, 0x98, 0xf4, 0xa4, 0x1c, 0x4, 0xc4, 0x80, 0xa8, 0x8, 0xb8, 0xb8, 0x80, 0xd8, 0x80, 0x2c, 0x40, 0xf0, 0x58, 0x60, 0xa0, 0xb0, 0xd0, 0x14, 0xc, 0x74, 0xd4, 0x38, 0xe4, 0x54, 0x70, 0xc, 0x44, 0xd4, 0x6c, 0xe4, 0x30, 0x70, 0x74, 0xc, 0x28, 0xc4, 0x50, 0xa8, 0x24, 0xf0, 0x20, 0x60, 0x14, 0xb0, 0xcc, 0x5c, 0x88, 0x94, 0xd4, 0x8, 0xe4, 0x48, 0x70, 0xc0, 0xc, 0x7c, 0x8c, 0x7c, 0x0, 0xbc, 0x68, 0xa4, 0xe4, 0x5c, 0xf8, 0x64, 0xbc, 0x10, 0xec, 0x50, 0xbc, 0xe4, 0x54, 0x38, 0x44, 0xec, 0x34, 0xbc, 0xd4, 0x54, 0x24, 0xc, 0x68, 0xd4, 0xf4, 0xe4, 0xb4, 0x38, 0x74, 0xa4, 0x28, 0x5c, 0x50, 0x2c, 0x24, 0xb8, 0x68, 0x80, 0xbc, 0xc8, 0x54, 0xe8, 0xc, 0x88, 0x9c, 0x8c, 0x4, 0x0, 0x8, 0x68, 0xb8, 0xe4, 0x90, 0xb0, 0x84, 0x14, 0x20, 0x74, 0x4c, 0x38, 0x60, 0x1c, 0x70, 0xa4, 0x44, 0x4c, 0x6c, 0x60, 0x30, 0x38, 0x3c, 0xec, 0x80, 0xbc, 0xc8, 0x1c, 0xa0, 0xec, 0x68, 0xe4, 0xf4, 0xf8, 0xb4, 0xf4, 0x3c, 0xc, 0xc8, 0xc4, 0x60, 0xe0, 0x38, 0x10, 0xa4, 0x18, 0x14, 0x4, 0xcc, 0x8, 0xc0, 0xb8, 0x34, 0xd8, 0x9c, 0x64, 0xc4, 0x10, 0x58, 0x50, 0xa0, 0xac, 0xd0, 0xd8, 0x44, 0xdc, 0x34, 0xa0, 0xd4, 0x98, 0x6c, 0xa4, 0x88, 0x4c, 0xc4, 0x28, 0xa8, 0x90, 0xb8, 0x3c, 0x80, 0x90, 0xc8, 0xcc, 0xe8, 0x88, 0xc0, 0xd4, 0x24, 0xac, 0x98, 0x90 - }; + const uint8_t prng_payload_sf7[] = { // OK + 0xdc, 0xec, 0xb0, 0xf4, 0x9c, 0xfc, 0xc4, 0xdc, 0x10, 0xf8, 0x40, 0x34, 0xa8, 0x5c, 0xf0, 0x94, 0x60, 0x8, 0xf8, 0x48, 0xbc, 0x88, 0xa4, 0xd4, 0x14, 0xe4, 0x84, 0x38, 0x68, 0xec, 0xe4, 0xbc, 0xb0, 0x1c, 0x14, 0xa4, 0x3c, 0x4c, 0x90, 0x60, 0x84, 0x70, 0x20, 0x44, 0x4, 0x24, 0x80, 0x98, 0x40, 0xa4, 0x58, 0x4, 0xa0, 0x80, 0x98, 0x40, 0xa4, 0x10, 0x4c, 0x40, 0x60, 0xa8, 0x38, 0xb8, 0xa4, 0x80, 0x14, 0xc8, 0x84, 0xa0, 0x68, 0x68, 0xac, 0xbc, 0x18, 0x1c, 0x8c, 0xa4, 0xb8, 0x4c, 0xd8, 0x28, 0x64, 0xd8, 0x58, 0xdc, 0xb0, 0xa0, 0x9c, 0xd0, 0xc4, 0x44, 0x10, 0x7c, 0x8, 0xb4, 0x0, 0x5c, 0x68, 0x94, 0xe4, 0x8, 0xb0, 0x0, 0x5c, 0x20, 0xdc, 0x4c, 0xa0, 0x60, 0x98, 0x70, 0xec, 0xc, 0xac, 0xc4, 0x18, 0xa8, 0x8c, 0xb8, 0xf0, 0xc8, 0x38, 0x28, 0x54, 0xd8, 0x44, 0xdc, 0x7c, 0xe8, 0x34, 0x30, 0x5c, 0x74, 0xdc, 0x60, 0xa0, 0xf8, 0x98, 0xf4, 0xa4, 0x1c, 0x4, 0xc4, 0x80, 0xa8, 0x8, 0xb8, 0xb8, 0x80, 0xd8, 0x80, 0x2c, 0x40, 0xf0, 0x58, 0x60, 0xa0, 0xb0, 0xd0, 0x14, 0xc, 0x74, 0xd4, 0x38, 0xe4, 0x54, 0x70, 0xc, 0x44, 0xd4, 0x6c, 0xe4, 0x30, 0x70, 0x74, 0xc, 0x28, 0xc4, 0x50, 0xa8, 0x24, 0xf0, 0x20, 0x60, 0x14, 0xb0, 0xcc, 0x5c, 0x88, 0x94, 0xd4, 0x8, 0xe4, 0x48, 0x70, 0xc0, 0xc, 0x7c, 0x8c, 0x7c, 0x0, 0xbc, 0x68, 0xa4, 0xe4, 0x5c, 0xf8, 0x64, 0xbc, 0x10, 0xec, 0x50, 0xbc, 0xe4, 0x54, 0x38, 0x44, 0xec, 0x34, 0xbc, 0xd4, 0x54, 0x24, 0xc, 0x68, 0xd4, 0xf4, 0xe4, 0xb4, 0x38, 0x74, 0xa4, 0x28, 0x5c, 0x50, 0x2c, 0x24, 0xb8, 0x68, 0x80, 0xbc, 0xc8, 0x54, 0xe8, 0xc, 0x88, 0x9c, 0x8c, 0x4, 0x0, 0x8, 0x68, 0xb8, 0xe4, 0x90, 0xb0, 0x84, 0x14, 0x20, 0x74, 0x4c, 0x38, 0x60, 0x1c, 0x70, 0xa4, 0x44, 0x4c, 0x6c, 0x60, 0x30, 0x38, 0x3c, 0xec, 0x80, 0xbc, 0xc8, 0x1c, 0xa0, 0xec, 0x68, 0xe4, 0xf4, 0xf8, 0xb4, 0xf4, 0x3c, 0xc, 0xc8, 0xc4, 0x60, 0xe0, 0x38, 0x10, 0xa4, 0x18, 0x14, 0x4, 0xcc, 0x8, 0xc0, 0xb8, 0x34, 0xd8, 0x9c, 0x64, 0xc4, 0x10, 0x58, 0x50, 0xa0, 0xac, 0xd0, 0xd8, 0x44, 0xdc, 0x34, 0xa0, 0xd4, 0x98, 0x6c, 0xa4, 0x88, 0x4c, 0xc4, 0x28, 0xa8, 0x90, 0xb8, 0x3c, 0x80, 0x90, 0xc8, 0xcc, 0xe8, 0x88, 0xc0, 0xd4, 0x24, 0xac, 0x98, 0x90 + }; - const uint8_t prng_payload_sf8[] = { - 0xbd, 0xdf, 0xa4, 0xfb, 0x16, 0x7f, 0x85, 0xfe, 0x40, 0xdf, 0x5b, 0xb0, 0xa2, 0x9e, 0xd0, 0x86, 0x26, 0x20, 0x68, 0x4f, 0xf7, 0x2a, 0xb6, 0xd3, 0x5e, 0x46, 0x97, 0x7c, 0x43, 0x7f, 0xe3, 0xb5, 0x73, 0x3e, 0x45, 0x83, 0x25, 0xcb, 0x9b, 0xe0, 0xc7, 0x13, 0x10, 0x51, 0x8, 0xad, 0x2, 0x98, 0x40, 0xa7, 0x5b, 0x4, 0xa2, 0x80, 0x9b, 0x1, 0xc7, 0x20, 0x5b, 0x4f, 0xe9, 0x2a, 0x7a, 0x98, 0xf4, 0xa7, 0xe, 0x4f, 0x8f, 0x61, 0x49, 0x79, 0xea, 0x94, 0x89, 0x1a, 0xc4, 0x7, 0xa8, 0x4a, 0x92, 0x8a, 0x76, 0x9d, 0x70, 0x4c, 0xb6, 0xab, 0x5e, 0xf2, 0x97, 0x62, 0x8, 0xf8, 0x2, 0xb5, 0x40, 0x3e, 0x5b, 0x83, 0xe9, 0x80, 0x31, 0x1, 0x5e, 0x20, 0xdc, 0x4f, 0xa2, 0x61, 0x9b, 0x32, 0x8c, 0x3e, 0x9a, 0xd8, 0x95, 0x22, 0x4e, 0x9a, 0xa0, 0xac, 0x23, 0x8e, 0x5d, 0x5, 0x64, 0xcf, 0xb9, 0xc1, 0xc4, 0x37, 0x2, 0xdd, 0xce, 0xe7, 0xe9, 0x58, 0xc9, 0xb1, 0xbf, 0x98, 0xa, 0xcd, 0xc0, 0x8b, 0x7b, 0xdd, 0xb2, 0xf, 0x54, 0x3, 0x6d, 0x60, 0xe2, 0x4f, 0x6e, 0x2e, 0xb9, 0xc3, 0x16, 0x5, 0x76, 0xb6, 0x2f, 0xe6, 0xd7, 0x30, 0x6c, 0x37, 0xc3, 0x62, 0x4a, 0xbb, 0xa1, 0x54, 0x5e, 0x4d, 0xfd, 0xc6, 0xa2, 0xee, 0xd4, 0xb2, 0x24, 0x3e, 0x23, 0xe9, 0x56, 0x3e, 0x8d, 0x53, 0x42, 0x6, 0x7b, 0x37, 0xf9, 0x9e, 0xf6, 0x86, 0xbc, 0x20, 0xcf, 0xe, 0xbc, 0x7d, 0xd7, 0x7b, 0x26, 0xdf, 0x61, 0xcb, 0x5d, 0x38, 0xe4, 0x54, 0x39, 0xe, 0xef, 0x87, 0xb8, 0xd5, 0x57, 0x67, 0x6e, 0xd8, 0x82, 0xeb, 0x4b, 0x3c, 0x7a, 0x56, 0xf4, 0xd, 0x45, 0x96, 0x31, 0xc6, 0x1b, 0x73, 0xc6, 0x95, 0x7b, 0x11, 0xb2, 0xa5, 0x1f, 0x9a, 0xc7, 0xe6, 0x30, 0x4f, 0x47, 0x2a, 0xeb, 0x1a, 0x63, 0xa7, 0x65, 0x54, 0x6e, 0xcf, 0x39, 0xe2, 0xdf, 0x5b, 0xb6, 0x51, 0x57, 0xe6, 0xe3, 0xb0, 0x38, 0x3e, 0xcf, 0xc2, 0xbc, 0xd7, 0x9c, 0xe2, 0x8c, 0x58, 0xb1, 0xca, 0x74, 0x3c, 0xb6, 0x9d, 0x5e, 0xae, 0xfc, 0xe5, 0xe9, 0xf8, 0xa9, 0xb5, 0x5b, 0x3e, 0xb7, 0xd9, 0x7, 0x61, 0x28, 0x3a, 0x90, 0x7e, 0x76, 0xb3, 0x3f, 0xcb, 0x5f, 0xab, 0xee, 0xf3, 0xeb, 0x72, 0xc0, 0xb3, 0x28, 0x5c, 0xd9, 0x4d, 0xb6, 0x9d, 0x57, 0x4c, 0xa0, 0x29, 0x92, 0xb9, 0x1d, 0x83, 0x95, 0xd3, 0x8e, 0x2b, 0xe9, 0xf3, 0xa2, 0x29, 0x25, 0x15, 0x40, 0xcf, 0x6d, 0xca, 0x89, 0xd6, 0xd4, 0xa1, 0xc3, 0x51, 0x39, 0x4d, 0x85, 0x8d, 0x49, 0x44, 0xe8, 0xe2, 0xbb, 0x1a, 0x84, 0x0, 0x49, 0x98, 0xec, 0x65, 0x72, 0x10, 0x6e, 0x80, 0x40, 0xdd, 0x76, 0x65, 0x3b, 0x51, 0x8e, 0x2, 0xb8, 0x70, 0xd7, 0xe2, 0xa7, 0x9a, 0x48, 0xab, 0xc2, 0x38, 0x7d, 0x5e, 0xbe, 0x6, 0x86, 0x4c, 0x9, 0x72, 0xaf, 0xf4, 0x58, 0x9e, 0x3c, 0x80, 0xed, 0xdd, 0xa5, 0x2e, 0x55, 0xf8, 0xbc, 0xd8, 0xed, 0x4d, 0xb9, 0xa0, 0x16, 0xfe, 0x5f, 0xf9, 0xde, 0x79, 0xa7, 0x34, 0xd8, 0x37, 0xf6, 0xb9, 0x77, 0x5a, 0x6b, 0x2e, 0x76, 0xf8, 0x36, 0xb5, 0x8b, 0xc3, 0xc2, 0xe2, 0xc1, 0x53, 0x38, 0xa, 0xfd, 0x2b, 0xf2, 0x83, 0xf5, 0x64, 0x25, 0x5e, 0x33, 0x49, 0x50, 0xfb, 0x64, 0xd5, 0xa2, 0x41, 0x21, 0x85, 0x5c, 0x41, 0x25, 0x4d, 0x9, 0xe6, 0xe3, 0xa5, 0x79, 0xd9, 0x77, 0x6, 0x6e, 0x4c, 0xc0, 0x77, 0x35, 0xfc, 0x11, 0xd5, 0x5d, 0xa, 0x97, 0x50, 0x5, 0xa9, 0xe9, 0xb3, 0x7b, 0x79, 0x96, 0x15, 0x13, 0x4, 0x8f, 0x62, 0x91, 0x5e, 0x74, 0x12, 0x75, 0x9e, 0x3e, 0xad, 0x4b, 0x97, 0xe3, 0xf0, 0x8a, 0x3, 0x41, 0x60, 0xd1, 0xd2, 0x4b, 0xbb, 0xda, 0x3f, 0x3d - }; + const uint8_t prng_payload_sf8[] = { + 0xbd, 0xdf, 0xa4, 0xfb, 0x16, 0x7f, 0x85, 0xfe, 0x40, 0xdf, 0x5b, 0xb0, 0xa2, 0x9e, 0xd0, 0x86, 0x26, 0x20, 0x68, 0x4f, 0xf7, 0x2a, 0xb6, 0xd3, 0x5e, 0x46, 0x97, 0x7c, 0x43, 0x7f, 0xe3, 0xb5, 0x73, 0x3e, 0x45, 0x83, 0x25, 0xcb, 0x9b, 0xe0, 0xc7, 0x13, 0x10, 0x51, 0x8, 0xad, 0x2, 0x98, 0x40, 0xa7, 0x5b, 0x4, 0xa2, 0x80, 0x9b, 0x1, 0xc7, 0x20, 0x5b, 0x4f, 0xe9, 0x2a, 0x7a, 0x98, 0xf4, 0xa7, 0xe, 0x4f, 0x8f, 0x61, 0x49, 0x79, 0xea, 0x94, 0x89, 0x1a, 0xc4, 0x7, 0xa8, 0x4a, 0x92, 0x8a, 0x76, 0x9d, 0x70, 0x4c, 0xb6, 0xab, 0x5e, 0xf2, 0x97, 0x62, 0x8, 0xf8, 0x2, 0xb5, 0x40, 0x3e, 0x5b, 0x83, 0xe9, 0x80, 0x31, 0x1, 0x5e, 0x20, 0xdc, 0x4f, 0xa2, 0x61, 0x9b, 0x32, 0x8c, 0x3e, 0x9a, 0xd8, 0x95, 0x22, 0x4e, 0x9a, 0xa0, 0xac, 0x23, 0x8e, 0x5d, 0x5, 0x64, 0xcf, 0xb9, 0xc1, 0xc4, 0x37, 0x2, 0xdd, 0xce, 0xe7, 0xe9, 0x58, 0xc9, 0xb1, 0xbf, 0x98, 0xa, 0xcd, 0xc0, 0x8b, 0x7b, 0xdd, 0xb2, 0xf, 0x54, 0x3, 0x6d, 0x60, 0xe2, 0x4f, 0x6e, 0x2e, 0xb9, 0xc3, 0x16, 0x5, 0x76, 0xb6, 0x2f, 0xe6, 0xd7, 0x30, 0x6c, 0x37, 0xc3, 0x62, 0x4a, 0xbb, 0xa1, 0x54, 0x5e, 0x4d, 0xfd, 0xc6, 0xa2, 0xee, 0xd4, 0xb2, 0x24, 0x3e, 0x23, 0xe9, 0x56, 0x3e, 0x8d, 0x53, 0x42, 0x6, 0x7b, 0x37, 0xf9, 0x9e, 0xf6, 0x86, 0xbc, 0x20, 0xcf, 0xe, 0xbc, 0x7d, 0xd7, 0x7b, 0x26, 0xdf, 0x61, 0xcb, 0x5d, 0x38, 0xe4, 0x54, 0x39, 0xe, 0xef, 0x87, 0xb8, 0xd5, 0x57, 0x67, 0x6e, 0xd8, 0x82, 0xeb, 0x4b, 0x3c, 0x7a, 0x56, 0xf4, 0xd, 0x45, 0x96, 0x31, 0xc6, 0x1b, 0x73, 0xc6, 0x95, 0x7b, 0x11, 0xb2, 0xa5, 0x1f, 0x9a, 0xc7, 0xe6, 0x30, 0x4f, 0x47, 0x2a, 0xeb, 0x1a, 0x63, 0xa7, 0x65, 0x54, 0x6e, 0xcf, 0x39, 0xe2, 0xdf, 0x5b, 0xb6, 0x51, 0x57, 0xe6, 0xe3, 0xb0, 0x38, 0x3e, 0xcf, 0xc2, 0xbc, 0xd7, 0x9c, 0xe2, 0x8c, 0x58, 0xb1, 0xca, 0x74, 0x3c, 0xb6, 0x9d, 0x5e, 0xae, 0xfc, 0xe5, 0xe9, 0xf8, 0xa9, 0xb5, 0x5b, 0x3e, 0xb7, 0xd9, 0x7, 0x61, 0x28, 0x3a, 0x90, 0x7e, 0x76, 0xb3, 0x3f, 0xcb, 0x5f, 0xab, 0xee, 0xf3, 0xeb, 0x72, 0xc0, 0xb3, 0x28, 0x5c, 0xd9, 0x4d, 0xb6, 0x9d, 0x57, 0x4c, 0xa0, 0x29, 0x92, 0xb9, 0x1d, 0x83, 0x95, 0xd3, 0x8e, 0x2b, 0xe9, 0xf3, 0xa2, 0x29, 0x25, 0x15, 0x40, 0xcf, 0x6d, 0xca, 0x89, 0xd6, 0xd4, 0xa1, 0xc3, 0x51, 0x39, 0x4d, 0x85, 0x8d, 0x49, 0x44, 0xe8, 0xe2, 0xbb, 0x1a, 0x84, 0x0, 0x49, 0x98, 0xec, 0x65, 0x72, 0x10, 0x6e, 0x80, 0x40, 0xdd, 0x76, 0x65, 0x3b, 0x51, 0x8e, 0x2, 0xb8, 0x70, 0xd7, 0xe2, 0xa7, 0x9a, 0x48, 0xab, 0xc2, 0x38, 0x7d, 0x5e, 0xbe, 0x6, 0x86, 0x4c, 0x9, 0x72, 0xaf, 0xf4, 0x58, 0x9e, 0x3c, 0x80, 0xed, 0xdd, 0xa5, 0x2e, 0x55, 0xf8, 0xbc, 0xd8, 0xed, 0x4d, 0xb9, 0xa0, 0x16, 0xfe, 0x5f, 0xf9, 0xde, 0x79, 0xa7, 0x34, 0xd8, 0x37, 0xf6, 0xb9, 0x77, 0x5a, 0x6b, 0x2e, 0x76, 0xf8, 0x36, 0xb5, 0x8b, 0xc3, 0xc2, 0xe2, 0xc1, 0x53, 0x38, 0xa, 0xfd, 0x2b, 0xf2, 0x83, 0xf5, 0x64, 0x25, 0x5e, 0x33, 0x49, 0x50, 0xfb, 0x64, 0xd5, 0xa2, 0x41, 0x21, 0x85, 0x5c, 0x41, 0x25, 0x4d, 0x9, 0xe6, 0xe3, 0xa5, 0x79, 0xd9, 0x77, 0x6, 0x6e, 0x4c, 0xc0, 0x77, 0x35, 0xfc, 0x11, 0xd5, 0x5d, 0xa, 0x97, 0x50, 0x5, 0xa9, 0xe9, 0xb3, 0x7b, 0x79, 0x96, 0x15, 0x13, 0x4, 0x8f, 0x62, 0x91, 0x5e, 0x74, 0x12, 0x75, 0x9e, 0x3e, 0xad, 0x4b, 0x97, 0xe3, 0xf0, 0x8a, 0x3, 0x41, 0x60, 0xd1, 0xd2, 0x4b, 0xbb, 0xda, 0x3f, 0x3d + }; - const uint8_t prng_payload_sf9[] = { - 0xfd, 0xbe, 0x94, 0xef, 0x1a, 0xf7, 0x7, 0xfd, 0x1, 0xff, 0xb, 0x94, 0xba, 0x1a, 0xda, 0x7, 0x64, 0x1, 0x78, 0xb, 0xdf, 0xba, 0xb0, 0xda, 0x9e, 0x64, 0x86, 0x78, 0xb, 0xdf, 0xf1, 0xb0, 0x3b, 0x9e, 0x57, 0x86, 0x2d, 0xb, 0xb9, 0xf1, 0x83, 0x3b, 0x80, 0x57, 0x1, 0x2d, 0x40, 0xb9, 0x10, 0x83, 0x47, 0x80, 0xa8, 0x3, 0x99, 0x41, 0xa3, 0x12, 0x5f, 0x3, 0x69, 0x8, 0x79, 0x98, 0xf4, 0xa7, 0xe, 0x5f, 0xcf, 0x6d, 0xe9, 0x79, 0xab, 0xf6, 0xb9, 0x2e, 0xd8, 0xcf, 0x2e, 0x41, 0xd1, 0x2b, 0x26, 0xb9, 0x68, 0xb8, 0xb8, 0x22, 0x1c, 0xd1, 0x7, 0x27, 0x60, 0x48, 0x14, 0xb8, 0x88, 0x9c, 0x48, 0x86, 0xa1, 0x60, 0x3, 0x4, 0x16, 0x94, 0x4e, 0x42, 0xa9, 0xa1, 0xf9, 0x3, 0xc8, 0x16, 0x3e, 0x46, 0x98, 0xab, 0x8e, 0xf9, 0xf1, 0xe8, 0x3b, 0x32, 0x57, 0x9e, 0xe7, 0x8c, 0x18, 0xf1, 0x80, 0x3f, 0x9e, 0x5f, 0xcd, 0xe7, 0xe8, 0x58, 0xe9, 0xb1, 0x9f, 0x92, 0x82, 0xcf, 0x0, 0xea, 0x2b, 0x88, 0xbe, 0xaf, 0x56, 0x82, 0x2e, 0x8, 0x92, 0x6b, 0x17, 0xae, 0x3b, 0x4a, 0xd7, 0x2a, 0x66, 0x92, 0x13, 0x76, 0x21, 0x3b, 0xa5, 0xd3, 0xd3, 0x65, 0x26, 0x13, 0x2, 0x41, 0x52, 0xad, 0x46, 0xd3, 0xa8, 0x26, 0xb2, 0x23, 0x52, 0x12, 0xb3, 0xce, 0x97, 0xa9, 0xc6, 0x33, 0x30, 0x72, 0x27, 0xbf, 0x7b, 0x1d, 0x7b, 0xc7, 0x9d, 0x30, 0xeb, 0x27, 0x38, 0x73, 0xd6, 0x79, 0x2f, 0x9f, 0x3, 0xeb, 0x39, 0x2c, 0xe8, 0xd6, 0x7b, 0x27, 0x9d, 0x43, 0x80, 0x59, 0xc9, 0xec, 0xae, 0x79, 0x93, 0x9f, 0x16, 0x80, 0x74, 0xc9, 0x3e, 0xae, 0x56, 0x93, 0x4d, 0x57, 0xcd, 0x64, 0x2b, 0x3e, 0xc8, 0x56, 0xea, 0x6d, 0xa8, 0x8d, 0x9b, 0xf, 0x86, 0xc8, 0x2, 0xe8, 0x80, 0xe9, 0x27, 0xbb, 0xdd, 0x86, 0xe5, 0x4a, 0x79, 0x80, 0x2e, 0x7, 0x9b, 0xd9, 0x89, 0xed, 0xe2, 0x73, 0x18, 0x2e, 0xe9, 0xeb, 0x38, 0xd9, 0x95, 0xe0, 0x8d, 0x98, 0xd1, 0x8a, 0x74, 0x2c, 0x32, 0x1d, 0x17, 0x8e, 0x2d, 0x91, 0xf5, 0x44, 0xbf, 0x36, 0x1d, 0x15, 0xc7, 0x2d, 0x7b, 0xb5, 0x96, 0xbb, 0x50, 0x1d, 0x6b, 0xc5, 0x53, 0x3b, 0x41, 0xb6, 0x85, 0x54, 0x1a, 0x6d, 0x8d, 0x53, 0xdb, 0x41, 0x85, 0xa5, 0x29, 0x9a, 0xca, 0x8e, 0x8a, 0xfa, 0xd6, 0x91, 0xb8, 0xa5, 0x98, 0xc1, 0x8c, 0xca, 0xda, 0xe6, 0xaa, 0xa1, 0x27, 0x18, 0xc8, 0x8d, 0x5, 0xfb, 0x8d, 0xca, 0x40, 0xa7, 0x40, 0x51, 0x29, 0x4d, 0xc5, 0xbd, 0x7d, 0x1c, 0xe0, 0x60, 0xfb, 0x31, 0x94, 0x44, 0x71, 0x7d, 0xfe, 0xe8, 0xb0, 0x7a, 0x55, 0xd7, 0xbc, 0x21, 0xfd, 0xda, 0xf6, 0xb8, 0xfc, 0xdf, 0xd4, 0x9c, 0x21, 0xfc, 0xf2, 0x76, 0xf9, 0xfc, 0xbe, 0x17, 0xaf, 0x41, 0xe3, 0xca, 0xbe, 0x6f, 0x5f, 0xbf, 0xc, 0x87, 0x2d, 0xc3, 0x48, 0x36, 0x74, 0x57, 0x73, 0x4d, 0x43, 0xad, 0xd0, 0x18, 0xe3, 0xe0, 0xb0, 0x72, 0x35, 0x6f, 0x49, 0xf4, 0x52, 0x6b, 0xac, 0xb4, 0xd8, 0x35, 0xf6, 0x59, 0x77, 0x5e, 0x7b, 0xae, 0xbc, 0xda, 0x3e, 0xf7, 0x32, 0x53, 0x84, 0xeb, 0x23, 0x30, 0x18, 0x7e, 0xb1, 0x52, 0x64, 0xb4, 0xbe, 0x5, 0x56, 0xa, 0xc, 0x91, 0x76, 0x50, 0xb1, 0x3e, 0x59, 0x57, 0x6e, 0xd, 0xa3, 0xb6, 0x7e, 0x89, 0x76, 0x4b, 0x7d, 0x6e, 0xde, 0x82, 0x90, 0x5e, 0xca, 0x72, 0x4d, 0x71, 0xe9, 0xdc, 0x82, 0xd0, 0x15, 0x9a, 0x83, 0x4d, 0x9, 0xeb, 0x20, 0x82, 0x6f, 0x74, 0x1e, 0x93, 0x18, 0x8d, 0xb, 0x20, 0xb1, 0x2e, 0x60, 0x1e, 0xf1, 0x10, 0xfd, 0xf, 0x5e, 0xb7, 0x9b, 0x40, 0x94, 0xa, 0x89, 0x2, 0xa0, 0xb7, 0x66, 0x43, 0x77, 0xb6, 0xc7, 0x3e, 0x3f, 0xdf - }; + const uint8_t prng_payload_sf9[] = { + 0xfd, 0xbe, 0x94, 0xef, 0x1a, 0xf7, 0x7, 0xfd, 0x1, 0xff, 0xb, 0x94, 0xba, 0x1a, 0xda, 0x7, 0x64, 0x1, 0x78, 0xb, 0xdf, 0xba, 0xb0, 0xda, 0x9e, 0x64, 0x86, 0x78, 0xb, 0xdf, 0xf1, 0xb0, 0x3b, 0x9e, 0x57, 0x86, 0x2d, 0xb, 0xb9, 0xf1, 0x83, 0x3b, 0x80, 0x57, 0x1, 0x2d, 0x40, 0xb9, 0x10, 0x83, 0x47, 0x80, 0xa8, 0x3, 0x99, 0x41, 0xa3, 0x12, 0x5f, 0x3, 0x69, 0x8, 0x79, 0x98, 0xf4, 0xa7, 0xe, 0x5f, 0xcf, 0x6d, 0xe9, 0x79, 0xab, 0xf6, 0xb9, 0x2e, 0xd8, 0xcf, 0x2e, 0x41, 0xd1, 0x2b, 0x26, 0xb9, 0x68, 0xb8, 0xb8, 0x22, 0x1c, 0xd1, 0x7, 0x27, 0x60, 0x48, 0x14, 0xb8, 0x88, 0x9c, 0x48, 0x86, 0xa1, 0x60, 0x3, 0x4, 0x16, 0x94, 0x4e, 0x42, 0xa9, 0xa1, 0xf9, 0x3, 0xc8, 0x16, 0x3e, 0x46, 0x98, 0xab, 0x8e, 0xf9, 0xf1, 0xe8, 0x3b, 0x32, 0x57, 0x9e, 0xe7, 0x8c, 0x18, 0xf1, 0x80, 0x3f, 0x9e, 0x5f, 0xcd, 0xe7, 0xe8, 0x58, 0xe9, 0xb1, 0x9f, 0x92, 0x82, 0xcf, 0x0, 0xea, 0x2b, 0x88, 0xbe, 0xaf, 0x56, 0x82, 0x2e, 0x8, 0x92, 0x6b, 0x17, 0xae, 0x3b, 0x4a, 0xd7, 0x2a, 0x66, 0x92, 0x13, 0x76, 0x21, 0x3b, 0xa5, 0xd3, 0xd3, 0x65, 0x26, 0x13, 0x2, 0x41, 0x52, 0xad, 0x46, 0xd3, 0xa8, 0x26, 0xb2, 0x23, 0x52, 0x12, 0xb3, 0xce, 0x97, 0xa9, 0xc6, 0x33, 0x30, 0x72, 0x27, 0xbf, 0x7b, 0x1d, 0x7b, 0xc7, 0x9d, 0x30, 0xeb, 0x27, 0x38, 0x73, 0xd6, 0x79, 0x2f, 0x9f, 0x3, 0xeb, 0x39, 0x2c, 0xe8, 0xd6, 0x7b, 0x27, 0x9d, 0x43, 0x80, 0x59, 0xc9, 0xec, 0xae, 0x79, 0x93, 0x9f, 0x16, 0x80, 0x74, 0xc9, 0x3e, 0xae, 0x56, 0x93, 0x4d, 0x57, 0xcd, 0x64, 0x2b, 0x3e, 0xc8, 0x56, 0xea, 0x6d, 0xa8, 0x8d, 0x9b, 0xf, 0x86, 0xc8, 0x2, 0xe8, 0x80, 0xe9, 0x27, 0xbb, 0xdd, 0x86, 0xe5, 0x4a, 0x79, 0x80, 0x2e, 0x7, 0x9b, 0xd9, 0x89, 0xed, 0xe2, 0x73, 0x18, 0x2e, 0xe9, 0xeb, 0x38, 0xd9, 0x95, 0xe0, 0x8d, 0x98, 0xd1, 0x8a, 0x74, 0x2c, 0x32, 0x1d, 0x17, 0x8e, 0x2d, 0x91, 0xf5, 0x44, 0xbf, 0x36, 0x1d, 0x15, 0xc7, 0x2d, 0x7b, 0xb5, 0x96, 0xbb, 0x50, 0x1d, 0x6b, 0xc5, 0x53, 0x3b, 0x41, 0xb6, 0x85, 0x54, 0x1a, 0x6d, 0x8d, 0x53, 0xdb, 0x41, 0x85, 0xa5, 0x29, 0x9a, 0xca, 0x8e, 0x8a, 0xfa, 0xd6, 0x91, 0xb8, 0xa5, 0x98, 0xc1, 0x8c, 0xca, 0xda, 0xe6, 0xaa, 0xa1, 0x27, 0x18, 0xc8, 0x8d, 0x5, 0xfb, 0x8d, 0xca, 0x40, 0xa7, 0x40, 0x51, 0x29, 0x4d, 0xc5, 0xbd, 0x7d, 0x1c, 0xe0, 0x60, 0xfb, 0x31, 0x94, 0x44, 0x71, 0x7d, 0xfe, 0xe8, 0xb0, 0x7a, 0x55, 0xd7, 0xbc, 0x21, 0xfd, 0xda, 0xf6, 0xb8, 0xfc, 0xdf, 0xd4, 0x9c, 0x21, 0xfc, 0xf2, 0x76, 0xf9, 0xfc, 0xbe, 0x17, 0xaf, 0x41, 0xe3, 0xca, 0xbe, 0x6f, 0x5f, 0xbf, 0xc, 0x87, 0x2d, 0xc3, 0x48, 0x36, 0x74, 0x57, 0x73, 0x4d, 0x43, 0xad, 0xd0, 0x18, 0xe3, 0xe0, 0xb0, 0x72, 0x35, 0x6f, 0x49, 0xf4, 0x52, 0x6b, 0xac, 0xb4, 0xd8, 0x35, 0xf6, 0x59, 0x77, 0x5e, 0x7b, 0xae, 0xbc, 0xda, 0x3e, 0xf7, 0x32, 0x53, 0x84, 0xeb, 0x23, 0x30, 0x18, 0x7e, 0xb1, 0x52, 0x64, 0xb4, 0xbe, 0x5, 0x56, 0xa, 0xc, 0x91, 0x76, 0x50, 0xb1, 0x3e, 0x59, 0x57, 0x6e, 0xd, 0xa3, 0xb6, 0x7e, 0x89, 0x76, 0x4b, 0x7d, 0x6e, 0xde, 0x82, 0x90, 0x5e, 0xca, 0x72, 0x4d, 0x71, 0xe9, 0xdc, 0x82, 0xd0, 0x15, 0x9a, 0x83, 0x4d, 0x9, 0xeb, 0x20, 0x82, 0x6f, 0x74, 0x1e, 0x93, 0x18, 0x8d, 0xb, 0x20, 0xb1, 0x2e, 0x60, 0x1e, 0xf1, 0x10, 0xfd, 0xf, 0x5e, 0xb7, 0x9b, 0x40, 0x94, 0xa, 0x89, 0x2, 0xa0, 0xb7, 0x66, 0x43, 0x77, 0xb6, 0xc7, 0x3e, 0x3f, 0xdf + }; - const uint8_t prng_payload_sf10[] = { - 0xfd, 0xfe, 0xf4, 0xdf, 0xe, 0xfb, 0x8f, 0x7f, 0x2, 0xfe, 0x4b, 0xb4, 0xea, 0x3e, 0xc2, 0x83, 0x6a, 0x88, 0xfa, 0x48, 0xfe, 0xaa, 0xf4, 0xf2, 0x4e, 0x62, 0xab, 0xf8, 0xd1, 0xfc, 0xe0, 0xb4, 0x73, 0x7e, 0x65, 0x83, 0x21, 0xcb, 0x93, 0x72, 0x87, 0x32, 0x40, 0x75, 0x50, 0x29, 0x2c, 0x19, 0x9a, 0x84, 0x4a, 0x0, 0xea, 0x30, 0xa9, 0x4, 0x8b, 0x88, 0x49, 0xa, 0xe2, 0x2a, 0x38, 0xb9, 0xa4, 0xa3, 0x16, 0xdf, 0xcd, 0x60, 0x4b, 0x7b, 0xea, 0x94, 0x89, 0x1a, 0xd0, 0x47, 0x28, 0x60, 0xd3, 0xaa, 0x26, 0xb9, 0x68, 0xb8, 0xb8, 0x22, 0x1c, 0xd1, 0x6, 0x65, 0x40, 0x18, 0x30, 0xa0, 0x3c, 0x92, 0xc9, 0x4c, 0xe2, 0x1, 0x73, 0x64, 0x2e, 0x34, 0xd0, 0x83, 0x20, 0xe2, 0x18, 0x32, 0x8e, 0x7c, 0x9a, 0xf8, 0xd5, 0x32, 0x46, 0x1e, 0xe0, 0xcd, 0x73, 0xca, 0x25, 0x8d, 0x7a, 0x64, 0xfe, 0x6a, 0xb5, 0x33, 0x5d, 0x35, 0xbd, 0x72, 0x96, 0xf0, 0x1b, 0xbf, 0x2, 0x1c, 0x40, 0xad, 0x30, 0x9e, 0x47, 0x55, 0x30, 0x4, 0xd3, 0x2, 0x6d, 0x62, 0xe2, 0x3f, 0x6a, 0x26, 0xb1, 0x41, 0x54, 0x2c, 0x2e, 0xb2, 0x3, 0x52, 0x1, 0xaf, 0xab, 0xd4, 0xd0, 0xe7, 0x26, 0x13, 0x62, 0x41, 0x6, 0xa5, 0x62, 0x53, 0xbb, 0xed, 0x92, 0x13, 0x17, 0x3a, 0x1b, 0x50, 0x94, 0x29, 0xd, 0xd2, 0x42, 0x6, 0x3b, 0x17, 0xc9, 0xda, 0xea, 0xe, 0x7e, 0x3, 0xbe, 0x2b, 0xe4, 0xa5, 0x69, 0xf4, 0xe8, 0x76, 0xb3, 0xfd, 0x14, 0x94, 0x36, 0x41, 0x64, 0xca, 0xf1, 0x28, 0xb6, 0xd6, 0x15, 0x46, 0x1f, 0x5c, 0x9a, 0x63, 0xcc, 0x3f, 0xfa, 0x54, 0xf4, 0xd, 0x5, 0xf6, 0x21, 0xe2, 0x93, 0xe9, 0x5, 0xb3, 0xa, 0x55, 0xfa, 0x29, 0x81, 0x1d, 0x9d, 0xa, 0x82, 0x4a, 0x48, 0x8b, 0x31, 0xfd, 0x2b, 0x23, 0x5f, 0x9d, 0x64, 0x48, 0x73, 0xc9, 0x4e, 0x57, 0xbf, 0xd1, 0x51, 0xe7, 0x67, 0x30, 0x39, 0x3e, 0xaf, 0xe3, 0xac, 0xef, 0x10, 0x70, 0x4e, 0x73, 0xa0, 0xde, 0x28, 0x90, 0xac, 0xba, 0x0, 0x4d, 0x86, 0x62, 0xa1, 0x58, 0x63, 0xd1, 0x26, 0x8a, 0x89, 0x45, 0xc9, 0xe2, 0xea, 0x13, 0x82, 0x7a, 0x5e, 0x3, 0x3d, 0xda, 0xdf, 0xa9, 0x6c, 0xf3, 0xc9, 0x12, 0xf0, 0x8f, 0xa0, 0x56, 0x1f, 0x27, 0xc7, 0xf8, 0x3b, 0xe8, 0xd6, 0x3e, 0xd4, 0x82, 0x2f, 0x85, 0x59, 0x29, 0x9d, 0xce, 0x96, 0x4e, 0x4c, 0xa4, 0xf, 0x9b, 0x12, 0xec, 0x1d, 0xce, 0xa1, 0x4e, 0x42, 0xaa, 0xeb, 0x50, 0x29, 0x8d, 0xc5, 0xfd, 0x5d, 0x4, 0xec, 0x46, 0x7a, 0x63, 0xf5, 0x94, 0x5, 0x21, 0x5e, 0x8e, 0xb9, 0xe1, 0x9d, 0xb3, 0x8f, 0x35, 0xb1, 0x32, 0x64, 0x97, 0xe1, 0x17, 0x35, 0xcf, 0x1c, 0x82, 0x16, 0x1c, 0x54, 0x7b, 0xed, 0xb3, 0xfe, 0xdd, 0xbf, 0x6, 0xc4, 0xc, 0x29, 0x46, 0xa7, 0x70, 0x8, 0xf5, 0xc, 0xd5, 0xd1, 0x61, 0x4f, 0x93, 0x80, 0x12, 0x4f, 0xc3, 0x20, 0x80, 0x43, 0x67, 0x49, 0xc2, 0xab, 0x6f, 0x5d, 0xf3, 0xef, 0x51, 0xe8, 0x81, 0xfb, 0xb5, 0xc9, 0x51, 0x72, 0x2e, 0x7e, 0xf8, 0x2e, 0xf7, 0xab, 0xd7, 0x86, 0x6a, 0xa3, 0xb1, 0x9, 0x7e, 0x91, 0x52, 0x4, 0x98, 0x2a, 0x8b, 0xd4, 0x48, 0x64, 0x0, 0xe2, 0x4c, 0x41, 0x28, 0x7c, 0xda, 0xac, 0x26, 0x93, 0xb3, 0x34, 0x49, 0x14, 0x52, 0x69, 0xa, 0x76, 0xd0, 0x75, 0xe, 0xc, 0x2c, 0xc4, 0x2b, 0x4, 0xf0, 0x42, 0x36, 0x6c, 0x3c, 0x89, 0x89, 0xab, 0x3a, 0x8e, 0xdd, 0xca, 0xa7, 0xeb, 0xbe, 0xa9, 0xd3, 0xe4, 0x94, 0xe7, 0xe0, 0xf9, 0xb8, 0x3e, 0xf9, 0xe7, 0xac, 0xd3, 0x9, 0x79, 0x61, 0x80, 0x82, 0x41, 0x31, 0x48, 0xa0, 0x50, 0x96, 0x2, 0x26, 0x64, 0x85, 0x16 - }; + const uint8_t prng_payload_sf10[] = { + 0xfd, 0xfe, 0xf4, 0xdf, 0xe, 0xfb, 0x8f, 0x7f, 0x2, 0xfe, 0x4b, 0xb4, 0xea, 0x3e, 0xc2, 0x83, 0x6a, 0x88, 0xfa, 0x48, 0xfe, 0xaa, 0xf4, 0xf2, 0x4e, 0x62, 0xab, 0xf8, 0xd1, 0xfc, 0xe0, 0xb4, 0x73, 0x7e, 0x65, 0x83, 0x21, 0xcb, 0x93, 0x72, 0x87, 0x32, 0x40, 0x75, 0x50, 0x29, 0x2c, 0x19, 0x9a, 0x84, 0x4a, 0x0, 0xea, 0x30, 0xa9, 0x4, 0x8b, 0x88, 0x49, 0xa, 0xe2, 0x2a, 0x38, 0xb9, 0xa4, 0xa3, 0x16, 0xdf, 0xcd, 0x60, 0x4b, 0x7b, 0xea, 0x94, 0x89, 0x1a, 0xd0, 0x47, 0x28, 0x60, 0xd3, 0xaa, 0x26, 0xb9, 0x68, 0xb8, 0xb8, 0x22, 0x1c, 0xd1, 0x6, 0x65, 0x40, 0x18, 0x30, 0xa0, 0x3c, 0x92, 0xc9, 0x4c, 0xe2, 0x1, 0x73, 0x64, 0x2e, 0x34, 0xd0, 0x83, 0x20, 0xe2, 0x18, 0x32, 0x8e, 0x7c, 0x9a, 0xf8, 0xd5, 0x32, 0x46, 0x1e, 0xe0, 0xcd, 0x73, 0xca, 0x25, 0x8d, 0x7a, 0x64, 0xfe, 0x6a, 0xb5, 0x33, 0x5d, 0x35, 0xbd, 0x72, 0x96, 0xf0, 0x1b, 0xbf, 0x2, 0x1c, 0x40, 0xad, 0x30, 0x9e, 0x47, 0x55, 0x30, 0x4, 0xd3, 0x2, 0x6d, 0x62, 0xe2, 0x3f, 0x6a, 0x26, 0xb1, 0x41, 0x54, 0x2c, 0x2e, 0xb2, 0x3, 0x52, 0x1, 0xaf, 0xab, 0xd4, 0xd0, 0xe7, 0x26, 0x13, 0x62, 0x41, 0x6, 0xa5, 0x62, 0x53, 0xbb, 0xed, 0x92, 0x13, 0x17, 0x3a, 0x1b, 0x50, 0x94, 0x29, 0xd, 0xd2, 0x42, 0x6, 0x3b, 0x17, 0xc9, 0xda, 0xea, 0xe, 0x7e, 0x3, 0xbe, 0x2b, 0xe4, 0xa5, 0x69, 0xf4, 0xe8, 0x76, 0xb3, 0xfd, 0x14, 0x94, 0x36, 0x41, 0x64, 0xca, 0xf1, 0x28, 0xb6, 0xd6, 0x15, 0x46, 0x1f, 0x5c, 0x9a, 0x63, 0xcc, 0x3f, 0xfa, 0x54, 0xf4, 0xd, 0x5, 0xf6, 0x21, 0xe2, 0x93, 0xe9, 0x5, 0xb3, 0xa, 0x55, 0xfa, 0x29, 0x81, 0x1d, 0x9d, 0xa, 0x82, 0x4a, 0x48, 0x8b, 0x31, 0xfd, 0x2b, 0x23, 0x5f, 0x9d, 0x64, 0x48, 0x73, 0xc9, 0x4e, 0x57, 0xbf, 0xd1, 0x51, 0xe7, 0x67, 0x30, 0x39, 0x3e, 0xaf, 0xe3, 0xac, 0xef, 0x10, 0x70, 0x4e, 0x73, 0xa0, 0xde, 0x28, 0x90, 0xac, 0xba, 0x0, 0x4d, 0x86, 0x62, 0xa1, 0x58, 0x63, 0xd1, 0x26, 0x8a, 0x89, 0x45, 0xc9, 0xe2, 0xea, 0x13, 0x82, 0x7a, 0x5e, 0x3, 0x3d, 0xda, 0xdf, 0xa9, 0x6c, 0xf3, 0xc9, 0x12, 0xf0, 0x8f, 0xa0, 0x56, 0x1f, 0x27, 0xc7, 0xf8, 0x3b, 0xe8, 0xd6, 0x3e, 0xd4, 0x82, 0x2f, 0x85, 0x59, 0x29, 0x9d, 0xce, 0x96, 0x4e, 0x4c, 0xa4, 0xf, 0x9b, 0x12, 0xec, 0x1d, 0xce, 0xa1, 0x4e, 0x42, 0xaa, 0xeb, 0x50, 0x29, 0x8d, 0xc5, 0xfd, 0x5d, 0x4, 0xec, 0x46, 0x7a, 0x63, 0xf5, 0x94, 0x5, 0x21, 0x5e, 0x8e, 0xb9, 0xe1, 0x9d, 0xb3, 0x8f, 0x35, 0xb1, 0x32, 0x64, 0x97, 0xe1, 0x17, 0x35, 0xcf, 0x1c, 0x82, 0x16, 0x1c, 0x54, 0x7b, 0xed, 0xb3, 0xfe, 0xdd, 0xbf, 0x6, 0xc4, 0xc, 0x29, 0x46, 0xa7, 0x70, 0x8, 0xf5, 0xc, 0xd5, 0xd1, 0x61, 0x4f, 0x93, 0x80, 0x12, 0x4f, 0xc3, 0x20, 0x80, 0x43, 0x67, 0x49, 0xc2, 0xab, 0x6f, 0x5d, 0xf3, 0xef, 0x51, 0xe8, 0x81, 0xfb, 0xb5, 0xc9, 0x51, 0x72, 0x2e, 0x7e, 0xf8, 0x2e, 0xf7, 0xab, 0xd7, 0x86, 0x6a, 0xa3, 0xb1, 0x9, 0x7e, 0x91, 0x52, 0x4, 0x98, 0x2a, 0x8b, 0xd4, 0x48, 0x64, 0x0, 0xe2, 0x4c, 0x41, 0x28, 0x7c, 0xda, 0xac, 0x26, 0x93, 0xb3, 0x34, 0x49, 0x14, 0x52, 0x69, 0xa, 0x76, 0xd0, 0x75, 0xe, 0xc, 0x2c, 0xc4, 0x2b, 0x4, 0xf0, 0x42, 0x36, 0x6c, 0x3c, 0x89, 0x89, 0xab, 0x3a, 0x8e, 0xdd, 0xca, 0xa7, 0xeb, 0xbe, 0xa9, 0xd3, 0xe4, 0x94, 0xe7, 0xe0, 0xf9, 0xb8, 0x3e, 0xf9, 0xe7, 0xac, 0xd3, 0x9, 0x79, 0x61, 0x80, 0x82, 0x41, 0x31, 0x48, 0xa0, 0x50, 0x96, 0x2, 0x26, 0x64, 0x85, 0x16 + }; - const uint8_t prng_payload_sf11[] = { - 0xfd, 0xfe, 0xb4, 0xbf, 0x3e, 0x8f, 0xa3, 0xd3, 0xd0, 0x75, 0x4, 0xfe, 0xc1, 0xb5, 0xea, 0x3e, 0xc2, 0xc3, 0x4a, 0x80, 0x6e, 0x2c, 0x7a, 0x59, 0xfe, 0x6a, 0x94, 0x82, 0x2a, 0x7a, 0x47, 0x62, 0x84, 0xf8, 0x42, 0xfd, 0xa0, 0x94, 0x43, 0x4a, 0x39, 0x27, 0xd3, 0x6, 0x2d, 0x43, 0x9a, 0x21, 0xe7, 0x61, 0x54, 0x3d, 0xbc, 0xd3, 0x3, 0x2d, 0x9, 0x98, 0xc0, 0xe7, 0x7b, 0x74, 0xf6, 0x98, 0xab, 0xb, 0x99, 0x80, 0x87, 0x47, 0x6b, 0x3b, 0xd5, 0x86, 0xec, 0x87, 0xfb, 0x3, 0xb7, 0x86, 0x5e, 0x6b, 0xb7, 0xd5, 0x57, 0xe8, 0xec, 0x7b, 0x31, 0xb6, 0x98, 0x5e, 0xec, 0xb7, 0xbe, 0x7, 0x3a, 0xec, 0xd3, 0xab, 0xee, 0x9a, 0x38, 0xe4, 0xe4, 0xfe, 0x76, 0xa, 0x99, 0x43, 0x5, 0x64, 0x1, 0x38, 0x63, 0xe4, 0x14, 0x6, 0x9b, 0x99, 0x4a, 0x4f, 0xe1, 0x0, 0x73, 0x22, 0xe, 0x44, 0xa0, 0x97, 0x44, 0xc4, 0xaa, 0x61, 0x99, 0x72, 0xac, 0x4e, 0xce, 0xe0, 0x35, 0x4c, 0x9d, 0xa0, 0x4e, 0x98, 0xa1, 0xad, 0x43, 0xce, 0x59, 0x25, 0x54, 0x5, 0xe4, 0xcf, 0x78, 0xa1, 0x94, 0x43, 0xa, 0x19, 0x10, 0x40, 0x85, 0x66, 0xaa, 0xfb, 0xd8, 0x85, 0xd7, 0x6a, 0x28, 0x4, 0x6, 0x45, 0xc6, 0x28, 0x60, 0xd9, 0x8a, 0xd7, 0xc7, 0x38, 0xe1, 0x56, 0xa1, 0x82, 0xd0, 0xc8, 0x2e, 0x8a, 0x3, 0xe6, 0x49, 0xe5, 0x58, 0x31, 0xec, 0x51, 0x32, 0x25, 0x35, 0x3, 0x29, 0x19, 0x12, 0xd0, 0xb, 0xef, 0xe1, 0x30, 0x72, 0x75, 0x2e, 0x39, 0xa4, 0x3a, 0xf8, 0x9b, 0x2b, 0xec, 0xd5, 0x72, 0x46, 0x2e, 0x17, 0xb5, 0x8a, 0xd8, 0xd6, 0x25, 0x5, 0xd3, 0x42, 0x46, 0x3b, 0x16, 0xfd, 0xce, 0xfe, 0x16, 0xf7, 0x4, 0xfc, 0x40, 0xdf, 0x3b, 0x90, 0xfd, 0xcd, 0xf6, 0x5b, 0xfb, 0x6c, 0xfe, 0xf0, 0xde, 0x65, 0xb0, 0x7e, 0xcd, 0xea, 0xdf, 0xe7, 0xe5, 0xfe, 0x71, 0xd4, 0x65, 0x51, 0x2e, 0xcd, 0xfa, 0x87, 0xfd, 0x58, 0xfc, 0x27, 0xd4, 0x68, 0x51, 0x8c, 0xed, 0xab, 0xa7, 0xd1, 0x50, 0x2e, 0x2f, 0xf9, 0x48, 0xc3, 0x9a, 0xb3, 0x23, 0xc5, 0xde, 0x28, 0x2c, 0xd8, 0xa8, 0xd7, 0xc5, 0x68, 0x93, 0xd, 0x41, 0xca, 0xa1, 0x40, 0xd9, 0x13, 0x97, 0x41, 0x28, 0xc2, 0x55, 0x6c, 0xec, 0x38, 0xe2, 0xdf, 0x13, 0xd6, 0x61, 0x73, 0xe2, 0x4f, 0x6c, 0x6c, 0xb0, 0x6b, 0x5d, 0x1f, 0x95, 0xa0, 0x43, 0xc6, 0x5b, 0x9, 0x60, 0x4f, 0x72, 0xa0, 0x9e, 0x48, 0xe0, 0xe8, 0xe6, 0x4, 0x15, 0x82, 0x4c, 0x47, 0xa0, 0x82, 0x8, 0x7, 0x98, 0x96, 0x3c, 0x2c, 0x9a, 0xe, 0x4d, 0x4a, 0x8a, 0x9a, 0x27, 0xea, 0xee, 0xe8, 0x1a, 0xf5, 0x5, 0xb1, 0xb, 0x74, 0xba, 0x39, 0xee, 0x79, 0xec, 0x8f, 0x67, 0x48, 0x38, 0xeb, 0x75, 0xe8, 0x79, 0x9f, 0x9, 0x99, 0x1f, 0xcd, 0x6, 0x29, 0xe9, 0xf2, 0xe9, 0x79, 0x9f, 0xd, 0x9d, 0x9b, 0x4b, 0x5, 0xac, 0xeb, 0x71, 0xa2, 0x19, 0x25, 0x1d, 0x98, 0x97, 0x4f, 0x7, 0x2e, 0xeb, 0xf8, 0xa2, 0x9a, 0x5, 0x6d, 0xb0, 0xe7, 0x5f, 0xfb, 0xae, 0xf5, 0xf9, 0x65, 0xf8, 0x3e, 0x39, 0xfe, 0x4b, 0x71, 0xe0, 0xb6, 0xf1, 0x75, 0x65, 0x12, 0x1e, 0xdb, 0xb6, 0xb3, 0xe3, 0x9c, 0xb2, 0xf, 0x75, 0xd0, 0x22, 0x10, 0xd7, 0x2d, 0x8d, 0x76, 0x91, 0x37, 0x8f, 0x35, 0xd1, 0x22, 0x0, 0xc4, 0x25, 0x5c, 0xfe, 0xf0, 0x36, 0xbc, 0x35, 0x8e, 0x42, 0xe8, 0xa4, 0x3, 0xfc, 0xd2, 0x7d, 0xa6, 0xbc, 0xba, 0xcf, 0xc8, 0xb8, 0x15, 0x23, 0x54, 0x58, 0xb3, 0x2f, 0x5f, 0xb2, 0x9c, 0xa8, 0x89, 0x65, 0x39, 0x54, 0x38, 0xb3, 0xd7, 0x5b, 0x8c, 0xdc, 0xa6, 0x99, 0xb1, 0x79, 0x18, 0x38, 0x10, 0x57, 0xc0 - }; + const uint8_t prng_payload_sf11[] = { + 0xfd, 0xfe, 0xb4, 0xbf, 0x3e, 0x8f, 0xa3, 0xd3, 0xd0, 0x75, 0x4, 0xfe, 0xc1, 0xb5, 0xea, 0x3e, 0xc2, 0xc3, 0x4a, 0x80, 0x6e, 0x2c, 0x7a, 0x59, 0xfe, 0x6a, 0x94, 0x82, 0x2a, 0x7a, 0x47, 0x62, 0x84, 0xf8, 0x42, 0xfd, 0xa0, 0x94, 0x43, 0x4a, 0x39, 0x27, 0xd3, 0x6, 0x2d, 0x43, 0x9a, 0x21, 0xe7, 0x61, 0x54, 0x3d, 0xbc, 0xd3, 0x3, 0x2d, 0x9, 0x98, 0xc0, 0xe7, 0x7b, 0x74, 0xf6, 0x98, 0xab, 0xb, 0x99, 0x80, 0x87, 0x47, 0x6b, 0x3b, 0xd5, 0x86, 0xec, 0x87, 0xfb, 0x3, 0xb7, 0x86, 0x5e, 0x6b, 0xb7, 0xd5, 0x57, 0xe8, 0xec, 0x7b, 0x31, 0xb6, 0x98, 0x5e, 0xec, 0xb7, 0xbe, 0x7, 0x3a, 0xec, 0xd3, 0xab, 0xee, 0x9a, 0x38, 0xe4, 0xe4, 0xfe, 0x76, 0xa, 0x99, 0x43, 0x5, 0x64, 0x1, 0x38, 0x63, 0xe4, 0x14, 0x6, 0x9b, 0x99, 0x4a, 0x4f, 0xe1, 0x0, 0x73, 0x22, 0xe, 0x44, 0xa0, 0x97, 0x44, 0xc4, 0xaa, 0x61, 0x99, 0x72, 0xac, 0x4e, 0xce, 0xe0, 0x35, 0x4c, 0x9d, 0xa0, 0x4e, 0x98, 0xa1, 0xad, 0x43, 0xce, 0x59, 0x25, 0x54, 0x5, 0xe4, 0xcf, 0x78, 0xa1, 0x94, 0x43, 0xa, 0x19, 0x10, 0x40, 0x85, 0x66, 0xaa, 0xfb, 0xd8, 0x85, 0xd7, 0x6a, 0x28, 0x4, 0x6, 0x45, 0xc6, 0x28, 0x60, 0xd9, 0x8a, 0xd7, 0xc7, 0x38, 0xe1, 0x56, 0xa1, 0x82, 0xd0, 0xc8, 0x2e, 0x8a, 0x3, 0xe6, 0x49, 0xe5, 0x58, 0x31, 0xec, 0x51, 0x32, 0x25, 0x35, 0x3, 0x29, 0x19, 0x12, 0xd0, 0xb, 0xef, 0xe1, 0x30, 0x72, 0x75, 0x2e, 0x39, 0xa4, 0x3a, 0xf8, 0x9b, 0x2b, 0xec, 0xd5, 0x72, 0x46, 0x2e, 0x17, 0xb5, 0x8a, 0xd8, 0xd6, 0x25, 0x5, 0xd3, 0x42, 0x46, 0x3b, 0x16, 0xfd, 0xce, 0xfe, 0x16, 0xf7, 0x4, 0xfc, 0x40, 0xdf, 0x3b, 0x90, 0xfd, 0xcd, 0xf6, 0x5b, 0xfb, 0x6c, 0xfe, 0xf0, 0xde, 0x65, 0xb0, 0x7e, 0xcd, 0xea, 0xdf, 0xe7, 0xe5, 0xfe, 0x71, 0xd4, 0x65, 0x51, 0x2e, 0xcd, 0xfa, 0x87, 0xfd, 0x58, 0xfc, 0x27, 0xd4, 0x68, 0x51, 0x8c, 0xed, 0xab, 0xa7, 0xd1, 0x50, 0x2e, 0x2f, 0xf9, 0x48, 0xc3, 0x9a, 0xb3, 0x23, 0xc5, 0xde, 0x28, 0x2c, 0xd8, 0xa8, 0xd7, 0xc5, 0x68, 0x93, 0xd, 0x41, 0xca, 0xa1, 0x40, 0xd9, 0x13, 0x97, 0x41, 0x28, 0xc2, 0x55, 0x6c, 0xec, 0x38, 0xe2, 0xdf, 0x13, 0xd6, 0x61, 0x73, 0xe2, 0x4f, 0x6c, 0x6c, 0xb0, 0x6b, 0x5d, 0x1f, 0x95, 0xa0, 0x43, 0xc6, 0x5b, 0x9, 0x60, 0x4f, 0x72, 0xa0, 0x9e, 0x48, 0xe0, 0xe8, 0xe6, 0x4, 0x15, 0x82, 0x4c, 0x47, 0xa0, 0x82, 0x8, 0x7, 0x98, 0x96, 0x3c, 0x2c, 0x9a, 0xe, 0x4d, 0x4a, 0x8a, 0x9a, 0x27, 0xea, 0xee, 0xe8, 0x1a, 0xf5, 0x5, 0xb1, 0xb, 0x74, 0xba, 0x39, 0xee, 0x79, 0xec, 0x8f, 0x67, 0x48, 0x38, 0xeb, 0x75, 0xe8, 0x79, 0x9f, 0x9, 0x99, 0x1f, 0xcd, 0x6, 0x29, 0xe9, 0xf2, 0xe9, 0x79, 0x9f, 0xd, 0x9d, 0x9b, 0x4b, 0x5, 0xac, 0xeb, 0x71, 0xa2, 0x19, 0x25, 0x1d, 0x98, 0x97, 0x4f, 0x7, 0x2e, 0xeb, 0xf8, 0xa2, 0x9a, 0x5, 0x6d, 0xb0, 0xe7, 0x5f, 0xfb, 0xae, 0xf5, 0xf9, 0x65, 0xf8, 0x3e, 0x39, 0xfe, 0x4b, 0x71, 0xe0, 0xb6, 0xf1, 0x75, 0x65, 0x12, 0x1e, 0xdb, 0xb6, 0xb3, 0xe3, 0x9c, 0xb2, 0xf, 0x75, 0xd0, 0x22, 0x10, 0xd7, 0x2d, 0x8d, 0x76, 0x91, 0x37, 0x8f, 0x35, 0xd1, 0x22, 0x0, 0xc4, 0x25, 0x5c, 0xfe, 0xf0, 0x36, 0xbc, 0x35, 0x8e, 0x42, 0xe8, 0xa4, 0x3, 0xfc, 0xd2, 0x7d, 0xa6, 0xbc, 0xba, 0xcf, 0xc8, 0xb8, 0x15, 0x23, 0x54, 0x58, 0xb3, 0x2f, 0x5f, 0xb2, 0x9c, 0xa8, 0x89, 0x65, 0x39, 0x54, 0x38, 0xb3, 0xd7, 0x5b, 0x8c, 0xdc, 0xa6, 0x99, 0xb1, 0x79, 0x18, 0x38, 0x10, 0x57, 0xc0 + }; - const uint8_t prng_payload_sf12[] = { - 0xfd, 0x7c, 0xb4, 0xff, 0x5f, 0x9f, 0xd7, 0xab, 0x28, 0x73, 0x91, 0xff, 0xc5, 0xb5, 0xab, 0x1c, 0x92, 0xe7, 0x76, 0x74, 0x54, 0x9f, 0xfd, 0xc3, 0x7f, 0xa9, 0xb5, 0xd2, 0x5e, 0x46, 0xb7, 0x5c, 0x47, 0x7f, 0xfb, 0xff, 0xef, 0x35, 0x33, 0x1d, 0x15, 0xe7, 0x3d, 0x5f, 0x85, 0x5d, 0x91, 0xea, 0x87, 0xb7, 0x2, 0x55, 0x41, 0x4d, 0x30, 0xed, 0x18, 0x17, 0x11, 0x8d, 0x4c, 0x2, 0xab, 0xa0, 0xd9, 0x24, 0xd7, 0x24, 0x3, 0x97, 0x43, 0x1, 0x62, 0x8a, 0x7b, 0x99, 0xf4, 0xa6, 0x4e, 0x6f, 0xbb, 0x55, 0x49, 0xed, 0xea, 0x73, 0xbb, 0x36, 0xd9, 0x3e, 0x9c, 0xe3, 0xb2, 0x97, 0x24, 0xe1, 0x51, 0x38, 0x6f, 0x9e, 0x38, 0xec, 0x64, 0xbe, 0x54, 0x1a, 0x99, 0x4b, 0x5, 0x60, 0x1, 0x79, 0x48, 0xd4, 0xf0, 0x5a, 0x37, 0x2f, 0xd7, 0x4f, 0x6f, 0x2, 0x30, 0x1, 0x5e, 0x60, 0xbc, 0x5f, 0xf7, 0x1d, 0xbf, 0x4e, 0x13, 0xb0, 0xcd, 0x1e, 0xe8, 0xed, 0xe9, 0x9e, 0xa4, 0x39, 0x6b, 0x55, 0x72, 0x4f, 0x38, 0xaa, 0x15, 0x39, 0x16, 0xe8, 0x40, 0x11, 0x7e, 0xd8, 0x3e, 0x34, 0x1f, 0x57, 0x8d, 0x6, 0xca, 0xc, 0x81, 0x55, 0x49, 0xa1, 0x8c, 0xc, 0x3, 0xcf, 0xc0, 0x88, 0x5b, 0xad, 0x92, 0x33, 0x2c, 0x1, 0x5c, 0x92, 0xae, 0x1, 0x93, 0x2b, 0x16, 0x9e, 0x1f, 0xa, 0xaf, 0xf6, 0x1d, 0xab, 0x64, 0xd1, 0x73, 0x44, 0x25, 0x35, 0x5, 0x9, 0xb8, 0x43, 0x9b, 0xe6, 0x61, 0x30, 0x70, 0x75, 0x2e, 0x19, 0xc4, 0x4e, 0xd8, 0xdf, 0x25, 0xee, 0x52, 0x30, 0x26, 0x76, 0x63, 0xd8, 0xf6, 0x5a, 0xc5, 0xbb, 0x26, 0x54, 0x2, 0x65, 0xb, 0x13, 0x90, 0x2a, 0x10, 0x23, 0x41, 0x1, 0xf6, 0x13, 0xfc, 0xc8, 0xf4, 0x83, 0x45, 0x2c, 0x2a, 0x53, 0x5, 0xfc, 0xa4, 0x7f, 0xdf, 0xb5, 0x27, 0x35, 0x8, 0x42, 0xa3, 0x87, 0x46, 0xb4, 0xaa, 0xdd, 0xd7, 0x67, 0x6e, 0x18, 0x82, 0xcb, 0x1a, 0x18, 0xfe, 0x82, 0x75, 0xdd, 0xb6, 0xae, 0x14, 0xf0, 0x3d, 0x52, 0xe5, 0xc8, 0xe1, 0x7f, 0x99, 0x34, 0xc9, 0x5c, 0xeb, 0x4d, 0xa9, 0x9d, 0xdf, 0x7f, 0xb6, 0x12, 0xf, 0xca, 0x41, 0xa9, 0x20, 0xfb, 0x23, 0x93, 0x7d, 0xa8, 0xec, 0xc6, 0x69, 0xc3, 0xb1, 0xf0, 0x5f, 0xd3, 0xb7, 0x1, 0x43, 0xf6, 0x13, 0xf0, 0xca, 0xb3, 0x7a, 0x17, 0xb8, 0xa7, 0xc4, 0xf, 0x72, 0x25, 0x26, 0x65, 0xc4, 0xf5, 0x73, 0xfe, 0x3b, 0x95, 0xc4, 0x4a, 0x62, 0x50, 0x3e, 0x44, 0xc6, 0xef, 0xe2, 0xfb, 0x63, 0x15, 0xe, 0xa, 0xdb, 0x64, 0x39, 0x40, 0x7, 0x6a, 0x8, 0x32, 0x81, 0x7e, 0x36, 0x93, 0x3f, 0xdf, 0xcf, 0x47, 0x5b, 0x38, 0xa7, 0x52, 0xda, 0x44, 0xbc, 0x57, 0xc6, 0x9d, 0x87, 0x42, 0x51, 0xa6, 0xa0, 0x18, 0x2, 0x6c, 0xfa, 0xde, 0x8e, 0x5d, 0xeb, 0x9e, 0x39, 0x0, 0x1a, 0x42, 0x8f, 0xcb, 0xf8, 0x96, 0xde, 0xb5, 0x13, 0x20, 0x80, 0x13, 0xc0, 0x4d, 0x68, 0x8b, 0x8b, 0xe6, 0xc0, 0xa5, 0x7f, 0x2d, 0x40, 0x59, 0xea, 0x2c, 0xf1, 0xb8, 0xd, 0xe8, 0xa, 0x75, 0x1a, 0x59, 0xf4, 0x73, 0x35, 0xb7, 0x14, 0x35, 0x17, 0x20, 0x1b, 0xb0, 0x24, 0x41, 0xd2, 0xaf, 0xc7, 0xd6, 0xa0, 0x6, 0x8, 0x17, 0x96, 0xca, 0x9a, 0x93, 0x21, 0xe7, 0x50, 0xe3, 0xa6, 0x58, 0x68, 0x9b, 0xc6, 0x4c, 0x89, 0xae, 0x55, 0x4a, 0x76, 0x67, 0xf7, 0x58, 0x65, 0xbb, 0x37, 0x2f, 0xbb, 0xc2, 0x47, 0x71, 0x4f, 0xbe, 0x20, 0x75, 0x62, 0x39, 0x5e, 0x5d, 0xba, 0xb5, 0x52, 0x3, 0x60, 0xe2, 0xf2, 0x93, 0x4, 0x6a, 0x21, 0x44, 0x48, 0x50, 0x5c, 0xef, 0x2e, 0x38, 0xda, 0x37, 0x96, 0x9, 0x33, 0x56, 0x6f, 0x68, 0x66, 0x24, 0xb2, 0x99, 0x5f - }; - } + const uint8_t prng_payload_sf12[] = { + 0xfd, 0x7c, 0xb4, 0xff, 0x5f, 0x9f, 0xd7, 0xab, 0x28, 0x73, 0x91, 0xff, 0xc5, 0xb5, 0xab, 0x1c, 0x92, 0xe7, 0x76, 0x74, 0x54, 0x9f, 0xfd, 0xc3, 0x7f, 0xa9, 0xb5, 0xd2, 0x5e, 0x46, 0xb7, 0x5c, 0x47, 0x7f, 0xfb, 0xff, 0xef, 0x35, 0x33, 0x1d, 0x15, 0xe7, 0x3d, 0x5f, 0x85, 0x5d, 0x91, 0xea, 0x87, 0xb7, 0x2, 0x55, 0x41, 0x4d, 0x30, 0xed, 0x18, 0x17, 0x11, 0x8d, 0x4c, 0x2, 0xab, 0xa0, 0xd9, 0x24, 0xd7, 0x24, 0x3, 0x97, 0x43, 0x1, 0x62, 0x8a, 0x7b, 0x99, 0xf4, 0xa6, 0x4e, 0x6f, 0xbb, 0x55, 0x49, 0xed, 0xea, 0x73, 0xbb, 0x36, 0xd9, 0x3e, 0x9c, 0xe3, 0xb2, 0x97, 0x24, 0xe1, 0x51, 0x38, 0x6f, 0x9e, 0x38, 0xec, 0x64, 0xbe, 0x54, 0x1a, 0x99, 0x4b, 0x5, 0x60, 0x1, 0x79, 0x48, 0xd4, 0xf0, 0x5a, 0x37, 0x2f, 0xd7, 0x4f, 0x6f, 0x2, 0x30, 0x1, 0x5e, 0x60, 0xbc, 0x5f, 0xf7, 0x1d, 0xbf, 0x4e, 0x13, 0xb0, 0xcd, 0x1e, 0xe8, 0xed, 0xe9, 0x9e, 0xa4, 0x39, 0x6b, 0x55, 0x72, 0x4f, 0x38, 0xaa, 0x15, 0x39, 0x16, 0xe8, 0x40, 0x11, 0x7e, 0xd8, 0x3e, 0x34, 0x1f, 0x57, 0x8d, 0x6, 0xca, 0xc, 0x81, 0x55, 0x49, 0xa1, 0x8c, 0xc, 0x3, 0xcf, 0xc0, 0x88, 0x5b, 0xad, 0x92, 0x33, 0x2c, 0x1, 0x5c, 0x92, 0xae, 0x1, 0x93, 0x2b, 0x16, 0x9e, 0x1f, 0xa, 0xaf, 0xf6, 0x1d, 0xab, 0x64, 0xd1, 0x73, 0x44, 0x25, 0x35, 0x5, 0x9, 0xb8, 0x43, 0x9b, 0xe6, 0x61, 0x30, 0x70, 0x75, 0x2e, 0x19, 0xc4, 0x4e, 0xd8, 0xdf, 0x25, 0xee, 0x52, 0x30, 0x26, 0x76, 0x63, 0xd8, 0xf6, 0x5a, 0xc5, 0xbb, 0x26, 0x54, 0x2, 0x65, 0xb, 0x13, 0x90, 0x2a, 0x10, 0x23, 0x41, 0x1, 0xf6, 0x13, 0xfc, 0xc8, 0xf4, 0x83, 0x45, 0x2c, 0x2a, 0x53, 0x5, 0xfc, 0xa4, 0x7f, 0xdf, 0xb5, 0x27, 0x35, 0x8, 0x42, 0xa3, 0x87, 0x46, 0xb4, 0xaa, 0xdd, 0xd7, 0x67, 0x6e, 0x18, 0x82, 0xcb, 0x1a, 0x18, 0xfe, 0x82, 0x75, 0xdd, 0xb6, 0xae, 0x14, 0xf0, 0x3d, 0x52, 0xe5, 0xc8, 0xe1, 0x7f, 0x99, 0x34, 0xc9, 0x5c, 0xeb, 0x4d, 0xa9, 0x9d, 0xdf, 0x7f, 0xb6, 0x12, 0xf, 0xca, 0x41, 0xa9, 0x20, 0xfb, 0x23, 0x93, 0x7d, 0xa8, 0xec, 0xc6, 0x69, 0xc3, 0xb1, 0xf0, 0x5f, 0xd3, 0xb7, 0x1, 0x43, 0xf6, 0x13, 0xf0, 0xca, 0xb3, 0x7a, 0x17, 0xb8, 0xa7, 0xc4, 0xf, 0x72, 0x25, 0x26, 0x65, 0xc4, 0xf5, 0x73, 0xfe, 0x3b, 0x95, 0xc4, 0x4a, 0x62, 0x50, 0x3e, 0x44, 0xc6, 0xef, 0xe2, 0xfb, 0x63, 0x15, 0xe, 0xa, 0xdb, 0x64, 0x39, 0x40, 0x7, 0x6a, 0x8, 0x32, 0x81, 0x7e, 0x36, 0x93, 0x3f, 0xdf, 0xcf, 0x47, 0x5b, 0x38, 0xa7, 0x52, 0xda, 0x44, 0xbc, 0x57, 0xc6, 0x9d, 0x87, 0x42, 0x51, 0xa6, 0xa0, 0x18, 0x2, 0x6c, 0xfa, 0xde, 0x8e, 0x5d, 0xeb, 0x9e, 0x39, 0x0, 0x1a, 0x42, 0x8f, 0xcb, 0xf8, 0x96, 0xde, 0xb5, 0x13, 0x20, 0x80, 0x13, 0xc0, 0x4d, 0x68, 0x8b, 0x8b, 0xe6, 0xc0, 0xa5, 0x7f, 0x2d, 0x40, 0x59, 0xea, 0x2c, 0xf1, 0xb8, 0xd, 0xe8, 0xa, 0x75, 0x1a, 0x59, 0xf4, 0x73, 0x35, 0xb7, 0x14, 0x35, 0x17, 0x20, 0x1b, 0xb0, 0x24, 0x41, 0xd2, 0xaf, 0xc7, 0xd6, 0xa0, 0x6, 0x8, 0x17, 0x96, 0xca, 0x9a, 0x93, 0x21, 0xe7, 0x50, 0xe3, 0xa6, 0x58, 0x68, 0x9b, 0xc6, 0x4c, 0x89, 0xae, 0x55, 0x4a, 0x76, 0x67, 0xf7, 0x58, 0x65, 0xbb, 0x37, 0x2f, 0xbb, 0xc2, 0x47, 0x71, 0x4f, 0xbe, 0x20, 0x75, 0x62, 0x39, 0x5e, 0x5d, 0xba, 0xb5, 0x52, 0x3, 0x60, 0xe2, 0xf2, 0x93, 0x4, 0x6a, 0x21, 0x44, 0x48, 0x50, 0x5c, 0xef, 0x2e, 0x38, 0xda, 0x37, 0x96, 0x9, 0x33, 0x56, 0x6f, 0x68, 0x66, 0x24, 0xb2, 0x99, 0x5f + }; + } } #endif /* TABLES_H */ diff --git a/lib/utilities.h b/lib/utilities.h index 7aad72d..8b957c9 100644 --- a/lib/utilities.h +++ b/lib/utilities.h @@ -1,87 +1,70 @@ #ifndef UTILITIES_H #define UTILITIES_H +#include + namespace gr { - namespace lora { + namespace lora { template - std::string to_bin(T v, int element_len_bits) { - T mask = 0; - unsigned int maxpow = element_len_bits; + std::string to_bin(T v, uint32_t bitwidth) { + unsigned long long maxpow = bitwidth ? (1ull << (bitwidth - 1)) : 0, + mask; + std::string result = ""; - for(int i = 0; i < maxpow; i++) { - mask = pow(2, i); - //std::cout << (unsigned int)v << " AND " << mask << " is " << (v & mask) << std::endl; - - if((v & mask) > 0) { - result += "1"; - } else { - result += "0"; - } - } - - return result; - } - - template - inline void print_vector(std::ostream& out, std::vector& v, std::string prefix, int element_len_bits) { - out << prefix << ": "; - for(int i = 0; i < v.size(); i++) { - out << to_bin(v[i], element_len_bits) << ", "; - } - out << std::endl << std::flush; - } - - template - inline void print_vector_raw(std::ostream& out, std::vector& v, int element_len_bits) { - for(int i = 0; i < v.size(); i++) { - out << to_bin(v[i], element_len_bits); - } - out << std::flush; - } - - bool check_parity(std::string word, bool even) { - int count = 0; - - for(int i = 0; i < 7; i++) { - if(word[i] == '1') - count += 1; - } - - if(even) - 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 result = 0; - - for(uint32_t j = 0; j < n; j++) { - uint32_t power = pow(2, indices[j]); - if((data & power) > 0) { - result += pow(2, j); - } + for (mask = 0x1; mask <= maxpow; mask <<= 1) { + result += (v & mask) ? "1" : "0"; } return result; } - 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; + template + inline void print_vector(std::ostream& out, std::vector& v, std::string prefix, int element_len_bits) { + out << prefix << ": "; - for(uint32_t i = 0; i < len; i+=2) { - uint8_t d1 = 0; - d1 = select_bits(in_data[i], indices, n) & 0xff; + for (T x : v) + out << to_bin(x, element_len_bits) << ", "; + out << std::endl << std::flush; + } - uint8_t d2 = 0; - if(i+1 < len) - d2 = select_bits(in_data[i+1], indices, n) & 0xff; + template + inline void print_vector_raw(std::ostream& out, std::vector& v, int element_len_bits) { - out_data[out_index] = (d1 << 4) | d2; - out_index++; + for (T x : v) + out << to_bin(x, element_len_bits); + + out << std::flush; + } + + bool check_parity(std::string& word, bool even) { + size_t count = 0, i = 0; + + while(i < 7) { + if (word[i++] == '1') + ++count; + } + + return (count & 0x1) == (even ? 0 : 1); + } + + uint32_t select_bits(uint32_t data, uint8_t *indices, uint8_t n) { + uint32_t r = 0; + + for(uint8_t i = 0; i < n; ++i) + r |= (data & (1 << indices[i])) ? (1 << i) : 0; + + return r; + } + + void fec_extract_data_only(uint8_t *in_data, uint32_t len, uint8_t *indices, uint8_t n, uint8_t *out_data) { + 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; + + out_data[out_index++] = d1; } } @@ -127,11 +110,7 @@ namespace gr { // p4 01010101 // Syndrome matrix = columns of "cover bits" above - uint8_t H[16]; - - for(uint8_t i = 0; i < 16; i++) { - H[i] = 0; - } + uint8_t H[16] = { 0 }; uint8_t i0 = pack_nibble(1, 0, 0, 0); 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)); - if(syndrome != 0) { - uint8_t index = H[syndrome]; - v = v ^ pow2[index]; + if (syndrome) { + v ^= pow2[ H[syndrome] ]; } uint8_t d1 = bit(v, 1); @@ -178,22 +156,16 @@ namespace gr { } // Manual Hamming - void hamming_decode_soft(uint8_t* words, uint32_t len, uint8_t* out_data) { - uint32_t out_index = 0; - for(int i = 0; i < len; i+=2) { - uint8_t d1 = 0; - d1 = hamming_decode_soft_byte(words[i]); + void hamming_decode_soft(uint8_t *words, uint32_t len, uint8_t *out_data) { + for (uint32_t i = 0, out_index = 0; i < len; i += 2) { + uint8_t d1 = hamming_decode_soft_byte(words[i]) << 4; + d1 |= (i + 1 < len) ? hamming_decode_soft_byte(words[i + 1]) : 0; - uint8_t d2 = 0; - if(i+1 < len) - d2 = hamming_decode_soft_byte(words[i+1]); - - out_data[out_index] = (d1 << 4) | d2; - out_index++; + out_data[out_index++] = d1; } } - } + } } #endif /* UTILITIES_H */