1 /*
2  * Copyright (c) 2024 A Labs GmbH
3  * Copyright (c) 2024 tado GmbH
4  * Copyright (c) 2022 Jiapeng Li
5  *
6  * Based on: https://github.com/JiapengLi/LoRaWANFragmentedDataBlockTransportAlgorithm
7  * Original algorithm: http://www.inference.org.uk/mackay/gallager/papers/ldpc.pdf
8  *
9  * SPDX-License-Identifier: Apache-2.0
10  */
11 
12 #include "frag_decoder_lowmem.h"
13 #include "frag_flash.h"
14 
15 #include <zephyr/logging/log.h>
16 #include <zephyr/sys/util.h>
17 #include <zephyr/sys/bitarray.h>
18 
19 LOG_MODULE_REGISTER(lorawan_frag_dec, CONFIG_LORAWAN_SERVICES_LOG_LEVEL);
20 
21 SYS_BITARRAY_DEFINE_STATIC(lost_frames, FRAG_MAX_NB);
22 SYS_BITARRAY_DEFINE_STATIC(lost_frames_matrix, (FRAG_TOLERANCE * (FRAG_TOLERANCE + 1) / 2));
23 SYS_BITARRAY_DEFINE_STATIC(matched_lost_frm_bm0, FRAG_TOLERANCE);
24 SYS_BITARRAY_DEFINE_STATIC(matched_lost_frm_bm1, FRAG_TOLERANCE);
25 SYS_BITARRAY_DEFINE_STATIC(matrix_line_bm, FRAG_MAX_NB);
26 
27 
matrix_location_to_index(size_t x,size_t y,size_t m)28 static inline size_t matrix_location_to_index(size_t x, size_t y, size_t m)
29 {
30 	/* We only store the top half of the matrix because it is triangular,
31 	 * but that means when mapping the coordinates into the flat representation
32 	 * we need to account for that
33 	 */
34 	return (y + 1) * (m + m - y) / 2 - (m - x);
35 }
36 
triangular_matrix_get_entry(struct sys_bitarray * m2tbm,size_t x,size_t y,size_t m)37 static bool triangular_matrix_get_entry(struct sys_bitarray *m2tbm, size_t x, size_t y, size_t m)
38 {
39 	/* We are dealing with triangular matrices, so we don't expect actions in the lower half */
40 	__ASSERT(x >= y, "x: %d, y: %d, m: %d", x, y, m);
41 	size_t bit;
42 	int ret;
43 
44 	ret = sys_bitarray_test_bit(m2tbm, matrix_location_to_index(x, y, m), &bit);
45 	__ASSERT_NO_MSG(ret == 0);
46 
47 	return bit != 0;
48 }
49 
triangular_matrix_set_entry(struct sys_bitarray * m2tbm,size_t x,size_t y,size_t m)50 static void triangular_matrix_set_entry(struct sys_bitarray *m2tbm, size_t x, size_t y, size_t m)
51 {
52 	/* We are dealing with triangular matrices, so we don't expect actions in the lower half */
53 	__ASSERT(x >= y, "x: %d, y: %d, m: %d", x, y, m);
54 	int ret;
55 
56 	ret = sys_bitarray_set_bit(m2tbm, matrix_location_to_index(x, y, m));
57 	__ASSERT_NO_MSG(ret == 0);
58 }
59 
triangular_matrix_clear_entry(struct sys_bitarray * m2tbm,size_t x,size_t y,size_t m)60 static void triangular_matrix_clear_entry(struct sys_bitarray *m2tbm, size_t x, size_t y, size_t m)
61 {
62 	/* We are dealing with triangular matrices, so we don't expect actions in the lower half */
63 	__ASSERT(x >= y, "x: %d, y: %d, m: %d", x, y, m);
64 
65 	int ret;
66 
67 	ret = sys_bitarray_clear_bit(m2tbm, matrix_location_to_index(x, y, m));
68 	__ASSERT_NO_MSG(ret == 0);
69 }
70 
bit_get(struct sys_bitarray * bitmap,size_t index)71 static inline bool bit_get(struct sys_bitarray *bitmap, size_t index)
72 {
73 	int bit, ret;
74 
75 	ret = sys_bitarray_test_bit(bitmap, index, &bit);
76 	__ASSERT_NO_MSG(ret == 0);
77 	return bit != 0;
78 }
79 
bit_set(struct sys_bitarray * bitmap,size_t index)80 static inline void bit_set(struct sys_bitarray *bitmap, size_t index)
81 {
82 	int ret;
83 
84 	ret = sys_bitarray_set_bit(bitmap, index);
85 	__ASSERT_NO_MSG(ret == 0);
86 }
87 
bit_clear(struct sys_bitarray * bitmap,size_t index)88 static inline void bit_clear(struct sys_bitarray *bitmap, size_t index)
89 {
90 	int ret;
91 
92 	ret = sys_bitarray_clear_bit(bitmap, index);
93 	__ASSERT_NO_MSG(ret == 0);
94 }
95 
bit_count_ones(struct sys_bitarray * bitmap,size_t index)96 static inline size_t bit_count_ones(struct sys_bitarray *bitmap, size_t index)
97 {
98 	size_t count;
99 	int ret;
100 
101 	ret = sys_bitarray_popcount_region(bitmap, index + 1, 0, &count);
102 	__ASSERT_NO_MSG(ret == 0);
103 	return count;
104 }
105 
bit_xor(struct sys_bitarray * des,struct sys_bitarray * src,size_t size)106 static inline void bit_xor(struct sys_bitarray *des, struct sys_bitarray *src, size_t size)
107 {
108 	int ret;
109 
110 	ret = sys_bitarray_xor(des, src, size, 0);
111 	__ASSERT_NO_MSG(ret == 0);
112 }
113 
bit_clear_all(struct sys_bitarray * bitmap,size_t size)114 static inline void bit_clear_all(struct sys_bitarray *bitmap, size_t size)
115 {
116 	int ret;
117 
118 	ret = sys_bitarray_clear_region(bitmap, size, 0);
119 	__ASSERT_NO_MSG(ret == 0);
120 }
121 
122 /**
123  * Generate a 23bit Pseudorandom Binary Sequence (PRBS)
124  *
125  * @param previous Previous value in the sequence
126  *
127  * @returns Next value in the pseudorandom sequence
128  */
prbs23(int32_t previous)129 static int32_t prbs23(int32_t previous)
130 {
131 	int32_t b0 = previous & 1;
132 	int32_t b1 = (previous & 32) / 32;
133 
134 	return (previous / 2) + ((b0 ^ b1) << 22);
135 }
136 
137 /**
138  * Generate vector for coded fragment n of the MxN parity matrix
139  *
140  * @param m Total number of uncoded fragments (M)
141  * @param n Coded fragment number (starting at 1 and not 0)
142  * @param vec Output vector (buffer size must be greater than m)
143  */
frag_dec_parity_matrix_vector(size_t m,size_t n,struct sys_bitarray * vec)144 static void frag_dec_parity_matrix_vector(size_t m, size_t n, struct sys_bitarray *vec)
145 {
146 	size_t mm, r;
147 	int32_t x;
148 	int ret;
149 
150 	ret = sys_bitarray_clear_region(vec, m, 0);
151 	__ASSERT_NO_MSG(ret == 0);
152 
153 	/*
154 	 * Powers of 2 must be treated differently to make sure matrix content is close
155 	 * to random. Powers of 2 tend to generate patterns.
156 	 */
157 	if (is_power_of_two(m)) {
158 		mm = m + 1;
159 	} else {
160 		mm = m;
161 	}
162 
163 	x = 1 + (1001 * n);
164 
165 	for (size_t nb_coeff = 0; nb_coeff < (m / 2); nb_coeff++) {
166 		r = (1 << 16);
167 		while (r >= m) {
168 			x = prbs23(x);
169 			r = x % mm;
170 		}
171 		ret = sys_bitarray_set_bit(vec, r);
172 		__ASSERT_NO_MSG(ret == 0);
173 	}
174 }
175 
frag_dec_init(struct frag_decoder * decoder,size_t nb_frag,size_t frag_size)176 void frag_dec_init(struct frag_decoder *decoder, size_t nb_frag, size_t frag_size)
177 {
178 	decoder->nb_frag = nb_frag;
179 	decoder->frag_size = frag_size;
180 	/* Set all frames lost, from 0 to nb_frag-1 */
181 	decoder->lost_frame_count = decoder->nb_frag;
182 	sys_bitarray_set_region(&lost_frames, decoder->nb_frag, 0);
183 
184 	sys_bitarray_clear_region(&lost_frames_matrix, (FRAG_TOLERANCE * (FRAG_TOLERANCE + 1) / 2),
185 				  0);
186 
187 	decoder->filled_lost_frame_count = 0;
188 	decoder->status = FRAG_DEC_STA_UNCODED;
189 }
190 
frag_dec_frame_received(struct frag_decoder * decoder,uint16_t index)191 void frag_dec_frame_received(struct frag_decoder *decoder, uint16_t index)
192 {
193 	int ret, was_set;
194 
195 	ret = sys_bitarray_test_and_clear_bit(&lost_frames, index, &was_set);
196 	__ASSERT_NO_MSG(ret == 0);
197 
198 	if (was_set != 0) {
199 		decoder->lost_frame_count--;
200 	}
201 }
202 
frag_dec_write_vector(struct sys_bitarray * matrix,uint16_t line_index,struct sys_bitarray * vector,size_t len)203 static void frag_dec_write_vector(struct sys_bitarray *matrix, uint16_t line_index,
204 				  struct sys_bitarray *vector, size_t len)
205 {
206 	for (size_t i = line_index; i < len; i++) {
207 		if (bit_get(vector, i)) {
208 			triangular_matrix_set_entry(matrix, i, line_index, len);
209 		} else {
210 			triangular_matrix_clear_entry(matrix, i, line_index, len);
211 		}
212 	}
213 }
214 
frag_dec_read_vector(struct sys_bitarray * matrix,uint16_t line_index,struct sys_bitarray * vector,size_t len)215 static void frag_dec_read_vector(struct sys_bitarray *matrix, uint16_t line_index,
216 				 struct sys_bitarray *vector, size_t len)
217 {
218 	for (size_t i = 0; i < len; i++) {
219 		if (i >= line_index && triangular_matrix_get_entry(matrix, i, line_index, len)) {
220 			bit_set(vector, i);
221 		} else {
222 			bit_clear(vector, i);
223 		}
224 	}
225 }
226 
frag_dec(struct frag_decoder * decoder,uint16_t frag_counter,const uint8_t * buf,size_t len)227 int frag_dec(struct frag_decoder *decoder, uint16_t frag_counter, const uint8_t *buf, size_t len)
228 {
229 	int ret;
230 	int i, j;
231 	size_t unmatched_frame_count;
232 	size_t lost_frame_index, frame_index;
233 	static uint8_t row_data_buf[FRAG_MAX_SIZE];
234 	static uint8_t xor_row_data_buf[FRAG_MAX_SIZE];
235 
236 	if (decoder->status == FRAG_DEC_STA_DONE) {
237 		return decoder->lost_frame_count;
238 	}
239 
240 	if (len != decoder->frag_size) {
241 		return FRAG_DEC_ERR_INVALID_FRAME;
242 	}
243 
244 	__ASSERT_NO_MSG(frag_counter > 0);
245 
246 	if (frag_counter <= decoder->nb_frag && decoder->status == FRAG_DEC_STA_UNCODED) {
247 		/* Mark new received frame */
248 		frag_dec_frame_received(decoder, frag_counter - 1);
249 		/* Save data to flash */
250 		frag_flash_write((frag_counter - 1) * decoder->frag_size, (uint8_t *)buf,
251 				 decoder->frag_size);
252 		/* If no frame was lost, we are already done */
253 		if (decoder->lost_frame_count == 0) {
254 			decoder->status = FRAG_DEC_STA_DONE;
255 			return decoder->lost_frame_count;
256 		}
257 		return FRAG_DEC_ONGOING;
258 	}
259 	/* At least one frame was lost, start recovering frames */
260 	decoder->status = FRAG_DEC_STA_CODED;
261 
262 	/* Clear all temporary bm and buf */
263 	bit_clear_all(&matched_lost_frm_bm0, decoder->lost_frame_count);
264 	bit_clear_all(&matched_lost_frm_bm1, decoder->lost_frame_count);
265 
266 	/* Copy data buffer because we need to manipulate it */
267 	memcpy(xor_row_data_buf, buf, decoder->frag_size);
268 
269 	if (decoder->lost_frame_count > FRAG_TOLERANCE) {
270 		return FRAG_DEC_ERR_TOO_MANY_FRAMES_LOST;
271 	}
272 
273 	unmatched_frame_count = 0;
274 	/* Build parity matrix vector for current line */
275 	frag_dec_parity_matrix_vector(decoder->nb_frag, frag_counter - decoder->nb_frag,
276 				      &matrix_line_bm);
277 	for (i = 0; i < decoder->nb_frag; i++) {
278 		if (!bit_get(&matrix_line_bm, i)) {
279 			/* This frame is not part of the recovery matrix for the current fragment */
280 			continue;
281 		}
282 		if (bit_get(&lost_frames, i)) {
283 			/* No match for this coded frame in the uncoded frames.
284 			 * Check which lost frame we are processing by checking how many have been
285 			 * lost between the start and the current coded fragment.
286 			 */
287 			bit_set(&matched_lost_frm_bm0, bit_count_ones(&lost_frames, i) - 1);
288 			unmatched_frame_count++;
289 		} else {
290 			/* Restore frame by XORing with already received frame */
291 			/* Load previously received data into buffer */
292 			frag_flash_read(i * decoder->frag_size, row_data_buf, decoder->frag_size);
293 			/* XOR previously received data with data for current frame */
294 			mem_xor_n(xor_row_data_buf, xor_row_data_buf, row_data_buf,
295 				  decoder->frag_size);
296 		}
297 	}
298 	if (unmatched_frame_count == 0) {
299 		return FRAG_DEC_ONGOING;
300 	}
301 
302 	/* &matched_lost_frm_bm0 now contains new coded frame which excludes all received
303 	 * frames content start to diagonal &matched_lost_frm_bm0
304 	 */
305 	do {
306 		ret = sys_bitarray_find_nth_set(&matched_lost_frm_bm0, 1, decoder->lost_frame_count,
307 						0, &lost_frame_index);
308 		if (ret == 1) {
309 			/* Not found */
310 			break;
311 		}
312 		if (ret != 0) {
313 			return FRAG_DEC_ERR;
314 		}
315 		/* We know which one is the next lost frame, try to find it in the lost frame bitmap
316 		 */
317 		ret = sys_bitarray_find_nth_set(&lost_frames, lost_frame_index + 1,
318 						decoder->nb_frag, 0, &frame_index);
319 		if (ret == 1) {
320 			/* Not found */
321 			break;
322 		}
323 		if (ret != 0) {
324 			return FRAG_DEC_ERR;
325 		}
326 		/* If current frame contains new information, save it */
327 		if (!triangular_matrix_get_entry(&lost_frames_matrix, lost_frame_index,
328 						 lost_frame_index, decoder->lost_frame_count)) {
329 			frag_dec_write_vector(&lost_frames_matrix, lost_frame_index,
330 					      &matched_lost_frm_bm0, decoder->lost_frame_count);
331 			frag_flash_write(frame_index * decoder->frag_size, xor_row_data_buf,
332 					 decoder->frag_size);
333 			decoder->filled_lost_frame_count++;
334 			break;
335 		}
336 
337 		frag_dec_read_vector(&lost_frames_matrix, lost_frame_index, &matched_lost_frm_bm1,
338 				     decoder->lost_frame_count);
339 		bit_xor(&matched_lost_frm_bm0, &matched_lost_frm_bm1, decoder->lost_frame_count);
340 		frag_flash_read(frame_index * decoder->frag_size, row_data_buf, decoder->frag_size);
341 		mem_xor_n(xor_row_data_buf, xor_row_data_buf, row_data_buf, decoder->frag_size);
342 	} while (!sys_bitarray_is_region_cleared(&matched_lost_frm_bm0, decoder->lost_frame_count,
343 						 0));
344 
345 	if (decoder->filled_lost_frame_count != decoder->lost_frame_count) {
346 		return FRAG_DEC_ONGOING;
347 	}
348 
349 	if (decoder->lost_frame_count < 2) {
350 		decoder->status = FRAG_DEC_STA_DONE;
351 		return decoder->lost_frame_count;
352 	}
353 
354 	/* All frame content is received, now to reconstruct the whole frame */
355 	for (i = (decoder->lost_frame_count - 2); i >= 0; i--) {
356 		ret = sys_bitarray_find_nth_set(&lost_frames, i + 1, decoder->nb_frag, 0,
357 						&frame_index);
358 		if (ret != 0) {
359 			return FRAG_DEC_ERR;
360 		}
361 		frag_flash_read(frame_index * decoder->frag_size, xor_row_data_buf,
362 				decoder->frag_size);
363 		frag_dec_read_vector(&lost_frames_matrix, i, &matched_lost_frm_bm1,
364 				     decoder->lost_frame_count);
365 		for (j = (decoder->lost_frame_count - 1); j > i; j--) {
366 			if (!bit_get(&matched_lost_frm_bm1, j)) {
367 				continue;
368 			}
369 			ret = sys_bitarray_find_nth_set(&lost_frames, j + 1, decoder->nb_frag, 0,
370 							&lost_frame_index);
371 			if (ret != 0) {
372 				return FRAG_DEC_ERR;
373 			}
374 			frag_dec_read_vector(&lost_frames_matrix, j, &matched_lost_frm_bm0,
375 					     decoder->lost_frame_count);
376 			bit_xor(&matched_lost_frm_bm1, &matched_lost_frm_bm0,
377 				decoder->lost_frame_count);
378 			frag_flash_read(lost_frame_index * decoder->frag_size, row_data_buf,
379 					decoder->frag_size);
380 			mem_xor_n(xor_row_data_buf, xor_row_data_buf, row_data_buf,
381 				  decoder->frag_size);
382 			frag_dec_write_vector(&lost_frames_matrix, i, &matched_lost_frm_bm1,
383 					      decoder->lost_frame_count);
384 		}
385 		frag_flash_write(frame_index * decoder->frag_size, xor_row_data_buf,
386 				 decoder->frag_size);
387 	}
388 	decoder->status = FRAG_DEC_STA_DONE;
389 	return decoder->lost_frame_count;
390 }
391