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
pull/32/head
Wosser1sProductions 2017-05-18 15:56:25 +02:00 zatwierdzone przez GitHub
rodzic 439e563bea
commit 30da0dedcd
8 zmienionych plików z 1104 dodań i 175 usunięć

Wyświetl plik

@ -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()

Wyświetl plik

@ -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))

Wyświetl plik

@ -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 |

Wyświetl plik

@ -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

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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
};

Wyświetl plik

@ -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]);
}
}