From 30da0dedcdef204685909c40814a5d642d07842d Mon Sep 17 00:00:00 2001 From: Wosser1sProductions Date: Thu, 18 May 2017 15:56:25 +0200 Subject: [PATCH] Merge deinterleave and comments - Removed break in sliding_cross_correlate_upchirp due to inaccuracy in noisy signals ~ Fixed upperbound in max_frequency_gradient_idx ~ Rewrote deinterleave function, now rotates bits to correct position instead of extensive masking + Added comment for every function (needs review) ~ Solved nibble reverse after Hamming decode by switching the order or ORing of the decoded data in Hamming itself + Added DBGR method to save timings to a file, a result for our setup can be found in examples/lora-timings + Added Python script to transmit and receive random data live for testing --- examples/_examplify_live.py | 270 ++++++++++++++ examples/lora-timings/avg_sd.py | 45 +++ examples/lora-timings/timing-results.txt | 33 ++ lib/dbugr.hpp | 39 +- lib/decoder_impl.cc | 135 ++++--- lib/decoder_impl.h | 454 +++++++++++++++++++---- lib/tables.h | 51 ++- lib/utilities.h | 252 +++++++++++-- 8 files changed, 1104 insertions(+), 175 deletions(-) create mode 100644 examples/_examplify_live.py create mode 100644 examples/lora-timings/avg_sd.py create mode 100644 examples/lora-timings/timing-results.txt diff --git a/examples/_examplify_live.py b/examples/_examplify_live.py new file mode 100644 index 0000000..b79d4c4 --- /dev/null +++ b/examples/_examplify_live.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python2 + +import struct +import time +import collections +import os +from loranode import RN2483Controller + +import lora, socket, pmt, osmosdr, random +from gnuradio import gr, blocks + +TestResultData = collections.namedtuple('TestResultData', ['SF', 'CR', 'passing', 'total', 'rate']) + +class ExamplifyLive: + def __init__(self, spreadingFactor = 7, codingRate = "4/5", gains = [10, 20, 20]): + ################################################## + # Variables # + ################################################## + self.target_freq = 868.1e6 + self.sf = spreadingFactor # 7 8 9 10 11 12 + self.samp_rate = 1e6 + self.capture_freq = 868.0e6 + self.bw = 125e3 + #self.symbols_per_sec = self.bw / (2**self.sf) + self.offset = -(self.capture_freq - self.target_freq) + #self.bitrate = self.sf * (1 / (2**self.sf / self.bw )) + self.crc = True + self.pwr = 1 + self.codingRate = codingRate # 4/5 4/6 4/7 4/8 + + self.pre_delay = 0.150 + self.post_delay = 0.350 + self.trans_delay = 0.250 + self.testResults = None + + # Socket connection for sink + self.host = "127.0.0.1" + self.port = 40868 + + self.server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.server.bind((self.host, self.port)) + self.server.setblocking(0) + + ################################################## + # LoRa transmitter # + ################################################## + try: + self.lc = RN2483Controller("/dev/lora") + self.lc.set_cr ( self.codingRate) + self.lc.set_bw ( self.bw / 1e3) + self.lc.set_sf ( self.sf ) + self.lc.set_crc( "on" if self.crc else "off") + self.lc.set_pwr( self.pwr ) + except: + raise Exception("Error initialising LoRa transmitter: RN2483Controller") + + ################################################## + # Blocks # + ################################################## + self.tb = gr.top_block () + + self.osmosdr_source_0 = osmosdr.source( args="numchan=" + str(1) + " " + '' ) + self.osmosdr_source_0.set_sample_rate(self.samp_rate) + self.osmosdr_source_0.set_center_freq(self.capture_freq, 0) + self.osmosdr_source_0.set_freq_corr(0, 0) + self.osmosdr_source_0.set_dc_offset_mode(0, 0) + self.osmosdr_source_0.set_iq_balance_mode(0, 0) + self.osmosdr_source_0.set_gain_mode(False, 0) + self.osmosdr_source_0.set_gain(gains[0], 0) + self.osmosdr_source_0.set_if_gain(gains[1], 0) + self.osmosdr_source_0.set_bb_gain(gains[2], 0) + self.osmosdr_source_0.set_antenna('', 0) + self.osmosdr_source_0.set_bandwidth(0, 0) + + self.lora_lora_receiver_0 = lora.lora_receiver(self.samp_rate, self.capture_freq, self.offset, self.sf, self.samp_rate, 0.01) + self.blocks_throttle_0 = blocks.throttle(gr.sizeof_gr_complex*1, self.samp_rate, True) + self.blocks_message_socket_sink_0 = lora.message_socket_sink() + + self.tb.connect( (self.osmosdr_source_0, 0), (self.blocks_throttle_0, 0)) + self.tb.connect( (self.blocks_throttle_0, 0), (self.lora_lora_receiver_0, 0)) + self.tb.msg_connect( (self.lora_lora_receiver_0, 'frames'), (self.blocks_message_socket_sink_0, 'in')) + + + def __del__(self): + self.lc = None + self.tb = None + self.server.close() + + def setPreDelay(self, delay_s): + self.pre_delay = delay_s + + def setPostDelay(self, delay_s): + self.post_delay = delay_s + + def setTransmitDelay(self, delay_s): + self.trans_delay = delay_s + + def getOutput(self): + return self.testResults + + # + # Listen on socket for data, append to list if any and return list of captured data. + # + def gatherFromSocket(self, amount): + total_data = [] + data = '' + + for i in range(amount): + try: + data = self.server.recv(4096) + if data: + total_data.append(data) + except: + pass + + return total_data + + def transmitRawData(self, data_list): + print ("Transmitting...") + time.sleep(self.pre_delay) + + for x in data_list: + self.lc.send_p2p(x) + time.sleep(self.trans_delay) + + time.sleep(self.post_delay) + + def transmitToCapture(self, data_list): + print ("Start run") + self.tb.start() + self.transmitRawData(data_list) + self.tb.lock() + self.tb.unlock() + # self.tb.wait() + self.tb.stop() + print ("Stop run") + + total_data = self.gatherFromSocket(len(data_list)) + + self.compareDataSets(data_list, total_data) + + def compareDataSets(self, transmitted, received): + # self.testResults.['SF', 'CR', 'passing', 'total', 'rate'] + passing = 0 + total = len(transmitted) + + for idx, val in enumerate(transmitted): + passed = True + + try: + # [6:] removes HDR + data_str = ("".join("{:02x}".format(ord(n)) for n in received[idx]))[6:] + + # print("Test: {0:16s} == {1:16s} ? {2:s}".format(val, data_str, "OK" if val == data_str else "FAIL")) + passed = (val == data_str) + except: + passed = False + + if passed: + passing += 1 + + self.testResults = TestResultData(self.sf, self.codingRate, passing, total, float(passing) / total * 100.0) + + +if __name__ == '__main__': + random.seed(None) + + # Default: [10, 20, 20] + # For an usrp it might me necessary to increase the gain and lower the detection threshold. + # e.g. [32, 38, 38] and t = 0.001, but be careful. + gains = [10, 20, 20] + + ############################################################################ + # SF / CR test with TimesPerSetting packets of len(2,16) on each setting + ############################################################################ + CodingRates = [ "4/5", "4/6", "4/7", "4/8" ] + SpreadingFactors = [ 7, 8, 9, 10, 11, 12 ] + + # CodingRates = [ "4/5"] + # SpreadingFactors = [ 7 ] + + TimesPerSetting = 100 + + # test_results = [] + # + # f = open('./live_example_results_SF_CR_tmp.csv', 'a') + # f.write('SF,CR,PASSED,TOTAL,RATE\n') + # f.close() + # + # for sf_i, sf in enumerate(SpreadingFactors): + # for cr_i, cr in enumerate(CodingRates): + # print "++++++++++ Starting test {0:3d} with SF: {1:2} CR: {2:2}\n".format((sf_i+1)*(cr_i+1), sf, cr) + # + # e = ExamplifyLive(sf, cr, gains) + # + # # Generate array of strings between 2 and 16 in length with chars from 0x0 to 0xF, of length TimesPerSetting + # rdata = [ "".join("{0:1x}".format(random.randrange(0x0, 0xF)) for x in range(random.randrange(2, 17))) for i in range(TimesPerSetting) ] + # # Pad with '0' to even length + # rdata = [ x if len(x) % 2 == 0 else '0'+x for x in rdata ] + # + # e.transmitToCapture(rdata) + # test_results.append(e.getOutput()) + # + # f = open('./live_example_results_SF_CR_tmp.csv', 'a') + # + # res = e.getOutput() + # print ("[SF{0:2d}, CR{1:s}] : Passed rate: {2:d} out of {3:d} ({4:.2f}%)" + # .format(res.SF, res.CR, res.passing, res.total, res.rate)) + # f.write('{0:d},{1:s},{2:d},{3:d},{4:.2f}\n' + # .format(res.SF, res.CR, res.passing, res.total, res.rate)) + # f.close() + # + # e = None + # + # + # # Report + # f = open('./live_example_results_SF_CR.csv', 'w') + # f.write('SF,CR,PASSED,TOTAL,RATE\n') + # + # for res in test_results: + # print ("[SF{0:2d}, CR{1:s}] : Passed rate: {2:d} out of {3:d} ({4:.2f}%)" + # .format(res.SF, res.CR, res.passing, res.total, res.rate)) + # f.write('{0:d},{1:s},{2:d},{3:d},{4:.2f}\n' + # .format(res.SF, res.CR, res.passing, res.total, res.rate)) + # f.close() + + ############################################################################ + # Length test with TimesPerSetting packets of each length len(2,16) on best setting + ############################################################################ + Ideal_SF = 7 + Ideal_CR = "4/8" + test_results_length = [] + + f = open('./live_example_results_length_tmp.csv', 'a') + f.write('SF,CR,LENGTH,PASSED,TOTAL,RATE\n') + f.close() + + for length in range(16, 66, 2): # range(2, 17): range(16, 34, 2): + print "++++++++++ Starting test with length {0:3d} and SF: {1:2} CR: {2:2}\n".format(length, Ideal_SF, Ideal_CR) + + e = ExamplifyLive(Ideal_SF, Ideal_CR, gains) + + # Generate array of strings between 2 and 16 in length with chars from 0x0 to 0xF, of length TimesPerSetting + rdata = [ "".join("{0:1x}".format(random.randrange(0x0, 0xF)) for x in range(length)) for i in range(TimesPerSetting) ] + # Pad with '0' to even length + rdata = [ x if len(x) % 2 == 0 else '0'+x for x in rdata ] + + e.transmitToCapture(rdata) + test_results_length.append( [e.getOutput(), length / 2] ) + + f = open('./live_example_results_length_tmp.csv', 'a') + res = e.getOutput() + f.write('{0:d},{1:s},{2:d},{3:d},{4:d},{5:.2f}\n' + .format(res.SF, res.CR, length / 2, res.passing, res.total, res.rate)) + f.close() + + e = None + + + # Report + f = open('./live_example_results_length.csv', 'a') + f.write('SF,CR,LENGTH,PASSED,TOTAL,RATE\n') + + for res in test_results_length: + print ("[SF{0:2d}, CR{1:s}, len={2:2d}] : Passed rate: {3:3d} out of {4:d} ({5:.2f}%)" + .format(res[0].SF, res[0].CR, res[1], res[0].passing, res[0].total, res[0].rate)) + f.write('{0:d},{1:s},{2:d},{3:d},{4:d},{5:.2f}\n' + .format(res[0].SF, res[0].CR, res[1], res[0].passing, res[0].total, res[0].rate)) + f.close() diff --git a/examples/lora-timings/avg_sd.py b/examples/lora-timings/avg_sd.py new file mode 100644 index 0000000..ba11adc --- /dev/null +++ b/examples/lora-timings/avg_sd.py @@ -0,0 +1,45 @@ +#!/usr/bin/python +import numpy as np + + +files = [ + "lora-time_SF7_grad_idx_DECODE_HEADER", # Run 6 times (9856 samples) + "lora-time_SF7_grad_idx_DECODE_PAYLOAD", + "lora-time_SF7_grad_idx_DETECT", + "lora-time_SF7_grad_idx_PAUSE", + "lora-time_SF7_grad_idx_SYNC", + "lora-time_SF7_grad_idx_only", # Run 3 times + + "lora-time_SF12_grad_idx_DECODE_HEADER", + "lora-time_SF12_grad_idx_DECODE_PAYLOAD", + "lora-time_SF12_grad_idx_DETECT", + "lora-time_SF12_grad_idx_PAUSE", + "lora-time_SF12_grad_idx_SYNC", + "lora-time_SF12_grad_idx_only", + + "lora-time_SF7_fft_idx_DECODE_HEADER", + "lora-time_SF7_fft_idx_DECODE_PAYLOAD", + "lora-time_SF7_fft_idx_DETECT", + "lora-time_SF7_fft_idx_PAUSE", + "lora-time_SF7_fft_idx_SYNC", + "lora-time_SF7_fft_idx_only", + + "lora-time_SF12_fft_idx_DECODE_HEADER", + "lora-time_SF12_fft_idx_DECODE_PAYLOAD", + "lora-time_SF12_fft_idx_DETECT", + "lora-time_SF12_fft_idx_PAUSE", + "lora-time_SF12_fft_idx_SYNC", + "lora-time_SF12_fft_idx_only" +] + +for name in files: + with open("./" + name) as f: + data = f.read() + + data = [float(x) for x in data.split('\n')[0:-1]] + + avg = np.mean(data) + std = np.std(data, dtype=np.float64) + + print("File: {0:s}\n\tLength: {1:d}\n\tAverage: {2:2.10f} ms\n\tStd: {3:2.10f}" + .format(name, len(data), avg, std)) diff --git a/examples/lora-timings/timing-results.txt b/examples/lora-timings/timing-results.txt new file mode 100644 index 0000000..012f3cb --- /dev/null +++ b/examples/lora-timings/timing-results.txt @@ -0,0 +1,33 @@ + SF | method | State | length | Average (ms) | Std +----+----------+----------------+--------+---------------+--------------- + | | DECODE_HEADER | 9856 | 0.1259104846 | 0.0083598489 + | | DECODE_PAYLOAD | 14854 | 0.1240467029 | 0.0058878571 + | | DETECT | 132937 | 0.0111668757 | 0.0863605383 + | grad_idx | PAUSE | 1232 | 0.0010141461 | 0.0011882927 + | | SYNC | 12334 | 0.1369446399 | 0.0072021918 + | | grad idx only | 11118 | 0.1189276336 | 0.0055137726 + | | ==> Total Time | 6 | 53.358s | + 7 |----------+----------------+--------+---------------+--------------- + | | DECODE_HEADER | 9856 | 0.0783100350 | 0.0131321480 + | | DECODE_PAYLOAD | 20440 | 0.0760118956 | 0.0125111634 + | | DETECT | 130172 | 0.0112985811 | 0.0876915475 + | fft | PAUSE | 1232 | 0.0010230820 | 0.0011295359 + | | SYNC | 12320 | 0.1378892978 | 0.0080917100 + | | fft only | 13512 | 0.0706166163 | 0.0098948093 + | | ==> Total Time | 2 | 53.384s | +----+----------+----------------+--------+---------------+--------------- + | | DECODE_HEADER | 9912 | 3.7912548676 | 0.2630857073 + | | DECODE_PAYLOAD | 12320 | 3.8091647075 | 0.2747977905 + | | DETECT | 3723 | 16.7026039911 | 21.1052133394 + | grad_idx | PAUSE | 1239 | 0.0012905004 | 0.0002020981 + | | SYNC | 10725 | 4.2126484056 | 0.2068078822 + | | grad idx only | 10002 | 3.7576346853 | 0.2498984209 + | | ==> Total Time | 2 | 206.236s | + 12 |----------+----------------+--------+---------------+--------------- + | | DECODE_HEADER | 9912 | 2.2331703327 | 0.2301535307 + | | DECODE_PAYLOAD | 12390 | 2.2217885565 | 0.2009724076 + | | DETECT | 3744 | 16.3153207676 | 20.9261960413 + | fft | PAUSE | 1239 | 0.0012535383 | 0.0001955853 + | | SYNC | 10613 | 4.2222761290 | 0.2295213521 + | | fft only | 10032 | 2.2098768243 | 0.2333799337 + | | ==> Total Time | 2 | 206.2945s | diff --git a/lib/dbugr.hpp b/lib/dbugr.hpp index 4760d34..edd6d07 100644 --- a/lib/dbugr.hpp +++ b/lib/dbugr.hpp @@ -83,6 +83,7 @@ DBGR_out_file.close(); \ if(PAUSE) DBGR_PAUSE(MSG); \ } while(0) + #endif // Chrono @@ -91,14 +92,39 @@ #define DBGR_START_TIME_MEASUREMENT_SAME_SCOPE(OUT, MSG) #define DBGR_INTERMEDIATE_TIME_MEASUREMENT() #define DBGR_STOP_TIME_MEASUREMENT(OUT) + #define DBGR_TIME_MEASUREMENT_TO_FILE(PRE) #else static int64_t DBGR_global_time = 0ll; static bool DBGR_totalled_time = false; static bool DBGR_intermediate_time = false; + static bool DBGR_time_to_file = false; + static std::string DBGR_fileprefix = ""; + static std::string DBGR_filename = ""; + + + #define DBGR_QUICK_TO_FILE(FILEPATH, APPEND, DATA, SIZE, FORMAT) \ + do { \ + int32_t DBGR_j; \ + char DBGR_buf[20]; \ + std::ofstream DBGR_out_file; \ + DBGR_out_file.open(FILEPATH, std::ios::out | (APPEND ? std::ios::app : std::ios::out )); \ + \ + for (DBGR_j = 0; DBGR_j < int32_t(SIZE); DBGR_j++) { \ + sprintf(DBGR_buf, FORMAT, DATA[DBGR_j]); \ + DBGR_out_file.write(DBGR_buf, strlen(DBGR_buf)); \ + } \ + \ + if (APPEND) DBGR_out_file.write("\n", sizeof(char)); \ + \ + } while(0) \ + #define DBGR_START_TIME_MEASUREMENT(OUT, MSG) \ DBGR_intermediate_time = OUT; \ - if (DBGR_intermediate_time) printf("[CHRONO] : Start in % 15s", MSG); \ + if (DBGR_intermediate_time) printf("[CHRONO] : Start in % 15s", std::string(MSG).c_str()); \ + if (DBGR_time_to_file) { \ + DBGR_filename = "/tmp/" + DBGR_fileprefix + "_" + std::string(MSG); \ + } \ DBGR_totalled_time = false; \ auto DBGR_start_time = std::chrono::high_resolution_clock::now(); @@ -112,13 +138,22 @@ if (!DBGR_totalled_time) { \ int64_t DBGR_duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - DBGR_start_time).count(); \ DBGR_global_time += DBGR_duration; \ - if (DBGR_intermediate_time) printf(" and took %fms\n", DBGR_duration / 1e6); \ + const float DBGR_ms_duration[] = { DBGR_duration / 1e6f }; \ + if (DBGR_intermediate_time) printf(" and took %fms\n", DBGR_ms_duration[0]); \ + if (DBGR_time_to_file) { \ + DBGR_QUICK_TO_FILE(DBGR_filename, true, DBGR_ms_duration, 1, "%f"); \ + } \ } #define DBGR_STOP_TIME_MEASUREMENT(OUT) \ if (OUT) printf("[CHRONO] : Packet took %fms to process.\n", DBGR_global_time / 1e6); \ DBGR_global_time = 0ll; \ DBGR_totalled_time = true; + + #define DBGR_TIME_MEASUREMENT_TO_FILE(PRE) \ + DBGR_time_to_file = true; \ + if (DBGR_fileprefix.length() == 0) DBGR_fileprefix = "lora-time_" + std::string(PRE); + #endif #endif // DBUGR_HPP diff --git a/lib/decoder_impl.cc b/lib/decoder_impl.cc index 71fcfd1..03fa9c8 100644 --- a/lib/decoder_impl.cc +++ b/lib/decoder_impl.cc @@ -101,6 +101,7 @@ namespace gr { this->d_delay_after_sync = this->d_samples_per_symbol / 4u; this->d_number_of_bins = (uint32_t)(1u << this->d_sf); this->d_number_of_bins_hdr = this->d_number_of_bins / 4u; + this->d_decim_factor = this->d_samples_per_symbol / this->d_number_of_bins; this->d_energy_threshold = 0.01f; @@ -109,7 +110,7 @@ namespace gr { 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; + std::cout << "Decimation: \t\t" << this->d_decim_factor << std::endl; //std::cout << "Magnitude threshold:\t" << this->d_energy_threshold << std::endl; this->build_ideal_chirps(); @@ -126,8 +127,6 @@ namespace gr { for (uint32_t i = 0u; 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 @@ -162,8 +161,8 @@ namespace gr { this->d_upchirp_ifreq.resize(this->d_samples_per_symbol); 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; + const double f0 = (this->d_bw / 2.0); + const double pre_dir = 2.0 * M_PI; double t; gr_complex cmx = gr_complex(1.0f, 1.0f); @@ -290,8 +289,8 @@ namespace gr { } /** - * Calculate normalized cross correlation of real values. - * See https://en.wikipedia.org/wiki/Cross-correlation#Normalized_cross-correlation. + * Calculate normalized cross correlation of real values. + * See https://en.wikipedia.org/wiki/Cross-correlation#Normalized_cross-correlation. */ float decoder_impl::cross_correlate_ifreq(const float *samples_ifreq, const std::vector& ideal_chirp, const uint32_t from_idx, const uint32_t to_idx) { float result = 0.0f; @@ -314,15 +313,20 @@ namespace gr { float samples_ifreq[window]; this->instantaneous_frequency(samples, samples_ifreq, window); - return this->cross_correlate_ifreq(samples_ifreq, this->d_downchirp_ifreq, 0u, window); + return this->cross_correlate_ifreq(samples_ifreq, this->d_downchirp_ifreq, 0u, window - 1u); } + /** + * 1. Skip through the window and look for a falling edge + * 2. Get the upper and lower bound of the upchrip edge, or the local maximum and minimum + * 3. Look for the highest cross correlation index between these points and return + */ float decoder_impl::sliding_norm_cross_correlate_upchirp(const float *samples_ifreq, const uint32_t window, int32_t *index) { bool found_change = false; uint32_t local_max_idx = 0u, local_min_idx; const uint32_t coeff = (this->d_sf + this->d_sf + this->d_sf / 2u); - const uint32_t len = this->d_samples_per_symbol; + const uint32_t len = this->d_samples_per_symbol - 1u; float max_correlation = 0.0f; @@ -340,19 +344,17 @@ namespace gr { } // Find top and bottom of falling edge after first upchirp in window - local_max_idx = std::max_element(samples_ifreq + gr::lora::clamp((int)(local_max_idx - 2u * coeff), 0, (int)window), - samples_ifreq + gr::lora::clamp(local_max_idx + coeff, 0u, window)) - samples_ifreq; - local_min_idx = std::min_element(samples_ifreq + gr::lora::clamp(local_max_idx + 1u, 0u, window), - samples_ifreq + gr::lora::clamp(local_max_idx + 3u * coeff, 0u, window)) - samples_ifreq; + local_max_idx = std::max_element(samples_ifreq + gr::lora::clamp((int)(local_max_idx - 2u * coeff), 0, (int)window), + samples_ifreq + gr::lora::clamp(local_max_idx + coeff, 0u, window)) - samples_ifreq; + local_min_idx = std::min_element(samples_ifreq + gr::lora::clamp(local_max_idx + 1u, 0u, window), + samples_ifreq + gr::lora::clamp(local_max_idx + 3u * coeff, 0u, window)) - samples_ifreq; // Cross correlate between start and end of falling edge instead of entire window for (uint32_t i = local_max_idx; i < local_min_idx; i++) { - const float max_corr = this->cross_correlate_ifreq(samples_ifreq + i, this->d_upchirp_ifreq, 0u, /*std::min(len, window - i)*/ len); + const float max_corr = this->cross_correlate_ifreq(samples_ifreq + i, this->d_upchirp_ifreq, 0u, /*std::min(len, window - i - 1u)*/ len); if (max_corr > max_correlation) { *index = i; max_correlation = max_corr; - } else if (max_corr < max_correlation) { - break; } } @@ -402,7 +404,7 @@ namespace gr { /** * Currently unused. */ - unsigned int decoder_impl::get_shift_fft(const gr_complex *samples) { + uint32_t decoder_impl::get_shift_fft(const gr_complex *samples) { float fft_mag[this->d_number_of_bins]; gr_complex mult_hf[this->d_samples_per_symbol]; @@ -428,7 +430,7 @@ namespace gr { // Perform decimation for (uint32_t i = 0u; i < this->d_number_of_bins; i++) { - firdecim_crcf_execute(this->d_decim, &mult_hf[this->d_decim_factor * i], &d_mult[i]); + firdecim_crcf_execute(this->d_decim, &mult_hf[this->d_decim_factor * i], &this->d_mult[i]); } samples_to_file("/tmp/resampled", &this->d_mult[0], this->d_number_of_bins, sizeof(gr_complex)); @@ -447,30 +449,36 @@ namespace gr { return (std::max_element(fft_mag, fft_mag + this->d_number_of_bins) - fft_mag); } - unsigned int decoder_impl::max_frequency_gradient_idx(const gr_complex *samples) { + uint32_t decoder_impl::max_frequency_gradient_idx(const gr_complex *samples) { float samples_ifreq[this->d_samples_per_symbol]; samples_to_file("/tmp/data", &samples[0], this->d_samples_per_symbol, sizeof(gr_complex)); this->instantaneous_frequency(samples, samples_ifreq, this->d_samples_per_symbol); - const uint32_t osr = this->d_samples_per_symbol / this->d_number_of_bins; - for (uint32_t i = 0u; i < this->d_number_of_bins - 1u; i++) { - if (samples_ifreq[osr*i] - samples_ifreq[osr * (i + 1u)] > 0.2f) { + if (samples_ifreq[this->d_decim_factor * i] - samples_ifreq[this->d_decim_factor * (i + 1u)] > 0.2f) { return i + 1u; } } - return (samples_ifreq[0u] - samples_ifreq[osr]) - > (samples_ifreq[(this->d_number_of_bins - 1u) * osr] - samples_ifreq[this->d_number_of_bins * osr]) + const float zero_bin = samples_ifreq[0u] - samples_ifreq[this->d_decim_factor]; + const float high_bin = samples_ifreq[(this->d_number_of_bins - 2u) * this->d_decim_factor] - samples_ifreq[(this->d_number_of_bins - 1u) * this->d_decim_factor]; + + return zero_bin > high_bin ? 0u : this->d_number_of_bins; } bool decoder_impl::demodulate(const gr_complex *samples, const bool is_header) { +// DBGR_TIME_MEASUREMENT_TO_FILE("SFxx_method"); + +// DBGR_START_TIME_MEASUREMENT(false, "only"); + uint32_t bin_idx = this->max_frequency_gradient_idx(samples); //uint32_t bin_idx = this->get_shift_fft(samples); +// DBGR_INTERMEDIATE_TIME_MEASUREMENT(); + // Header has additional redundancy if (is_header) { bin_idx /= 4u; @@ -480,14 +488,14 @@ namespace gr { const uint32_t word = bin_idx ^ (bin_idx >> 1u); #ifndef NDEBUG - this->d_debug << gr::lora::to_bin(word, is_header ? this->d_sf - 2 : this->d_sf) << " " << bin_idx << std::endl; + this->d_debug << gr::lora::to_bin(word, is_header ? this->d_sf - 2u : 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); + this->deinterleave(is_header ? this->d_sf - 2u : this->d_sf); return true; // Signal that a block is ready for decoding } @@ -495,41 +503,31 @@ namespace gr { return false; // We need more words in order to decode a block } + /** + * Correct the interleaving by extracting each column of bits after rotating to the left. + *
(The words were interleaved diagonally, by rotating we make them straight into columns.) + */ void decoder_impl::deinterleave(const uint32_t ppm) { - const unsigned int bits_per_word = this->d_words.size(); + const uint32_t bits_per_word = this->d_words.size(); + const uint32_t offset_start = ppm - 1u; + + std::vector words_deinterleaved(ppm, 0u); if (bits_per_word > 8u) { // Not sure if this can ever occur. It would imply coding rate high than 4/8 e.g. 4/9. std::cerr << "[LoRa Decoder] WARNING : Deinterleaver: More than 8 bits per word. uint8_t will not be sufficient!\nBytes need to be stored in intermediate array and then packed into words_deinterleaved!" << std::endl; } - std::deque words_deinterleaved; - uint32_t offset_start = ppm - 1u, offset_diag, i; - uint8_t d; + for (uint32_t i = 0u; i < bits_per_word; i++) { + const uint32_t word = gr::lora::rotl(this->d_words[i], i, ppm); - for (i = 0u; i < ppm; i++) { - d = 0u; - offset_diag = offset_start; - - for (uint32_t j = 0u; j < bits_per_word; j++) { - const uint8_t power = 1u << j; - const uint32_t power_check = 1u << offset_diag; - - if (this->d_words[j] & power_check) { // Mask triggers - d += power; - } - - if (offset_diag) offset_diag--; - else offset_diag = ppm - 1u; + for (uint32_t j = (1u << offset_start), x = offset_start; j; j >>= 1u, x--) { + words_deinterleaved[x] |= !!(word & j) << i; } - - offset_start--; - words_deinterleaved.push_front(d); } #ifndef NDEBUG - std::vector wd(words_deinterleaved.begin(), words_deinterleaved.begin() + ppm - 1u); - print_vector(this->d_debug, wd, "D", sizeof(uint8_t) * 8u); + print_vector(this->d_debug, words_deinterleaved, "D", sizeof(uint8_t) * 8u); #endif // Add to demodulated data @@ -539,16 +537,13 @@ namespace gr { this->d_words.clear(); } - int decoder_impl::decode(uint8_t *out_data, const bool is_header) { - const uint8_t shuffle_pattern[] = {7, 6, 3, 4, 2, 1, 0, 5}; + void decoder_impl::decode(uint8_t *out_data, const bool is_header) { + static const uint8_t shuffle_pattern[] = {7, 6, 3, 4, 2, 1, 0, 5}; this->deshuffle(shuffle_pattern, is_header); this->dewhiten(is_header ? gr::lora::prng_header : this->d_whitening_sequence); 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; @@ -567,12 +562,10 @@ namespace gr { this->d_data.insert(this->d_data.end(), out_data, out_data + 3u); std::cout << result.str(); } - - return 0; } void decoder_impl::deshuffle(const uint8_t *shuffle_pattern, const bool is_header) { - const uint32_t to_decode = is_header ? 5 : this->d_demodulated.size(); + const uint32_t to_decode = is_header ? 5u : this->d_demodulated.size(); const uint32_t len = sizeof(shuffle_pattern) / sizeof(uint8_t); uint8_t original, result; @@ -591,13 +584,13 @@ namespace gr { #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); + print_vector_raw(this->d_debug, this->d_words_deshuffled, sizeof(uint8_t) * 8u); this->d_debug << std::endl; #endif // 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.erase(this->d_demodulated.begin(), this->d_demodulated.begin() + 5u); } else { this->d_demodulated.clear(); } @@ -630,13 +623,14 @@ namespace gr { } void decoder_impl::hamming_decode(uint8_t *out_data) { - uint8_t data_indices[4] = {1, 2, 3, 5}; + static const uint8_t data_indices[4] = {1, 2, 3, 5}; switch(this->d_cr) { - case 4: case 3: + case 4: case 3: // Hamming(8,4) or Hamming(7,4) 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 + case 2: case 1: // Hamming(6,4) or Hamming(5,4) + // 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, 4u, out_data); break; } @@ -712,6 +706,9 @@ namespace gr { return -1; } + /** + * Look for a signal with an absolute value above `this->d_energy_threshold`. + */ int decoder_impl::find_preamble_start_fast(const gr_complex *samples) { const uint32_t decimation = this->d_corr_decim_factor * 4u; const uint32_t decim_size = this->d_samples_per_symbol / decimation; @@ -758,7 +755,9 @@ namespace gr { const gr_complex *raw_input = (gr_complex *) input_items[1]; // float *out = (float *)output_items[0]; - DBGR_START_TIME_MEASUREMENT(false, gr::lora::DecoderStateToString(this->d_state).c_str()); +// DBGR_TIME_MEASUREMENT_TO_FILE("SF7_fft_idx"); + + DBGR_START_TIME_MEASUREMENT(false, gr::lora::DecoderStateToString(this->d_state)); switch (this->d_state) { case gr::lora::DecoderState::DETECT: { @@ -769,9 +768,9 @@ namespace gr { if (i != -1) { int32_t index_correction = 0; - float c = this->detect_upchirp(&input[i], - this->d_samples_per_symbol * 2u, - &index_correction); + const float c = this->detect_upchirp(&input[i], + this->d_samples_per_symbol * 2u, + &index_correction); if (c > 0.9f) { #ifndef NDEBUG @@ -795,7 +794,7 @@ namespace gr { } case gr::lora::DecoderState::SYNC: { - float c = this->detect_downchirp(input, this->d_samples_per_symbol); + const float c = this->detect_downchirp(input, this->d_samples_per_symbol); #ifndef NDEBUG this->d_debug << "Cd: " << c << std::endl; @@ -889,7 +888,7 @@ namespace gr { this->d_data.clear(); DBGR_STOP_TIME_MEASUREMENT(true); - DBGR_PAUSE(); +// DBGR_PAUSE(); } } diff --git a/lib/decoder_impl.h b/lib/decoder_impl.h index 367ffce..5b20fb5 100644 --- a/lib/decoder_impl.h +++ b/lib/decoder_impl.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2016 Pieter Robyns. + * Copyright 2017 Pieter Robyns, William Thenaers. * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,10 +23,8 @@ #include #include "lora/decoder.h" -#include #include #include -#include #include #define DECIMATOR_FILTER_SIZE (2*8*1 + 1) // 2*decim_factor*delay+1 @@ -34,6 +32,9 @@ namespace gr { namespace lora { + /** + * \brief **DecoderState** : Each state the LoRa decoder can be in. + */ enum class DecoderState { DETECT, SYNC, @@ -43,110 +44,427 @@ namespace gr { STOP }; + /** + * \brief Return the DecoderState as string for debugging purposes. + * + * \param s + * The state to return to string. + */ static std::string DecoderStateToString(DecoderState s) { static std::string DecoderStateLUT[] = { "DETECT", "SYNC", "PAUSE", "DECODE_HEADER", "DECODE_PAYLOAD", "STOP" }; return DecoderStateLUT[ (size_t)s ]; } + /** + * \brief **LoRa Decoder** + *
The main class for the LoRa decoder. + * Contains all variables and methods necessary for succesfully decoding LoRa PHY. + *
Only the sample rate and spreading factor are needed. + * The other settings, like packet length and coding rate, are extracted from the (explicit) HDR. + */ class decoder_impl : public decoder { private: - DecoderState d_state; + DecoderState d_state; ///< Holds the current state of the decoder (state machine). - /// using std::complex = gr_complex - std::vector d_downchirp; - std::vector d_downchirp_ifreq; + std::vector d_downchirp; ///< The complex ideal downchirp. + std::vector d_downchirp_ifreq; ///< The instantaneous frequency of the ideal downchirp. - std::vector d_upchirp; - std::vector d_upchirp_ifreq; + std::vector d_upchirp; ///< The complex ideal upchirp. + std::vector d_upchirp_ifreq; ///< The instantaneous frequency of the ideal upchirp. - std::vector d_fft; - std::vector d_mult; + std::vector d_fft; ///< Vector containing the FFT resuls. + std::vector d_mult; ///< Vector containing the FFT decimation. - 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; - int32_t d_payload_symbols; - uint32_t d_payload_length; - uint32_t d_corr_fails; - float d_energy_threshold; - const uint8_t *d_whitening_sequence; + uint8_t d_sf; ///< The Spreading Factor. + uint32_t d_bw; ///< The receiver bandwidth (fixed to `125kHz`). + uint8_t d_cr; ///< The Coding Rate. + double d_bits_per_second; ///< Indicator of how many bits are transferred each second. + uint32_t d_delay_after_sync; ///< The amount of samples to skip in `DecoderState::PAUSE`. + uint32_t d_samples_per_second; ///< The amount of samples taken per second by GNU Radio. + double d_symbols_per_second; ///< Indicator of how many symbols (read: chirps) are transferred each second. + uint32_t d_bits_per_symbol; ///< The amount of bits each of the symbols contain. + uint32_t d_samples_per_symbol; ///< The amount of samples in one symbol. + uint32_t d_number_of_bins; ///< Indicates in how many parts or bins a symbol is decimated, i.e. the max value to decode out of one payload symbol. + uint32_t d_number_of_bins_hdr; ///< Indicates in how many parts or bins a HDR symbol is decimated, i.e. the max value to decode out of one HDR symbol. + int32_t d_payload_symbols; ///< The amount of symbols needed to decode the payload. Calculated from an indicator in the HDR. + uint32_t d_payload_length; ///< The amount of words after decoding the HDR or payload. Calculated from an indicator in the HDR. + uint32_t d_corr_fails; ///< Indicates how many times the correlation failed. After some tries, the state will revert to `DecoderState::DETECT`. + float d_energy_threshold; ///< The absolute threshold to distinguish signal from noise. + const uint8_t *d_whitening_sequence; ///< A pointer to the whitening sequence to be used in decoding. Determined by the SF in the ctor. - std::vector d_words; - std::vector d_demodulated; - std::vector d_words_deshuffled; - std::vector d_words_dewhitened; - std::vector d_data; + std::vector d_words; ///< Vector containing the demodulated words. + std::vector d_demodulated; ///< Vector containing the words after deinterleaving. + std::vector d_words_deshuffled; ///< Vector containing the words after deshuffling. + std::vector d_words_dewhitened; ///< Vector containing the words after dewhitening. + std::vector d_data; ///< Vector containing the words after Hamming decode or the final decoded words. - std::ofstream d_debug_samples; - std::ofstream d_debug; + std::ofstream d_debug_samples; ///< Debug utputstream for complex values. + std::ofstream d_debug; ///< Outputstream for the debug log. - fftplan d_q; + fftplan d_q; ///< The LiquidDSP::FFT_Plan. - 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; + float d_decim_h[DECIMATOR_FILTER_SIZE]; ///< The reversed decimation filter for LiquidDSP. + uint32_t d_corr_decim_factor; ///< The decimation factor used in finding the preamble start. + uint32_t d_decim_factor; ///< The amount of samples (data points) in each bin. + firdecim_crcf d_decim = nullptr; ///< The LiquidDSP FIR decimation filter used to decimate the FFT imput. + float d_cfo_estimation; ///< An estimation for the current Center Frequency Offset. + double d_dt; ///< Indicates how fast the frequency changes in a symbol (chirp). - bool calc_energy_threshold(const gr_complex *samples, const uint32_t window_size, const float threshold); - void build_ideal_chirps(void); - void samples_to_file(const std::string path, const gr_complex *v, const uint32_t length, const uint32_t elem_size); - void samples_debug(const gr_complex *v, const uint32_t length); - float sliding_norm_cross_correlate_upchirp(const float *samples_ifreq, const uint32_t window, int32_t *index); - float detect_downchirp(const gr_complex *samples, const uint32_t window); - float detect_upchirp(const gr_complex *samples, const uint32_t window, int32_t *index); - float cross_correlate(const gr_complex *samples_1, const gr_complex *samples_2, const uint32_t window); - float cross_correlate_ifreq(const float *samples_ifreq, const std::vector& ideal_chirp, const uint32_t from_idx, const uint32_t to_idx); + /** + * \brief Calculates the average energy from the given samples and returns whether its higher than the given threshold. + * + * \param samples + * The samples to calculate and compare the energy to. + * \param window_size + * The length of the samples array. + * \param threshold + * The threshold to compare to. + */ + bool calc_energy_threshold(const gr_complex *samples, const uint32_t window_size, const float threshold); + + /** + * \brief Generate the ideal up- and downchirps. + */ + void build_ideal_chirps(void); + + /** + * \brief Debug method to dump the given complex array to the given file in binary format. + * + * \param path + * The path to the file to dump to. + * \param v + * The complex array to dump. + * \param length + * Length of said array. + * \param elem_size + * `sizeof` the data in the array. + */ + void samples_to_file(const std::string path, const gr_complex *v, const uint32_t length, const uint32_t elem_size); + + /** + * \brief Write the given complex array to the debug outputstream. + * + * \param v + * The complex array. + * \param length + * Length of said complex array. + */ + void samples_debug(const gr_complex *v, const uint32_t length); + + /** + * \brief Correct the shift of the given symbol to match the ideal upchirp by sliding cross correlating. + * + * \param samples_ifreq + * The symbol to shift. + * \param window + * The window in which the symbol can be shifted (length of given sample array). + * \param index + * The new start index in the window for the found upchirp. + * \return Also return the correlation coefficient. + */ + float sliding_norm_cross_correlate_upchirp(const float *samples_ifreq, const uint32_t window, int32_t *index); + + /** + * \brief Base method to start downchirp correlation and return the correlation coefficient. + * + * \param samples + * The complex array of samples to detect a downchirp in. + * \param window + * Length of said sample. + */ + float detect_downchirp(const gr_complex *samples, const uint32_t window); + + /** + * \brief Base method to start upchirp detection by calling `sliding_norm_cross_correlate_upchirp`. + *
Sets up the instantaneous frequency of the given complex symbol. + * + * \param samples + * The complex array of samples to detect an upchirp in. + * \param window + * Length of said sample. + * \param index + * The index to shift with so the upchirp is correctly synced inside its window. + * \return Also return the correlation coefficient. + */ + float detect_upchirp(const gr_complex *samples, const uint32_t window, int32_t *index); + + /** + * \brief Returns the correlation coefficient when correlating the given complex symbols in the given window. + * + * \param samples_1 + * The first complex symbol to correlate with. + * \param samples_2 + * The second complex symbol to correlate with. + * \param window + * The window in which to perform correlation. + */ + float cross_correlate(const gr_complex *samples_1, const gr_complex *samples_2, const uint32_t window); + + /** + * \brief Returns the correlation coefficient when correlating the given symbols in the given range. + * + * \param samples_ifreq + * The instantaneous frequency of the symbol to correlate with. + * \param ideal_chirp + * The vector containing the ideal chirp to correlate with. + * \param from_idx + * Correlation start index. + * \param to_idx + * Correlation end index. + */ + float cross_correlate_ifreq(const float *samples_ifreq, const std::vector& ideal_chirp, const uint32_t from_idx, const uint32_t to_idx); + + /** + * \brief Returns the index to shift the given symbol so that it overlaps the ideal upchirp. + * + * \param samples_ifreq + * The instantaneous frequency of the symbol to analyse. + * \param window + * Length of said symbol. + */ int32_t slide_phase_shift_upchirp_perfect(const float* samples_ifreq, const uint32_t window); - unsigned int get_shift_fft(const gr_complex *samples); + /** + * \brief Returns the index of the bin containing the frequency change by using FFT. + * + * \param samples + * The complex symbol to analyse. + */ + uint32_t get_shift_fft(const gr_complex *samples); - void determine_cfo(const gr_complex *samples); - void correct_cfo(gr_complex *samples, const uint32_t num_samples); - int find_preamble_start(const gr_complex *samples); - int find_preamble_start_fast(const gr_complex *samples); + /** + * \brief Determine the center frequency offset in the given symbol. + * + * \param samples + * The complex symbol to analyse. + */ + void determine_cfo(const gr_complex *samples); - unsigned int max_frequency_gradient_idx(const gr_complex *samples); + /** + * \brief Correct the center frequency offset in the given symbol. + * + * \param samples + * The complex symbol to analyse. + * \param num_samples + * Length of said symbol. + */ + void correct_cfo(gr_complex *samples, const uint32_t num_samples); - bool demodulate(const gr_complex *samples, const bool is_header); - void deinterleave(const uint32_t ppm); - int decode(uint8_t *out_data, const bool is_header); - void deshuffle(const uint8_t *shuffle_pattern, const bool is_header); - void dewhiten(const uint8_t *prng); - void hamming_decode(uint8_t *out_data); - void nibble_reverse(uint8_t *out_data, const uint32_t len); - float stddev(const float *values, const uint32_t len, const float mean); + /** + * \brief Find a valid signal that identifies the start of the preamble. + * + * \param samples + * The complex symbol to analyse. + */ + int find_preamble_start(const gr_complex *samples); + /** + * \brief Skip through the given symbol to find a signal. + * + * \param samples + * The complex symbol to analyse. + */ + int find_preamble_start_fast(const gr_complex *samples); + + /** + * \brief Returns the index of the bin containing the frequency change. + * + * \param samples + * The complex symbol to analyse. + */ + uint32_t max_frequency_gradient_idx(const gr_complex *samples); + + /** + * \brief Demodulate the given symbol and return true if all expected symbols have been parsed. + * + * \param samples + * The complex symbol to demodulate. + * \param is_header + * Whether the demodulated words were from the HDR. + */ + bool demodulate(const gr_complex *samples, const bool is_header); + + /** + * \brief Deinterleave the raw demodulated words by reversing the interleave pattern. + * + * \param ppm + * The amount of words that zere interleaved. Depends on `SF`. + */ + void deinterleave(const uint32_t ppm); + + /** + * \brief The process of decoding the demodulated words to get the actual payload. + *
1. Deshuffle the words + *
2. Dewhiten the words + *
3. Hamming decoding + *

The result is printed to the standard outputstream + *
and passed as a `blob` to the `frames` output in GRC, for further use. + * + * \param out_data + * An array to store the decoded payload words. + * \param is_header + * Whether the demodulated words were from the HDR. + */ + void decode(uint8_t *out_data, const bool is_header); + + /** + * \brief Deshuffle the demodulated words by the given pattern. + * + * \param shuffle_pattern + * The order in which the bits appear. + * \param is_header + * Whether the demodulated words were from the HDR. + */ + void deshuffle(const uint8_t *shuffle_pattern, const bool is_header); + + /** + * \brief Dewhiten the deshuffled words by XORing with the whitening sequence. + * + * \param prng + * The whitening sequence to XOR with. + */ + void dewhiten(const uint8_t *prng); + + /** + * \brief Use Hamming to decode the dewhitened words. + *
- CR 4 or 3: Hamming(8,4) or Hamming(7,4) with parity correction + *
- CR 2 or 1: Extract data only (can only find parity errors, not correct them) + * + * \param out_data + * The result after decoding the words. + */ + void hamming_decode(uint8_t *out_data); + + /** + * \brief Reverse the nibbles for each byte in the given array. + *
`MSB LSB` nibbles --> `LSB MSB` + * + * \param out_data + * The array of bytes to reverse the nibbles in. + * \param len + * Length of said array. + */ + void nibble_reverse(uint8_t *out_data, const uint32_t len); + + /** + * \brief Return the standard deviation for the given array. + *
Used for cross correlating. + * + * \param values + * The array to calculate the standard deviation for. + * \param len + * Length of said array. + * \param mean + * The mean (average) of the values in the array. + */ + float stddev(const float *values, const uint32_t len, const float mean); + + /** + * \brief Calculate the instantaneous phase for the given complex symbol. + * + * \param in_samples + * The complex array to calculate the instantaneous phase for. + * \param out_iphase + * The output `float` array containing the instantaneous phase. + * \param window + * The size of said arrays. + */ inline void instantaneous_phase(const gr_complex *in_samples, float *out_iphase, const uint32_t window); + + /** + * \brief Calculate the instantaneous frequency for the given complex symbol. + * + * \param in_samples + * The complex array to calculate the instantaneous frequency for. + * \param out_ifreq + * The output `float` array containing the instantaneous frequency. + * \param window + * The size of said arrays. + */ inline void instantaneous_frequency(const gr_complex *in_samples, float *out_ifreq, const uint32_t window); + /** + * \brief Return the coding rate from the given HDR byte from a LUT. + * + * \param bytevalue + * The LSB nibble to decode. + */ uint8_t lookup_cr(const uint8_t bytevalue); + + /** + * \brief Output a complex array to the GRC `"debug"` port. + * + * \param raw_samples + * The complex array to output. + * \param num_samples + * Size of said complex array. + */ void msg_raw_chirp_debug(const gr_complex *raw_samples, const uint32_t num_samples); + + /** + * \brief Unimplemented + * + * \param frame_bytes + * \param frame_len + */ void msg_lora_frame(const uint8_t *frame_bytes, const uint32_t frame_len); public: + /** + * \brief Default ctor. + * + * \param samp_rate + * The sample rate of the input signal given to `work` later. + * \param sf + * The expected spreqding factor. + */ decoder_impl(float samp_rate, uint8_t sf); + + /** + * Default dtor. + */ ~decoder_impl(); - /// Where all the action really happens + /** + * \brief The main method called by GNU Radio to perform tasks on the given input. + * + * \param noutput_items + * The requested amount of output items. + * \param input_items + * An array with samples to process. + * \param output_items + * An array to return processed samples. + * \return Returns the amount of output items generated. + */ int work(int noutput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items); - /// GRC interfaces + /** + * \brief Set th current spreading factor. + *
**Currently not supported, restart GNU Radio with different settings instead.** + * \param sf + * The new spreading factor. + */ virtual void set_sf(const uint8_t sf); + + /** + * \brief Set the current sample rate. + *
**Currently not supported, restart GNU Radio with different settings instead.** + * + * \param samp_rate + * The new sample rate. + */ virtual void set_samp_rate(const float samp_rate); + + /** + * \brief Set the absolute threshold to distinguish signal from noise. + *
Should be around 0.01f (default) for normal environments, + *
or as low as 0.001f for the very noise-resistant USRP. + * + * \param threshold + * The new threshold value. + */ virtual void set_abs_threshold(const float threshold); }; } // namespace lora diff --git a/lib/tables.h b/lib/tables.h index 552e3e6..03e7bcc 100644 --- a/lib/tables.h +++ b/lib/tables.h @@ -1,12 +1,42 @@ +/* -*- c++ -*- */ +/* + * Copyright 2017 Pieter Robyns, William Thenaers. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + #ifndef TABLES_H #define TABLES_H +/** + * This file contians the found whitening sequences to be XORed with a word. + */ namespace gr { namespace lora { + + /** + * HDR whitening sequence. + */ const uint8_t prng_header[] = { 0x22, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + /** + * Whitening sequence for payload with SF 7. + */ const uint8_t prng_payload_sf7[] = { // OK 0xdc, 0xec, 0xb0, 0xf4, 0x9c, 0xfc, 0xc4, 0xdc, 0x10, 0xf8, 0x40, 0x34, 0xa8, 0x5c, 0xf0, 0x94, 0x60, 0x08, 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, 0x04, 0x24, 0x80, 0x98, 0x40, 0xa4, 0x58, 0x04, 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, 0x08, 0xb4, 0x00, 0x5c, 0x68, 0x94, 0xe4, 0x08, 0xb0, 0x00, 0x5c, 0x20, 0xdc, 0x4c, 0xa0, 0x60, 0x98, 0x70, 0xec, 0x0c, 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, 0x04, 0xc4, 0x80, 0xa8, 0x08, 0xb8, 0xb8, 0x80, 0xd8, 0x80, 0x2c, 0x40, 0xf0, 0x58, 0x60, 0xa0, 0xb0, 0xd0, 0x14, 0x0c, 0x74, 0xd4, 0x38, 0xe4, 0x54, 0x70, 0x0c, 0x44, 0xd4, 0x6c, 0xe4, 0x30, 0x70, 0x74, 0x0c, 0x28, 0xc4, 0x50, 0xa8, 0x24, 0xf0, 0x20, 0x60, 0x14, 0xb0, 0xcc, 0x5c, 0x88, 0x94, 0xd4, 0x08, 0xe4, 0x48, 0x70, 0xc0, 0x0c, 0x7c, 0x8c, 0x7c, 0x00, 0xbc, 0x68, 0xa4, 0xe4, 0x5c, 0xf8, 0x64, 0xbc, 0x10, 0xec, 0x50, 0xbc, 0xe4, 0x54, 0x38, 0x44, 0xec, 0x34, 0xbc, 0xd4, 0x54, 0x24, 0x0c, 0x68, 0xd4, 0xf4, 0xe4, 0xb4, 0x38, 0x74, 0xa4, 0x28, 0x5c, 0x50, 0x2c, 0x24, 0xb8, 0x68, 0x80, 0xbc, 0xc8, 0x54, 0xe8, 0x0c, 0x88, 0x9c, 0x8c, 0x04, 0x00, 0x08, 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, 0x0c, 0xc8, 0xc4, 0x60, 0xe0, 0x38, 0x10, 0xa4, 0x18, 0x14, 0x04, 0xcc, 0x08, 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 }; @@ -17,6 +47,9 @@ namespace gr { // }; + /** + * Whitening sequence for payload with SF 8. + */ 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, 0x08, 0xad, 0x02, 0x98, 0x40, 0xa7, 0x5b, 0x04, 0xa2, 0x80, 0x9b, 0x01, 0xc7, 0x20, 0x5b, 0x4f, 0xe9, 0x2a, 0x7a, 0x98, 0xf4, 0xa7, 0x0e, 0x4f, 0x8f, 0x61, 0x49, 0x79, 0xea, 0x94, 0x89, 0x1a, 0xc4, 0x07, 0xa8, 0x4a, 0x92, 0x8a, 0x76, 0x9d, 0x70, 0x4c, 0xb6, 0xab, 0x5e, 0xf2, 0x97, 0x62, 0x08, 0xf8, 0x02, 0xb5, 0x40, 0x3e, 0x5b, 0x83, 0xe9, 0x80, 0x31, 0x01, 0x5e, 0x20, 0xdc, 0x4f, 0xa2, 0x61, 0x9b, 0x32, 0x8c, 0x3e, 0x9a, 0xd8, 0x95, 0x22, 0x4e, 0x9a, 0xa0, 0xac, 0x23, 0x8e, 0x5d, 0x05, 0x64, 0xcf, 0xb9, 0xc1, 0xc4, 0x37, 0x02, 0xdd, 0xce, 0xe7, 0xe9, 0x58, 0xc9, 0xb1, 0xbf, 0x98, 0x0a, 0xcd, 0xc0, 0x8b, 0x7b, 0xdd, 0xb2, 0x0f, 0x54, 0x03, 0x6d, 0x60, 0xe2, 0x4f, 0x6e, 0x2e, 0xb9, 0xc3, 0x16, 0x50, 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, 0x06, 0x7b, 0x37, 0xf9, 0x9e, 0xf6, 0x86, 0xbc, 0x20, 0xcf, 0x0e, 0xbc, 0x7d, 0xd7, 0x7b, 0x26, 0xdf, 0x61, 0xcb, 0x5d, 0x38, 0xe4, 0x54, 0x39, 0x0e, 0xef, 0x87, 0xb8, 0xd5, 0x57, 0x67, 0x6e, 0xd8, 0x82, 0xeb, 0x4b, 0x3c, 0x7a, 0x56, 0xf4, 0x0d, 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, 0x07, 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, 0x00, 0x49, 0x98, 0xec, 0x65, 0x72, 0x10, 0x6e, 0x80, 0x40, 0xdd, 0x76, 0x65, 0x3b, 0x51, 0x8e, 0x02, 0xb8, 0x70, 0xd7, 0xe2, 0xa7, 0x9a, 0x48, 0xab, 0xc2, 0x38, 0x7d, 0x5e, 0xbe, 0x06, 0x86, 0x4c, 0x09, 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, 0x0a, 0xfd, 0x2b, 0xf2, 0x83, 0xf5, 0x64, 0x25, 0x5e, 0x33, 0x49, 0x50, 0xfb, 0x64, 0xd5, 0xa2, 0x41, 0x21, 0x85, 0x5c, 0x41, 0x25, 0x4d, 0x09, 0xe6, 0xe3, 0xa5, 0x79, 0xd9, 0x77, 0x06, 0x6e, 0x4c, 0xc0, 0x77, 0x35, 0xfc, 0x11, 0xd5, 0x5d, 0x0a, 0x97, 0x50, 0x05, 0xa9, 0xe9, 0xb3, 0x7b, 0x79, 0x96, 0x15, 0x13, 0x04, 0x8f, 0x62, 0x91, 0x5e, 0x74, 0x12, 0x75, 0x9e, 0x3e, 0xad, 0x4b, 0x97, 0xe3, 0xf0, 0x8a, 0x03, 0x41, 0x60, 0xd1, 0xd2, 0x4b, 0xbb, 0xda, 0x3f, 0x3d }; @@ -27,6 +60,9 @@ namespace gr { // }; + /** + * Whitening sequence for payload with SF 9. + */ const uint8_t prng_payload_sf9[] = { 0xfd, 0xbe, 0x94, 0xef, 0x1a, 0xf7, 0x07, 0xfd, 0x01, 0xff, 0x0b, 0x94, 0xba, 0x1a, 0xda, 0x07, 0x64, 0x01, 0x78, 0x0b, 0xdf, 0xba, 0xb0, 0xda, 0x9e, 0x64, 0x86, 0x78, 0x0b, 0xdf, 0xf1, 0xb0, 0x3b, 0x9e, 0x57, 0x86, 0x2d, 0x0b, 0xb9, 0xf1, 0x83, 0x3b, 0x80, 0x57, 0x01, 0x2d, 0x40, 0xb9, 0x10, 0x83, 0x47, 0x80, 0xa8, 0x03, 0x99, 0x41, 0xa3, 0x12, 0x5f, 0x03, 0x69, 0x08, 0x79, 0x98, 0xf4, 0xa7, 0x0e, 0x5f, 0xcf, 0x6d, 0xe9, 0x79, 0xab, 0xf6, 0xb9, 0x2e, 0xd8, 0xcf, 0x2e, 0x41, 0xd1, 0x2b, 0x26, 0xb9, 0x68, 0xb8, 0xb8, 0x22, 0x1c, 0xd1, 0x07, 0x27, 0x60, 0x48, 0x14, 0xb8, 0x88, 0x9c, 0x48, 0x86, 0xa1, 0x60, 0x03, 0x04, 0x16, 0x94, 0x4e, 0x42, 0xa9, 0xa1, 0xf9, 0x03, 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, 0x00, 0xea, 0x2b, 0x88, 0xbe, 0xaf, 0x56, 0x82, 0x2e, 0x08, 0x92, 0x6b, 0x17, 0xae, 0x3b, 0x4a, 0xd7, 0x2a, 0x66, 0x92, 0x13, 0x76, 0x21, 0x3b, 0xa5, 0xd3, 0xd3, 0x65, 0x26, 0x13, 0x02, 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, 0x03, 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, 0x0f, 0x86, 0xc8, 0x02, 0xe8, 0x80, 0xe9, 0x27, 0xbb, 0xdd, 0x86, 0xe5, 0x4a, 0x79, 0x80, 0x2e, 0x07, 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, 0x05, 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, 0x0c, 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, 0x05, 0x56, 0x0a, 0x0c, 0x91, 0x76, 0x50, 0xb1, 0x3e, 0x59, 0x57, 0x6e, 0x0d, 0xa3, 0xb6, 0x7e, 0x89, 0x76, 0x4b, 0x7d, 0x6e, 0xde, 0x82, 0x90, 0x5e, 0xca, 0x72, 0x4d, 0x71, 0xe9, 0xdc, 0x82, 0xd0, 0x15, 0x9a, 0x83, 0x4d, 0x09, 0xeb, 0x20, 0x82, 0x6f, 0x74, 0x1e, 0x93, 0x18, 0x8d, 0x0b, 0x20, 0xb1, 0x2e, 0x60, 0x1e, 0xf1, 0x10, 0xfd, 0x0f, 0x5e, 0xb7, 0x9b, 0x40, 0x94, 0x0a, 0x89, 0x02, 0xa0, 0xb7, 0x66, 0x43, 0x77, 0xb6, 0xc7, 0x3e, 0x3f, 0xdf }; @@ -37,6 +73,9 @@ namespace gr { // }; + /** + * Whitening sequence for payload with SF 10. + */ const uint8_t prng_payload_sf10[] = { 0xfd, 0xfe, 0xf4, 0xdf, 0x0e, 0xfb, 0x8f, 0x7f, 0x02, 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, 0x00, 0xea, 0x30, 0xa9, 0x04, 0x8b, 0x88, 0x49, 0x0a, 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, 0x06, 0x65, 0x40, 0x18, 0x30, 0xa0, 0x3c, 0x92, 0xc9, 0x4c, 0xe2, 0x01, 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, 0x02, 0x1c, 0x40, 0xad, 0x30, 0x9e, 0x47, 0x55, 0x30, 0x04, 0xd3, 0x02, 0x6d, 0x62, 0xe2, 0x3f, 0x6a, 0x26, 0xb1, 0x41, 0x54, 0x2c, 0x2e, 0xb2, 0x03, 0x52, 0x01, 0xaf, 0xab, 0xd4, 0xd0, 0xe7, 0x26, 0x13, 0x62, 0x41, 0x06, 0xa5, 0x62, 0x53, 0xbb, 0xed, 0x92, 0x13, 0x17, 0x3a, 0x1b, 0x50, 0x94, 0x29, 0x0d, 0xd2, 0x42, 0x06, 0x3b, 0x17, 0xc9, 0xda, 0xea, 0x0e, 0x7e, 0x03, 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, 0x0d, 0x05, 0xf6, 0x21, 0xe2, 0x93, 0xe9, 0x05, 0xb3, 0x0a, 0x55, 0xfa, 0x29, 0x81, 0x1d, 0x9d, 0x0a, 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, 0x00, 0x4d, 0x86, 0x62, 0xa1, 0x58, 0x63, 0xd1, 0x26, 0x8a, 0x89, 0x45, 0xc9, 0xe2, 0xea, 0x13, 0x82, 0x7a, 0x5e, 0x03, 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, 0x0f, 0x9b, 0x12, 0xec, 0x1d, 0xce, 0xa1, 0x4e, 0x42, 0xaa, 0xeb, 0x50, 0x29, 0x8d, 0xc5, 0xfd, 0x5d, 0x04, 0xec, 0x46, 0x7a, 0x63, 0xf5, 0x94, 0x05, 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, 0x06, 0xc4, 0x0c, 0x29, 0x46, 0xa7, 0x70, 0x08, 0xf5, 0x0c, 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, 0x09, 0x7e, 0x91, 0x52, 0x04, 0x98, 0x2a, 0x8b, 0xd4, 0x48, 0x64, 0x00, 0xe2, 0x4c, 0x41, 0x28, 0x7c, 0xda, 0xac, 0x26, 0x93, 0xb3, 0x34, 0x49, 0x14, 0x52, 0x69, 0x0a, 0x76, 0xd0, 0x75, 0x0e, 0x0c, 0x2c, 0xc4, 0x2b, 0x04, 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, 0x09, 0x79, 0x61, 0x80, 0x82, 0x41, 0x31, 0x48, 0xa0, 0x50, 0x96, 0x02, 0x26, 0x64, 0x85, 0x16 }; @@ -47,6 +86,9 @@ namespace gr { // }; + /** + * Whitening sequence for payload with SF 11. + */ const uint8_t prng_payload_sf11[] = { 0xfd, 0xfe, 0xb4, 0xbf, 0x3e, 0x8f, 0xa3, 0xd3, 0xd0, 0x75, 0x04, 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, 0x06, 0x2d, 0x43, 0x9a, 0x21, 0xe7, 0x61, 0x54, 0x3d, 0xbc, 0xd3, 0x03, 0x2d, 0x09, 0x98, 0xc0, 0xe7, 0x7b, 0x74, 0xf6, 0x98, 0xab, 0x0b, 0x99, 0x80, 0x87, 0x47, 0x6b, 0x3b, 0xd5, 0x86, 0xec, 0x87, 0xfb, 0x03, 0xb7, 0x86, 0x5e, 0x6b, 0xb7, 0xd5, 0x57, 0xe8, 0xec, 0x7b, 0x31, 0xb6, 0x98, 0x5e, 0xec, 0xb7, 0xbe, 0x07, 0x3a, 0xec, 0xd3, 0xab, 0xee, 0x9a, 0x38, 0xe4, 0xe4, 0xfe, 0x76, 0x0a, 0x99, 0x43, 0x05, 0x64, 0x01, 0x38, 0x63, 0xe4, 0x14, 0x06, 0x9b, 0x99, 0x4a, 0x4f, 0xe1, 0x00, 0x73, 0x22, 0x0e, 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, 0x05, 0xe4, 0xcf, 0x78, 0xa1, 0x94, 0x43, 0x0a, 0x19, 0x10, 0x40, 0x85, 0x66, 0xaa, 0xfb, 0xd8, 0x85, 0xd7, 0x6a, 0x28, 0x04, 0x06, 0x45, 0xc6, 0x28, 0x60, 0xd9, 0x8a, 0xd7, 0xc7, 0x38, 0xe1, 0x56, 0xa1, 0x82, 0xd0, 0xc8, 0x2e, 0x8a, 0x03, 0xe6, 0x49, 0xe5, 0x58, 0x31, 0xec, 0x51, 0x32, 0x25, 0x35, 0x03, 0x29, 0x19, 0x12, 0xd0, 0x0b, 0xef, 0xe1, 0x30, 0x72, 0x75, 0x2e, 0x39, 0xa4, 0x3a, 0xf8, 0x9b, 0x2b, 0xec, 0xd5, 0x72, 0x46, 0x2e, 0x17, 0xb5, 0x8a, 0xd8, 0xd6, 0x25, 0x05, 0xd3, 0x42, 0x46, 0x3b, 0x16, 0xfd, 0xce, 0xfe, 0x16, 0xf7, 0x04, 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, 0x0d, 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, 0x09, 0x60, 0x4f, 0x72, 0xa0, 0x9e, 0x48, 0xe0, 0xe8, 0xe6, 0x04, 0x15, 0x82, 0x4c, 0x47, 0xa0, 0x82, 0x08, 0x07, 0x98, 0x96, 0x3c, 0x2c, 0x9a, 0x0e, 0x4d, 0x4a, 0x8a, 0x9a, 0x27, 0xea, 0xee, 0xe8, 0x1a, 0xf5, 0x05, 0xb1, 0x0b, 0x74, 0xba, 0x39, 0xee, 0x79, 0xec, 0x8f, 0x67, 0x48, 0x38, 0xeb, 0x75, 0xe8, 0x79, 0x9f, 0x09, 0x99, 0x1f, 0xcd, 0x06, 0x29, 0xe9, 0xf2, 0xe9, 0x79, 0x9f, 0x0d, 0x9d, 0x9b, 0x4b, 0x05, 0xac, 0xeb, 0x71, 0xa2, 0x19, 0x25, 0x1d, 0x98, 0x97, 0x4f, 0x07, 0x2e, 0xeb, 0xf8, 0xa2, 0x9a, 0x05, 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, 0x0f, 0x75, 0xd0, 0x22, 0x10, 0xd7, 0x2d, 0x8d, 0x76, 0x91, 0x37, 0x8f, 0x35, 0xd1, 0x22, 0x00, 0xc4, 0x25, 0x5c, 0xfe, 0xf0, 0x36, 0xbc, 0x35, 0x8e, 0x42, 0xe8, 0xa4, 0x03, 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 }; @@ -57,6 +99,9 @@ namespace gr { // }; + /** + * Whitening sequence for payload with SF 12. + */ 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, 0x02, 0x55, 0x41, 0x4d, 0x30, 0xed, 0x18, 0x17, 0x11, 0x8d, 0x4c, 0x02, 0xab, 0xa0, 0xd9, 0x24, 0xd7, 0x24, 0x03, 0x97, 0x43, 0x01, 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, 0x05, 0x60, 0x01, 0x79, 0x48, 0xd4, 0xf0, 0x5a, 0x37, 0x2f, 0xd7, 0x4f, 0x6f, 0x02, 0x30, 0x01, 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, 0x06, 0xca, 0x0c, 0x81, 0x55, 0x49, 0xa1, 0x8c, 0x0c, 0x03, 0xcf, 0xc0, 0x88, 0x5b, 0xad, 0x92, 0x33, 0x2c, 0x01, 0x5c, 0x92, 0xae, 0x01, 0x93, 0x2b, 0x16, 0x9e, 0x1f, 0x0a, 0xaf, 0xf6, 0x1d, 0xab, 0x64, 0xd1, 0x73, 0x44, 0x25, 0x35, 0x05, 0x09, 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, 0x02, 0x65, 0x0b, 0x13, 0x90, 0x2a, 0x10, 0x23, 0x41, 0x01, 0xf6, 0x13, 0xfc, 0xc8, 0xf4, 0x83, 0x45, 0x2c, 0x2a, 0x53, 0x05, 0xfc, 0xa4, 0x7f, 0xdf, 0xb5, 0x27, 0x35, 0x08, 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, 0x0f, 0xca, 0x41, 0xa9, 0x20, 0xfb, 0x23, 0x93, 0x7d, 0xa8, 0xec, 0xc6, 0x69, 0xc3, 0xb1, 0xf0, 0x5f, 0xd3, 0xb7, 0x01, 0x43, 0xf6, 0x13, 0xf0, 0xca, 0xb3, 0x7a, 0x17, 0xb8, 0xa7, 0xc4, 0x0f, 0x72, 0x25, 0x26, 0x65, 0xc4, 0xf5, 0x73, 0xfe, 0x3b, 0x95, 0xc4, 0x4a, 0x62, 0x50, 0x3e, 0x44, 0xc6, 0xef, 0xe2, 0xfb, 0x63, 0x15, 0x0e, 0x0a, 0xdb, 0x64, 0x39, 0x40, 0x07, 0x6a, 0x08, 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, 0x02, 0x6c, 0xfa, 0xde, 0x8e, 0x5d, 0xeb, 0x9e, 0x39, 0x00, 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, 0x0d, 0xe8, 0x0a, 0x75, 0x1a, 0x59, 0xf4, 0x73, 0x35, 0xb7, 0x14, 0x35, 0x17, 0x20, 0x1b, 0xb0, 0x24, 0x41, 0xd2, 0xaf, 0xc7, 0xd6, 0xa0, 0x06, 0x08, 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, 0x03, 0x60, 0xe2, 0xf2, 0x93, 0x04, 0x6a, 0x21, 0x44, 0x48, 0x50, 0x5c, 0xef, 0x2e, 0x38, 0xda, 0x37, 0x96, 0x09, 0x33, 0x56, 0x6f, 0x68, 0x66, 0x24, 0xb2, 0x99, 0x5f }; @@ -71,7 +116,11 @@ namespace gr { // 0x7D, 0xFE, 0xB7, 0xFF, 0x5E, 0x9C, 0xD4, 0xA8, 0x28, 0x70, 0x10, 0xFC, 0xC4, 0xB4, 0xA8, 0x1C, 0x90, 0xE4, 0x74, 0x74, 0x54, 0x1C, 0xFC, 0xC0, 0x7C, 0x28, 0xBC, 0xD0, 0x5C, 0x44, 0x3C, 0x5C, 0x44, 0x78, 0xF0, 0x74, 0xEC, 0x34, 0x30, 0x1C, 0x1C, 0x64, 0x74, 0x5C, 0x14, 0x5C, 0x90, 0xE8, 0x04, 0x34, 0x00, 0x54, 0x40, 0x4C, 0x30, 0xFC, 0x1C, 0x1C, 0x10, 0x04, 0x4C, 0x00, 0x28, 0x20, 0x58, 0x64, 0x7C, 0x24, 0x00, 0x94, 0x40, 0x00, 0x60, 0x88, 0x78, 0x18, 0x7C, 0xA4, 0x4C, 0x2C, 0x10, 0x44, 0x4C, 0x64, 0x68, 0x70, 0xB8, 0x3C, 0xD8, 0x3C, 0x14, 0xE8, 0x50, 0x14, 0x20, 0x60, 0x50, 0x38, 0x6C, 0x1C, 0x38, 0x2C, 0x44, 0xEC, 0x50, 0x10, 0x10, 0x48, 0x04, 0x60, 0x00, 0x78, 0x48, 0x9C, 0xD0, 0x50, 0x34, 0x2C, 0xD4, 0x44, 0x64, 0x00, 0x30, 0x00, 0x54, 0x60, 0xFC, 0x7C, 0x64, 0x18, 0x34, 0x4C, 0x18, 0xB0, 0xCC, 0x1C, 0xE8 // }; - // New + /** + * Whitening sequence for payload with SF 6. + * The RN2483 apparently does not support SF6, so this should be the same as SF12...? + * TO-DO: test with other transmitters + */ const uint8_t prng_payload_sf6[] = { 0x65, 0xFA, 0xB7, 0xFF, 0x5E, 0x14, 0x5C, 0x20, 0x28, 0x78, 0x10, 0x74, 0x4C, 0x34, 0x28, 0x14, 0x10, 0x6C, 0x7C, 0x74, 0x54, 0x1C, 0x74, 0x48, 0x7C, 0x28, 0x3C, 0x50, 0x54, 0x4C, 0x3C, 0x5C, 0x44, 0x78, 0x70, 0x74, 0x64, 0x3C, 0x38, 0x14, 0x1C, 0x6C, 0x74, 0x7C, 0x04, 0x5C, 0x10, 0x68, 0x0C, 0x34, 0x00, 0x5C, 0x40, 0x44, 0x30, 0x7C, 0x1C, 0x1C, 0x10, 0x04, 0x4C, 0x00, 0x28, 0x20, 0x58, 0x64, 0x7C, 0x34, 0x04, 0x1C, 0x40, 0x00, 0x60, 0x08, 0x78, 0x18, 0x7C, 0x2C, 0x44, 0x2C, 0x10, 0x44, 0x4C, 0x64, 0x68, 0x70, 0x38, 0x3C, 0x58, 0x34, 0x14, 0x68, 0x50, 0x04, 0x20, 0x60, 0x50, 0x38, 0x6C, 0x1C, 0x38, 0x24, 0x4C, 0x6C, 0x78, 0x10, 0x10, 0x48, 0x04, 0x68, 0x00, 0x78, 0x40, 0x1C, 0x58, 0x40, 0x30, 0x24, 0x54, 0x44, 0x64, 0x00, 0x30, 0x00, 0x54, 0x20, 0x54, 0x6C, 0x60, 0x10, 0x3C, 0x4C, 0x18, 0x38, 0x44, 0x14, 0x68, 0x64, 0x28, 0x3C, 0x3C, 0x3C, 0x60, 0x54, 0x70, 0x44, 0x30, 0x28, 0x14, 0x78, 0x7C, 0x50, 0x54, 0x1C, 0x74, 0x58, 0x34, 0x34, 0x14, 0x5C, 0x04, 0x4C, 0x38, 0x3C, 0x04, 0x4C, 0x44, 0x30, 0x04, 0x14, 0x00, 0x44, 0x40, 0x48, 0x70, 0x7C, 0x34, 0x28, 0x2C, 0x04, 0x54, 0x10, 0x24, 0x00, 0x10, 0x68, 0x3C, 0x1C, 0x1C, 0x10, 0x2C, 0x58, 0x54, 0x20, 0x6C, 0x50, 0x70, 0x0C, 0x0C, 0x24, 0x28, 0x00, 0x38, 0x50, 0x50, 0x6C, 0x64, 0x30, 0x70, 0x3C, 0x04, 0x20, 0x78, 0x10, 0x50, 0x5C, 0x28, 0x64, 0x50, 0x30, 0x2C, 0x34, 0x00, 0x60, 0x08, 0x54, 0x44, 0x30, 0x24, 0x54, 0x00, 0x6C, 0x00, 0x10, 0x50, 0x00, 0x00, 0x24, 0x40, 0x00, 0x7C, 0x18, 0x74, 0x48, 0x7C, 0x00, 0x0C, 0x0C, 0x30, 0x54, 0x04, 0x7C, 0x2C, 0x74, 0x5C, 0x34, 0x2C, 0x7C, 0x68, 0x78, 0x7C, 0x28, 0x48, 0x34, 0x30, 0x5C, 0x5C, 0x6C, 0x64 }; diff --git a/lib/utilities.h b/lib/utilities.h index 20e21f9..87a1fc7 100644 --- a/lib/utilities.h +++ b/lib/utilities.h @@ -1,3 +1,23 @@ +/* -*- c++ -*- */ +/* + * Copyright 2017 Pieter Robyns, William Thenaers. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + #ifndef UTILITIES_H #define UTILITIES_H @@ -25,40 +45,115 @@ namespace gr { return t > max ? max : t; } + /** + * \brief Rotate the given bits to the left and return the result. + * + * \param bits + * The value to rotate. + * \param count + * The amount of bits to rotate (shift to left and add to right). + * \param size + * The size in bits used in `bits`. + *
e.g. 1 byte given => size = 8 + *
e.g. only 6 bits in use => size = 6, and all bits higher than (1 << size-1) will be zeroed. + */ + static inline uint32_t rotl(uint32_t bits, uint32_t count = 1u, const uint32_t size = 8u) { + const uint32_t len_mask = (1u << size) - 1u; + + count %= size; // Limit bit rotate count to size + bits &= len_mask; // Limit given bits to size + + return ((bits << count) & len_mask) | (bits >> (size - count)); + } + + /** + * \brief Return the `v` represented in a binary string. + * + * \tparam T + * The type of variable to convert. + * \param v + * The value to convert. + * \param bitwidth + * The length in bits of the given variable `v`. + */ template std::string to_bin(const T v, const uint32_t bitwidth) { - const uint64_t maxpow = bitwidth ? (1ull << (bitwidth - 1)) : 0; - uint64_t mask; + #ifdef REV_BITS + const uint64_t maxpow = bitwidth ? (1ull << (bitwidth - 1)) : 0; + uint64_t mask; - std::string result = ""; + std::string result = ""; - for (mask = 0x1; mask <= maxpow; mask <<= 1) { - result += (v & mask) ? "1" : "0"; - } + for (mask = 0x1; mask <= maxpow; mask <<= 1) { + result += (v & mask) ? "1" : "0"; + } + #else + uint64_t mask = bitwidth ? (1ull << bitwidth) : 0; + + std::string result = ""; + + while(mask >>= 1) { + result += (v & mask) ? "1" : "0"; + } + #endif return result; } + /** + * \brief Append the data in a given vector to an output stream with a comma delimiter. + * + * \tparam T + * The type of variable to append. + * \param out + * The output stream to append to. + * \param v + * The vector containing the data to append. + * \param prefix + * A prefix to include before appending the data. + * \param element_len_bits + * The length in bits of the data in `v`. + */ template inline void print_vector(std::ostream& out, const std::vector& v, const std::string& prefix, const int element_len_bits) { out << prefix << ": "; - for (T x : v) + for (const T& x : v) out << to_bin(x, element_len_bits) << ", "; out << std::endl << std::flush; } + /** + * \brief Append the data in a given vector to an output stream. + * + * \tparam T + * The type of variable to append. + * \param out + * The output stream to append to. + * \param v + * The vector containing the data to append. + * \param element_len_bits + * The length in bits of the data in `v`. + */ template inline void print_vector_raw(std::ostream& out, const std::vector& v, const int element_len_bits) { - for (T x : v) + for (const T& x : v) out << to_bin(x, element_len_bits); out << std::flush; } - bool check_parity(const std::string& word, const bool even) { + /** + * \brief Check whether the parity of the given binary string is even. + * + * \param word + * The string to check. + * \param even + * Check for even (`true`) or uneven (`false`) parity. + */ + bool check_parity_string(const std::string& word, const bool even = true) { size_t count = 0, i = 0; while(i < 7) { @@ -69,6 +164,33 @@ namespace gr { return (count & 0x1) == !even; } + /** + * \brief Check whether the parity of the given uint64_t is even. + *
See https://graphics.stanford.edu/~seander/bithacks.html for more. + * + * \param word + * The uint64_t to check. + * \param even + * Check for even (`true`) or uneven (`false`) parity. + */ + bool check_parity(uint64_t word, const bool even = true) { + word ^= word >> 1; + word ^= word >> 2; + word = (word & 0x1111111111111111UL) * 0x1111111111111111UL; + + return ((word >> 60ull) & 1ull) == !even; + } + + /** + * \brief Select the bits in data given by the indices in `*indices`. + * + * \param data + * The data to select bits from. + * \param *indices + * Array with the indices to select. + * \param n + * The amount of indices. + */ uint32_t select_bits(const uint32_t data, const uint8_t *indices, const uint8_t n) { uint32_t r = 0u; @@ -78,29 +200,68 @@ namespace gr { return r; } + /** + * \brief **Forward Error Correction** : Extract only the data in the given bytes. + * + * \param in_data + * The data to extract from. + * \param len + * The amount of data words. + * \param *indices + * The indices containing the actual data bits. + * \param n + * The amount of data bits. + * \param out_data + * The resulting data words. + */ void fec_extract_data_only(const uint8_t *in_data, const uint32_t len, const uint8_t *indices, const uint8_t n, uint8_t *out_data) { for (uint32_t i = 0u, out_index = 0u; i < len; i += 2u) { - uint8_t d1 = (select_bits(in_data[i], indices, n) & 0xff) << 4u; + const uint8_t d2 = (i + 1u < len) ? select_bits(in_data[i + 1u], indices, n) & 0xFF + : 0u; - d1 |= (i + 1u < len) ? select_bits(in_data[i + 1u], indices, n) & 0xff : 0u; - - out_data[out_index++] = d1; + out_data[out_index++] = (d2 << 4u) | (select_bits(in_data[i], indices, n) & 0xFF); } } + /** + * \brief Select a single bit from the given byte. + * + * \param v + * The byte to select from. + * \param i + * The index to select the bit from starting from the LSB. + */ inline uint8_t bit(const uint8_t v, const uint8_t i) { return ((v >> i) & 0x01); } + /** + * \brief Pack the given 8 bits in a byte with: `hgfe dcba` + * + * \param a-h + * The bits to pack with the LSB first. + */ inline uint8_t pack_byte(const uint8_t a, const uint8_t b, const uint8_t c, const uint8_t d, const uint8_t e, const uint8_t f, const uint8_t g, const uint8_t h) { return a | (b << 1) | (c << 2) | (d << 3) | (e << 4) | (f << 5) | (g << 6) | (h << 7); } + /** + * \brief Pack the given 4 bits in a nibble with: `dcba` + * + * \param a-d + * The bits to pack with the LSB first. + */ inline uint8_t pack_nibble(const uint8_t a, const uint8_t b, const uint8_t c, const uint8_t d) { return a | (b << 1) | (c << 2) | (d << 3); } + /** + * \brief Encode the given word with standard Hamming(7,4) and return a byte with the set parity bits. + * + * \param v + * The nibble to encode. + */ inline uint8_t hamming_encode_soft(const uint8_t v) { const uint8_t p1 = bit(v, 1) ^ bit(v, 2) ^ bit(v, 3); const uint8_t p2 = bit(v, 0) ^ bit(v, 1) ^ bit(v, 2); @@ -110,6 +271,13 @@ namespace gr { return pack_byte(p1, bit(v, 0), bit(v, 1), bit(v, 2), p2, bit(v, 3), p3, p4); } + /** + * \brief Hamming(8,4) decoding by constructing a Syndrome matrix LUT for XORing on parity errors. + * + * \param v + * The byte to decode. + * \return Returs a nibble containing the corrected data. + */ uint8_t hamming_decode_soft_byte(uint8_t v) { // Precalculation // Which bits are covered (including self)? @@ -119,25 +287,28 @@ namespace gr { // p4 01010101 // Syndrome matrix = columns of "cover bits" above - uint8_t H[16] = { 0u }; +// uint8_t H[16] = { 0u }; - const uint8_t i0 = pack_nibble(1, 0, 0, 0), - i1 = pack_nibble(0, 1, 1, 1), - i2 = pack_nibble(1, 1, 1, 0), - i3 = pack_nibble(1, 1, 0, 1), - i4 = pack_nibble(0, 1, 0, 0), - i5 = pack_nibble(1, 0, 1, 1), - i6 = pack_nibble(0, 0, 1, 0), - i7 = pack_nibble(0, 0, 0, 1); +// const uint8_t i0 = pack_nibble(1, 0, 0, 0), +// i1 = pack_nibble(0, 1, 1, 1), +// i2 = pack_nibble(1, 1, 1, 0), +// i3 = pack_nibble(1, 1, 0, 1), +// i4 = pack_nibble(0, 1, 0, 0), +// i5 = pack_nibble(1, 0, 1, 1), +// i6 = pack_nibble(0, 0, 1, 0), +// i7 = pack_nibble(0, 0, 0, 1); - H[i0] = 0; - H[i1] = 1; - H[i2] = 2; - H[i3] = 3; - H[i4] = 4; - H[i5] = 5; - H[i6] = 6; - H[i7] = 7; +// H[i0] = 0; +// H[i1] = 1; +// H[i2] = 2; +// H[i3] = 3; +// H[i4] = 4; +// H[i5] = 5; +// H[i6] = 6; +// H[i7] = 7; + + static const uint8_t H[16] = { 0x0, 0x0, 0x4, 0x0, 0x6, 0x0, 0x0, 0x2, + 0x7, 0x0, 0x0, 0x3, 0x0, 0x5, 0x1, 0x0 }; // Decode // Bit positions for data bits in codeword @@ -153,21 +324,30 @@ namespace gr { const uint8_t syndrome = pack_nibble((uint8_t)(p1 != p1c), (uint8_t)(p2 != p2c), (uint8_t)(p3 != p3c), (uint8_t)(p4 != p4c)); if (syndrome) { - //v ^= pow2[ H[syndrome] ]; v ^= 1u << H[syndrome]; } return pack_nibble( bit(v, 1), bit(v, 2), bit(v, 3), bit(v, 5)); } - // Manual Hamming + /** + * \brief Hamming(8,4) decoding by calling `hamming_decode_soft_byte` on each byte. + *
Each byte is decoded in pairs, the first one becoming the LSB nibble + *
and the second one the MSB nibble (if even; else just zeroes). + * + * \param words + * The byte array to decode. + * \param len + * The amount of words to decode. + * \param out_data + * The decoded result words. + */ void hamming_decode_soft(const uint8_t *words, const uint32_t len, uint8_t *out_data) { for (uint32_t i = 0u, out_index = 0u; i < len; i += 2u) { - uint8_t d1 = hamming_decode_soft_byte(words[i]) << 4u; + const uint8_t d2 = (i + 1u < len) ? hamming_decode_soft_byte(words[i + 1u]) + : 0u; - d1 |= (i + 1u < len) ? hamming_decode_soft_byte(words[i + 1u]) : 0u; - - out_data[out_index++] = d1; + out_data[out_index++] = (d2 << 4u) | hamming_decode_soft_byte(words[i]); } }