kopia lustrzana https://github.com/rpp0/gr-lora
Some progress on different SFs and CRs
rodzic
90486a0ed0
commit
380be3e20d
|
@ -18,6 +18,5 @@
|
|||
# Boston, MA 02110-1301, USA.
|
||||
|
||||
install(FILES
|
||||
lora_decoder.xml
|
||||
lora_receiver.xml DESTINATION share/gnuradio/grc/blocks
|
||||
)
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<block>
|
||||
<name>LoRa Decoder</name>
|
||||
<key>lora_decoder</key>
|
||||
<category>lora</category>
|
||||
<import>import lora</import>
|
||||
<make>lora.lora_decoder()</make>
|
||||
|
||||
<sink>
|
||||
<name>in</name>
|
||||
<type>float</type>
|
||||
</sink>
|
||||
|
||||
<sink>
|
||||
<name>in</name>
|
||||
<type>complex</type>
|
||||
</sink>
|
||||
|
||||
<source>
|
||||
<name>out</name>
|
||||
<type>float</type>
|
||||
<optional>1</optional>
|
||||
</source>
|
||||
|
||||
<source>
|
||||
<name>out</name>
|
||||
<type>float</type>
|
||||
<optional>1</optional>
|
||||
</source>
|
||||
</block>
|
|
@ -4,13 +4,13 @@
|
|||
<key>lora_lora_receiver</key>
|
||||
<category>lora</category>
|
||||
<import>import lora</import>
|
||||
<make>lora.lora_receiver($in_samp_rate, $freq, $offset, $finetune, $realtime)</make>
|
||||
<make>lora.lora_receiver($in_samp_rate, $freq, $offset, $sf, $realtime)</make>
|
||||
|
||||
<callback>set_finetune($finetune)</callback>
|
||||
<callback>set_sf($sf)</callback>
|
||||
<callback>set_offset($offset)</callback>
|
||||
<param>
|
||||
<name>Finetune</name>
|
||||
<key>finetune</key>
|
||||
<name>Spreading factor</name>
|
||||
<key>sf</key>
|
||||
<type>int</type>
|
||||
</param>
|
||||
|
||||
|
|
|
@ -703,9 +703,9 @@ namespace gr {
|
|||
* class. lora::decoder::make is the public interface for
|
||||
* creating new instances.
|
||||
*/
|
||||
static sptr make(int finetune);
|
||||
static sptr make(int sf);
|
||||
|
||||
virtual void set_finetune(int32_t finetune) = 0;
|
||||
virtual void set_sf(uint8_t sf) = 0;
|
||||
};
|
||||
|
||||
} // namespace lora
|
||||
|
|
|
@ -30,8 +30,6 @@
|
|||
#include "tables.h"
|
||||
#include "utilities.h"
|
||||
|
||||
#define CORRELATION_SEARCH_RANGE 1024
|
||||
#define DELAY_AFTER_SYNC 262
|
||||
//#define NO_TMP_WRITES 1
|
||||
//#define CFO_CORRECT 1
|
||||
|
||||
|
@ -39,34 +37,35 @@ namespace gr {
|
|||
namespace lora {
|
||||
|
||||
decoder::sptr
|
||||
decoder::make(int finetune) {
|
||||
decoder::make(int sf) {
|
||||
return gnuradio::get_initial_sptr
|
||||
(new decoder_impl(finetune));
|
||||
(new decoder_impl(sf));
|
||||
}
|
||||
|
||||
/*
|
||||
* The private constructor
|
||||
*/
|
||||
decoder_impl::decoder_impl(int finetune) : gr::sync_block("decoder",
|
||||
decoder_impl::decoder_impl(int sf) : gr::sync_block("decoder",
|
||||
gr::io_signature::make(1, -1, sizeof(gr_complex)),
|
||||
gr::io_signature::make(0, 2, sizeof(float))) {
|
||||
d_state = DETECT;
|
||||
|
||||
d_debug_samples.open("/tmp/grlora_debug", std::ios::out | std::ios::binary);
|
||||
d_debug.open("/tmp/grlora_debug_txt", std::ios::out);
|
||||
d_sf = 7; // Only affects PHY send
|
||||
d_sf = sf; // Only affects PHY send
|
||||
d_bw = 125000;
|
||||
d_cr = 4;
|
||||
d_bits_per_second = (double)d_sf * 1.0f / (pow(2.0f, d_sf) / d_bw);
|
||||
d_bits_per_second = (double)d_sf * (4.0f/4.0f+d_cr) / (pow(2.0f, d_sf) / d_bw);
|
||||
d_samples_per_second = 1000000;
|
||||
d_symbols_per_second = (double)d_bw / pow(2.0f, d_sf);
|
||||
d_bits_per_symbol = (uint32_t)(d_bits_per_second / d_symbols_per_second);
|
||||
d_samples_per_symbol = (uint32_t)(d_samples_per_second / d_symbols_per_second);
|
||||
d_delay_after_sync = d_samples_per_symbol / 4; //262;
|
||||
d_delay_after_sync += (uint32_t)d_delay_after_sync * 2.4f / 100.0f;
|
||||
d_corr_decim_factor = 8; // samples_per_symbol / corr_decim_factor = correlation window. Also serves as preamble decimation factor
|
||||
d_number_of_bins = (uint32_t)pow(2, d_sf);
|
||||
d_number_of_bins_hdr = d_number_of_bins / 4;
|
||||
d_compression = 8;
|
||||
d_payload_symbols = 0;
|
||||
d_finetune = finetune;
|
||||
d_cfo_estimation = 0.0f;
|
||||
d_cfo_step = 0;
|
||||
d_dt = 1.0f / d_samples_per_second;
|
||||
|
@ -287,18 +286,18 @@ namespace gr {
|
|||
float samples_ifreq[window];
|
||||
|
||||
instantaneous_frequency(samples, samples_ifreq, window);
|
||||
return sliding_norm_cross_correlate(samples_ifreq, &d_upchirp_ifreq[0], window, 128, index);
|
||||
return sliding_norm_cross_correlate(samples_ifreq, &d_upchirp_ifreq[0], window, d_samples_per_symbol / d_corr_decim_factor, index);
|
||||
}
|
||||
|
||||
unsigned int decoder_impl::get_shift_fft(gr_complex* samples) {
|
||||
float fft_mag[d_number_of_bins];
|
||||
gr_complex mult_hf[d_samples_per_symbol];
|
||||
|
||||
/*#ifdef CFO_CORRECT
|
||||
#ifdef CFO_CORRECT
|
||||
determine_cfo(&samples[0]);
|
||||
std::cout << "CFO: " << d_cfo_estimation << std::endl;
|
||||
d_debug << "CFO: " << d_cfo_estimation << std::endl;
|
||||
correct_cfo(&samples[0], d_samples_per_symbol);
|
||||
#endif*/
|
||||
#endif
|
||||
|
||||
samples_to_file("/tmp/data", &samples[0], d_samples_per_symbol, sizeof(gr_complex));
|
||||
|
||||
|
@ -386,17 +385,15 @@ namespace gr {
|
|||
|
||||
// Decode (actually gray encode) the bin to get the symbol value
|
||||
unsigned int word = gray_encode(bin_idx);
|
||||
d_debug << bin_idx << " " << to_bin(word, is_header ? 5 : 7) << " ! " << bin_idx_test << std::endl;
|
||||
d_debug << to_bin(word, is_header ? d_sf-2 : d_sf) << " " << bin_idx << std::endl;
|
||||
d_words.push_back(word);
|
||||
|
||||
// Look for 4+cr symbols and stop
|
||||
if(d_words.size() == (4 + d_cr)) {
|
||||
// Deinterleave
|
||||
if(is_header) {
|
||||
//print_vector(d_words, "M", d_sf - 2);
|
||||
deinterleave(d_sf - 2);
|
||||
} else {
|
||||
//print_vector(d_words, "M", d_sf);
|
||||
deinterleave(d_sf);
|
||||
}
|
||||
|
||||
|
@ -473,6 +470,8 @@ namespace gr {
|
|||
|
||||
if(!is_header)
|
||||
std::cout << result.str() << std::endl;
|
||||
else
|
||||
std::cout << result.str();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -513,9 +512,22 @@ namespace gr {
|
|||
}
|
||||
|
||||
void decoder_impl::hamming_decode(uint8_t* out_data) {
|
||||
unsigned int n = ceil(d_words_dewhitened.size() * 4.0 / 8.0);
|
||||
unsigned int n = ceil(d_words_dewhitened.size() * 4.0f / (4.0f + d_cr));
|
||||
fec_scheme fs = LIQUID_FEC_HAMMING84;
|
||||
|
||||
if(d_cr == 4)
|
||||
fs = LIQUID_FEC_HAMMING84;
|
||||
else if(d_cr == 3)
|
||||
fs = LIQUID_FEC_HAMMING74;
|
||||
else if(d_cr == 2)
|
||||
fs = LIQUID_FEC_HAMMING128;
|
||||
else if(d_cr == 1) { // TODO: Temporary, since Liquid doesn't support 4/5 Hamming so we need to implement it ourselves / extend Liquid.
|
||||
uint8_t indices[4] = {1, 2, 3, 5};
|
||||
fec_extract_data_only(&d_words_dewhitened[0], d_words_dewhitened.size(), indices, 4, out_data);
|
||||
d_words_dewhitened.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int k = fec_get_enc_msg_length(fs, n);
|
||||
fec hamming = fec_create(fs, NULL);
|
||||
|
||||
|
@ -548,10 +560,10 @@ namespace gr {
|
|||
}
|
||||
|
||||
float sum = 0.0f;
|
||||
for(int i = 0; i < d_samples_per_symbol; i++) {
|
||||
for(int i = 0; i < d_samples_per_symbol-1; i++) {
|
||||
sum += instantaneous_freq[i];
|
||||
}
|
||||
sum /= d_samples_per_symbol;
|
||||
sum /= d_samples_per_symbol-1;
|
||||
|
||||
d_cfo_estimation = sum;
|
||||
|
||||
|
@ -575,26 +587,32 @@ namespace gr {
|
|||
}
|
||||
|
||||
int decoder_impl::find_preamble_start_fast(gr_complex* samples, uint32_t len) {
|
||||
int decimation = d_decim_factor; // TODO: Replace with d_decimation
|
||||
int decimation = d_corr_decim_factor;
|
||||
int decim_size = d_samples_per_symbol / decimation;
|
||||
float decim[decim_size];
|
||||
float gradient[decim_size];
|
||||
float decim[decimation];
|
||||
float gradient[decimation];
|
||||
uint32_t rising = 0;
|
||||
uint32_t rising_required = 2;
|
||||
|
||||
gradient[0] = 0.0f;
|
||||
|
||||
|
||||
for(int i = 0; i < decim_size; i++) {
|
||||
for(int i = 0; i < decimation; i++) {
|
||||
float s[2] = {
|
||||
arg(samples[i*decimation]),
|
||||
arg(samples[(i+1)*decimation])
|
||||
arg(samples[i*decim_size]),
|
||||
arg(samples[(i+1)*decim_size])
|
||||
};
|
||||
liquid_unwrap_phase(s, 2);
|
||||
|
||||
decim[i] = (s[1] - s[0]) / (2.0f * M_PI) * d_samples_per_second;
|
||||
}
|
||||
|
||||
for(int i = 1; i < decim_size; i++) {
|
||||
for(int i = 1; i < decimation; i++) {
|
||||
gradient[i] = decim[i] - decim[i-1];
|
||||
if(gradient[i] <= -20000) {
|
||||
return i*decimation;
|
||||
if(gradient[i] > gradient[i-1])
|
||||
rising++;
|
||||
if(rising >= rising_required && gradient[i] <= -20000) { // TODO: Make this a bit more logical, e.g. d_bw / decimation * 2 -> 2 steps down
|
||||
return i*decim_size;
|
||||
}
|
||||
//d_debug << "G:" << gradient[i] << std::endl;
|
||||
}
|
||||
|
@ -602,6 +620,31 @@ namespace gr {
|
|||
return -1;
|
||||
}
|
||||
|
||||
uint8_t decoder_impl::lookup_cr(uint8_t bytevalue) {
|
||||
switch (bytevalue & 0x0f) {
|
||||
case 0x0a: {
|
||||
return 4;
|
||||
break;
|
||||
}
|
||||
case 0x0b: {
|
||||
return 3;
|
||||
break;
|
||||
}
|
||||
case 0x0c: {
|
||||
return 2;
|
||||
break;
|
||||
}
|
||||
case 0x08: {
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int decoder_impl::work(int noutput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items) {
|
||||
|
@ -635,9 +678,10 @@ namespace gr {
|
|||
if(c > 0.8f) {
|
||||
d_debug << "SYNC: " << c << std::endl;
|
||||
// Debug stuff
|
||||
samples_to_file("/tmp/sync", &input[0], CORRELATION_SEARCH_RANGE, sizeof(gr_complex));
|
||||
samples_to_file("/tmp/sync", &input[0], d_samples_per_symbol, sizeof(gr_complex));
|
||||
|
||||
d_state = PAUSE;
|
||||
d_cr = 4;
|
||||
consume_each(d_samples_per_symbol);
|
||||
} else {
|
||||
d_corr_fails++;
|
||||
|
@ -651,8 +695,8 @@ namespace gr {
|
|||
case PAUSE: {
|
||||
d_state = DECODE_HEADER;
|
||||
|
||||
samples_debug(input, d_samples_per_symbol + DELAY_AFTER_SYNC);
|
||||
consume_each(d_samples_per_symbol + DELAY_AFTER_SYNC);
|
||||
samples_debug(input, d_samples_per_symbol + d_delay_after_sync);
|
||||
consume_each(d_samples_per_symbol + d_delay_after_sync);
|
||||
break;
|
||||
}
|
||||
case DECODE_HEADER: {
|
||||
|
@ -663,7 +707,7 @@ namespace gr {
|
|||
|
||||
nibble_reverse(decoded, 1); // TODO: Why?
|
||||
d_payload_length = decoded[0];
|
||||
d_cr = 4; // TODO: Get from header instead of hardcode
|
||||
d_cr = lookup_cr(decoded[2]);
|
||||
|
||||
int symbols_per_block = d_cr + 4;
|
||||
int bits_needed = ((d_payload_length * 8) + 16) * (symbols_per_block / 4);
|
||||
|
@ -710,8 +754,9 @@ namespace gr {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void decoder_impl::set_finetune(int32_t finetune) {
|
||||
d_finetune = finetune;
|
||||
void decoder_impl::set_sf(uint8_t sf) {
|
||||
if(sf >= 7 && sf <= 13)
|
||||
d_sf = sf;
|
||||
}
|
||||
|
||||
} /* namespace lora */
|
||||
|
|
|
@ -51,11 +51,11 @@ namespace gr {
|
|||
std::vector<float> d_upchirp_ifreq;
|
||||
std::vector<gr_complex> d_fft;
|
||||
std::vector<gr_complex> d_mult;
|
||||
int32_t d_finetune;
|
||||
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;
|
||||
|
@ -75,6 +75,7 @@ namespace gr {
|
|||
std::ofstream d_debug;
|
||||
fftplan d_q;
|
||||
float d_decim_h[DECIMATOR_FILTER_SIZE];
|
||||
uint32_t d_corr_decim_factor;
|
||||
int d_decim_factor;
|
||||
firdecim_crcf d_decim;
|
||||
float d_cfo_estimation;
|
||||
|
@ -106,6 +107,7 @@ namespace gr {
|
|||
float stddev(const float *values, int len, float mean);
|
||||
inline void instantaneous_phase(const gr_complex* in_samples, float* out_iphase, uint32_t window);
|
||||
void instantaneous_frequency(const gr_complex* in_samples, float* out_ifreq, uint32_t window);
|
||||
uint8_t lookup_cr(uint8_t bytevalue);
|
||||
|
||||
|
||||
public:
|
||||
|
@ -118,7 +120,7 @@ namespace gr {
|
|||
gr_vector_void_star &output_items);
|
||||
|
||||
// GRC interfaces
|
||||
virtual void set_finetune(int32_t finetune);
|
||||
virtual void set_sf(uint8_t sf);
|
||||
};
|
||||
|
||||
} // namespace lora
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
namespace gr {
|
||||
namespace lora {
|
||||
const uint8_t prng_header[] = {
|
||||
0x22, 0x11, 0x0, 0x0, 0x0
|
||||
0x22, 0x11, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
const uint8_t prng_payload[] = {
|
||||
|
|
|
@ -47,7 +47,19 @@ namespace gr {
|
|||
return (((count+1) % 2) == 0);
|
||||
}
|
||||
|
||||
|
||||
void fec_extract_data_only(uint8_t* in_data, uint32_t len, uint8_t* indices, uint8_t n, uint8_t* out_data) {
|
||||
for(uint32_t i = 0; i < len; i++) {
|
||||
uint8_t d = 0;
|
||||
for(uint32_t j = 0; j < n; j++) {
|
||||
uint8_t power = pow(2, indices[j]);
|
||||
if((in_data[i] & power) > 0) {
|
||||
d += power;
|
||||
}
|
||||
}
|
||||
out_data[i] = d;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,15 +30,15 @@ class lora_receiver(gr.hier_block2):
|
|||
"""
|
||||
docstring for block lora_receiver
|
||||
"""
|
||||
def __init__(self, in_samp_rate, freq, offset, finetune, realtime):
|
||||
def __init__(self, in_samp_rate, freq, offset, sf, realtime):
|
||||
gr.hier_block2.__init__(self,
|
||||
"lora_receiver", # Min, Max, gr.sizeof_<type>
|
||||
gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
|
||||
gr.io_signature(0, 0, 0)) # Output signature
|
||||
|
||||
# Parameters
|
||||
self.finetune = finetune
|
||||
self.offset = offset
|
||||
self.sf = sf
|
||||
self.in_samp_rate = in_samp_rate
|
||||
self.realtime = realtime
|
||||
bw = 125000
|
||||
|
@ -47,7 +47,7 @@ class lora_receiver(gr.hier_block2):
|
|||
null1 = null_sink(gr.sizeof_float)
|
||||
null2 = null_sink(gr.sizeof_float)
|
||||
if realtime:
|
||||
self.c_decoder = lora.decoder(finetune)
|
||||
self.c_decoder = lora.decoder(sf)
|
||||
out_samp_rate = 1000000
|
||||
else:
|
||||
decoder = lora_decoder()
|
||||
|
@ -73,13 +73,13 @@ class lora_receiver(gr.hier_block2):
|
|||
self.connect((decoder, 0), (null1, 0))
|
||||
self.connect((decoder, 1), (null2, 0))
|
||||
|
||||
def get_finetune(self):
|
||||
return self.finetune
|
||||
def set_sf(self):
|
||||
return self.sf
|
||||
|
||||
def set_finetune(self, finetune):
|
||||
self.finetune = finetune
|
||||
def set_sf(self, sf):
|
||||
self.sf = sf
|
||||
if self.realtime:
|
||||
self.c_decoder.set_finetune(self.finetune)
|
||||
self.c_decoder.set_sf(self.sf)
|
||||
|
||||
def get_offset(self):
|
||||
return self.offset
|
||||
|
|
Ładowanie…
Reference in New Issue