1 //
2 // This file is part of Dire Wolf, an amateur radio packet TNC.
3 //
4 // Copyright (C) 2019 John Langner, WB2OSZ
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 2 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 //
19
20
21
22 /********************************************************************************
23 *
24 * File: fx25_rec.c
25 *
26 * Purpose: Extract FX.25 codeblocks from a stream of bits and process them.
27 *
28 *******************************************************************************/
29
30 #include "direwolf.h"
31
32 #include <stdio.h>
33 #include <assert.h>
34 #include <string.h>
35 #include <stdint.h>
36 #include <stdlib.h>
37 //#if __WIN32__
38 //#include <fcntl.h>
39 //#endif
40
41 #include "fx25.h"
42
43 #include "fcs_calc.h"
44 #include "textcolor.h"
45 #include "multi_modem.h"
46 #include "demod.h"
47
48 struct fx_context_s {
49
50 enum { FX_TAG=0, FX_DATA, FX_CHECK } state;
51 uint64_t accum; // Accumulate bits for matching to correlation tag.
52 int ctag_num; // Correlation tag number, CTAG_MIN to CTAG_MAX if approx. match found.
53 int k_data_radio; // Expected size of "data" sent over radio.
54 int coffs; // Starting offset of the check part.
55 int nroots; // Expected number of check bytes.
56 int dlen; // Accumulated length in "data" below.
57 int clen; // Accumulated length in "check" below.
58 unsigned char imask; // Mask for storing a bit.
59 unsigned char block[FX25_BLOCK_SIZE+1];
60 };
61
62 static struct fx_context_s *fx_context[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS];
63
64 static void process_rs_block (int chan, int subchan, int slice, struct fx_context_s *F);
65
66 static int my_unstuff (int chan, int subchan, int slice, unsigned char * restrict pin, int ilen, unsigned char * restrict frame_buf);
67
68 //#define FXTEST 1 // Define for standalone test application.
69 // It expects to find files fx01.dat, fx02.dat, ..., fx0b.dat/
70
71 #if FXTEST
72 static int fx25_test_count = 0;
73
main()74 int main ()
75 {
76 fx25_init(3);
77
78 for (int i = CTAG_MIN; i <= CTAG_MAX; i++) {
79
80 char fname[32];
81 snprintf (fname, sizeof(fname), "fx%02x.dat", i);
82 FILE *fp = fopen(fname, "rb");
83 if (fp == NULL) {
84 text_color_set(DW_COLOR_ERROR);
85 dw_printf ("\n");
86 dw_printf ("****** Could not open %s ******\n", fname);
87 dw_printf ("****** Did you generate the test files first? ******\n");
88 exit (EXIT_FAILURE);
89 }
90
91 //#if 0 // reminder for future if reading from stdin.
92 //#if __WIN32__
93 // // So 0x1a byte does not signal EOF.
94 // _setmode(_fileno(stdin), _O_BINARY);
95 //#endif
96 // fp = stdin;
97 //#endif
98 unsigned char ch;
99 while (fread(&ch, 1, 1, fp) == 1) {
100 for (unsigned char imask = 0x01; imask != 0; imask <<=1) {
101 fx25_rec_bit (0, 0, 0, ch & imask);
102 }
103 }
104 fclose (fp);
105 }
106
107 if (fx25_test_count == 11) {
108 text_color_set(DW_COLOR_REC);
109 dw_printf ("\n");
110 dw_printf ("\n");
111 dw_printf ("\n");
112 dw_printf ("***** FX25 unit test Success - all tests passed. *****\n");
113 exit (EXIT_SUCCESS);
114 }
115 text_color_set(DW_COLOR_ERROR);
116 dw_printf ("\n");
117 dw_printf ("\n");
118 dw_printf ("***** FX25 unit test FAILED. Only %d/11 tests passed. *****\n", fx25_test_count);
119 exit (EXIT_SUCCESS);
120
121 } // end main
122
123 #endif // FXTEST
124
125
126
127 /***********************************************************************************
128 *
129 * Name: fx25_rec_bit
130 *
131 * Purpose: Extract FX.25 codeblocks from a stream of bits.
132 * In a completely integrated AX.25 / FX.25 receive system,
133 * this would see the same bit stream as hdlc_rec_bit.
134 *
135 * Inputs: chan - Channel number.
136 *
137 * subchan - This allows multiple demodulators per channel.
138 *
139 * slice - Allows multiple slicers per demodulator (subchannel).
140 *
141 * dbit - Data bit after NRZI and any descrambling.
142 * Any non-zero value is logic '1'.
143 *
144 * Description: This is called once for each received bit.
145 * For each valid frame, process_rec_frame() is called for further processing.
146 * It can gather multiple candidates from different parallel demodulators
147 * ("subchannels") and slicers, then decide which one is the best.
148 *
149 ***********************************************************************************/
150
151 #define FENCE 0x55 // to detect buffer overflow.
152
fx25_rec_bit(int chan,int subchan,int slice,int dbit)153 void fx25_rec_bit (int chan, int subchan, int slice, int dbit)
154 {
155
156 // Allocate context blocks only as needed.
157
158 struct fx_context_s *F = fx_context[chan][subchan][slice];
159 if (F == NULL) {
160 assert (chan >= 0 && chan < MAX_CHANS);
161 assert (subchan >= 0 && subchan < MAX_SUBCHANS);
162 assert (slice >= 0 && slice < MAX_SLICERS);
163 F = fx_context[chan][subchan][slice] = (struct fx_context_s *)malloc(sizeof (struct fx_context_s));
164 assert (F != NULL);
165 memset (F, 0, sizeof(struct fx_context_s));
166 }
167
168 // State machine to identify correlation tag then gather appropriate number of data and check bytes.
169
170 switch (F->state) {
171 case FX_TAG:
172 F->accum >>= 1;
173 if (dbit) F->accum |= 1LL << 63;
174 int c = fx25_tag_find_match (F->accum);
175 if (c >= CTAG_MIN && c <= CTAG_MAX) {
176
177 F->ctag_num = c;
178 F->k_data_radio = fx25_get_k_data_radio (F->ctag_num);
179 F->nroots = fx25_get_nroots (F->ctag_num);
180 F->coffs = fx25_get_k_data_rs (F->ctag_num);
181 assert (F->coffs == FX25_BLOCK_SIZE - F->nroots);
182
183 if (fx25_get_debug() >= 2) {
184 text_color_set(DW_COLOR_INFO);
185 dw_printf ("FX.25[%d.%d]: Matched correlation tag 0x%02x with %d bit errors. Expecting %d data & %d check bytes.\n",
186 chan, slice, // ideally subchan too only if applicable
187 c,
188 __builtin_popcountll(F->accum ^ fx25_get_ctag_value(c)),
189 F->k_data_radio, F->nroots);
190 }
191
192 F->imask = 0x01;
193 F->dlen = 0;
194 F->clen = 0;
195 memset (F->block, 0, sizeof(F->block));
196 F->block[FX25_BLOCK_SIZE] = FENCE;
197 F->state = FX_DATA;
198 }
199 break;
200
201 case FX_DATA:
202 if (dbit) F->block[F->dlen] |= F->imask;
203 F->imask <<= 1;
204 if (F->imask == 0) {
205 F->imask = 0x01;
206 F->dlen++;
207 if (F->dlen >= F->k_data_radio) {
208 F->state = FX_CHECK;
209 }
210 }
211 break;
212
213 case FX_CHECK:
214 if (dbit) F->block[F->coffs + F->clen] |= F->imask;
215 F->imask <<= 1;
216 if (F->imask == 0) {
217 F->imask = 0x01;
218 F->clen++;
219 if (F->clen >= F->nroots) {
220
221 process_rs_block (chan, subchan, slice, F); // see below
222
223 F->ctag_num = -1;
224 F->accum = 0;
225 F->state = FX_TAG;
226 }
227 }
228 break;
229 }
230 }
231
232
233
234 /***********************************************************************************
235 *
236 * Name: fx25_rec_busy
237 *
238 * Purpose: Is FX.25 reception currently in progress?
239 *
240 * Inputs: chan - Channel number.
241 *
242 * Returns: True if currently in progress for the specified channel.
243 *
244 * Description: This is required for duplicate removal. One channel and can have
245 * multiple demodulators (called subchannels) running in parallel.
246 * Each of them can have multiple slicers. Duplicates need to be
247 * removed. Normally a delay of a couple bits (or more accurately
248 * symbols) was fine because they all took about the same amount of time.
249 * Now, we can have an additional delay of up to 64 check bytes and
250 * some filler in the data portion. We can't simply wait that long.
251 * With normal AX.25 a couple frames can come and go during that time.
252 * We want to delay the duplicate removal while FX.25 block reception
253 * is going on.
254 *
255 ***********************************************************************************/
256
fx25_rec_busy(int chan)257 int fx25_rec_busy (int chan)
258 {
259 assert (chan >= 0 && chan < MAX_CHANS);
260
261 // This could be a litle faster if we knew number of
262 // subchannels and slicers but it is probably insignificant.
263
264 for (int i = 0; i < MAX_SUBCHANS; i++) {
265 for (int j = 0; j < MAX_SLICERS; j++) {
266 if (fx_context[chan][i][j] != NULL) {
267 if (fx_context[chan][i][j]->state != FX_TAG) {
268 return (1);
269 }
270 }
271 }
272 }
273 return (0);
274
275 } // end fx25_rec_busy
276
277
278
279 /***********************************************************************************
280 *
281 * Name: process_rs_block
282 *
283 * Purpose: After the correlation tag was detected and the appropriate number
284 * of data and check bytes are accumulated, this performs the processing
285 *
286 * Inputs: chan, subchan, slice
287 *
288 * F->ctag_num - Correlation tag number (index into table)
289 *
290 * F->dlen - Number of "data" bytes.
291 *
292 * F->clen - Number of "check" bytes"
293 *
294 * F->block - Codeblock. Always 255 total bytes.
295 * Anything left over after data and check
296 * bytes is filled with zeros.
297 *
298 * <- - - - - - - - - - - 255 bytes total - - - - - - - - ->
299 * +-----------------------+---------------+---------------+
300 * | dlen bytes "data" | zero fill | check bytes |
301 * +-----------------------+---------------+---------------+
302 *
303 * Description: Use Reed-Solomon decoder to fix up any errors.
304 * Extract the AX.25 frame from the corrected data.
305 *
306 ***********************************************************************************/
307
process_rs_block(int chan,int subchan,int slice,struct fx_context_s * F)308 static void process_rs_block (int chan, int subchan, int slice, struct fx_context_s *F)
309 {
310 if (fx25_get_debug() >= 3) {
311 text_color_set(DW_COLOR_DEBUG);
312 dw_printf ("FX.25[%d.%d]: Received RS codeblock.\n", chan, slice);
313 fx_hex_dump (F->block, FX25_BLOCK_SIZE);
314 }
315 assert (F->block[FX25_BLOCK_SIZE] == FENCE);
316
317 int derrlocs[FX25_MAX_CHECK]; // Half would probably be OK.
318 struct rs *rs = fx25_get_rs(F->ctag_num);
319
320 int derrors = DECODE_RS(rs, F->block, derrlocs, 0);
321
322 if (derrors >= 0) { // -1 for failure. >= 0 for success, number of bytes corrected.
323
324 if (fx25_get_debug() >= 2) {
325 text_color_set(DW_COLOR_INFO);
326 if (derrors == 0) {
327 dw_printf ("FX.25[%d.%d]: FEC complete with no errors.\n", chan, slice);
328 }
329 else {
330 dw_printf ("FX.25[%d.%d]: FEC complete, fixed %2d errors in byte positions:", chan, slice, derrors);
331 for (int k = 0; k < derrors; k++) {
332 dw_printf (" %d", derrlocs[k]);
333 }
334 dw_printf ("\n");
335 }
336 }
337
338 unsigned char frame_buf[FX25_MAX_DATA+1]; // Out must be shorter than input.
339 int frame_len = my_unstuff (chan, subchan, slice, F->block, F->dlen, frame_buf);
340
341 if (frame_len >= 14 + 1 + 2) { // Minimum length: Two addresses & control & FCS.
342
343 unsigned short actual_fcs = frame_buf[frame_len-2] | (frame_buf[frame_len-1] << 8);
344 unsigned short expected_fcs = fcs_calc (frame_buf, frame_len - 2);
345 if (actual_fcs == expected_fcs) {
346
347 if (fx25_get_debug() >= 3) {
348 text_color_set(DW_COLOR_DEBUG);
349 dw_printf ("FX.25[%d.%d]: Extracted AX.25 frame:\n", chan, slice);
350 fx_hex_dump (frame_buf, frame_len);
351 }
352
353 #if FXTEST
354 fx25_test_count++;
355 #else
356 alevel_t alevel = demod_get_audio_level (chan, subchan);
357
358 multi_modem_process_rec_frame (chan, subchan, slice, frame_buf, frame_len - 2, alevel, derrors, 1); /* len-2 to remove FCS. */
359
360 #endif
361 } else {
362 // Most likely cause is defective sender software.
363 text_color_set(DW_COLOR_ERROR);
364 dw_printf ("FX.25[%d.%d]: Bad FCS for AX.25 frame.\n", chan, slice);
365 fx_hex_dump (F->block, F->dlen);
366 fx_hex_dump (frame_buf, frame_len);
367 }
368 }
369 else {
370 // Most likely cause is defective sender software.
371 text_color_set(DW_COLOR_ERROR);
372 dw_printf ("FX.25[%d.%d]: AX.25 frame is shorter than minimum length.\n", chan, slice);
373 fx_hex_dump (F->block, F->dlen);
374 fx_hex_dump (frame_buf, frame_len);
375 }
376 }
377 else if (fx25_get_debug() >= 2) {
378 text_color_set(DW_COLOR_ERROR);
379 dw_printf ("FX.25[%d.%d]: FEC failed. Too many errors.\n", chan, slice);
380 }
381
382 } // process_rs_block
383
384
385 /***********************************************************************************
386 *
387 * Name: my_unstuff
388 *
389 * Purpose: Remove HDLC it stuffing and surrounding flag delimiters.
390 *
391 * Inputs: chan, subchan, slice - For error messages.
392 *
393 * pin - "data" part of RS codeblock.
394 * First byte must be HDLC "flag".
395 * May be followed by additional flags.
396 * There must be terminating flag but it might not be byte aligned.
397 *
398 * ilen - Number of bytes in pin.
399 *
400 * Outputs: frame_buf - Frame contents including FCS.
401 * Bit stuffing is gone so it should be a whole number of bytes.
402 *
403 * Returns: Number of bytes in frame_buf, including 2 for FCS.
404 * This can never be larger than the max "data" size.
405 * 0 if any error.
406 *
407 * Errors: First byte is not not flag.
408 * Found seven '1' bits in a row.
409 * Result is not whole number of bytes after removing bit stuffing.
410 * Trailing flag not found.
411 * Most likely cause, for all of these, is defective sender software.
412 *
413 ***********************************************************************************/
414
my_unstuff(int chan,int subchan,int slice,unsigned char * restrict pin,int ilen,unsigned char * restrict frame_buf)415 static int my_unstuff (int chan, int subchan, int slice, unsigned char * restrict pin, int ilen, unsigned char * restrict frame_buf)
416 {
417 unsigned char pat_det = 0; // Pattern detector.
418 unsigned char oacc = 0; // Accumulator for a byte out.
419 int olen = 0; // Number of good bits in oacc.
420 int frame_len = 0; // Number of bytes accumulated, including CRC.
421
422 if (*pin != 0x7e) {
423 text_color_set(DW_COLOR_ERROR);
424 dw_printf ("FX.25[%d.%d] error: Data section did not start with 0x7e.\n", chan, slice);
425 fx_hex_dump (pin, ilen);
426 return (0);
427 }
428 while (ilen > 0 && *pin == 0x7e) {
429 ilen--;
430 pin++; // Skip over leading flag byte(s).
431 }
432
433 for (int i=0; i<ilen; pin++, i++) {
434 for (unsigned char imask = 0x01; imask != 0; imask <<= 1) {
435 unsigned char dbit = (*pin & imask) != 0;
436
437 pat_det >>= 1; // Shift the most recent eight bits thru the pattern detector.
438 pat_det |= dbit << 7;
439
440 if (pat_det == 0xfe) {
441 text_color_set(DW_COLOR_ERROR);
442 dw_printf ("FX.25[%d.%d]: Invalid AX.25 frame - Seven '1' bits in a row.\n", chan, slice);
443 fx_hex_dump (pin, ilen);
444 return 0;
445 }
446
447 if (dbit) {
448 oacc >>= 1;
449 oacc |= 0x80;
450 } else {
451 if (pat_det == 0x7e) { // "flag" pattern - End of frame.
452 if (olen == 7) {
453 return (frame_len); // Whole number of bytes in result including CRC
454 }
455 else {
456 text_color_set(DW_COLOR_ERROR);
457 dw_printf ("FX.25[%d.%d]: Invalid AX.25 frame - Not a whole number of bytes.\n", chan, slice);
458 fx_hex_dump (pin, ilen);
459 return (0);
460 }
461 } else if ( (pat_det >> 2) == 0x1f ) {
462 continue; // Five '1' bits in a row, followed by '0'. Discard the '0'.
463 }
464 oacc >>= 1;
465 }
466
467 olen++;
468 if (olen & 8) {
469 olen = 0;
470 frame_buf[frame_len++] = oacc;
471 }
472 }
473 } /* end of loop on all bits in block */
474
475 text_color_set(DW_COLOR_ERROR);
476 dw_printf ("FX.25[%d.%d]: Invalid AX.25 frame - Terminating flag not found.\n", chan, slice);
477 fx_hex_dump (pin, ilen);
478
479 return (0); // Should never fall off the end.
480
481 } // my_unstuff
482
483 // end fx25_rec.c