Merge branch 'dev' of https://github.com/Wosser1sProductions/gr-lora into Wosser1sProductions-dev

pull/38/head
Pieter Robyns 2017-06-08 15:15:39 +02:00
commit 2043e8e0bd
8 zmienionych plików z 242 dodań i 214 usunięć

Wyświetl plik

@ -13,7 +13,7 @@ import lora
LoRaReceiver = collections.namedtuple('LoRaReceiver', ['name', 'available'])
class LoRaReceiveAll:
def __init__(self, receiver, spreadingFactor = 7, samp_rate = 1e6, capture_freq = 868.0e6, target_freq = 868.1e6, threshold = 0.01):
def __init__(self, receiver, spreadingFactor = 7, samp_rate = 1e6, capture_freq = 868.0e6, target_freq = 868.1e6, center_offset = 0, threshold = 0.01):
##################################################
# Variables #
##################################################
@ -30,17 +30,23 @@ class LoRaReceiveAll:
# self.codingRate = codingRate # 4/5 4/6 4/7
self.threshold = threshold
# For FFT, determine Center Frequency Offset first, then set here.
# For RN2483, usually -14.1e3
self.center_offset = center_offset
##################################################
# Blocks #
##################################################
self.tb = gr.top_block ()
self.source = receiver
self.lora_lora_receiver_0 = lora.lora_receiver(self.samp_rate, self.capture_freq, self.offset, self.sf, self.samp_rate, self.threshold)
self.blocks_throttle_0 = blocks.throttle(gr.sizeof_gr_complex*1, self.samp_rate, True)
self.source = receiver
self.lora_lora_receiver_0 = lora.lora_receiver(self.samp_rate, self.capture_freq, self.offset, self.sf, self.samp_rate, self.threshold)
self.blocks_throttle_0 = blocks.throttle(gr.sizeof_gr_complex*1, self.samp_rate, True)
self.freq_xlating_fir_filter_0 = filter.freq_xlating_fir_filter_ccc(1, (firdes.low_pass(1, self.samp_rate, 200000, 50000)), self.center_offset, self.samp_rate)
self.tb.connect( (self.source, 0), (self.blocks_throttle_0, 0))
self.tb.connect( (self.blocks_throttle_0, 0), (self.lora_lora_receiver_0, 0))
self.tb.connect( (self.source, 0), (self.blocks_throttle_0, 0))
self.tb.connect( (self.blocks_throttle_0, 0), (self.freq_xlating_fir_filter_0, 0))
self.tb.connect( (self.freq_xlating_fir_filter_0, 0), (self.lora_lora_receiver_0, 0))
def start(self):
# self.tb.Start(True)
@ -60,9 +66,10 @@ if __name__ == '__main__':
if sf in range(6, 13):
break
target_freq = 868.1e6
samp_rate = 1e6
capture_freq = 868.0e6
target_freq = 868.1e6
samp_rate = 1e6
capture_freq = 868.0e6
center_offset = 0
print("Available sources:")
for i, r in enumerate(receivers):
@ -121,5 +128,5 @@ if __name__ == '__main__':
print("Warning: No receiver set!")
exit()
sdr = LoRaReceiveAll(receiver, sf, samp_rate, capture_freq, target_freq)
sdr = LoRaReceiveAll(receiver, sf, samp_rate, capture_freq, target_freq, center_offset)
sdr.start()

Wyświetl plik

@ -166,8 +166,10 @@ class ExamplifyLive:
if __name__ == '__main__':
random.seed(None)
gains = [32, 38, 38] # [10, 20, 20], for usrp: [32, 38, 38]
# 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

Wyświetl plik

