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