kopia lustrzana https://github.com/rpp0/gr-lora
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 testingpull/32/head
rodzic
439e563bea
commit
30da0dedcd
|
@ -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()
|
|
@ -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))
|
|
@ -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 |
|
|
@ -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::nanoseconds>(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
|
||||
|
|
|
@ -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<float>& 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.
|
||||
* <BR>(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<uint8_t> 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<uint8_t> 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<uint8_t> 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <liquid/liquid.h>
|
||||
#include "lora/decoder.h"
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <fstream>
|
||||
|
||||
#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**
|
||||
* <BR>The main class for the LoRa decoder.
|
||||
* Contains all variables and methods necessary for succesfully decoding LoRa PHY.
|
||||
* <BR>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<gr_complex> d_downchirp;
|
||||
std::vector<float> d_downchirp_ifreq;
|
||||
std::vector<gr_complex> d_downchirp; ///< The complex ideal downchirp.
|
||||
std::vector<float> d_downchirp_ifreq; ///< The instantaneous frequency of the ideal downchirp.
|
||||
|
||||
std::vector<gr_complex> d_upchirp;
|
||||
std::vector<float> d_upchirp_ifreq;
|
||||
std::vector<gr_complex> d_upchirp; ///< The complex ideal upchirp.
|
||||
std::vector<float> d_upchirp_ifreq; ///< The instantaneous frequency of the ideal upchirp.
|
||||
|
||||
std::vector<gr_complex> d_fft;
|
||||
std::vector<gr_complex> d_mult;
|
||||
std::vector<gr_complex> d_fft; ///< Vector containing the FFT resuls.
|
||||
std::vector<gr_complex> 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<unsigned int> d_words;
|
||||
std::vector<uint8_t> d_demodulated;
|
||||
std::vector<uint8_t> d_words_deshuffled;
|
||||
std::vector<uint8_t> d_words_dewhitened;
|
||||
std::vector<uint8_t> d_data;
|
||||
std::vector<uint32_t> d_words; ///< Vector containing the demodulated words.
|
||||
std::vector<uint8_t> d_demodulated; ///< Vector containing the words after deinterleaving.
|
||||
std::vector<uint8_t> d_words_deshuffled; ///< Vector containing the words after deshuffling.
|
||||
std::vector<uint8_t> d_words_dewhitened; ///< Vector containing the words after dewhitening.
|
||||
std::vector<uint8_t> 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<float>& 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`.
|
||||
* <BR>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<float>& 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.
|
||||
* <BR>1. Deshuffle the words
|
||||
* <BR>2. Dewhiten the words
|
||||
* <BR>3. Hamming decoding
|
||||
* <BR><BR>The result is printed to the standard outputstream
|
||||
* <BR>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.
|
||||
* <BR>- CR 4 or 3: Hamming(8,4) or Hamming(7,4) with parity correction
|
||||
* <BR>- 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.
|
||||
* <BR>`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.
|
||||
* <BR>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.
|
||||
* <BR>**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.
|
||||
* <BR>**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.
|
||||
* <BR>Should be around 0.01f (default) for normal environments,
|
||||
* <BR>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
|
||||
|
|
51
lib/tables.h
51
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
|
||||
};
|
||||
|
|
252
lib/utilities.h
252
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`.
|
||||
* <BR>e.g. 1 byte given => size = 8
|
||||
* <BR>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 <typename T>
|
||||
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 <typename T>
|
||||
inline void print_vector(std::ostream& out, const std::vector<T>& 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 <typename T>
|
||||
inline void print_vector_raw(std::ostream& out, const std::vector<T>& 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.
|
||||
* <BR>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.
|
||||
* <BR>Each byte is decoded in pairs, the first one becoming the LSB nibble
|
||||
* <BR>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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue