GNU Radio's LORA Package
decoder_impl.h
Go to the documentation of this file.
1/* -*- c++ -*- */
2/*
3 * Copyright 2017 Pieter Robyns, William Thenaers.
4 *
5 * This is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3, or (at your option)
8 * any later version.
9 *
10 * This software is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this software; see the file COPYING. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#ifndef INCLUDED_LORA_DECODER_IMPL_H
22#define INCLUDED_LORA_DECODER_IMPL_H
23
24#include <liquid/liquid.h>
25#include "lora/decoder.h"
26#include <string>
27#include <vector>
28#include <fstream>
29#include <lora/debugger.h>
30#include <volk/volk.h>
31#include <lora/loraphy.h>
32#include <boost/circular_buffer.hpp>
33
34namespace gr {
35 namespace lora {
36
37 /**
38 * \brief **DecoderState** : Each state the LoRa decoder can be in.
39 */
40 enum class DecoderState {
41 DETECT,
42 SYNC,
44 PAUSE,
47 STOP
48 };
49
50 /**
51 * \brief Return the DecoderState as string for debugging purposes.
52 *
53 * \param s
54 * The state to return to string.
55 */
56 /*static std::string state_to_string(DecoderState s) {
57 static std::string DecoderStateLUT[] = { "DETECT", "SYNC", "FIND_SFD", "PAUSE", "DECODE_HEADER", "DECODE_PAYLOAD", "STOP" };
58 return DecoderStateLUT[ (size_t)s ];
59 }*/
60
61 /**
62 * \brief **LoRa Decoder**
63 * <br/>The main class for the LoRa decoder.
64 * Contains all variables and methods necessary for succesfully decoding LoRa PHY.
65 * <br/>Only the sample rate and spreading factor are needed.
66 * The other settings, like packet length and coding rate, are extracted from the (explicit) HDR.
67 */
68 class decoder_impl : public decoder {
69 private:
70 debugger d_dbg; ///< Debugger for plotting samples, printing output, etc.
71 DecoderState d_state; ///< Holds the current state of the decoder (state machine).
72
73 std::vector<gr_complex> d_downchirp; ///< The complex ideal downchirp.
74 std::vector<float> d_downchirp_ifreq; ///< The instantaneous frequency of the ideal downchirp.
75
76 std::vector<gr_complex> d_upchirp; ///< The complex ideal upchirp.
77 std::vector<float> d_upchirp_ifreq; ///< The instantaneous frequency of the ideal upchirp.
78 std::vector<float> d_upchirp_ifreq_v; ///< The instantaneous frequency of the ideal upchirp.
79
80 std::vector<gr_complex> d_fft; ///< Vector containing the FFT resuls.
81 std::vector<gr_complex> d_mult_hf; ///< Vector containing the FFT decimation.
82 std::vector<gr_complex> d_tmp; ///< Vector containing the FFT decimation.
83
84 bool d_implicit; ///< Implicit header mode.
85 bool d_reduced_rate; ///< Use reduced rate (only configurable in implicit header mode).
86 uint8_t d_sf; ///< The Spreading Factor.
87 uint32_t d_bw; ///< The receiver bandwidth (fixed to `125kHz`).
88 loraphy_header_t d_phdr; ///< LoRa PHY header.
89 uint16_t d_mac_crc; ///< The MAC CRC.
90 double d_bits_per_second; ///< Indicator of how many bits are transferred each second.
91 uint32_t d_delay_after_sync; ///< The number of samples to skip in `DecoderState::PAUSE`.
92 uint32_t d_samples_per_second; ///< The number of samples taken per second by GNU Radio.
93 double d_symbols_per_second; ///< Indicator of how many symbols (read: chirps) are transferred each second.
94 double d_bits_per_symbol; ///< The number of bits each of the symbols contain.
95 uint32_t d_samples_per_symbol; ///< The number of samples in one symbol.
96 double d_period; ///< Period of the symbol.
97 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.
98 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.
99 int32_t d_payload_symbols; ///< The number of symbols needed to decode the payload. Calculated from an indicator in the HDR.
100 uint32_t d_payload_length; ///< The number of words after decoding the HDR or payload. Calculated from an indicator in the HDR.
101 uint32_t d_corr_fails; ///< Indicates how many times the correlation failed. After some tries, the state will revert to `DecoderState::DETECT`.
102 float d_energy_threshold; ///< The absolute threshold to distinguish signal from noise.
103 float d_snr; ///< Signal to noise ratio
104 boost::circular_buffer<float> d_pwr_queue; ///< Queue holding symbol power values
105
106 std::vector<uint32_t> d_words; ///< Vector containing the demodulated words.
107 std::vector<uint8_t> d_demodulated; ///< Vector containing the words after deinterleaving.
108 std::vector<uint8_t> d_words_deshuffled; ///< Vector containing the words after deshuffling.
109 std::vector<uint8_t> d_words_dewhitened; ///< Vector containing the words after dewhitening.
110 std::vector<uint8_t> d_decoded; ///< Vector containing the words after Hamming decode or the final decoded words.
111
112 std::ofstream d_debug_samples; ///< Debug utputstream for complex values.
113 std::ofstream d_debug; ///< Outputstream for the debug log.
114
115 fftplan d_q; ///< The LiquidDSP::FFT_Plan.
116 fftplan d_qr; ///< The LiquidDSP::FFT_Plan in reverse.
117 fec d_h48_fec; ///< LiquidDSP Hamming 4/8 FEC.
118
119 uint32_t d_decim_factor; ///< The number of samples (data points) in each bin.
120 float d_cfo_estimation; ///< An estimation for the current Center Frequency Offset.
121 double d_dt; ///< Indicates how fast the frequency changes in a symbol (chirp).
122 bool d_enable_fine_sync; ///< Enable drift correction
123 int32_t d_fine_sync; ///< Amount of drift correction to apply for next symbol
124
125 /**
126 * \brief TODO
127 */
128 float cross_correlate_ifreq_fast(const float *samples_ifreq, const float *ideal_chirp, const uint32_t window);
129
130 /**
131 * \brief TODO
132 */
133 float cross_correlate_fast(const gr_complex* samples, const gr_complex* ideal_chirp, const uint32_t window);
134
135 /**
136 * \brief TODO
137 */
138 void fine_sync(const gr_complex* in_samples, int32_t bin_idx, int32_t search_space);
139
140 /**
141 * \brief Schmidl-Cox autocorrelation approach for approximately detecting the preamble.
142 */
143 float detect_preamble_autocorr(const gr_complex *samples, uint32_t window);
144
145 /**
146 * \brief TODO
147 */
148 float experimental_determine_cfo(const gr_complex *samples, uint32_t window);
149
150 /**
151 * \brief Generate the ideal up- and downchirps.
152 */
153 void build_ideal_chirps(void);
154
155 /**
156 * \brief Debug method to dump the given complex array to the given file in binary format.
157 *
158 * \param path
159 * The path to the file to dump to.
160 * \param v
161 * The complex array to dump.
162 * \param length
163 * Length of said array.
164 * \param elem_size
165 * `sizeof` the data in the array.
166 */
167 void samples_to_file(const std::string path, const gr_complex *v, const uint32_t length, const uint32_t elem_size);
168
169 /**
170 * \brief Debug method to dump the given values array to a file in textual format.
171 *
172 * \param path
173 * The path to the file to dump to.
174 * \param v
175 * The values array to dump.
176 * \param length
177 * Length of said array.
178 * \param ppm
179 * PPM value of the data.
180 */
181 void values_to_file(const std::string path, const unsigned char *v, const uint32_t length, const uint32_t ppm);
182
183 /**
184 * \brief Write the given complex array to the debug outputstream.
185 *
186 * \param v
187 * The complex array.
188 * \param length
189 * Length of said complex array.
190 */
191 void samples_debug(const gr_complex *v, const uint32_t length);
192
193 /**
194 * \brief Correct the shift of the given symbol to match the ideal upchirp by sliding cross correlating.
195 *
196 * \param samples_ifreq
197 * The symbol to shift.
198 * \param window
199 * The window in which the symbol can be shifted (length of given sample array).
200 * \param index
201 * The new start index in the window for the found upchirp.
202 * \return Also return the correlation coefficient.
203 */
204 float sliding_norm_cross_correlate_upchirp(const float *samples_ifreq, const uint32_t window, int32_t *index);
205
206 /**
207 * \brief Base method to start downchirp correlation and return the correlation coefficient.
208 *
209 * \param samples
210 * The complex array of samples to detect a downchirp in.
211 * \param window
212 * Length of said sample.
213 */
214 float detect_downchirp(const gr_complex *samples, const uint32_t window);
215
216 /**
217 * \brief Base method to start upchirp detection by calling `sliding_norm_cross_correlate_upchirp`.
218 * <br/>Sets up the instantaneous frequency of the given complex symbol.
219 *
220 * \param samples
221 * The complex array of samples to detect an upchirp in.
222 * \param window
223 * Length of said sample.
224 * \param index
225 * The index to shift with so the upchirp is correctly synced inside its window.
226 * \return Also return the correlation coefficient.
227 */
228 float detect_upchirp(const gr_complex *samples, const uint32_t window, int32_t *index);
229
230 /**
231 * \brief Returns the correlation coefficient when correlating the given complex symbols in the given window.
232 *
233 * \param samples_1
234 * The first complex symbol to correlate with.
235 * \param samples_2
236 * The second complex symbol to correlate with.
237 * \param window
238 * The window in which to perform correlation.
239 */
240 float cross_correlate(const gr_complex *samples_1, const gr_complex *samples_2, const uint32_t window);
241
242 /**
243 * \brief Returns the correlation coefficient of a real signal.
244 * See https://en.wikipedia.org/wiki/Cross-correlation#Normalized_cross-correlation.
245 *
246 * \param samples_ifreq
247 * The instantaneous frequency of the symbol to correlate with.
248 * \param ideal_chirp
249 * The vector containing the ideal chirp to correlate with.
250 * \param to_idx
251 * Correlation end index.
252 */
253 float cross_correlate_ifreq(const float *samples_ifreq, const std::vector<float>& ideal_chirp, const uint32_t to_idx);
254
255 /**
256 * \brief Returns the index of the bin containing the frequency change by using FFT.
257 *
258 * \param samples
259 * The complex symbol to analyse.
260 */
261 uint32_t get_shift_fft(const gr_complex *samples);
262
263 /**
264 * \brief Determine the center frequency offset in the given symbol.
265 *
266 * \param samples
267 * The complex symbol to analyse.
268 */
269 void determine_cfo(const gr_complex *samples);
270
271 /**
272 * \brief Determine the energy of a symbol.
273 *
274 * \param samples
275 * The complex symbol to analyse.
276 */
277 float determine_energy(const gr_complex *samples);
278
279 /**
280 * \brief Determine the SNR
281 */
282 void determine_snr();
283
284 /**
285 * \brief Returns the index of the bin containing the frequency change.
286 *
287 * \param samples
288 * The complex symbol to analyze.
289 */
290 uint32_t max_frequency_gradient_idx(const gr_complex *samples);
291
292 /**
293 * \brief Demodulate the given symbol and return true if all expected symbols have been parsed.
294 *
295 * \param samples
296 * The complex symbol to demodulate.
297 * \param is_header
298 * Whether the demodulated words were from the HDR.
299 */
300 bool demodulate(const gr_complex *samples, const bool reduced_rate);
301
302 /**
303 * \brief Deinterleave the raw demodulated words by reversing the interleave pattern.
304 *
305 * \param ppm
306 * The number of words that zere interleaved. Depends on `SF`.
307 */
308 void deinterleave(const uint32_t ppm);
309
310 /**
311 * \brief The process of decoding the demodulated words to get the actual payload.
312 * <br/>1. Deshuffle the words
313 * <br/>2. Dewhiten the words
314 * <br/>3. Hamming decoding
315 *
316 * \param is_header
317 * Whether the demodulated words were from the HDR.
318 */
319 void decode(const bool is_header);
320
321 /**
322 * \brief Deshuffle the demodulated words by the given pattern.
323 *
324 * \param shuffle_pattern
325 * The order in which the bits appear.
326 * \param is_header
327 * Whether the demodulated words were from the HDR.
328 */
329 void deshuffle(const uint8_t *shuffle_pattern, const bool is_header);
330
331 /**
332 * \brief Dewhiten the deshuffled words by XORing with the whitening sequence.
333 *
334 * \param prng
335 * The whitening sequence to XOR with.
336 */
337 void dewhiten(const uint8_t *prng);
338
339 /**
340 * \brief Use Hamming to decode the dewhitened words.
341 * <br/>- CR 4 or 3: Hamming(8,4) or Hamming(7,4) with parity correction
342 * <br/>- CR 2 or 1: Extract data only (can only find parity errors, not correct them)
343 *
344 * \param is_header
345 * Decoding for the header?
346 */
347 void hamming_decode(bool is_header);
348
349 /**
350 * \brief Extract only the data in the given bytes.
351 *
352 * \param is_header
353 * Decoding for the header?
354 */
355 void extract_data_only(bool is_header);
356
357 /**
358 * \brief Hamming(8,4) decoding by calling `hamming_decode_soft_byte` on each byte.
359 * <BR>Each byte is decoded in pairs, the first one becoming the LSB nibble
360 * <BR>and the second one the MSB nibble (if even; else just zeroes).
361 *
362 * \param is_header
363 * Decoding for the header?
364 */
365 void hamming_decode_soft(bool is_header);
366
367 /**
368 * \brief Return the standard deviation for the given array.
369 * <br/>Used for cross correlating.
370 *
371 * \param values
372 * The array to calculate the standard deviation for.
373 * \param len
374 * Length of said array.
375 * \param mean
376 * The mean (average) of the values in the array.
377 */
378 float stddev(const float *values, const uint32_t len, const float mean);
379
380 /**
381 * \brief Calculate the instantaneous phase for the given complex symbol.
382 *
383 * \param in_samples
384 * The complex array to calculate the instantaneous phase for.
385 * \param out_iphase
386 * The output `float` array containing the instantaneous phase.
387 * \param window
388 * The size of said arrays.
389 */
390 inline void instantaneous_phase(const gr_complex *in_samples, float *out_iphase, const uint32_t window);
391
392 /**
393 * \brief Calculate the instantaneous frequency for the given complex symbol.
394 *
395 * \param in_samples
396 * The complex array to calculate the instantaneous frequency for.
397 * \param out_ifreq
398 * The output `float` array containing the instantaneous frequency.
399 * \param window
400 * The size of said arrays.
401 */
402 inline void instantaneous_frequency(const gr_complex *in_samples, float *out_ifreq, const uint32_t window);
403
404 /**
405 * \brief TODO
406 */
407 void msg_lora_frame(void);
408
409 public:
410 /**
411 * \brief Default constructor.
412 *
413 * \param samp_rate
414 * The sample rate of the input signal given to `work` later.
415 * \param sf
416 * The expected spreqding factor.
417 */
418 decoder_impl(float samp_rate, uint32_t bandwidth, uint8_t sf, bool implicit, uint8_t cr, bool crc, bool reduced_rate, bool disable_drift_correction);
419
420 /**
421 * Default destructor.
422 */
424
425 /**
426 * \brief The main method called by GNU Radio to perform tasks on the given input.
427 *
428 * \param noutput_items
429 * The requested amoutn of output items.
430 * \param input_items
431 * An array with samples to process.
432 * \param output_items
433 * An array to return processed samples.
434 * \return Returns the number of output items generated.
435 */
436 int work(int noutput_items,
437 gr_vector_const_void_star& input_items,
438 gr_vector_void_star& output_items);
439
440 /**
441 * \brief Set th current spreading factor.
442 * <br/>**Currently not supported, restart GNU Radio with different settings instead.**
443 * \param sf
444 * The new spreading factor.
445 */
446 virtual void set_sf(const uint8_t sf);
447
448 /**
449 * \brief Set the current sample rate.
450 * <br/>**Currently not supported, restart GNU Radio with different settings instead.**
451 *
452 * \param samp_rate
453 * The new sample rate.
454 */
455 virtual void set_samp_rate(const float samp_rate);
456 };
457 } // namespace lora
458} // namespace gr
459
460#endif /* INCLUDED_LORA_DECODER_IMPL_H */
Return the DecoderState as string for debugging purposes.
Definition: decoder_impl.h:68
virtual void set_samp_rate(const float samp_rate)
Set the current sample rate. Currently not supported, restart GNU Radio with different settings inst...
int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items)
The main method called by GNU Radio to perform tasks on the given input.
virtual void set_sf(const uint8_t sf)
Set th current spreading factor. Currently not supported, restart GNU Radio with different settings ...
decoder_impl(float samp_rate, uint32_t bandwidth, uint8_t sf, bool implicit, uint8_t cr, bool crc, bool reduced_rate, bool disable_drift_correction)
Default constructor.
<+description of block+>
Definition: decoder.h:693
cr
Definition: loraphy.h:23
loraphy_header_t
Definition: loraphy.h:32
sf
Definition: loratap.h:33
DecoderState
DecoderState : Each state the LoRa decoder can be in.
Definition: decoder_impl.h:40
Definition: channelizer.h:28