diff --git a/src/fx25_init.c b/src/fx25_init.c index bc754be..9031a6d 100644 --- a/src/fx25_init.c +++ b/src/fx25_init.c @@ -130,6 +130,9 @@ static const struct correlation_tag_s tags[16] = { // 12 got many false matches with random noise. // Even 8 might be too high. We see 2 or 4 bit errors here // at the point where decoding the block is very improbable. + // After 2 months of continuous operation as a digipeater/iGate, + // no false triggers were observed. So 8 doesn't seem to be too + // high for 1200 bps. No study has been done for 9600 bps. // Given a 64 bit correlation tag value, find acceptable match in table. // Return index into table or -1 for no match. @@ -215,14 +218,14 @@ void fx25_init ( int debug_level ) assert (tags[j].n_block_rs == FX25_BLOCK_SIZE); } - assert (fx25_pick_mode (1, 239) == 1); - assert (fx25_pick_mode (1, 240) == -1); + assert (fx25_pick_mode (100+1, 239) == 1); + assert (fx25_pick_mode (100+1, 240) == -1); - assert (fx25_pick_mode (5, 223) == 5); - assert (fx25_pick_mode (5, 224) == -1); + assert (fx25_pick_mode (100+5, 223) == 5); + assert (fx25_pick_mode (100+5, 224) == -1); - assert (fx25_pick_mode (9, 191) == 9); - assert (fx25_pick_mode (9, 192) == -1); + assert (fx25_pick_mode (100+9, 191) == 9); + assert (fx25_pick_mode (100+9, 192) == -1); assert (fx25_pick_mode (16, 32) == 4); assert (fx25_pick_mode (16, 64) == 3); @@ -241,6 +244,16 @@ void fx25_init ( int debug_level ) assert (fx25_pick_mode (64, 191) == 9); assert (fx25_pick_mode (64, 192) == -1); + assert (fx25_pick_mode (1, 32) == 4); + assert (fx25_pick_mode (1, 33) == 3); + assert (fx25_pick_mode (1, 64) == 3); + assert (fx25_pick_mode (1, 65) == 6); + assert (fx25_pick_mode (1, 128) == 6); + assert (fx25_pick_mode (1, 191) == 9); + assert (fx25_pick_mode (1, 223) == 5); + assert (fx25_pick_mode (1, 239) == 1); + assert (fx25_pick_mode (1, 240) == -1); + } // fx25_init @@ -290,12 +303,13 @@ int fx25_get_debug (void) * Purpose: Pick suitable transmission format based on user preference * and size of data part required. * - * Inputs: fx_mode - Normally, this would be 16, 32, or 64 for the desired number - * of check bytes. The shortest format, adequate for the - * required data length will be picked automatically. - * 0x01 thru 0x0b may also be specified for a specific format - * but this is expected to be mostly for testing, not normal - * operation. + * Inputs: fx_mode - 0 = none. + * 1 = pick a tag automatically. + * 16, 32, 64 = use this many check bytes. + * 100 + n = use tag n. + * + * 0 and 1 would be the most common. + * Others are mostly for testing. * * dlen - Required size for transmitted "data" part, in bytes. * This includes the AX.25 frame with bit stuffing and a flag @@ -303,24 +317,29 @@ int fx25_get_debug (void) * * Returns: Correlation tag number in range of CTAG_MIN thru CTAG_MAX. * -1 is returned for failure. - * - * Future: Might be more accomodating. - * For example, if 64 check bytes were specified for 200 data bytes, - * we might automatically drop it down to 32 check bytes, print a - * warning and continue. Keep it simple at first. Fine tune later. + * The caller should fall back to using plain old AX.25. * *--------------------------------------------------------------*/ int fx25_pick_mode (int fx_mode, int dlen) { - if (fx_mode >= CTAG_MIN && fx_mode <= CTAG_MAX) { - if (dlen <= fx25_get_k_data_radio(fx_mode)) { - return (fx_mode); + if (fx_mode <= 0) return (-1); + +// Specify a specific tag by adding 100 to the number. +// Fails if data won't fit. + + if (fx_mode - 100 >= CTAG_MIN && fx_mode - 100 <= CTAG_MAX) { + if (dlen <= fx25_get_k_data_radio(fx_mode - 100)) { + return (fx_mode - 100); } else { return (-1); // Assuming caller prints failure message. } } + +// Specify number of check bytes. +// Pick the shortest one that can handle the required data length. + else if (fx_mode == 16 || fx_mode == 32 || fx_mode == 64) { for (int k = CTAG_MAX; k >= CTAG_MIN; k--) { if (fx_mode == fx25_get_nroots(k) && dlen <= fx25_get_k_data_radio(k)) { @@ -329,11 +348,41 @@ int fx25_pick_mode (int fx_mode, int dlen) } return (-1); } - else { - text_color_set(DW_COLOR_ERROR); - dw_printf("FX.25: Transmission format %d must be 0x00 thru 0x0b, 16, 32, or 64.\n", fx_mode); - exit(EXIT_FAILURE); + +// For any other number, [[ or if the preference was not possible, ?? ]] +// try to come up with something reasonable. For shorter frames, +// use smaller overhead. For longer frames, where an error is +// more probable, use more check bytes. When the data gets even +// larger, check bytes must be reduced to fit in block size. +// When all else fails, fall back to normal AX.25. +// Some of this is from observing UZ7HO Soundmodem behavior. +// +// Tag Data Check Max Num +// Number Bytes Bytes Repaired +// ------ ----- ----- ----- +// 0x04 32 16 8 +// 0x03 64 16 8 +// 0x06 128 32 16 +// 0x09 191 64 32 +// 0x05 223 32 16 +// 0x01 239 16 8 +// none larger +// +// The PRUG FX.25 TNC has additional modes that will handle larger frames +// by using multiple RS blocks. This is a future possibility but needs +// to be coordinated with other FX.25 developers so we maintain compatibility. + + static const int prefer[6] = { 0x04, 0x03, 0x06, 0x09, 0x05, 0x01 }; + for (int k = 0; k < 6; k++) { + int m = prefer[k]; + if (dlen <= fx25_get_k_data_radio(m)) { + return (m); + } } + return (-1); + +// TODO: revisit error messages, produced by caller, when this returns -1. + } diff --git a/src/fx25_rec.c b/src/fx25_rec.c index a25dc8d..3ab7851 100644 --- a/src/fx25_rec.c +++ b/src/fx25_rec.c @@ -63,7 +63,7 @@ static struct fx_context_s *fx_context[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS]; static void process_rs_block (int chan, int subchan, int slice, struct fx_context_s *F); -static int my_unstuff (unsigned char * restrict pin, int ilen, unsigned char * restrict frame_buf); +static int my_unstuff (int chan, int subchan, int slice, unsigned char * restrict pin, int ilen, unsigned char * restrict frame_buf); //#define FXTEST 1 // Define for standalone test application. // It expects to find files fx01.dat, fx02.dat, ..., fx0b.dat/ @@ -182,7 +182,8 @@ void fx25_rec_bit (int chan, int subchan, int slice, int dbit) if (fx25_get_debug() >= 2) { text_color_set(DW_COLOR_INFO); - dw_printf ("FX.25: Matched correlation tag 0x%02x with %d bit errors. Expecting %d data & %d check bytes.\n", + dw_printf ("FX.25[%d.%d]: Matched correlation tag 0x%02x with %d bit errors. Expecting %d data & %d check bytes.\n", + chan, slice, // ideally subchan too only if applicable c, __builtin_popcountll(F->accum ^ fx25_get_ctag_value(c)), F->k_data_radio, F->nroots); @@ -308,7 +309,7 @@ static void process_rs_block (int chan, int subchan, int slice, struct fx_contex { if (fx25_get_debug() >= 3) { text_color_set(DW_COLOR_DEBUG); - dw_printf ("FX.25: Received RS codeblock.\n"); + dw_printf ("FX.25[%d.%d]: Received RS codeblock.\n", chan, slice); fx_hex_dump (F->block, FX25_BLOCK_SIZE); } assert (F->block[FX25_BLOCK_SIZE] == FENCE); @@ -323,10 +324,10 @@ static void process_rs_block (int chan, int subchan, int slice, struct fx_contex if (fx25_get_debug() >= 2) { text_color_set(DW_COLOR_INFO); if (derrors == 0) { - dw_printf ("FX.25: FEC complete with no errors.\n"); + dw_printf ("FX.25[%d.%d]: FEC complete with no errors.\n", chan, slice); } else { - dw_printf ("FX.25: FEC complete, fixed %2d errors in byte positions:",derrors); + dw_printf ("FX.25[%d.%d]: FEC complete, fixed %2d errors in byte positions:", chan, slice, derrors); for (int k = 0; k < derrors; k++) { dw_printf (" %d", derrlocs[k]); } @@ -335,7 +336,7 @@ static void process_rs_block (int chan, int subchan, int slice, struct fx_contex } unsigned char frame_buf[FX25_MAX_DATA+1]; // Out must be shorter than input. - int frame_len = my_unstuff (F->block, F->dlen, frame_buf); + int frame_len = my_unstuff (chan, subchan, slice, F->block, F->dlen, frame_buf); if (frame_len >= 14 + 1 + 2) { // Minimum length: Two addresses & control & FCS. @@ -345,7 +346,7 @@ static void process_rs_block (int chan, int subchan, int slice, struct fx_contex if (fx25_get_debug() >= 3) { text_color_set(DW_COLOR_DEBUG); - dw_printf ("FX.25: Extracted AX.25 frame:\n"); + dw_printf ("FX.25[%d.%d]: Extracted AX.25 frame:\n", chan, slice); fx_hex_dump (frame_buf, frame_len); } @@ -360,7 +361,7 @@ static void process_rs_block (int chan, int subchan, int slice, struct fx_contex } else { // Most likely cause is defective sender software. text_color_set(DW_COLOR_ERROR); - dw_printf ("FX.25: Bad FCS for AX.25 frame.\n"); + dw_printf ("FX.25[%d.%d]: Bad FCS for AX.25 frame.\n", chan, slice); fx_hex_dump (F->block, F->dlen); fx_hex_dump (frame_buf, frame_len); } @@ -368,14 +369,14 @@ static void process_rs_block (int chan, int subchan, int slice, struct fx_contex else { // Most likely cause is defective sender software. text_color_set(DW_COLOR_ERROR); - dw_printf ("FX.25: AX.25 frame is shorter than minimum length.\n"); + dw_printf ("FX.25[%d.%d]: AX.25 frame is shorter than minimum length.\n", chan, slice); fx_hex_dump (F->block, F->dlen); fx_hex_dump (frame_buf, frame_len); } } else if (fx25_get_debug() >= 2) { text_color_set(DW_COLOR_ERROR); - dw_printf ("FX.25: FEC failed. Too many errors.\n"); + dw_printf ("FX.25[%d.%d]: FEC failed. Too many errors.\n", chan, slice); } } // process_rs_block @@ -387,7 +388,9 @@ static void process_rs_block (int chan, int subchan, int slice, struct fx_contex * * Purpose: Remove HDLC it stuffing and surrounding flag delimiters. * - * Inputs: pin - "data" part of RS codeblock. + * Inputs: chan, subchan, slice - For error messages. + * + * pin - "data" part of RS codeblock. * First byte must be HDLC "flag". * May be followed by additional flags. * There must be terminating flag but it might not be byte aligned. @@ -409,7 +412,7 @@ static void process_rs_block (int chan, int subchan, int slice, struct fx_contex * ***********************************************************************************/ -static int my_unstuff (unsigned char * restrict pin, int ilen, unsigned char * restrict frame_buf) +static int my_unstuff (int chan, int subchan, int slice, unsigned char * restrict pin, int ilen, unsigned char * restrict frame_buf) { unsigned char pat_det = 0; // Pattern detector. unsigned char oacc = 0; // Accumulator for a byte out. @@ -418,7 +421,7 @@ static int my_unstuff (unsigned char * restrict pin, int ilen, unsigned char * r if (*pin != 0x7e) { text_color_set(DW_COLOR_ERROR); - dw_printf ("FX.25 error: Data section did not start with 0x7e.\n"); + dw_printf ("FX.25[%d.%d] error: Data section did not start with 0x7e.\n", chan, slice); fx_hex_dump (pin, ilen); return (0); } @@ -436,7 +439,7 @@ static int my_unstuff (unsigned char * restrict pin, int ilen, unsigned char * r if (pat_det == 0xfe) { text_color_set(DW_COLOR_ERROR); - dw_printf ("FX.25: Invalid AX.25 frame - Seven '1' bits in a row.\n"); + dw_printf ("FX.25[%d.%d]: Invalid AX.25 frame - Seven '1' bits in a row.\n", chan, slice); fx_hex_dump (pin, ilen); return 0; } @@ -451,7 +454,7 @@ static int my_unstuff (unsigned char * restrict pin, int ilen, unsigned char * r } else { text_color_set(DW_COLOR_ERROR); - dw_printf ("FX.25: Invalid AX.25 frame - Not a whole number of bytes.\n"); + dw_printf ("FX.25[%d.%d]: Invalid AX.25 frame - Not a whole number of bytes.\n", chan, slice); fx_hex_dump (pin, ilen); return (0); } @@ -470,7 +473,7 @@ static int my_unstuff (unsigned char * restrict pin, int ilen, unsigned char * r } /* end of loop on all bits in block */ text_color_set(DW_COLOR_ERROR); - dw_printf ("FX.25: Invalid AX.25 frame - Terminating flag not found.\n"); + dw_printf ("FX.25[%d.%d]: Invalid AX.25 frame - Terminating flag not found.\n", chan, slice); fx_hex_dump (pin, ilen); return (0); // Should never fall off the end. diff --git a/src/fx25_send.c b/src/fx25_send.c index f0cb309..7435be9 100644 --- a/src/fx25_send.c +++ b/src/fx25_send.c @@ -61,7 +61,7 @@ int main () dw_printf("Run fxrec as second part of test.\n"); fx25_init (3); - for (int i = CTAG_MIN; i <= CTAG_MAX; i++) { + for (int i = 100 + CTAG_MIN; i <= 100 + CTAG_MAX; i++) { fx25_send_frame (0, preload, (int)sizeof(preload)-3, i); } exit(EXIT_SUCCESS); @@ -115,7 +115,7 @@ int fx25_send_frame (int chan, unsigned char *fbuf, int flen, int fx_mode) if (fx25_get_debug() >= 3) { text_color_set(DW_COLOR_DEBUG); dw_printf ("------\n"); - dw_printf ("FX.25 send frame: chan = %d, FX.25 mode = %d\n", chan, fx_mode); + dw_printf ("FX.25[%d] send frame: FX.25 mode = %d\n", chan, fx_mode); fx_hex_dump (fbuf, flen); } @@ -138,7 +138,7 @@ int fx25_send_frame (int chan, unsigned char *fbuf, int flen, int fx_mode) assert (data[FX25_MAX_DATA] == fence); if (dlen < 0) { text_color_set(DW_COLOR_ERROR); - dw_printf ("FX.25: Frame length of %d + overhead is too large to encode.\n", flen); + dw_printf ("FX.25[%d]: Frame length of %d + overhead is too large to encode.\n", chan, flen); return (-1); } @@ -150,7 +150,7 @@ int fx25_send_frame (int chan, unsigned char *fbuf, int flen, int fx_mode) if (ctag_num < CTAG_MIN || ctag_num > CTAG_MAX) { text_color_set(DW_COLOR_ERROR); - dw_printf ("FX.25: Could not find suitable format for requested %d and data length %d.\n", fx_mode, dlen); + dw_printf ("FX.25[%d]: Could not find suitable format for requested %d and data length %d.\n", chan, fx_mode, dlen); return (-1); } @@ -179,9 +179,9 @@ int fx25_send_frame (int chan, unsigned char *fbuf, int flen, int fx_mode) if (fx25_get_debug() >= 3) { text_color_set(DW_COLOR_DEBUG); - dw_printf ("FX.25: transmit %d data bytes, ctag number 0x%02x\n", k_data_radio, ctag_num); + dw_printf ("FX.25[%d]: transmit %d data bytes, ctag number 0x%02x\n", chan, k_data_radio, ctag_num); fx_hex_dump (data, k_data_radio); - dw_printf ("FX.25: transmit %d check bytes:\n", NROOTS); + dw_printf ("FX.25[%d]: transmit %d check bytes:\n", chan, NROOTS); fx_hex_dump (check, NROOTS); dw_printf ("------\n"); } @@ -212,6 +212,11 @@ int fx25_send_frame (int chan, unsigned char *fbuf, int flen, int fx_mode) #else // Normal usage. Send bits to modulator. +// Temp hack for testing. Corrupt first 8 bytes. +// for (int j = 0; j < 16; j++) { +// data[j] = ~ data[j]; +// } + for (int k = 0; k < 8; k++) { unsigned char b = (ctag_value >> (k * 8)) & 0xff; send_bytes (chan, &b, 1);