@ -43,110 +43,3 @@ for name in files:
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))
#################
#### Results ####
#################
# File: lora-time_SF7_grad_idx_DECODE_HEADER
# Length: 9856
# Average: 0.1259104846 ms
# Std: 0.0083598489
# File: lora-time_SF7_grad_idx_DECODE_PAYLOAD
# Length: 14854
# Average: 0.1240467029 ms
# Std: 0.0058878571
# File: lora-time_SF7_grad_idx_DETECT
# Length: 132937
# Average: 0.0111668757 ms
# Std: 0.0863605383
# File: lora-time_SF7_grad_idx_PAUSE
# Length: 1232
# Average: 0.0010141461 ms
# Std: 0.0011882927
# File: lora-time_SF7_grad_idx_SYNC
# Length: 12334
# Average: 0.1369446399 ms
# Std: 0.0072021918
# File: lora-time_SF7_grad_idx_only
# Length: 11118
# Average: 0.1189276336 ms
# Std: 0.0055137726
# File: lora-time_SF12_grad_idx_DECODE_HEADER
# Length: 9912
# Average: 3.7912548676 ms
# Std: 0.2630857073
# File: lora-time_SF12_grad_idx_DECODE_PAYLOAD
# Length: 12320
# Average: 3.8091647075 ms
# Std: 0.2747977905
# File: lora-time_SF12_grad_idx_DETECT
# Length: 3723
# Average: 16.7026039911 ms
# Std: 21.1052133394
# File: lora-time_SF12_grad_idx_PAUSE
# Length: 1239
# Average: 0.0012905004 ms
# Std: 0.0002020981
# File: lora-time_SF12_grad_idx_SYNC
# Length: 10725
# Average: 4.2126484056 ms
# Std: 0.2068078822
# File: lora-time_SF12_grad_idx_only
# Length: 10002
# Average: 3.7576346853 ms
# Std: 0.2498984209
# File: lora-time_SF7_fft_idx_DECODE_HEADER
# Length: 9856
# Average: 0.0783100350 ms
# Std: 0.0131321480
# File: lora-time_SF7_fft_idx_DECODE_PAYLOAD
# Length: 20440
# Average: 0.0760118956 ms
# Std: 0.0125111634
# File: lora-time_SF7_fft_idx_DETECT
# Length: 130172
# Average: 0.0112985811 ms
# Std: 0.0876915475
# File: lora-time_SF7_fft_idx_PAUSE
# Length: 1232
# Average: 0.0010230820 ms
# Std: 0.0011295359
# File: lora-time_SF7_fft_idx_SYNC
# Length: 12320
# Average: 0.1378892978 ms
# Std: 0.0080917100
# File: lora-time_SF7_fft_idx_only
# Length: 13512
# Average: 0.0706166163 ms
# Std: 0.0098948093
# File: lora-time_SF12_fft_idx_DECODE_HEADER
# Length: 9912
# Average: 2.2331703327 ms
# Std: 0.2301535307
# File: lora-time_SF12_fft_idx_DECODE_PAYLOAD
# Length: 12390
# Average: 2.2217885565 ms
# Std: 0.2009724076
# File: lora-time_SF12_fft_idx_DETECT
# Length: 3744
# Average: 16.3153207676 ms
# Std: 20.9261960413
# File: lora-time_SF12_fft_idx_PAUSE
# Length: 1239
# Average: 0.0012535383 ms
# Std: 0.0001955853
# File: lora-time_SF12_fft_idx_SYNC
# Length: 10613
# Average: 4.2222761290 ms
# Std: 0.2295213521
# File: lora-time_SF12_fft_idx_only
# Length: 10032
# Average: 2.2098768243 ms
# Std: 0.2333799337

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 DBUGR_HPP
#define DBUGR_HPP
@ -7,7 +27,7 @@
#include "utilities.h"
#define CLAMP_VAL 0.7f //1000000.0f //0.7f
#define CLAMP_VAL 0.7e6f //1000000.0f //0.7f
#undef NDEBUG /// Debug printing
//#define NDEBUG /// No debug printing
@ -45,26 +65,19 @@
std::ofstream DBGR_out_file; \
DBGR_out_file.open("/tmp/DBGR.txt", std::ios::out); \
\
\
/*printf("DBGR_Ideal\n");*/ \
for (DBGR_j = 0; DBGR_j < int32_t(WINDOW); DBGR_j++) { \
sprintf(DBGR_buf, "%f\n", IDEAL_SIG_FP[DBGR_j]); \
DBGR_out_file.write(DBGR_buf, strlen(DBGR_buf)); \
} DBGR_out_file.write(DBGR_delim, strlen(DBGR_delim)); \
\
/*printf("%s", DBGR_delim);*/ \
\
/*printf("DBGR_Before\n");*/ \
for (DBGR_j = 0; DBGR_j < int32_t(WINDOW); DBGR_j++) { \
sprintf(DBGR_buf, "%f\n", gr::lora::clamp(SAMPLE_SIG_FP[DBGR_j], -CLAMP_VAL, CLAMP_VAL)); \
sprintf(DBGR_buf, "%f\n", gr::lora::clamp(SAMPLE_SIG_FP[DBGR_j], -CLAMP_VAL, CLAMP_VAL)); \
DBGR_out_file.write(DBGR_buf, strlen(DBGR_buf)); \
} DBGR_out_file.write(DBGR_delim, strlen(DBGR_delim)); \
\
/*printf("%s", DBGR_delim);*/ \
\
printf("DBGR_After %d of %d in %d\n", MIN, MAX, WINDOW); \
for (DBGR_j = OFFSET; DBGR_j < int32_t(OFFSET > 0 ? WINDOW : MAX); DBGR_j++) { \
sprintf(DBGR_buf, "%f\n", gr::lora::clamp(*(SAMPLE_SIG_FP + DBGR_j), -CLAMP_VAL, CLAMP_VAL)); \
sprintf(DBGR_buf, "%f\n", gr::lora::clamp(*(SAMPLE_SIG_FP + DBGR_j), -CLAMP_VAL, CLAMP_VAL)); \
DBGR_out_file.write(DBGR_buf, strlen(DBGR_buf)); \
} DBGR_out_file.write(DBGR_delim, strlen(DBGR_delim)); \
\
@ -79,7 +92,6 @@
printf("%s", DBGR_delim); \
}*/ \
\
/*printf("DBGR_End\n");*/ \
DBGR_out_file.close(); \
if(PAUSE) DBGR_PAUSE(MSG); \
} while(0)
@ -119,39 +131,39 @@
} while(0) \
#define DBGR_START_TIME_MEASUREMENT(OUT, MSG) \
DBGR_intermediate_time = OUT; \
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; \
#define DBGR_START_TIME_MEASUREMENT(OUT, MSG) \
DBGR_intermediate_time = OUT; \
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();
#define DBGR_START_TIME_MEASUREMENT_SAME_SCOPE(OUT, MSG) \
DBGR_intermediate_time = OUT; \
if (DBGR_intermediate_time) printf("[CHRONO] : Start with % 15s", MSG); \
DBGR_totalled_time = false; \
#define DBGR_START_TIME_MEASUREMENT_SAME_SCOPE(OUT, MSG) \
DBGR_intermediate_time = OUT; \
if (DBGR_intermediate_time) printf("[CHRONO] : Start with % 15s", MSG); \
DBGR_totalled_time = false; \
DBGR_start_time = std::chrono::high_resolution_clock::now();
#define DBGR_INTERMEDIATE_TIME_MEASUREMENT() \
if (!DBGR_totalled_time) { \
#define DBGR_INTERMEDIATE_TIME_MEASUREMENT() \
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; \
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"); \
} \
DBGR_global_time += DBGR_duration; \
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; \
#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; \
#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

Wyświetl plik

@ -32,7 +32,7 @@
#include "utilities.h"
//#define NO_TMP_WRITES 1 /// Debug output file write
#define CFO_CORRECT 1 /// Correct shift fft estimation
//#define CFO_CORRECT 1 /// Correct shift fft estimation
//#undef NDEBUG /// Debug printing
//#define NDEBUG /// No debug printing
@ -116,18 +116,24 @@ namespace gr {
this->build_ideal_chirps();
this->set_output_multiple(2 * this->d_samples_per_symbol);
this->d_fft.resize(this->d_number_of_bins);
this->d_mult.resize(this->d_number_of_bins);
this->d_q = fft_create_plan(this->d_number_of_bins, &this->d_mult[0], &this->d_fft[0], LIQUID_FFT_FORWARD, 0);
this->d_fft.resize(this->d_samples_per_symbol);
this->d_mult_hf.resize(this->d_samples_per_symbol);
this->d_tmp.resize(this->d_number_of_bins);
this->d_q = fft_create_plan(this->d_samples_per_symbol, &this->d_mult_hf[0], &this->d_fft[0], LIQUID_FFT_FORWARD, 0);
this->d_qr = fft_create_plan(this->d_number_of_bins, &this->d_tmp[0], &this->d_mult_hf[0], LIQUID_FFT_BACKWARD, 0);
// Decimation filter
float g[DECIMATOR_FILTER_SIZE];
liquid_firdes_rrcos(8, 1, 0.5f, 0.3f, g); // Filter for interpolating
const int delay = 2;
const int decim_filter_size = (2 * this->d_decim_factor * delay + 1);
float g[decim_filter_size];
float d_decim_h[decim_filter_size]; ///< The reversed decimation filter for LiquidDSP.
liquid_firdes_rrcos(this->d_decim_factor, delay, 0.5f, 0.3f, g); // Filter for interpolating
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];
for (uint32_t i = 0u; i < decim_filter_size; i++) // Reverse it to get decimation filter
d_decim_h[i] = g[decim_filter_size - i - 1u];
this->d_decim = firdecim_crcf_create(this->d_decim_factor, this->d_decim_h, DECIMATOR_FILTER_SIZE);
this->d_decim = firdecim_crcf_create(this->d_decim_factor, d_decim_h, decim_filter_size);
// Register gnuradio ports
this->message_port_register_out(pmt::mp("frames"));
@ -151,6 +157,7 @@ namespace gr {
#endif
fft_destroy_plan(this->d_q);
fft_destroy_plan(this->d_qr);
firdecim_crcf_destroy(this->d_decim);
}
@ -182,6 +189,19 @@ namespace gr {
samples_to_file("/tmp/upchirp", &this->d_upchirp[0], this->d_upchirp.size(), sizeof(gr_complex));
}
void decoder_impl::values_to_file(const std::string path, const unsigned char *v, const uint32_t length, const uint32_t ppm) {
std::ofstream out_file;
out_file.open(path.c_str(), std::ios::out | std::ios::app);
for (uint32_t i = 0u; i < length; i++) {
std::string tmp = gr::lora::to_bin(v[i], ppm);
out_file.write(tmp.c_str(), tmp.length());
}
out_file.write("\n", 1);
out_file.close();
}
void decoder_impl::samples_to_file(const std::string path, const gr_complex *v, const uint32_t length, const uint32_t elem_size) {
#ifndef NO_TMP_WRITES
std::ofstream out_file;
@ -292,28 +312,28 @@ namespace gr {
* 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 decoder_impl::cross_correlate_ifreq(const float *samples_ifreq, const std::vector<float>& ideal_chirp, const uint32_t to_idx) {
float result = 0.0f;
const float average = std::accumulate(samples_ifreq + from_idx, samples_ifreq + to_idx, 0.0f) / (float)(to_idx - from_idx);
const float chirp_avg = std::accumulate(&ideal_chirp[from_idx] , &ideal_chirp[to_idx] , 0.0f) / (float)(to_idx - from_idx);
const float sd = this->stddev(samples_ifreq + from_idx , (to_idx - from_idx) , average)
* this->stddev(&ideal_chirp[from_idx] , (to_idx - from_idx) , chirp_avg);
const float average = std::accumulate(samples_ifreq , samples_ifreq + to_idx, 0.0f) / (float)(to_idx);
const float chirp_avg = std::accumulate(&ideal_chirp[0], &ideal_chirp[to_idx] , 0.0f) / (float)(to_idx);
const float sd = this->stddev(samples_ifreq , to_idx, average)
* this->stddev(&ideal_chirp[0] , to_idx, chirp_avg);
for (uint32_t i = from_idx; i < to_idx; i++) {
for (uint32_t i = 0u; i < to_idx; i++) {
result += (samples_ifreq[i] - average) * (ideal_chirp[i] - chirp_avg) / sd;
}
result /= (float)(to_idx - from_idx - 1u);
result /= (float)(to_idx - 1u);
return result;
}
float decoder_impl::detect_downchirp(const gr_complex *samples, const uint32_t window) {
float samples_ifreq[window];
this->instantaneous_frequency(samples, samples_ifreq, window);
return this->cross_correlate_ifreq(samples_ifreq, this->d_downchirp_ifreq, 0u, window - 1u);
return this->cross_correlate_ifreq(samples_ifreq, this->d_downchirp_ifreq, window - 1u);
}
/**
@ -340,6 +360,7 @@ namespace gr {
}
if (!found_change) {
//printf("No falling edge?\n");
return 0.0f;
}
@ -350,14 +371,18 @@ namespace gr {
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 - 1u)*/ len);
for (uint32_t i = local_max_idx; i < local_min_idx && (i + len) < window; i++) {
const float max_corr = this->cross_correlate_ifreq(samples_ifreq + i, this->d_upchirp_ifreq, len);
if (max_corr > max_correlation) {
*index = i;
max_correlation = max_corr;
}
}
// Signal from local_max_idx vs shifted with *index
//DBGR_WRITE_SIGNAL(this->d_upchirp_ifreq, (samples_ifreq + local_max_idx), len, (*index - local_max_idx), 0u, window, false, true, Printed graphs in sliding_norm_cross_correlate_upchirp);
return max_correlation;
}
@ -402,14 +427,14 @@ namespace gr {
}
/**
* Currently unused.
* Currently unstable due to center frequency offset.
*/
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];
gr_complex sample[this->d_samples_per_symbol];
memcpy(sample, samples, this->d_samples_per_symbol);
memcpy(sample, samples, this->d_samples_per_symbol * sizeof(gr_complex));
#ifdef CFO_CORRECT
this->determine_cfo(&samples[0]);
#ifndef NDEBUG
@ -423,50 +448,102 @@ namespace gr {
// Multiply with ideal downchirp
for (uint32_t i = 0u; i < this->d_samples_per_symbol; i++) {
mult_hf[i] = std::conj(sample[i] * this->d_downchirp[i]);
this->d_mult_hf[i] = std::conj(sample[i] * this->d_downchirp[i]);
}
samples_to_file("/tmp/mult", &mult_hf[0], this->d_samples_per_symbol, sizeof(gr_complex));
samples_to_file("/tmp/mult", &this->d_mult_hf[0], this->d_samples_per_symbol, sizeof(gr_complex));
// 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], &this->d_mult[i]);
}
//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], &this->d_mult[i]);
//}
samples_to_file("/tmp/resampled", &this->d_mult[0], this->d_number_of_bins, sizeof(gr_complex));
//samples_to_file("/tmp/resampled", &this->d_mult[0], this->d_number_of_bins, sizeof(gr_complex));
// Perform FFT
fft_execute(this->d_q);
// Decimate. Note: assumes fft size is multiple of decimation factor and number of bins is even
const uint32_t N = this->d_number_of_bins;
memcpy(&this->d_tmp[0], &this->d_fft[0], (N + 1u) / 2u * sizeof(gr_complex));
memcpy(&this->d_tmp[ (N + 1u) / 2u ], &this->d_fft[this->d_samples_per_symbol - (N / 2u)], N / 2u * sizeof(gr_complex));
this->d_tmp[N / 2u] += this->d_fft[N / 2u];
// Note that you have to kill the grc before checking the plots!
// Get magnitude
for (uint32_t i = 0u; i < this->d_number_of_bins; i++) {
fft_mag[i] = std::abs(this->d_fft[i]);
fft_mag[i] = std::abs(this->d_tmp[i]);
}
samples_to_file("/tmp/fft", &this->d_fft[0], this->d_number_of_bins, sizeof(gr_complex));
samples_to_file("/tmp/fft", &this->d_tmp[0], this->d_number_of_bins, sizeof(gr_complex));
fft_execute(this->d_qr); // debug
samples_to_file("/tmp/resampled", &this->d_mult_hf[0], this->d_number_of_bins, sizeof(gr_complex));
// Return argmax here
return (std::max_element(fft_mag, fft_mag + this->d_number_of_bins) - fft_mag);
}
uint32_t decoder_impl::max_frequency_gradient_idx(const gr_complex *samples) {
uint32_t decoder_impl::max_frequency_gradient_idx(const gr_complex *samples, const bool is_header) {
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);
for (uint32_t i = 0u; i < this->d_number_of_bins - 1u; i++) {
/*** Visualize bins in plot ******************************************/
//#define PLOT_BINS // Uncomment for use
#ifdef PLOT_BINS
uint32_t gradbins = this->d_number_of_bins;
uint32_t graddecim = this->d_decim_factor;
if (is_header) {
gradbins /= 4u;
graddecim *= 4u;
}
printf("Bins: %d, len: %d\n", gradbins, graddecim);
float samples_bins[this->d_samples_per_symbol];
for (uint32_t i = 0u; i < this->d_samples_per_symbol; i++) {
samples_bins[i] = i % (graddecim * 2u) == 0u ? 0.5f : i % graddecim == 0u ? -0.5f : 0.0f;
}
DBGR_WRITE_SIGNAL(samples_bins, samples_ifreq, this->d_samples_per_symbol, 0, 0, this->d_samples_per_symbol, false, false, Printed bins in freq_grad_idx);
#endif
/*********************************************************************/
for (uint32_t i = 1u; i < this->d_number_of_bins - 2u; i++) {
if (samples_ifreq[this->d_decim_factor * i] - samples_ifreq[this->d_decim_factor * (i + 1u)] > 0.2f) {
return i + 1u;
#ifdef PLOT_BINS
printf("[Freq_Grad] Down on idx: %4d in bin %4d (in [%4d, %4d])\n",
this->d_decim_factor * i,
is_header ? (i + !is_header) / 4u : (i + !is_header),
graddecim * (is_header ? i / 4u : i),
graddecim * ((is_header ? i / 4u : i) + 1u));
DBGR_PAUSE();
#endif
return i + !is_header;
}
}
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];
const float zero_bin = samples_ifreq[0u] - samples_ifreq[this->d_decim_factor * 2u];
const float high_bin = samples_ifreq[(this->d_number_of_bins - 2u) * this->d_decim_factor] - samples_ifreq[this->d_number_of_bins * this->d_decim_factor - 1u];
return zero_bin > high_bin
? 0u : this->d_number_of_bins;
#ifdef PLOT_BINS
if (zero_bin > 0.2f || zero_bin > high_bin)
printf("[Freq_Grad] Down on idx: %4d in bin %4d (at %4d)\n", 0, 0, 1);
else
printf("[Freq_Grad] Down on idx: %4d in bin %4d (at %4d)\n",
this->d_decim_factor * (this->d_number_of_bins - 1u),
this->d_number_of_bins,
this->d_decim_factor * this->d_number_of_bins);
DBGR_PAUSE();
#endif
// Prefer first bin over last. (First bin == 0 or 1?)
return zero_bin > 0.2f || zero_bin > high_bin
? 1u : this->d_number_of_bins;
}
bool decoder_impl::demodulate(const gr_complex *samples, const bool is_header) {
@ -474,7 +551,7 @@ namespace gr {
// DBGR_START_TIME_MEASUREMENT(false, "only");
uint32_t bin_idx = this->max_frequency_gradient_idx(samples);
uint32_t bin_idx = this->max_frequency_gradient_idx(samples, is_header);
//uint32_t bin_idx = this->get_shift_fft(samples);
// DBGR_INTERMEDIATE_TIME_MEASUREMENT();
@ -509,7 +586,7 @@ namespace gr {
*/
void decoder_impl::deinterleave(const uint32_t ppm) {
const uint32_t bits_per_word = this->d_words.size();
const uint32_t offset_start = ppm - 1u;
const uint32_t offset_start = ppm - 1u;
std::vector<uint8_t> words_deinterleaved(ppm, 0u);
@ -541,6 +618,10 @@ namespace gr {
static const uint8_t shuffle_pattern[] = {7, 6, 3, 4, 2, 1, 0, 5};
this->deshuffle(shuffle_pattern, is_header);
if (!is_header)
this->values_to_file("/tmp/after_deshuffle", &this->d_words_deshuffled[0], this->d_words_deshuffled.size(), 8);
this->dewhiten(is_header ? gr::lora::prng_header : this->d_whitening_sequence);
this->hamming_decode(out_data);
@ -567,16 +648,13 @@ namespace gr {
void decoder_impl::deshuffle(const uint8_t *shuffle_pattern, const bool is_header) {
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;
uint8_t result;
for (uint32_t i = 0u; i < to_decode; i++) {
original = this->d_demodulated[i];
result = 0u;
result = 0u;
for (uint32_t j = 0u; j < len; j++) {
if (original & (1u << shuffle_pattern[j])) {
result |= 1u << j;
}
result |= !!(this->d_demodulated[i] & (1u << shuffle_pattern[j])) << j;
}
this->d_words_deshuffled.push_back(result);
@ -603,7 +681,6 @@ namespace gr {
// if (prng != gr::lora::prng_header)
// DBGR_QUICK_TO_FILE("/tmp/whitening_out", true, this->d_words_deshuffled, len, "0x%02X,");
for (uint32_t i = 0u; i < len; i++) {
uint8_t xor_b = this->d_words_deshuffled[i] ^ prng[i];
@ -800,7 +877,7 @@ namespace gr {
this->d_debug << "Cd: " << c << std::endl;
#endif
if (c > 0.98f) {
if (c > 0.99f) {
#ifndef NDEBUG
this->d_debug << "SYNC: " << c << std::endl;
#endif
@ -889,6 +966,21 @@ namespace gr {
DBGR_STOP_TIME_MEASUREMENT(true);
// DBGR_PAUSE();
//---- Whitening rejecter ----
// bool all_zero = true;
// for (uint32_t i = 0u; i < this->d_payload_length; i++) {
// if (decoded[i]) {
// all_zero = false;
// break;
// }
// }
// // Remove last line in whitening output file if decoded values were not all zero
// if (!all_zero) {
// system("sed -i '$ d' /tmp/whitening_out");
// }
//----------------------------
}
}

Wyświetl plik

@ -73,7 +73,8 @@ namespace gr {
std::vector<float> d_upchirp_ifreq; ///< The instantaneous frequency of the ideal upchirp.
std::vector<gr_complex> d_fft; ///< Vector containing the FFT resuls.
std::vector<gr_complex> d_mult; ///< Vector containing the FFT decimation.
std::vector<gr_complex> d_mult_hf; ///< Vector containing the FFT decimation.
std::vector<gr_complex> d_tmp; ///< Vector containing the FFT decimation.
uint8_t d_sf; ///< The Spreading Factor.
uint32_t d_bw; ///< The receiver bandwidth (fixed to `125kHz`).
@ -102,8 +103,8 @@ namespace gr {
std::ofstream d_debug; ///< Outputstream for the debug log.
fftplan d_q; ///< The LiquidDSP::FFT_Plan.
fftplan d_qr; ///< The LiquidDSP::FFT_Plan in reverse.
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.
@ -141,6 +142,20 @@ namespace gr {
*/
void samples_to_file(const std::string path, const gr_complex *v, const uint32_t length, const uint32_t elem_size);
/**
* \brief Debug method to dump the given values array to a file in textual format.
*
* \param path
* The path to the file to dump to.
* \param v
* The values array to dump.
* \param length
* Length of said array.
* \param ppm
* PPM value of the data.
*/
void values_to_file(const std::string path, const unsigned char *v, const uint32_t length, const uint32_t ppm);
/**
* \brief Write the given complex array to the debug outputstream.
*
@ -207,12 +222,10 @@ namespace gr {
* 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);
float cross_correlate_ifreq(const float *samples_ifreq, const std::vector<float>& ideal_chirp, const uint32_t to_idx);
/**
* \brief Returns the index to shift the given symbol so that it overlaps the ideal upchirp.
@ -271,8 +284,10 @@ namespace gr {
*
* \param samples
* The complex symbol to analyse.
* \param is_header
* Whether the given symbol is part of a HDR.
*/
uint32_t max_frequency_gradient_idx(const gr_complex *samples);
uint32_t max_frequency_gradient_idx(const gr_complex *samples, const bool is_header = false);
/**
* \brief Demodulate the given symbol and return true if all expected symbols have been parsed.
@ -399,7 +414,7 @@ namespace gr {
* \param num_samples
* Size of said complex array.
*/
void msg_raw_chirp_debug(const gr_complex *raw_samples, const uint32_t num_samples);
void msg_raw_chirp_debug(const gr_complex *raw_samples, const uint32_t num_samples);
/**
* \brief Unimplemented
@ -407,7 +422,7 @@ namespace gr {
* \param frame_bytes
* \param frame_len
*/
void msg_lora_frame(const uint8_t *frame_bytes, const uint32_t frame_len);
void msg_lora_frame(const uint8_t *frame_bytes, const uint32_t frame_len);
public:
/**

Wyświetl plik

@ -166,6 +166,7 @@ namespace gr {
/**
* \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.
@ -296,7 +297,6 @@ namespace gr {
// 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;

Wyświetl plik

@ -4,7 +4,8 @@ import collections, datetime
import os.path
import xmltodict
from gnuradio import gr, gr_unittest, blocks
from gnuradio import gr, gr_unittest, blocks, filter
from gnuradio.filter import firdes
TestResultData = collections.namedtuple('TestResultData', ['id', 'fromfile', 'passing', 'total', 'rate'])
TestSerieSettings = collections.namedtuple('TestSerieSettings', ['data', 'times'])
@ -82,6 +83,10 @@ class qa_BasicTest_XML (gr_unittest.TestCase):
#self.bitrate = self.sf * (1 / (2**self.sf / self.bw ))
self.hasHDR = True
# For FFT, determine Center Frequency Offset first, then set here.
# For RN2483, usually -14.1e3
self.center_offset = 0
self.inputFile = "./"
# Socket connection for sink
@ -215,9 +220,11 @@ class qa_BasicTest_XML (gr_unittest.TestCase):
self.lora_lora_receiver_0 = lora.lora_receiver(self.samp_rate, self.capture_freq, self.offset, self.sf, self.samp_rate)
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.freq_xlating_fir_filter_0 = filter.freq_xlating_fir_filter_ccc(1, (firdes.low_pass(1, self.samp_rate, 500000, 100000, firdes.WIN_HAMMING, 6.67)), self.center_offset, self.samp_rate)
self.tb.connect( (self.file_source, 0), (self.blocks_throttle_0, 0))
self.tb.connect( (self.blocks_throttle_0, 0), (self.lora_lora_receiver_0, 0))
self.tb.connect( (self.blocks_throttle_0, 0), (self.freq_xlating_fir_filter_0, 0))
self.tb.connect( (self.freq_xlating_fir_filter_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'))
self.tb.run ()