GNU Radio's LORA Package
utilities.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 UTILITIES_H
22#define UTILITIES_H
23
24#include <cstdint>
25#include <string.h>
26#include <iomanip>
27#include <sstream>
28
29#define MAC_CRC_SIZE 2u
30#define MAX_PWR_QUEUE_SIZE 4
31#define SM(value, shift, mask) (((value) << (shift)) & (mask))
32#define MS(value, mask, shift) (((value) & (mask)) >> (shift))
33
34namespace gr {
35 namespace lora {
36 static std::vector<std::string> term_colors = {
37 "\e[0m",
38 "\e[1m\e[91m",
39 "\e[1m\e[92m",
40 "\e[1m\e[93m",
41 "\e[1m\e[94m",
42 "\e[1m\e[95m",
43 "\e[1m\e[96m",
44 "\e[1m\e[97m",
45 "\e[1m\e[31m",
46 "\e[1m\e[32m",
47 "\e[1m\e[33m",
48 "\e[1m\e[34m",
49 "\e[1m\e[35m",
50 "\e[1m\e[36m"
51 };
52
53 /**
54 * \brief Wrap indices Python-like, i.e. array[wrap_index(-1, array_length)] gets the last element.
55 *
56 * \param i
57 * Index of array
58 * \param n
59 * Length of array
60 */
61 inline int32_t wrap_index(int32_t i, int32_t n) {
62 return ((i % n) + n) % n;
63 }
64
65 /**
66 * \brief Clamp given value in the given range.
67 *
68 * \tparam T
69 * The type of variable to clamp.
70 * <br>`d`, `min` and `max` must be of this type.
71 * \param d
72 * The value to clamp.
73 * \param min
74 * The lower bound of the range.
75 * \param max
76 * The upper bound of the range.
77 */
78 template <class T>
79 inline T clamp(const T d, const T min, const T max) {
80 const T t = d < min ? min : d;
81 return t > max ? max : t;
82 }
83
84 /**
85 * \brief Rotate the given bits to the left and return the result.
86 *
87 * \param bits
88 * The value to rotate.
89 * \param count
90 * The amount of bits to rotate (shift to left and add to right).
91 * \param size
92 * The size in bits used in `bits`.
93 * <BR>e.g. 1 byte given => size = 8
94 * <BR>e.g. only 6 bits in use => size = 6, and all bits higher than (1 << size-1) will be zeroed.
95 */
96 inline uint32_t rotl(uint32_t bits, uint32_t count = 1u, const uint32_t size = 8u) {
97 const uint32_t len_mask = (1u << size) - 1u;
98
99 count %= size; // Limit bit rotate count to size
100 bits &= len_mask; // Limit given bits to size
101
102 return ((bits << count) & len_mask) | (bits >> (size - count));
103 }
104
105 /**
106 * \brief Return the `v` represented in a binary string.
107 *
108 * \tparam T
109 * The type of variable to convert.
110 * \param v
111 * The value to convert.
112 * \param bitwidth
113 * The length in bits of the given variable `v`.
114 */
115 template <typename T>
116 inline std::string to_bin(const T v, const uint32_t bitwidth) {
117 #ifdef LSB_FIRST
118 const uint64_t maxpow = bitwidth ? (1ull << (bitwidth - 1)) : 0;
119 uint64_t mask;
120
121 std::string result = "";
122
123 for (mask = 0x1; mask <= maxpow; mask <<= 1) {
124 result += (v & mask) ? "1" : "0";
125 }
126 #else
127 uint64_t mask = bitwidth ? (1ull << bitwidth) : 0;
128
129 std::string result = "";
130
131 while(mask >>= 1) {
132 result += (v & mask) ? "1" : "0";
133 }
134 #endif
135
136 return result;
137 }
138
139 /**
140 * \brief Append the data in a given vector to an output stream with a comma delimiter.
141 *
142 * \tparam T
143 * The type of variable to append.
144 * \param out
145 * The output stream to append to.
146 * \param v
147 * The vector containing the data to append.
148 * \param prefix
149 * A prefix to include before appending the data.
150 * \param element_len_bits
151 * The length in bits of the data in `v`.
152 */
153 template <typename T>
154 inline void print_vector_bin(std::ostream& out, const std::vector<T>& v, const std::string& prefix, const int element_len_bits) {
155 out << prefix << ": ";
156
157 for (const T& x : v)
158 out << to_bin(x, element_len_bits) << ", ";
159
160 out << std::endl << std::flush;
161 }
162
163 /**
164 * \brief Check whether the parity of the given binary string is even.
165 *
166 * \param word
167 * The string to check.
168 * \param even
169 * Check for even (`true`) or uneven (`false`) parity.
170 */
171 inline bool check_parity_string(const std::string& word, const bool even = true) {
172 size_t count = 0, i = 0;
173
174 while(i < 7) {
175 if (word[i++] == '1')
176 ++count;
177 }
178
179 return (count & 0x1) == !even;
180 }
181
182 /**
183 * \brief Check whether the parity of the given uint64_t is even.
184 * <BR>See https://graphics.stanford.edu/~seander/bithacks.html for more.
185 *
186 * \param word
187 * The uint64_t to check.
188 * \param even
189 * Check for even (`true`) or uneven (`false`) parity.
190 */
191 inline bool check_parity(uint64_t word, const bool even = true) {
192 word ^= word >> 1;
193 word ^= word >> 2;
194 word = (word & 0x1111111111111111UL) * 0x1111111111111111UL;
195
196 return ((word >> 60ull) & 1ull) == !even;
197 }
198
199 /**
200 * \brief Select the bits in data given by the indices in `*indices`.
201 *
202 * \param data
203 * The data to select bits from.
204 * \param *indices
205 * Array with the indices to select.
206 * \param n
207 * The amount of indices.
208 */
209 inline uint32_t select_bits(const uint32_t data, const uint8_t *indices, const uint8_t n) {
210 uint32_t r = 0u;
211
212 for(uint8_t i = 0u; i < n; ++i)
213 r |= (data & (1u << indices[i])) ? (1u << i) : 0u;
214
215 return r;
216 }
217
218 /**
219 * \brief Select a single bit from the given byte.
220 *
221 * \param v
222 * The byte to select from.
223 * \param i
224 * The index to select the bit from starting from the LSB.
225 */
226 inline uint8_t bit(const uint8_t v, const uint8_t i) {
227 return ((v >> i) & 0x01);
228 }
229
230 /**
231 * \brief Pack the given 8 bits in a byte with: `hgfe dcba`
232 *
233 * \param a-h
234 * The bits to pack with the LSB first.
235 */
236 inline uint8_t pack_byte(const uint8_t a, const uint8_t b, const uint8_t c, const uint8_t d,
237 const uint8_t e, const uint8_t f, const uint8_t g, const uint8_t h) {
238 return a | (b << 1) | (c << 2) | (d << 3) | (e << 4) | (f << 5) | (g << 6) | (h << 7);
239 }
240
241 /**
242 * \brief Pack the given 4 bits in a nibble with: `dcba`
243 *
244 * \param a-d
245 * The bits to pack with the LSB first.
246 */
247 inline uint8_t pack_nibble(const uint8_t a, const uint8_t b, const uint8_t c, const uint8_t d) {
248 return a | (b << 1) | (c << 2) | (d << 3);
249 }
250
251 /**
252 * \brief Encode the given word with standard Hamming(7,4) and return a byte with the set parity bits.
253 *
254 * \param v
255 * The nibble to encode.
256 */
257 inline uint8_t hamming_encode_soft(const uint8_t v) {
258 const uint8_t p1 = bit(v, 1) ^ bit(v, 2) ^ bit(v, 3);
259 const uint8_t p2 = bit(v, 0) ^ bit(v, 1) ^ bit(v, 2);
260 const uint8_t p3 = bit(v, 0) ^ bit(v, 1) ^ bit(v, 3);
261 const uint8_t p4 = bit(v, 0) ^ bit(v, 2) ^ bit(v, 3);
262
263 return pack_byte(p1, bit(v, 0), bit(v, 1), bit(v, 2), p2, bit(v, 3), p3, p4);
264 }
265
266 /**
267 * \brief Swap nibbles of a byte array.
268 *
269 * \param array
270 * Array of uint8_t bytes
271 * \param length
272 * Length of the array
273 */
274 inline void swap_nibbles(uint8_t* array, uint32_t length) {
275 for(uint32_t i = 0; i < length; i++) {
276 array[i] = ((array[i] & 0x0f) << 4) | ((array[i] & 0xf0) >> 4);
277 }
278 }
279
280 /**
281 * DEPRECATED
282 * \brief Hamming(8,4) decoding by constructing a Syndrome matrix LUT for XORing on parity errors.
283 *
284 * \param v
285 * The byte to decode.
286 * \return Returs a nibble containing the corrected data.
287 */
288 static inline uint8_t hamming_decode_soft_byte(uint8_t v) {
289 // Precalculation
290 // Which bits are covered (including self)?
291 // p1 10110100
292 // p2 01111000
293 // p3 01100110
294 // p4 01010101
295
296 // Syndrome matrix = columns of "cover bits" above
297 /*
298 uint8_t H[16] = { 0u };
299
300 const uint8_t i0 = pack_nibble(1, 0, 0, 0),
301 i1 = pack_nibble(0, 1, 1, 1),
302 i2 = pack_nibble(1, 1, 1, 0),
303 i3 = pack_nibble(1, 1, 0, 1),
304 i4 = pack_nibble(0, 1, 0, 0),
305 i5 = pack_nibble(1, 0, 1, 1),
306 i6 = pack_nibble(0, 0, 1, 0),
307 i7 = pack_nibble(0, 0, 0, 1);
308 H[i0] = 0;
309 H[i1] = 1;
310 H[i2] = 2;
311 H[i3] = 3;
312 H[i4] = 4;
313 H[i5] = 5;
314 H[i6] = 6;
315 H[i7] = 7;
316 */
317
318 static const uint8_t H[16] = { 0x0, 0x0, 0x4, 0x0, 0x6, 0x0, 0x0, 0x2,
319 0x7, 0x0, 0x0, 0x3, 0x0, 0x5, 0x1, 0x0 };
320
321 // Decode
322 // Bit positions for data bits in codeword
323 const uint8_t p1 = bit(v, 0),
324 p2 = bit(v, 4),
325 p3 = bit(v, 6),
326 p4 = bit(v, 7),
327 p1c = bit(v, 2) ^ bit(v, 3) ^ bit(v, 5),
328 p2c = bit(v, 1) ^ bit(v, 2) ^ bit(v, 3),
329 p3c = bit(v, 1) ^ bit(v, 2) ^ bit(v, 5),
330 p4c = bit(v, 1) ^ bit(v, 3) ^ bit(v, 5);
331
332 const uint8_t syndrome = pack_nibble((uint8_t)(p1 != p1c), (uint8_t)(p2 != p2c), (uint8_t)(p3 != p3c), (uint8_t)(p4 != p4c));
333
334 if (syndrome) {
335 v ^= 1u << H[syndrome];
336 }
337
338 return pack_nibble( bit(v, 1), bit(v, 2), bit(v, 3), bit(v, 5));
339 }
340
341 template <typename T>
342 inline void print_vector(std::ostream& out, const T* v, const std::string& prefix, const int size, const int element_len_bits) {
343 out << prefix << ": ";
344
345 for (int i = 0; i < size; i++)
346 out << to_bin(v[i], element_len_bits) << ", ";
347
348 out << std::endl << std::flush;
349 }
350
351 template <typename T>
352 inline void print_vector_hex(std::ostream& out, const T* v, const uint32_t size, bool endline, bool print_ascii) {
353 std::stringstream ss;
354
355 for (uint32_t i = 0u; i < size; i++) {
356 out << " " << std::hex << std::setw(2) << std::setfill('0') << (int)v[i];
357 if (v[i] >= ' ' && v[i] <= '~')
358 ss << v[i];
359 }
360
361 if (print_ascii)
362 out << " (" << ss.str() << ")";
363
364 if(endline)
365 out << std::endl;
366
367 out << std::flush;
368 }
369
370 template <typename T>
371 inline void print_interleave_matrix(std::ostream& out, const std::vector<T>& v, const uint32_t sf) {
372 uint32_t cr = v.size();
373
374 for(uint32_t i = 0; i < cr; i++)
375 out << "-";
376 out << std::endl;
377
378 out << "LSB" << std::endl;
379
380 for(int32_t i = sf-1; i >= 0; i--) {
381 for(int32_t j = 0; j < (int32_t)cr; j++) {
382 out << term_colors[wrap_index(j-i, (int32_t)sf)+1] << to_bin(v[j], sf)[i] << term_colors[0];
383 }
384 out << std::endl;
385 }
386
387 out << "MSB" << std::endl;
388
389 for(uint32_t i = 0; i < cr; i++)
390 out << "-";
391 out << std::endl;
392
393 out << std::flush;
394 }
395
396 inline bool header_checksum(const uint8_t* header) {
397 (void) header;
398 /*Found valid order for bit #0: (1, 4, 8, 9, 10, 11)
399 Found valid order for bit #1: (0, 2, 5, 8, 9, 10)
400 Found valid order for bit #2: (0, 3, 6, 9, 11)
401 Found valid order for bit #3: (1, 2, 3, 7, 8)
402 Found valid order for bit #4: (4, 5, 6, 7)*/
403 return true;
404 }
405
406 inline uint32_t dissect_packet(const void **header, uint32_t header_size, const uint8_t *buffer, uint32_t offset) {
407 (*header) = buffer + offset;
408 return offset + header_size;
409 }
410
411 inline uint32_t build_packet(uint8_t *buffer, uint32_t offset, const void* header, uint32_t header_size) {
412 memcpy(buffer + offset, header, header_size);
413 offset += header_size;
414
415 return offset;
416 }
417
418 }
419}
420
421#endif /* UTILITIES_H */
cr
Definition loraphy.h:23
sf
Definition loratap.h:33
uint32_t build_packet(uint8_t *buffer, uint32_t offset, const void *header, uint32_t header_size)
Definition utilities.h:411
static uint8_t hamming_decode_soft_byte(uint8_t v)
Hamming(8,4) decoding by constructing a Syndrome matrix LUT for XORing on parity errors.
Definition utilities.h:288
bool check_parity_string(const std::string &word, const bool even=true)
Check whether the parity of the given binary string is even.
Definition utilities.h:171
uint32_t dissect_packet(const void **header, uint32_t header_size, const uint8_t *buffer, uint32_t offset)
Definition utilities.h:406
T clamp(const T d, const T min, const T max)
Clamp given value in the given range.
Definition utilities.h:79
void print_vector_hex(std::ostream &out, const T *v, const uint32_t size, bool endline, bool print_ascii)
Definition utilities.h:352
bool check_parity(uint64_t word, const bool even=true)
Check whether the parity of the given uint64_t is even. See https://graphics.stanford....
Definition utilities.h:191
static std::vector< std::string > term_colors
Definition utilities.h:36
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)
Pack the given 8 bits in a byte with: hgfe dcba
Definition utilities.h:236
bool header_checksum(const uint8_t *header)
Definition utilities.h:396
void print_vector(std::ostream &out, const T *v, const std::string &prefix, const int size, const int element_len_bits)
Definition utilities.h:342
void print_interleave_matrix(std::ostream &out, const std::vector< T > &v, const uint32_t sf)
Definition utilities.h:371
int32_t wrap_index(int32_t i, int32_t n)
Wrap indices Python-like, i.e. array[wrap_index(-1, array_length)] gets the last element.
Definition utilities.h:61
std::string to_bin(const T v, const uint32_t bitwidth)
Return the v represented in a binary string.
Definition utilities.h:116
uint8_t hamming_encode_soft(const uint8_t v)
Encode the given word with standard Hamming(7,4) and return a byte with the set parity bits.
Definition utilities.h:257
uint32_t rotl(uint32_t bits, uint32_t count=1u, const uint32_t size=8u)
Rotate the given bits to the left and return the result.
Definition utilities.h:96
void swap_nibbles(uint8_t *array, uint32_t length)
Swap nibbles of a byte array.
Definition utilities.h:274
void print_vector_bin(std::ostream &out, const std::vector< T > &v, const std::string &prefix, const int element_len_bits)
Append the data in a given vector to an output stream with a comma delimiter.
Definition utilities.h:154
uint8_t pack_nibble(const uint8_t a, const uint8_t b, const uint8_t c, const uint8_t d)
Pack the given 4 bits in a nibble with: dcba
Definition utilities.h:247
uint8_t bit(const uint8_t v, const uint8_t i)
Select a single bit from the given byte.
Definition utilities.h:226
uint32_t select_bits(const uint32_t data, const uint8_t *indices, const uint8_t n)
Select the bits in data given by the indices in *indices.
Definition utilities.h:209
Definition channelizer.h:28