From 4cd63df5bee58fdc407892fbbb3dc6b7be61af15 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Wed, 26 Apr 2023 01:07:05 +0100 Subject: [PATCH 01/36] Use channel rather than port when dumping KISS frame. --- src/kiss_frame.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/kiss_frame.c b/src/kiss_frame.c index e304a838..aa581dd2 100644 --- a/src/kiss_frame.c +++ b/src/kiss_frame.c @@ -40,7 +40,8 @@ * * The first byte of the frame contains: * - * * port number (radio channel) in upper nybble. + * * radio channel in upper nybble. + * (KISS doc uses "port" but I don't like that because it has too many meanings.) * * command in lower nybble. * * @@ -954,7 +955,7 @@ void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int p = pmsg; if (*p == FEND) p++; - dw_printf ("%s %s %s KISS client application, port %d, total length = %d\n", + dw_printf ("%s %s %s KISS client application, channel %d, total length = %d\n", prefix[(int)fromto], function[p[0] & 0xf], direction[(int)fromto], (p[0] >> 4) & 0xf, msg_len); } From 4ac666df6a03f81f6938d656173c52096f482e10 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sun, 30 Apr 2023 21:48:51 +0100 Subject: [PATCH 02/36] Clean up atest EAS receive. --- man/atest.1 | 4 ++++ src/atest.c | 26 ++++++++++++-------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/man/atest.1 b/man/atest.1 index a1b554c2..58c90f64 100644 --- a/man/atest.1 +++ b/man/atest.1 @@ -37,6 +37,10 @@ Data rate in bits/sec. Standard values are 300, 1200, 2400, 4800, 9600. 4800 bps uses 8PSK based on V.27 standard. .P 9600 bps and up uses K9NG/G3RUH standard. +.P +AIS for ship Automatic Identification System. +.P +EAS for Emergency Alert System (EAS) Specific Area Message Encoding (SAME). .RE .RE .PD diff --git a/src/atest.c b/src/atest.c index aec626f2..733e8c4f 100644 --- a/src/atest.c +++ b/src/atest.c @@ -2,7 +2,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2019, 2021, 2022 John Langner, WB2OSZ +// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2019, 2021, 2022, 2023 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -276,12 +276,12 @@ int main (int argc, char *argv[]) case 'B': /* -B for data Bit rate */ /* Also implies modem type based on speed. */ - /* Special case "AIS" rather than number. */ + /* Special cases AIS, EAS rather than number. */ if (strcasecmp(optarg, "AIS") == 0) { - B_opt = 12345; // See special case below. + B_opt = 0xA15A15; // See special case below. } else if (strcasecmp(optarg, "EAS") == 0) { - B_opt = 23456; // See special case below. + B_opt = 0xEA5EA5; // See special case below. } else { B_opt = atoi(optarg); @@ -425,11 +425,6 @@ int main (int argc, char *argv[]) my_audio_config.achan[0].baud = B_opt; - if (my_audio_config.achan[0].baud < MIN_BAUD || my_audio_config.achan[0].baud > MAX_BAUD) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Use a more reasonable bit rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD); - exit (EXIT_FAILURE); - } /* We have similar logic in direwolf.c, config.c, gen_packets.c, and atest.c, */ /* that need to be kept in sync. Maybe it could be a common function someday. */ @@ -438,7 +433,6 @@ int main (int argc, char *argv[]) my_audio_config.achan[0].modem_type = MODEM_AFSK; my_audio_config.achan[0].mark_freq = 1615; my_audio_config.achan[0].space_freq = 1785; - //strlcpy (my_audio_config.achan[0].profiles, "A", sizeof(my_audio_config.achan[0].profiles)); } else if (my_audio_config.achan[0].baud < 600) { // e.g. HF SSB packet my_audio_config.achan[0].modem_type = MODEM_AFSK; @@ -446,13 +440,11 @@ int main (int argc, char *argv[]) my_audio_config.achan[0].space_freq = 1800; // Previously we had a "D" which was fine tuned for 300 bps. // In v1.7, it's not clear if we should use "B" or just stick with "A". - //strlcpy (my_audio_config.achan[0].profiles, "B", sizeof(my_audio_config.achan[0].profiles)); } else if (my_audio_config.achan[0].baud < 1800) { // common 1200 my_audio_config.achan[0].modem_type = MODEM_AFSK; my_audio_config.achan[0].mark_freq = DEFAULT_MARK_FREQ; my_audio_config.achan[0].space_freq = DEFAULT_SPACE_FREQ; - // Should default to E+ or something similar later. } else if (my_audio_config.achan[0].baud < 3600) { my_audio_config.achan[0].modem_type = MODEM_QPSK; @@ -466,14 +458,14 @@ int main (int argc, char *argv[]) my_audio_config.achan[0].space_freq = 0; strlcpy (my_audio_config.achan[0].profiles, "", sizeof(my_audio_config.achan[0].profiles)); } - else if (my_audio_config.achan[0].baud == 12345) { // Hack for different use of 9600 + else if (my_audio_config.achan[0].baud == 0xA15A15) { // Hack for different use of 9600 my_audio_config.achan[0].modem_type = MODEM_AIS; my_audio_config.achan[0].baud = 9600; my_audio_config.achan[0].mark_freq = 0; my_audio_config.achan[0].space_freq = 0; strlcpy (my_audio_config.achan[0].profiles, " ", sizeof(my_audio_config.achan[0].profiles)); // avoid getting default later. } - else if (my_audio_config.achan[0].baud == 23456) { + else if (my_audio_config.achan[0].baud == 0xEA5EA5) { my_audio_config.achan[0].modem_type = MODEM_EAS; my_audio_config.achan[0].baud = 521; // Actually 520.83 but we have an integer field here. // Will make more precise in afsk demod init. @@ -488,6 +480,12 @@ int main (int argc, char *argv[]) strlcpy (my_audio_config.achan[0].profiles, " ", sizeof(my_audio_config.achan[0].profiles)); // avoid getting default later. } + if (my_audio_config.achan[0].baud < MIN_BAUD || my_audio_config.achan[0].baud > MAX_BAUD) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Use a more reasonable bit rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD); + exit (EXIT_FAILURE); + } + /* * -g option means force g3RUH regardless of speed. */ From 110b85a781f5a84be08c916e6464fe6399404175 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Mon, 1 May 2023 02:41:05 +0100 Subject: [PATCH 03/36] Add EAS to gen_packets. --- man/gen_packets.1 | 2 + src/gen_packets.c | 95 ++++++++++++++++++++++----- src/gen_tone.c | 159 ++++++++++++++++++++++++++++++++++++++++++++-- src/gen_tone.h | 4 +- src/hdlc_send.c | 80 ++++++++++++++++++++++- src/hdlc_send.h | 7 +- 6 files changed, 323 insertions(+), 24 deletions(-) diff --git a/man/gen_packets.1 b/man/gen_packets.1 index ba782fe1..740d4db4 100644 --- a/man/gen_packets.1 +++ b/man/gen_packets.1 @@ -46,6 +46,8 @@ Data rate in bits/sec for first channel. Standard values are 300, 1200, 2400, 4 4800 bps uses 8PSK based on V.27 standard. .P 9600 bps and up uses K9NG/G3RUH standard. +.P +EAS for Emergency Alert System (EAS) Specific Area Message Encoding (SAME). .RE .RE .PD diff --git a/src/gen_packets.c b/src/gen_packets.c index 97286119..57b2741c 100644 --- a/src/gen_packets.c +++ b/src/gen_packets.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2013, 2014, 2015, 2016, 2019, 2021 John Langner, WB2OSZ +// Copyright (C) 2011, 2013, 2014, 2015, 2016, 2019, 2021, 2023 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -118,13 +118,48 @@ static void send_packet (char *str) packet_t pp; unsigned char fbuf[AX25_MAX_PACKET_LEN+2]; int flen; - int c; + int c = 0; // channel number. if (g_morse_wpm > 0) { - // TODO: Why not use the destination field instead of command line option? + // Why not use the destination field instead of command line option? + // For one thing, this is not in TNC-2 monitor format. - morse_send (0, str, g_morse_wpm, 100, 100); + morse_send (c, str, g_morse_wpm, 100, 100); + } + else if (modem.achan[0].modem_type == MODEM_EAS) { + +// Generate EAS SAME signal FOR RESEARCH AND TESTING ONLY!!! +// There could be legal consequences for sending unauhorized SAME +// over the radio so don't do it! + + // I'm expecting to see TNC 2 monitor format. + // The source and destination are ignored. + // The optional destination SSID is the number of times to repeat. + // The user defined data type indicator can optionally be used + // for compatibility with how it is received and presented to client apps. + // Examples: + // X>X-3:{DEZCZC-WXR-RWT-033019-033017-033015-033013-033011-025011-025017-033007-033005-033003-033001-025009-025027-033009+0015-1691525-KGYX/NWS- + // X>X:NNNN + + pp = ax25_from_text (str, 1); + if (pp == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("\"%s\" is not valid TNC2 monitoring format.\n", str); + return; + } + unsigned char *pinfo; + int info_len = ax25_get_info (pp, &pinfo); + if (info_len >= 3 && strncmp((char*)pinfo, "{DE", 3) == 0) { + pinfo += 3; + info_len -= 3; + } + + int repeat = ax25_get_ssid (pp, AX25_DESTINATION); + if (repeat == 0) repeat = 1; + + eas_send (c, pinfo, repeat, 500, 500); + ax25_delete (pp); } else { pp = ax25_from_text (str, 1); @@ -135,6 +170,9 @@ static void send_packet (char *str) } flen = ax25_pack (pp, fbuf); (void)flen; + + // If stereo, put same thing in each channel. + for (c=0; c MAX_BAUD)) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Use a more reasonable bit rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD); - exit (EXIT_FAILURE); - } /* We have similar logic in direwolf.c, config.c, gen_packets.c, and atest.c, */ /* that need to be kept in sync. Maybe it could be a common function someday. */ - if (modem.achan[0].baud == 100) { + if (modem.achan[0].baud == 100) { // What was this for? modem.achan[0].modem_type = MODEM_AFSK; modem.achan[0].mark_freq = 1615; modem.achan[0].space_freq = 1785; } + else if (modem.achan[0].baud == 0xEA5EA5) { + modem.achan[0].baud = 521; // Fine tuned later. 520.83333 + // Proper fix is to make this float. + modem.achan[0].modem_type = MODEM_EAS; + modem.achan[0].mark_freq = 2083.3333; // Ideally these should be floating point. + modem.achan[0].space_freq = 1562.5000 ; + } else if (modem.achan[0].baud < 600) { modem.achan[0].modem_type = MODEM_AFSK; modem.achan[0].mark_freq = 1600; // Typical for HF SSB @@ -334,6 +380,11 @@ int main(int argc, char **argv) text_color_set(DW_COLOR_INFO); dw_printf ("Using scrambled baseband signal rather than AFSK.\n"); } + if (modem.achan[0].baud != 100 && (modem.achan[0].baud < MIN_BAUD || modem.achan[0].baud > MAX_BAUD)) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Use a more reasonable bit rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD); + exit (EXIT_FAILURE); + } break; case 'g': /* -g for g3ruh scrambling */ @@ -740,14 +791,23 @@ int main(int argc, char **argv) } else { + // This should send a total of 6. + // Note that sticking in the user defined type {DE is optional. + + if (modem.achan[0].modem_type == MODEM_EAS) { + send_packet ("X>X-3:{DEZCZC-WXR-RWT-033019-033017-033015-033013-033011-025011-025017-033007-033005-033003-033001-025009-025027-033009+0015-1691525-KGYX/NWS-"); + send_packet ("X>X-2:{DENNNN"); + send_packet ("X>X:NNNN"); + } + else { /* * Builtin default 4 packets. */ - - send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog! 1 of 4"); - send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog! 2 of 4"); - send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog! 3 of 4"); - send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog! 4 of 4"); + send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog! 1 of 4"); + send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog! 2 of 4"); + send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog! 3 of 4"); + send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog! 4 of 4"); + } } audio_file_close(); @@ -765,7 +825,7 @@ static void usage (char **argv) dw_printf ("Options:\n"); dw_printf (" -a Signal amplitude in range of 0 - 200%%. Default 50.\n"); dw_printf (" -b Bits / second for data. Default is %d.\n", DEFAULT_BAUD); - dw_printf (" -B Bits / second for data. Proper modem selected for 300, 1200, 2400, 4800, 9600.\n"); + dw_printf (" -B Bits / second for data. Proper modem selected for 300, 1200, 2400, 4800, 9600, EAS.\n"); dw_printf (" -g Scrambled baseband rather than AFSK.\n"); dw_printf (" -j 2400 bps QPSK compatible with direwolf <= 1.5.\n"); dw_printf (" -J 2400 bps QPSK compatible with MFJ-2400.\n"); @@ -788,6 +848,7 @@ static void usage (char **argv) dw_printf ("the default built-in message. The format should correspond to\n"); dw_printf ("the standard packet monitoring representation such as,\n\n"); dw_printf (" WB2OSZ-1>APDW12,WIDE2-2:!4237.14NS07120.83W#\n"); + dw_printf ("User defined content can't be used with -n option.\n"); dw_printf ("\n"); dw_printf ("Example: gen_packets -o x.wav \n"); dw_printf ("\n"); diff --git a/src/gen_tone.c b/src/gen_tone.c index 3317aa32..6a816556 100644 --- a/src/gen_tone.c +++ b/src/gen_tone.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2014, 2015, 2016, 2019 John Langner, WB2OSZ +// Copyright (C) 2011, 2014, 2015, 2016, 2019, 2023 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -70,6 +70,7 @@ static int ticks_per_sample[MAX_CHANS]; /* Same for both channels of same soundc static int ticks_per_bit[MAX_CHANS]; static int f1_change_per_sample[MAX_CHANS]; static int f2_change_per_sample[MAX_CHANS]; +static float samples_per_symbol[MAX_CHANS]; static short sine_table[256]; @@ -198,8 +199,11 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets) ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / ((double)audio_config_p->achan[chan].baud * 0.5)) + 0.5); f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5); f2_change_per_sample[chan] = f1_change_per_sample[chan]; // Not used. + samples_per_symbol[chan] = 2. * (float)audio_config_p->adev[a].samples_per_sec / (float)audio_config_p->achan[chan].baud; tone_phase[chan] = PHASE_SHIFT_45; // Just to mimic first attempt. + // ??? Why? We are only concerned with the difference + // from one symbol to the next. break; case MODEM_8PSK: @@ -211,6 +215,7 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets) ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / ((double)audio_config_p->achan[chan].baud / 3.)) + 0.5); f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5); f2_change_per_sample[chan] = f1_change_per_sample[chan]; // Not used. + samples_per_symbol[chan] = 3. * (float)audio_config_p->adev[a].samples_per_sec / (float)audio_config_p->achan[chan].baud; break; case MODEM_BASEBAND: @@ -220,11 +225,23 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets) // Tone is half baud. ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->achan[chan].baud ) + 0.5); f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].baud * 0.5 * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5); + samples_per_symbol[chan] = (float)audio_config_p->adev[a].samples_per_sec / (float)audio_config_p->achan[chan].baud; + break; + + case MODEM_EAS: // EAS. + + // TODO: Proper fix would be to use float for baud, mark, space. + + ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / 520.833333333333 ) + 0.5); + samples_per_symbol[chan] = (int)((audio_config_p->adev[a].samples_per_sec / 520.83333) + 0.5); + f1_change_per_sample[chan] = (int) ((2083.33333333333 * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5); + f2_change_per_sample[chan] = (int) ((1562.5000000 * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5); break; default: // AFSK ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->achan[chan].baud ) + 0.5); + samples_per_symbol[chan] = (float)audio_config_p->adev[a].samples_per_sec / (float)audio_config_p->achan[chan].baud; f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5); f2_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].space_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5); break; @@ -285,9 +302,64 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets) * *--------------------------------------------------------------------*/ +// Interpolate between two values. +// My original approximation simply jumped between phases, producing a discontinuity, +// and increasing bandwidth. +// According to multiple sources, we should transition more gently. +// Below see see a rough approximation of: +// * A step function, immediately going to new value. +// * Linear interpoation. +// * Raised cosine. Square root of cosine is also mentioned. +// +// new - / -- +// | / / +// | / | +// | / / +// old ------- / -- +// step linear raised cosine +// +// Inputs are the old (previous value), new value, and a blending control +// 0 -> take old value +// 1 -> take new value. +// inbetween some sort of weighted average. + +static inline float interpol8 (float oldv, float newv, float bc) +{ + // Step function. + //return (newv); // 78 on 11/7 + + assert (bc >= 0); + assert (bc <= 1.1); + + if (bc < 0) return (oldv); + if (bc > 1) return (newv); + + // Linear interpolation, just for comparison. + //return (bc * newv + (1.0f - bc) * oldv); // 39 on 11/7 + + float rc = 0.5f * (cosf(bc * M_PI - M_PI) + 1.0f); + float rrc = bc >= 0.5f + ? 0.5f * (sqrtf(fabsf(cosf(bc * M_PI - M_PI))) + 1.0f) + : 0.5f * (-sqrtf(fabsf(cosf(bc * M_PI - M_PI))) + 1.0f); + + (void)rrc; + return (rc * newv + (1.0f - bc) * oldv); // 49 on 11/7 + //return (rrc * newv + (1.0f - bc) * oldv); // 55 on 11/7 +} + static const int gray2phase_v26[4] = {0, 1, 3, 2}; static const int gray2phase_v27[8] = {1, 0, 2, 3, 6, 7, 5, 4}; +// #define PSKIQ 1 // not ready for prime time yet. +#if PSKIQ +static int xmit_octant[MAX_CHANS]; // absolute phase in 45 degree units. +static int xmit_prev_octant[MAX_CHANS]; // from previous symbol. + +// For PSK, we generate the final signal by combining fixed frequency cosine and +// sine by the following weights. +static const float ci[8] = { 1, .7071, 0, -.7071, -1, -.7071, 0, .7071 }; +static const float sq[8] = { 0, .7071, 1, .7071, 0, -.7071, -1, -.7071 }; +#endif void tone_gen_put_bit (int chan, int dat) { @@ -324,14 +396,28 @@ void tone_gen_put_bit (int chan, int dat) // All zero bits should give us steady 1800 Hz. // All one bits should flip phase by 180 degrees each time. + // For V.26B, add another 45 degrees. + // This seems to work a little better. dibit = (save_bit[chan] << 1) | dat; - symbol = gray2phase_v26[dibit]; + symbol = gray2phase_v26[dibit]; // 0 .. 3 for QPSK. +#if PSKIQ + // One phase shift unit is 45 degrees. + // Remember what it was last time and calculate new. + // values 0 .. 7. + xmit_prev_octant[chan] = xmit_octant[chan]; + xmit_octant[chan] += symbol * 2; + if (save_audio_config_p->achan[chan].v26_alternative == V26_B) { + xmit_octant[chan] += 1; + } + xmit_octant[chan] &= 0x7; +#else tone_phase[chan] += symbol * PHASE_SHIFT_90; if (save_audio_config_p->achan[chan].v26_alternative == V26_B) { tone_phase[chan] += PHASE_SHIFT_45; } +#endif bit_count[chan]++; } @@ -370,7 +456,9 @@ void tone_gen_put_bit (int chan, int dat) lfsr[chan] = (lfsr[chan] << 1) | (x & 1); dat = x; } - +#if PSKIQ + int blend = 1; +#endif do { /* until enough audio samples for this symbol. */ int sam; @@ -395,9 +483,58 @@ void tone_gen_put_bit (int chan, int dat) gen_tone_put_sample (chan, a, sam); break; + case MODEM_EAS: + + tone_phase[chan] += dat ? f1_change_per_sample[chan] : f2_change_per_sample[chan]; + sam = sine_table[(tone_phase[chan] >> 24) & 0xff]; + gen_tone_put_sample (chan, a, sam); + break; + case MODEM_QPSK: - case MODEM_8PSK: +#if DEBUG2 + text_color_set(DW_COLOR_DEBUG); + dw_printf ("tone_gen_put_bit %d PSK\n", __LINE__); +#endif + tone_phase[chan] += f1_change_per_sample[chan]; +#if PSKIQ +#if 1 // blend JWL + // remove loop invariant + float old_i = ci[xmit_prev_octant[chan]]; + float old_q = sq[xmit_prev_octant[chan]]; + + float new_i = ci[xmit_octant[chan]]; + float new_q = sq[xmit_octant[chan]]; + + float b = blend / samples_per_symbol[chan]; // roughly 0 to 1 + blend++; + // b = (b - 0.5) * 20 + 0.5; + // if (b < 0) b = 0; + // if (b > 1) b = 1; + // b = b > 0.5; + //b = 1; // 78 decoded with this. + // only 39 without. + + + //float blended_i = new_i * b + old_i * (1.0f - b); + //float blended_q = new_q * b + old_q * (1.0f - b); + + float blended_i = interpol8 (old_i, new_i, b); + float blended_q = interpol8 (old_q, new_q, b); + + sam = blended_i * sine_table[((tone_phase[chan] - PHASE_SHIFT_90) >> 24) & 0xff] + + blended_q * sine_table[(tone_phase[chan] >> 24) & 0xff]; +#else // jump + sam = ci[xmit_octant[chan]] * sine_table[((tone_phase[chan] - PHASE_SHIFT_90) >> 24) & 0xff] + + sq[xmit_octant[chan]] * sine_table[(tone_phase[chan] >> 24) & 0xff]; +#endif +#else + sam = sine_table[(tone_phase[chan] >> 24) & 0xff]; +#endif + gen_tone_put_sample (chan, a, sam); + break; + + case MODEM_8PSK: #if DEBUG2 text_color_set(DW_COLOR_DEBUG); dw_printf ("tone_gen_put_bit %d PSK\n", __LINE__); @@ -521,6 +658,20 @@ void gen_tone_put_sample (int chan, int a, int sam) { } } +void gen_tone_put_quiet_ms (int chan, int time_ms) { + + int a = ACHAN2ADEV(chan); /* device for channel. */ + int sam = 0; + + int nsamples = (int) ((time_ms * (float)save_audio_config_p->adev[a].samples_per_sec / 1000.) + 0.5); + + for (int j=0; j>= 1; + } +} + +int eas_send (int chan, unsigned char *str, int repeat, int txdelay, int txtail) +{ + int bytes_sent = 0; + const int gap = 1000; + int gaps_sent = 0; + + gen_tone_put_quiet_ms (chan, txdelay); + + for (int r=0; r Date: Tue, 2 May 2023 02:27:15 +0100 Subject: [PATCH 04/36] Remove capability to download tocalls.txt, etc. --- data/CMakeLists.txt | 85 +++++++++------------------------------------ 1 file changed, 16 insertions(+), 69 deletions(-) diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index 9f7c40e4..7972cc23 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -1,4 +1,19 @@ # +# Update: 1 May 2023 (still 1.7 dev version) +# +# The original intention was to allow an easy way to download the most +# recent versions of some files. +# +# "update-data" would only work once. +# +# These locations are no longer being maintained: +# http://www.aprs.org/aprs11/tocalls.txt -- 14 Dec 2021 +# http://www.aprs.org/symbols/symbols-new.txt -- 17 Mar 2021 +# http://www.aprs.org/symbols/symbolsX.txt -- 25 Nov 2015 +# so there is no reason to provide a capability grab the latest version. +# +# Rather than fixing an obsolete capability, it will just be removed. +# # The destination field is often used to identify the manufacturer/model. # These are not hardcoded into Dire Wolf. Instead they are read from # a file called tocalls.txt at application start up time. @@ -6,24 +21,13 @@ # The original permanent symbols are built in but the "new" symbols, # using overlays, are often updated. These are also read from files. # -# You can obtain an updated copy by typing "make data-update". -# This is not part of the normal build process. You have to do this explicitly. -# -# The locations below appear to be the most recent. -# The copy at http://www.aprs.org/tocalls.txt is out of date. -# + include(ExternalProject) set(TOCALLS_TXT "tocalls.txt") -set(TOCALLS_TXT_BKP "tocalls.txt.old") -set(TOCALLS_URL "http://www.aprs.org/aprs11/tocalls.txt") set(SYMBOLS-NEW_TXT "symbols-new.txt") -set(SYMBOLS-NEW_TXT_BKP "symbols-new.txt.old") -set(SYMBOLS-NEW_URL "http://www.aprs.org/symbols/symbols-new.txt") set(SYMBOLSX_TXT "symbolsX.txt") -set(SYMBOLSX_TXT_BKP "symbolsX.txt.old") -set(SYMBOLSX_URL "http://www.aprs.org/symbols/symbolsX.txt") set(CUSTOM_BINARY_DATA_DIR "${CMAKE_BINARY_DIR}/data") # we can also move to a separate cmake file and use file(download) @@ -32,63 +36,6 @@ file(COPY "${CUSTOM_DATA_DIR}/${TOCALLS_TXT}" DESTINATION "${CUSTOM_BINARY_DATA_ file(COPY "${CUSTOM_DATA_DIR}/${SYMBOLS-NEW_TXT}" DESTINATION "${CUSTOM_BINARY_DATA_DIR}") file(COPY "${CUSTOM_DATA_DIR}/${SYMBOLSX_TXT}" DESTINATION "${CUSTOM_BINARY_DATA_DIR}") -add_custom_target(data_rename - COMMAND ${CMAKE_COMMAND} -E rename "${CUSTOM_BINARY_DATA_DIR}/${TOCALLS_TXT}" "${CUSTOM_BINARY_DATA_DIR}/${TOCALLS_TXT_BKP}" - COMMAND ${CMAKE_COMMAND} -E rename "${CUSTOM_BINARY_DATA_DIR}/${SYMBOLS-NEW_TXT}" "${CUSTOM_BINARY_DATA_DIR}/${SYMBOLS-NEW_TXT_BKP}" - COMMAND ${CMAKE_COMMAND} -E rename "${CUSTOM_BINARY_DATA_DIR}/${SYMBOLSX_TXT}" "${CUSTOM_BINARY_DATA_DIR}/${SYMBOLSX_TXT_BKP}" - ) - -ExternalProject_Add(download_tocalls - DEPENDS data_rename - URL ${TOCALLS_URL} - PREFIX "" - DOWNLOAD_DIR "${CUSTOM_BINARY_DATA_DIR}" - DOWNLOAD_NAME "${TOCALLS_TXT}" - DOWNLOAD_NO_EXTRACT 0 - EXCLUDE_FROM_ALL 1 - UPDATE_COMMAND "" - PATCH_COMMAND "" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" - ) - -ExternalProject_Add(download_symbols-new - DEPENDS data_rename - URL ${SYMBOLS-NEW_URL} - PREFIX "" - DOWNLOAD_DIR "${CUSTOM_BINARY_DATA_DIR}" - DOWNLOAD_NAME "${SYMBOLS-NEW_TXT}" - DOWNLOAD_NO_EXTRACT 0 - EXCLUDE_FROM_ALL 1 - UPDATE_COMMAND "" - PATCH_COMMAND "" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" - ) - -ExternalProject_Add(download_symbolsx - DEPENDS data_rename - URL ${SYMBOLSX_URL} - PREFIX "" - DOWNLOAD_DIR "${CUSTOM_BINARY_DATA_DIR}" - DOWNLOAD_NAME "${SYMBOLSX_TXT}" - DOWNLOAD_NO_EXTRACT 0 - EXCLUDE_FROM_ALL 1 - UPDATE_COMMAND "" - PATCH_COMMAND "" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" - ) - -add_custom_target(update-data) -add_dependencies(update-data data_rename download_tocalls download_symbols-new download_symbolsx) - install(FILES "${CUSTOM_BINARY_DATA_DIR}/${TOCALLS_TXT}" DESTINATION ${INSTALL_DATA_DIR}) install(FILES "${CUSTOM_BINARY_DATA_DIR}/${SYMBOLS-NEW_TXT}" DESTINATION ${INSTALL_DATA_DIR}) install(FILES "${CUSTOM_BINARY_DATA_DIR}/${SYMBOLSX_TXT}" DESTINATION ${INSTALL_DATA_DIR}) From 8000e46c02e7abde1b90af9993d86dc12a9a4938 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Thu, 4 May 2023 18:56:09 +0100 Subject: [PATCH 05/36] Issue 427 - callsign order for AGW protocol 'Y'. --- src/ax25_link.c | 78 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 10 deletions(-) diff --git a/src/ax25_link.c b/src/ax25_link.c index ab2875d9..25d66089 100644 --- a/src/ax25_link.c +++ b/src/ax25_link.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2016, 2017, 2018 John Langner, WB2OSZ +// Copyright (C) 2016, 2017, 2018, 2023 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -1580,13 +1580,49 @@ void dl_unregister_callsign (dlq_item_t *E) * - Incoming connected data, from application still in the queue. * - I frames which have been transmitted but not yet acknowledged. * + * Confusion: https://github.com/wb2osz/direwolf/issues/427 + * + * There are different, inconsistent versions of the protocol spec. + * + * One of them simply has: + * + * CallFrom is our call + * CallTo is the call of the other station + * + * A more detailed version has the same thing in the table of fields: + * + * CallFrom 10 bytes Our CallSign + * CallTo 10 bytes Other CallSign + * + * (My first implementation went with that.) + * + * HOWEVER, shortly after that, is contradictory information: + * + * Careful must be exercised to fill correctly both the CallFrom + * and CallTo fields to match the ones of an existing connection, + * otherwise AGWPE won’t return any information at all from this query. + * + * The order of the CallFrom and CallTo is not trivial, it should + * reflect the order used to start the connection, so + * + * * If we started the connection CallFrom=US and CallTo=THEM + * * If the other end started the connection CallFrom=THEM and CallTo=US + * + * This seems to make everything unnecessarily more complicated. + * We should only care about the stream going from the local station to the + * remote station. Why would it matter who reqested the link? The state + * machine doesn't even contain this information so the TNC doesn't know. + * The client app interface needs to behave differently for the two cases. + * + * The new code, below, May 2023, should handle both of those cases. + * *------------------------------------------------------------------------------*/ void dl_outstanding_frames_request (dlq_item_t *E) { ax25_dlsm_t *S; - int ok_to_create = 0; // must exist already. - + const int ok_to_create = 0; // must exist already. + int reversed_addrs = 0; if (s_debug_client_app) { text_color_set(DW_COLOR_DEBUG); @@ -1594,12 +1630,28 @@ void dl_outstanding_frames_request (dlq_item_t *E) } S = get_link_handle (E->addrs, E->num_addr, E->chan, E->client, ok_to_create); - - if (S == NULL) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Can't get outstanding frames for %s -> %s, chan %d\n", E->addrs[OWNCALL], E->addrs[PEERCALL], E->chan); - server_outstanding_frames_reply (E->chan, E->client, E->addrs[OWNCALL], E->addrs[PEERCALL], 0); - return; + if (S != NULL) { + int reversed_addrs = 1; + } + else { + // Try swapping the addresses. + // this is communicating with the client app, not over the air, + // so we don't need to worry about digipeaters. + + char swapped[AX25_MAX_REPEATERS][AX25_MAX_ADDR_LEN]; + memset (swapped, 0, sizeof(swapped)); + strlcpy (swapped[PEERCALL], E->addrs[OWNCALL], sizeof(swapped[PEERCALL])); + strlcpy (swapped[OWNCALL], E->addrs[PEERCALL], sizeof(swapped[OWNCALL])); + S = get_link_handle (swapped, E->num_addr, E->chan, E->client, ok_to_create); + if (S != NULL) { + int reversed_addrs = 1; + } + else { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Can't get outstanding frames for %s -> %s, chan %d\n", E->addrs[OWNCALL], E->addrs[PEERCALL], E->chan); + server_outstanding_frames_reply (E->chan, E->client, E->addrs[OWNCALL], E->addrs[PEERCALL], 0); + return; + } } // Add up these @@ -1628,7 +1680,13 @@ void dl_outstanding_frames_request (dlq_item_t *E) } } - server_outstanding_frames_reply (S->chan, S->client, S->addrs[OWNCALL], S->addrs[PEERCALL], count1 + count2); + if (reversed_addrs) { + // Other end initiated the link. + server_outstanding_frames_reply (S->chan, S->client, S->addrs[PEERCALL], S->addrs[OWNCALL], count1 + count2); + } + else { + server_outstanding_frames_reply (S->chan, S->client, S->addrs[OWNCALL], S->addrs[PEERCALL], count1 + count2); + } } // end dl_outstanding_frames_request From 8e32286604cf6d0798a7966828cfb7f597d9b8cb Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sun, 7 May 2023 02:38:00 +0100 Subject: [PATCH 06/36] Issue 275 - AGW 'd' would not abort a connect attempt in progress. --- src/ax25_link.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/ax25_link.c b/src/ax25_link.c index 25d66089..52d7c157 100644 --- a/src/ax25_link.c +++ b/src/ax25_link.c @@ -1075,12 +1075,31 @@ void dl_disconnect_request (dlq_item_t *E) case state_1_awaiting_connection: case state_5_awaiting_v22_connection: -// TODO: "requeue." Not sure what to do here. -// If we put it back in the queue we will get it back again probably still in same state. -// Need a way to defer it until the next state change. +// Erratum: The protocol spec says "requeue." If we put disconnect req back in the +// queue we will probably get it back again here while still in same state. +// I don't think we would want to delay it until the next state transition. +// Suppose someone tried to connect to another station, which is not responding, and decided to cancel +// before all of the SABMe retries were used up. I think we would want to transmit a DISC, send a disc +// notice to the user, and go directly into disconnected state, rather than into awaiting release. + +// New code v1.7 dev, May 6 2023 + + text_color_set(DW_COLOR_INFO); + dw_printf ("Stream %d: In progress connection attempt to %s terminated by user.\n", S->stream_id, S->addrs[PEERCALL]); + discard_i_queue (S); + SET_RC(0); + int p1 = 1; + int nopid0 = 0; + packet_t pp15 = ax25_u_frame (S->addrs, S->num_addr, cr_cmd, frame_type_U_DISC, p1, nopid0, NULL, 0); + lm_data_request (S->chan, TQ_PRIO_1_LO, pp15); + + STOP_T1; // started in establish_data_link. + STOP_T3; // probably don't need. + enter_new_state (S, state_0_disconnected, __func__, __LINE__); + server_link_terminated (S->chan, S->client, S->addrs[PEERCALL], S->addrs[OWNCALL], 0); break; - + case state_2_awaiting_release: { // We have previously started the disconnect sequence and are waiting @@ -1631,7 +1650,7 @@ void dl_outstanding_frames_request (dlq_item_t *E) S = get_link_handle (E->addrs, E->num_addr, E->chan, E->client, ok_to_create); if (S != NULL) { - int reversed_addrs = 1; + reversed_addrs = 0; } else { // Try swapping the addresses. @@ -1644,7 +1663,7 @@ void dl_outstanding_frames_request (dlq_item_t *E) strlcpy (swapped[OWNCALL], E->addrs[PEERCALL], sizeof(swapped[OWNCALL])); S = get_link_handle (swapped, E->num_addr, E->chan, E->client, ok_to_create); if (S != NULL) { - int reversed_addrs = 1; + reversed_addrs = 1; } else { text_color_set(DW_COLOR_ERROR); From b1727345e0175543242e7f6edb665beba5e8c2ef Mon Sep 17 00:00:00 2001 From: wb2osz Date: Mon, 8 May 2023 02:27:59 +0100 Subject: [PATCH 07/36] Issue 401 - Avoid receiving own transmission due to audio crosstalk. --- src/demod.c | 21 +++++++++++++++++++-- src/demod.h | 5 ++++- src/ptt.c | 18 +++++++++++++++++- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/demod.c b/src/demod.c index 9f94dd8d..4ed28425 100644 --- a/src/demod.c +++ b/src/demod.c @@ -852,7 +852,6 @@ int demod_init (struct audio_s *pa) #define FSK_READ_ERR (256*256) - __attribute__((hot)) int demod_get_sample (int a) { @@ -862,7 +861,6 @@ int demod_get_sample (int a) assert (save_audio_config_p->adev[a].bits_per_sample == 8 || save_audio_config_p->adev[a].bits_per_sample == 16); - if (save_audio_config_p->adev[a].bits_per_sample == 8) { x1 = audio_get(a); @@ -929,6 +927,21 @@ int demod_get_sample (int a) * *--------------------------------------------------------------------*/ +static int mute_input[MAX_CHANS]; + +// New in 1.7. +// A few people have a really bad audio cross talk situation where they receive their own transmissions. +// It usually doesn't cause a problem but it is confusing to look at. +// "half duplex" setting applied only to the transmit logic. i.e. wait for clear channel before sending. +// Receiving was still active. +// I think the simplest solution is to mute/unmute the audio input at this point if not full duplex. +// This is called from ptt_set for half duplex. + +void demod_mute_input (int chan, int mute_during_xmit) +{ + assert (chan >= 0 && chan < MAX_CHANS); + mute_input[chan] = mute_during_xmit; +} __attribute__((hot)) void demod_process_sample (int chan, int subchan, int sam) @@ -942,6 +955,10 @@ void demod_process_sample (int chan, int subchan, int sam) assert (chan >= 0 && chan < MAX_CHANS); assert (subchan >= 0 && subchan < MAX_SUBCHANS); + if (mute_input[chan]) { + sam = 0; + }; + D = &demodulator_state[chan][subchan]; diff --git a/src/demod.h b/src/demod.h index 3233b9ba..f1120cd0 100644 --- a/src/demod.h +++ b/src/demod.h @@ -8,10 +8,13 @@ int demod_init (struct audio_s *pa); +void demod_mute_input (int chan, int mute); + int demod_get_sample (int a); void demod_process_sample (int chan, int subchan, int sam); void demod_print_agc (int chan, int subchan); -alevel_t demod_get_audio_level (int chan, int subchan); \ No newline at end of file +alevel_t demod_get_audio_level (int chan, int subchan); + diff --git a/src/ptt.c b/src/ptt.c index 3afef5e4..fdeff420 100644 --- a/src/ptt.c +++ b/src/ptt.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2013, 2014, 2015, 2016, 2017 John Langner, WB2OSZ +// Copyright (C) 2011, 2013, 2014, 2015, 2016, 2017, 2023 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -176,6 +176,7 @@ typedef int HANDLE; #include "audio.h" #include "ptt.h" #include "dlq.h" +#include "demod.h" // to mute recv audio during xmit if half duplex. #if __WIN32__ @@ -1141,6 +1142,8 @@ void ptt_init (struct audio_s *audio_config_p) * *--------------------------------------------------------------------*/ +// JWL - save status and new get_ptt function. + void ptt_set (int ot, int chan, int ptt_signal) { @@ -1164,6 +1167,19 @@ void ptt_set (int ot, int chan, int ptt_signal) return; } +// New in 1.7. +// A few people have a really bad audio cross talk situation where they receive their own transmissions. +// It usually doesn't cause a problem but it is confusing to look at. +// "half duplex" setting applied only to the transmit logic. i.e. wait for clear channel before sending. +// Receiving was still active. +// I think the simplest solution is to mute/unmute the audio input at this point if not full duplex. + +#ifndef TEST + if ( ! save_audio_config_p->achan[chan].fulldup) { + demod_mute_input (chan, ptt_signal); + } +#endif + /* * The data link state machine has an interest in activity on the radio channel. * This is a very convenient place to get that information. From 24a06aef9e10caf2a10e38cd20e0f66e89487d7a Mon Sep 17 00:00:00 2001 From: wb2osz Date: Mon, 8 May 2023 03:23:26 +0100 Subject: [PATCH 08/36] 1.7 dev version G --- src/demod.c | 2 +- src/direwolf.c | 2 +- src/ptt.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/demod.c b/src/demod.c index 4ed28425..cc522271 100644 --- a/src/demod.c +++ b/src/demod.c @@ -927,7 +927,7 @@ int demod_get_sample (int a) * *--------------------------------------------------------------------*/ -static int mute_input[MAX_CHANS]; +static volatile int mute_input[MAX_CHANS]; // New in 1.7. // A few people have a really bad audio cross talk situation where they receive their own transmissions. diff --git a/src/direwolf.c b/src/direwolf.c index 6215e96b..0bf21167 100644 --- a/src/direwolf.c +++ b/src/direwolf.c @@ -301,7 +301,7 @@ int main (int argc, char *argv[]) text_color_init(t_opt); text_color_set(DW_COLOR_INFO); //dw_printf ("Dire Wolf version %d.%d (%s) Beta Test 4\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); - dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "F", __DATE__); + dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "G", __DATE__); //dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION); diff --git a/src/ptt.c b/src/ptt.c index fdeff420..d5e28160 100644 --- a/src/ptt.c +++ b/src/ptt.c @@ -1175,7 +1175,7 @@ void ptt_set (int ot, int chan, int ptt_signal) // I think the simplest solution is to mute/unmute the audio input at this point if not full duplex. #ifndef TEST - if ( ! save_audio_config_p->achan[chan].fulldup) { + if ( ot == OCTYPE_PTT && ! save_audio_config_p->achan[chan].fulldup) { demod_mute_input (chan, ptt_signal); } #endif From 92a2097d30d33d2b0294532179fdff429d381a57 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Thu, 11 May 2023 21:29:45 +0100 Subject: [PATCH 09/36] New warnings for gcc 11.3. --- src/ax25_link.c | 4 ++-- src/dlq.c | 11 +++++------ src/dlq.h | 4 ++-- src/server.c | 18 ++++++++++-------- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/ax25_link.c b/src/ax25_link.c index 52d7c157..05638a83 100644 --- a/src/ax25_link.c +++ b/src/ax25_link.c @@ -250,7 +250,7 @@ typedef struct ax25_dlsm_s { // notifications about state changes. - char addrs[AX25_MAX_REPEATERS][AX25_MAX_ADDR_LEN]; + char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN]; // Up to 10 addresses, same order as in frame. int num_addr; // Number of addresses. Should be in range 2 .. 10. @@ -1657,7 +1657,7 @@ void dl_outstanding_frames_request (dlq_item_t *E) // this is communicating with the client app, not over the air, // so we don't need to worry about digipeaters. - char swapped[AX25_MAX_REPEATERS][AX25_MAX_ADDR_LEN]; + char swapped[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN]; memset (swapped, 0, sizeof(swapped)); strlcpy (swapped[PEERCALL], E->addrs[OWNCALL], sizeof(swapped[PEERCALL])); strlcpy (swapped[OWNCALL], E->addrs[PEERCALL], sizeof(swapped[OWNCALL])); diff --git a/src/dlq.c b/src/dlq.c index 0d4b4a41..698df5a2 100644 --- a/src/dlq.c +++ b/src/dlq.c @@ -728,8 +728,7 @@ void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int n * * Purpose: Register callsigns that we will recognize for incoming connection requests. * - * Inputs: addrs - Source (owncall), destination (peercall), - * and possibly digipeaters. + * Inputs: addr - Callsign to [un]register. * * chan - Channel, 0 is first. * @@ -749,7 +748,7 @@ void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int n *--------------------------------------------------------------------*/ -void dlq_register_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client) +void dlq_register_callsign (char *addr, int chan, int client) { struct dlq_item_s *pnew; @@ -773,7 +772,7 @@ void dlq_register_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client) pnew->type = DLQ_REGISTER_CALLSIGN; pnew->chan = chan; - strlcpy (pnew->addrs[0], addr, AX25_MAX_ADDR_LEN); + strlcpy (pnew->addrs[0], addr, sizeof(pnew->addrs[0])); pnew->num_addr = 1; pnew->client = client; @@ -784,7 +783,7 @@ void dlq_register_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client) } /* end dlq_register_callsign */ -void dlq_unregister_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client) +void dlq_unregister_callsign (char *addr, int chan, int client) { struct dlq_item_s *pnew; @@ -808,7 +807,7 @@ void dlq_unregister_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client pnew->type = DLQ_UNREGISTER_CALLSIGN; pnew->chan = chan; - strlcpy (pnew->addrs[0], addr, AX25_MAX_ADDR_LEN); + strlcpy (pnew->addrs[0], addr, sizeof(pnew->addrs[0])); pnew->num_addr = 1; pnew->client = client; diff --git a/src/dlq.h b/src/dlq.h index 87716362..f07d3303 100644 --- a/src/dlq.h +++ b/src/dlq.h @@ -116,9 +116,9 @@ void dlq_outstanding_frames_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LE void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client, int pid, char *xdata_ptr, int xdata_len); -void dlq_register_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client); +void dlq_register_callsign (char *addr, int chan, int client); -void dlq_unregister_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client); +void dlq_unregister_callsign (char *addr, int chan, int client); void dlq_channel_busy (int chan, int activity, int status); diff --git a/src/server.c b/src/server.c index da20d0df..4faf7b75 100644 --- a/src/server.c +++ b/src/server.c @@ -1937,8 +1937,10 @@ static THREAD_F cmd_listen_thread (void *arg) case 'D': /* Send Connected Data */ { - char callsigns[2][AX25_MAX_ADDR_LEN]; - const int num_calls = 2; + char callsigns[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN]; + memset (callsigns, 0, sizeof(callsigns)); + const int num_calls = 2; // only first 2 used. Digipeater path + // must be remembered from connect request. strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE])); strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE])); @@ -1951,8 +1953,9 @@ static THREAD_F cmd_listen_thread (void *arg) case 'd': /* Disconnect, Terminate an AX.25 Connection */ { - char callsigns[2][AX25_MAX_ADDR_LEN]; - const int num_calls = 2; + char callsigns[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN]; + memset (callsigns, 0, sizeof(callsigns)); + const int num_calls = 2; // only first 2 used. strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE])); strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE])); @@ -2102,15 +2105,14 @@ static THREAD_F cmd_listen_thread (void *arg) { - char callsigns[2][AX25_MAX_ADDR_LEN]; - const int num_calls = 2; + char callsigns[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN]; + memset (callsigns, 0, sizeof(callsigns)); + const int num_calls = 2; // only first 2 used. strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE])); strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE])); - // Issue 169. Proper implementation for 'Y'. dlq_outstanding_frames_request (callsigns, num_calls, cmd.hdr.portx, client); - } break; From f8b9cae461032e23ea601e5fbe697734673cfb9a Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sun, 14 May 2023 23:25:02 +0100 Subject: [PATCH 10/36] Improved error messages. --- src/config.c | 16 +++++++++++++--- src/direwolf.c | 13 +++++++------ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/config.c b/src/config.c index fdaa143f..de84b047 100644 --- a/src/config.c +++ b/src/config.c @@ -709,6 +709,14 @@ static char *split (char *string, int rest_of_line) * *--------------------------------------------------------------------*/ +static void rtfm() +{ + text_color_set(DW_COLOR_ERROR); + dw_printf ("See online documentation:\n"); + dw_printf (" stable release: https://github.com/wb2osz/direwolf/tree/master/doc\n"); + dw_printf (" development version: https://github.com/wb2osz/direwolf/tree/dev/doc\n"); + dw_printf (" additional topics: https://github.com/wb2osz/direwolf-doc\n"); +} void config_init (char *fname, struct audio_s *p_audio_config, struct digi_config_s *p_digi_config, @@ -970,7 +978,8 @@ void config_init (char *fname, struct audio_s *p_audio_config, text_color_set(DW_COLOR_ERROR); dw_printf ("ERROR - Could not open config file %s\n", filepath); dw_printf ("Try using -c command line option for alternate location.\n"); - return; + rtfm(); + exit(EXIT_FAILURE); } dw_printf ("\nReading config file %s\n", filepath); @@ -1027,7 +1036,8 @@ void config_init (char *fname, struct audio_s *p_audio_config, if (t == NULL) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: Missing name of audio device for ADEVICE command on line %d.\n", line); - continue; + rtfm(); + exit(EXIT_FAILURE); } p_audio_config->adev[adevice].defined = 1; @@ -1986,7 +1996,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, dw_printf ("Config file line %d: %s with CM108 is only available when USB Audio GPIO support is enabled.\n", line, otname); dw_printf ("You must rebuild direwolf with CM108 Audio Adapter GPIO PTT support.\n"); dw_printf ("See Interface Guide for details.\n"); - + rtfm(); exit (EXIT_FAILURE); #endif } diff --git a/src/direwolf.c b/src/direwolf.c index 0bf21167..1a92968a 100644 --- a/src/direwolf.c +++ b/src/direwolf.c @@ -138,7 +138,7 @@ static BOOL cleanup_win (int); static void cleanup_linux (int); #endif -static void usage (char **argv); +static void usage (); #if defined(__SSE__) && !defined(__APPLE__) @@ -598,7 +598,7 @@ int main (int argc, char *argv[]) case '?': /* For '?' unknown option message was already printed. */ - usage (argv); + usage (); break; case 'd': /* Set debug option. */ @@ -742,7 +742,7 @@ int main (int argc, char *argv[]) /* Should not be here. */ text_color_set(DW_COLOR_DEBUG); dw_printf("?? getopt returned character code 0%o ??\n", c); - usage (argv); + usage (); } } /* end while(1) for options */ @@ -987,6 +987,7 @@ int main (int argc, char *argv[]) text_color_set(DW_COLOR_ERROR); dw_printf ("Pointless to continue without audio device.\n"); SLEEP_SEC(5); + usage (); exit (1); } @@ -1722,16 +1723,16 @@ static void usage (char **argv) dw_printf ("\n"); #if __WIN32__ - dw_printf ("Complete documentation can be found in the 'doc' folder\n"); + dw_printf ("Documentation can be found in the 'doc' folder\n"); #else // TODO: Could vary by platform and build options. - dw_printf ("Complete documentation can be found in /usr/local/share/doc/direwolf\n"); + dw_printf ("Documentation can be found in /usr/local/share/doc/direwolf\n"); #endif dw_printf ("or online at https://github.com/wb2osz/direwolf/tree/master/doc\n"); + dw_printf ("additional topics: https://github.com/wb2osz/direwolf-doc\n"); text_color_set(DW_COLOR_INFO); exit (EXIT_FAILURE); } - /* end direwolf.c */ From 577b2b3cf2640377c3a4e2fb791c7bb29fefcb41 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sun, 14 May 2023 23:27:45 +0100 Subject: [PATCH 11/36] Automated test case for EAS SAME. --- test/CMakeLists.txt | 8 ++++++++ test/scripts/check-modemeas | 4 ++++ 2 files changed, 12 insertions(+) create mode 100644 test/scripts/check-modemeas diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a95cbaf1..91e06a2c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -26,6 +26,7 @@ set(TEST_CHECK-MODEM2400-a_FILE "check-modem2400-a") set(TEST_CHECK-MODEM2400-b_FILE "check-modem2400-b") set(TEST_CHECK-MODEM2400-g_FILE "check-modem2400-g") set(TEST_CHECK-MODEM4800_FILE "check-modem4800") +set(TEST_CHECK-MODEMEAS_FILE "check-modemeas") # generate the scripts that run the tests @@ -101,6 +102,12 @@ configure_file( @ONLY ) +configure_file( + "${CUSTOM_TEST_SCRIPTS_DIR}/${TEST_CHECK-MODEMEAS_FILE}" + "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEMEAS_FILE}${CUSTOM_SCRIPT_SUFFIX}" + @ONLY + ) + # global includes # not ideal but not so slow @@ -498,6 +505,7 @@ add_test(check-modem2400-a "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM2400-a_F add_test(check-modem2400-b "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM2400-b_FILE}${CUSTOM_SCRIPT_SUFFIX}") add_test(check-modem2400-g "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM2400-g_FILE}${CUSTOM_SCRIPT_SUFFIX}") add_test(check-modem4800 "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM4800_FILE}${CUSTOM_SCRIPT_SUFFIX}") +add_test(check-modemeas "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEMEAS_FILE}${CUSTOM_SCRIPT_SUFFIX}") diff --git a/test/scripts/check-modemeas b/test/scripts/check-modemeas new file mode 100644 index 00000000..c2a88539 --- /dev/null +++ b/test/scripts/check-modemeas @@ -0,0 +1,4 @@ +@CUSTOM_SHELL_SHABANG@ + +@GEN_PACKETS_BIN@ -B EAS -o testeas.wav +@ATEST_BIN@ -B EAS -L 6 -G 6 testeas.wav From b1b3e854be21be31bd017ac89e0bd1197d5fdbc2 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sun, 14 May 2023 23:30:44 +0100 Subject: [PATCH 12/36] Remove Ubuntu 18 from automated test build. --- .github/workflows/ci.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82c129b0..0cc4d34d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,15 +80,7 @@ jobs: build_type: 'Release', cmake_extra_flags: '' } - - { - name: 'Ubuntu 18.04', - os: ubuntu-18.04, - cc: 'gcc', - cxx: 'g++', - arch: 'x86_64', - build_type: 'Release', - cmake_extra_flags: '' - } + steps: - name: checkout uses: actions/checkout@v2 From 0058145eff7dcbdbaf2fc4e48da8ca230bdb0730 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sun, 14 May 2023 23:32:05 +0100 Subject: [PATCH 13/36] Minor typo. --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index ba28d1d2..e860f991 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,7 +15,7 @@ - Improved Layer 2 Protocol [(IL2P)](https://en.wikipedia.org/wiki/FX.25_Forward_Error_Correction). Use "-I 1" on command line to enable transmit for first channel. Compatible with Nino TNC for 1200 and 9600 bps. -- Limited support for CM109/CM119 GPIO PTT on Windows. +- Limited support for CM108/CM119 GPIO PTT on Windows. - Dire Wolf now advertises itself using DNS Service Discovery. This allows suitable APRS / Packet Radio applications to find a network KISS TNC without knowing the IP address or TCP port. Thanks to Hessu for providing this. Currently available only for Linux and Mac OSX. [Read all about it here.](https://github.com/hessu/aprs-specs/blob/master/TCP-KISS-DNS-SD.md) From e53fa0c1106c67e8929042d1cea67002f99f4979 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sun, 14 May 2023 23:33:06 +0100 Subject: [PATCH 14/36] Fix Windows 11 build. --- external/regex/regex.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/external/regex/regex.h b/external/regex/regex.h index 52b5fede..a84f6a99 100644 --- a/external/regex/regex.h +++ b/external/regex/regex.h @@ -208,7 +208,8 @@ typedef unsigned long int reg_syntax_t; some interfaces). When a regexp is compiled, the syntax used is stored in the pattern buffer, so changing this does not affect already-compiled regexps. */ -REGEX_VARIABLE_IMPEXP reg_syntax_t re_syntax_options; +//REGEX_VARIABLE_IMPEXP reg_syntax_t re_syntax_options; +extern reg_syntax_t re_syntax_options; /* Define combinations of the above bits for the standard possibilities. (The [[[ comments delimit what gets put into the Texinfo file, so From a3d3143d21d74abf3c130f6a6b5c179477826d9e Mon Sep 17 00:00:00 2001 From: wb2osz Date: Mon, 15 May 2023 00:45:02 +0100 Subject: [PATCH 15/36] +x permission --- test/scripts/check-modemeas | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 test/scripts/check-modemeas diff --git a/test/scripts/check-modemeas b/test/scripts/check-modemeas old mode 100644 new mode 100755 From 415a08da1eadd69125e21264c3d223ecb79cd33f Mon Sep 17 00:00:00 2001 From: wb2osz Date: Thu, 18 May 2023 00:17:39 +0100 Subject: [PATCH 16/36] Add AIOC to list for cm108 ptt. --- src/cm108.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/cm108.c b/src/cm108.c index 8c8fc5ed..ff3ff792 100644 --- a/src/cm108.c +++ b/src/cm108.c @@ -34,6 +34,7 @@ * We have a few commercial products: * * DINAH https://hamprojects.info/dinah/ + * PAUL https://hamprojects.info/paul/ * DMK URI http://www.dmkeng.com/URI_Order_Page.htm * RB-USB RIM http://www.repeater-builder.com/products/usb-rim-lite.html * RA-35 http://www.masterscommunications.com/products/radio-adapter/ra35.html @@ -93,6 +94,12 @@ * with a single USB Audio Adapter, but does not automatically handle the multiple device case. * Manual configuration needs to be used in this case. * + * Here is something new and interesting. The All in One cable (AIOC). + * https://github.com/skuep/AIOC/tree/master + * + * A microcontroller is used to emulate a CM108-compatible soundcard + * and a serial port. It fits right on the side of a Bao Feng or similar. + * *---------------------------------------------------------------*/ #include "direwolf.h" @@ -178,6 +185,11 @@ static int cm108_write (char *name, int iomask, int iodata); #define SSS_PID2 0x1607 #define SSS_PID3 0x160b +// https://github.com/skuep/AIOC/blob/master/stm32/aioc-fw/Src/usb_descriptors.h + +#define AIOC_VID 0x1209 +#define AIOC_PID 0x7388 + // Device VID PID Number of GPIO // ------ --- --- -------------- @@ -217,7 +229,9 @@ static int cm108_write (char *name, int iomask, int iodata); || p == CMEDIA_PID_CM119A \ || p == CMEDIA_PID_CM119B )) \ || \ - (v == SSS_VID && (p == SSS_PID1 || p == SSS_PID2 || p == SSS_PID3)) ) + (v == SSS_VID && (p == SSS_PID1 || p == SSS_PID2 || p == SSS_PID3)) \ + || \ + (v == AIOC_VID && p == AIOC_PID) ) // Look out for null source pointer, and avoid buffer overflow on destination. @@ -243,6 +257,12 @@ static void substr_se (char *dest, const char *src, int start, int endp1) #endif +// Maximum length of name for PTT HID. +// For Linux, this was originally 17 to handle names like /dev/hidraw3. +// Windows has more complicated names. The longest I saw was 95 but longer have been reported. + +#define MAXX_HIDRAW_NAME_LEN 128 + /* * Result of taking inventory of USB soundcards and USB HIDs. */ @@ -258,7 +278,8 @@ struct thing_s { // Oversized to silence a compiler warning. char plughw2[72]; // With name rather than number. char devpath[128]; // Kernel dev path. Does not include /sys mount point. - char devnode_hidraw[128]; // e.g. /dev/hidraw3 - for Linux - was length 17 + char devnode_hidraw[MAXX_HIDRAW_NAME_LEN]; + // e.g. /dev/hidraw3 - for Linux - was length 17 // The Windows path for a HID looks like this, lengths up to 95 seen. // \\?\hid#vid_0d8c&pid_000c&mi_03#8&164d11c9&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} char devnode_usb[25]; // e.g. /dev/bus/usb/001/012 @@ -852,7 +873,7 @@ void cm108_find_ptt (char *output_audio_device, char *ptt_device, int ptt_devic * * Errors: A descriptive error message will be printed for any problem. * - * Future: For our initial implementation we are making the simplifying + * Shortcut: For our initial implementation we are making the simplifying * restriction of using only one GPIO pin per device and limit * configuration to PTT only. * Longer term, we might want to have DCD, and maybe other @@ -882,7 +903,6 @@ int cm108_set_gpio_pin (char *name, int num, int state) iomask = 1 << (num - 1); // 0=input, 1=output iodata = state << (num - 1); // 0=low, 1=high - return (cm108_write (name, iomask, iodata)); } /* end cm108_set_gpio_pin */ From d6ae84daad853d87e37ddb7eb364d4cfe5cbc0d4 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sun, 21 May 2023 21:27:48 +0100 Subject: [PATCH 17/36] Set AIOC HID permission. --- conf/99-direwolf-cmedia.rules | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/conf/99-direwolf-cmedia.rules b/conf/99-direwolf-cmedia.rules index 24e7c160..94e1828f 100644 --- a/conf/99-direwolf-cmedia.rules +++ b/conf/99-direwolf-cmedia.rules @@ -28,3 +28,9 @@ SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0d8c", GROUP="audio", MODE="0660" # # Read the User Guide and run the "cm108" application for more information. # + +# +# Same thing for the "All In One Cable." +# + +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="7388", GROUP="audio", MODE="0660" From 4008153334407dfb9082485666dc3f2bca560c2d Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sun, 21 May 2023 21:32:54 +0100 Subject: [PATCH 18/36] Silently ignore AGW protocol application login. --- src/server.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/server.c b/src/server.c index 4faf7b75..6b41af25 100644 --- a/src/server.c +++ b/src/server.c @@ -1816,6 +1816,11 @@ static THREAD_F cmd_listen_thread (void *arg) break; + case 'P': /* Application Login */ + + // Silently ignore it. + break; + case 'X': /* Register CallSign */ { From f9cf42b2918f4c0bf245dff03945b3e1407f7f28 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sat, 3 Jun 2023 20:24:30 +0100 Subject: [PATCH 19/36] Better interpretation of bulletin identifiers. --- src/decode_aprs.c | 53 +++++++++++++++++++++++++++++++++++++++++++---- src/decode_aprs.h | 2 ++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/decode_aprs.c b/src/decode_aprs.c index 8a729428..cf628bb9 100644 --- a/src/decode_aprs.c +++ b/src/decode_aprs.c @@ -1628,10 +1628,15 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int * It's a lot more complicated with different types of addressees * and replies with acknowledgement or rejection. * - * There is even a special case for telemetry metadata. + * Is it an elegant generalization to lump all of these special cases + * together or was it a big mistake that will cause confusion and incorrect + * implementations? The decision to put telemetry metadata here is baffling. * * - * Cases: :xxxxxxxxx:PARM. Telemetry metadata, parameter name + * Cases: :BLNxxxxxx: ... Bulletin. + * :NWSxxxxxx: ... National Weather Service Bulletin. + * + * :xxxxxxxxx:PARM. Telemetry metadata, parameter name * :xxxxxxxxx:UNIT. Telemetry metadata, unit/label * :xxxxxxxxx:EQNS. Telemetry metadata, Equation Coefficients * :xxxxxxxxx:BITS. Telemetry metadata, Bit Sense/Project Name @@ -1736,6 +1741,46 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q strlcpy (A->g_addressee, addressee, sizeof(A->g_addressee)); +/* + * Addressee starting with BLN or NWS is a bulletin. + */ + if (strlen(addressee) >= 3 && strncmp(addressee,"BLN",3) == 0) { + + // Interpret 3 cases of identifiers. + // BLN9 "general bulletin" has a single digit. + // BLNX "announcement" has a single uppercase letter. + // BLN9xxxxx "group bulletin" has single digit group id and group name up to 5 characters. + + if (strlen(addressee) == 4 && isdigit(addressee[3])) { + snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "General Bulletin with identifier \"%s\"", addressee+3); + } + else if (strlen(addressee) == 4 && isupper(addressee[3])) { + snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Announcement with identifier \"%s\"", addressee+3); + } + if (strlen(addressee) >=5 && isdigit(addressee[3])) { + snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Group Bulletin with identifier \"%c\", group name \"%s\"", addressee[3], addressee+4); + } + else { + // Not one of the official formats. + snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Bulletin with identifier \"%s\"", addressee+3); + } + A->g_message_subtype = message_subtype_bulletin; + strlcpy (A->g_comment, p->message, sizeof(A->g_comment)); + } + + else if (strlen(addressee) >= 3 && strncmp(addressee,"NWS",3) == 0) { + // NWS-xxxxx + + if (strlen(addressee) >=4 && addressee[3] == '-') { + snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "NWS bulletin with identifier \"%s\"", addressee+4); + } + else { + snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "NWS bulletin with identifier \"%s\", missing - after NWS", addressee+3); + } + A->g_message_subtype = message_subtype_nws; + strlcpy (A->g_comment, p->message, sizeof(A->g_comment)); + } + /* * Special message formats contain telemetry metadata. @@ -1748,7 +1793,7 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q * Why not use other characters after the "T" for metadata? */ - if (strncmp(p->message,"PARM.",5) == 0) { + else if (strncmp(p->message,"PARM.",5) == 0) { snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Parameter Name Message for \"%s\"", addressee); A->g_message_subtype = message_subtype_telem_parm; telemetry_name_message (addressee, p->message+5); @@ -1847,7 +1892,7 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q // X>Y:}A>B::WA1XYX-15:Howdy y'all{toolong else { - // Look for message number. + // Normal messaage case. Look for message number. char *pno = strchr(p->message, '{'); if (pno != NULL) { strlcpy (A->g_message_number, pno+1, sizeof(A->g_message_number)); diff --git a/src/decode_aprs.h b/src/decode_aprs.h index f25d1e91..e9d5b443 100644 --- a/src/decode_aprs.h +++ b/src/decode_aprs.h @@ -70,6 +70,8 @@ typedef struct decode_aprs_s { message_subtype_message, message_subtype_ack, message_subtype_rej, + message_subtype_bulletin, + message_subtype_nws, message_subtype_telem_parm, message_subtype_telem_unit, message_subtype_telem_eqns, From e84a6225151b260c0a87c790e5461fcfca6a7348 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Wed, 14 Jun 2023 01:37:08 +0100 Subject: [PATCH 20/36] Look past third party header for packet filtering. --- CHANGES.md | 8 ++ src/decode_aprs.c | 209 +++++++++++++++++++++++++++++++++----- src/decode_aprs.h | 21 +++- src/igate.c | 62 ++++++++++-- src/mheard.c | 50 ++++++++- src/pfilter.c | 252 ++++++++++++++++++++++------------------------ 6 files changed, 436 insertions(+), 166 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e860f991..d842b19a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -30,6 +30,14 @@ > Add: "FX25TX 1" (or 16 or 32 or 64) + +### Bugs Fixed: ### + +- The t/m packet filter incorrectly included bulletins. It now allows only "messages" to specific stations. Use of t/m is discouraged. i/180 is the preferred filter for messages to users recently heard locally. + +- Packet filtering now skips over any third party header before classifying packet types. + + ## Version 1.6 -- October 2020 ## ### New Build Procedure: ### diff --git a/src/decode_aprs.c b/src/decode_aprs.c index cf628bb9..fc6443e9 100644 --- a/src/decode_aprs.c +++ b/src/decode_aprs.c @@ -169,7 +169,9 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr //dw_printf ("DEBUG decode_aprs info=\"%s\"\n", pinfo); - memset (A, 0, sizeof (*A)); + if (third_party_src == NULL) { + memset (A, 0, sizeof (*A)); + } A->g_quiet = quiet; @@ -234,6 +236,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr memcpy(payload_src, (char*)pinfo+1, sizeof(payload_src)-1); char *q = strchr(payload_src, '>'); if (q != NULL) *q = '\0'; + A->g_has_thirdparty_header = 1; decode_aprs (A, pp_payload, quiet, payload_src); // 1 means used recursively ax25_delete (pp_payload); return; @@ -318,6 +321,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr { aprs_ll_pos (A, pinfo, info_len); } + A->g_packet_type = packet_type_position; break; @@ -330,10 +334,12 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr if (strncmp((char*)pinfo, "$ULTW", 5) == 0) { aprs_ultimeter (A, (char*)pinfo, info_len); // TODO: produce obsolete error. + A->g_packet_type = packet_type_weather; } else { aprs_raw_nmea (A, pinfo, info_len); + A->g_packet_type = packet_type_position; } break; @@ -341,17 +347,20 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr case '`': /* Current Mic-E Data (not used in TM-D700) */ aprs_mic_e (A, pp, pinfo, info_len); + A->g_packet_type = packet_type_position; break; case ')': /* Item. */ aprs_item (A, pinfo, info_len); + A->g_packet_type = packet_type_item; break; case '/': /* Position with timestamp (no APRS messaging) */ case '@': /* Position with timestamp (with APRS messaging) */ aprs_ll_pos_time (A, pinfo, info_len); + A->g_packet_type = packet_type_position; break; @@ -360,42 +369,76 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr /* Telemetry metadata. */ aprs_message (A, pinfo, info_len, quiet); + + switch (A->g_message_subtype) { + case message_subtype_message: + case message_subtype_ack: + case message_subtype_rej: + A->g_packet_type = packet_type_message; + break; + + case message_subtype_nws: + A->g_packet_type = packet_type_nws; + break; + + case message_subtype_bulletin: + default: + break; + + case message_subtype_telem_parm: + case message_subtype_telem_unit: + case message_subtype_telem_eqns: + case message_subtype_telem_bits: + A->g_packet_type = packet_type_telemetry; + break; + + case message_subtype_directed_query: + A->g_packet_type = packet_type_query; + break; + } break; case ';': /* Object */ aprs_object (A, pinfo, info_len); + A->g_packet_type = packet_type_object; break; case '<': /* Station Capabilities */ aprs_station_capabilities (A, (char*)pinfo, info_len); + A->g_packet_type = packet_type_capabilities; break; case '>': /* Status Report */ aprs_status_report (A, (char*)pinfo, info_len); + A->g_packet_type = packet_type_status; break; case '?': /* General Query */ aprs_general_query (A, (char*)pinfo, info_len, quiet); + A->g_packet_type = packet_type_query; break; case 'T': /* Telemetry */ aprs_telemetry (A, (char*)pinfo, info_len, quiet); + A->g_packet_type = packet_type_telemetry; break; case '_': /* Positionless Weather Report */ aprs_positionless_weather_report (A, pinfo, info_len); + A->g_packet_type = packet_type_weather; break; case '{': /* user defined data */ aprs_user_defined (A, (char*)pinfo, info_len); + A->g_packet_type = packet_type_userdefined; break; case 't': /* Raw touch tone data - NOT PART OF STANDARD */ @@ -404,6 +447,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr /* Might move into user defined data, above. */ aprs_raw_touch_tone (A, (char*)pinfo, info_len); + // no packet type for t/ filter break; case 'm': /* Morse Code data - NOT PART OF STANDARD */ @@ -413,6 +457,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr /* Might move into user defined data, above. */ aprs_morse_code (A, (char*)pinfo, info_len); + // no packet type for t/ filter break; //case '}': /* third party header */ @@ -1091,7 +1136,9 @@ static void aprs_raw_nmea (decode_aprs_t *A, unsigned char *info, int ilen) * * Function: aprs_mic_e * - * Purpose: Decode MIC-E (also Kenwood D7 & D700) packet. + * Purpose: Decode MIC-E (e.g. Kenwood D7 & D700) packet. + * This format is an overzelous quest to make the packet as short as possible. + * It uses non-printable characters and hacks wrapped in kludges. * * Inputs: info - Pointer to Information field. * ilen - Information field length. @@ -1100,31 +1147,120 @@ static void aprs_raw_nmea (decode_aprs_t *A, unsigned char *info, int ilen) * * Description: * - * Destination Address Field - + * AX.25 Destination Address Field - * - * The 7-byte Destination Address field contains + * The 6-byte Destination Address field contains * the following encoded information: * - * * The 6 latitude digits. - * * A 3-bit Mic-E message identifier, specifying one of 7 Standard Mic-E - * Message Codes or one of 7 Custom Message Codes or an Emergency - * Message Code. - * * The North/South and West/East Indicators. - * * The Longitude Offset Indicator. - * * The generic APRS digipeater path code. - * + * Byte 1: Lat digit 1, message bit A + * Byte 2: Lat digit 2, message bit B + * Byte 3: Lat digit 3, message bit C + * Byte 4: Lat digit 4, N/S lat indicator + * Byte 5: Lat digit 5, Longitude offset + * Byte 6: Lat digit 6, W/E Long indicator + * * * "Although the destination address appears to be quite unconventional, it is * still a valid AX.25 address, consisting only of printable 7-bit ASCII values." * - * References: Mic-E TYPE CODES -- http://www.aprs.org/aprs12/mic-e-types.txt + * AX.25 Information Field - Starts with ' or ` + * + * Bytes 1,2,3: Longitude + * Bytes 4,5,6: Speed and Course + * Byte 6: Symbol code + * Byte 7: Symbol Table ID + * + * The rest of it is a complicated comment field which can hold various information + * and must be intrepreted in a particular order. At this point we look for any + * prefix and/or suffix to identify the equipment type. + * + * References: Mic-E TYPE CODES -- http://www.aprs.org/aprs12/mic-e-types.txt + * Mic-E TEST EXAMPLES -- http://www.aprs.org/aprs12/mic-e-examples.txt + * + * Next, we have what Addedum 1.2 calls the "type byte." This prefix can be + * space Original MIC-E. + * > Kenwood HT. + * ] Kenwood Mobile. + * none. + * + * We also need to look at the last byte or two + * for a possible suffix to distinguish equipment types. Examples: + * >...... is D7 + * >......= is D72 + * >......^ is D74 + * + * For other brands, it gets worse. There might a 2 character suffix. + * The prefix indicates whether messaging-capable. Examples: + * `....._.% Yaesu FTM-400DR + * `......_) Yaesu FTM-100D + * `......_3 Yaesu FT5D + * + * '......|3 Byonics TinyTrack3 + * '......|4 Byonics TinyTrack4 * - * This is up to date with the 24 Aug 16 version mentioning the TH-D74. + * Any prefix and suffix must be removed before futher processsing. + * + * Pick one: MIC-E Telemetry Data or "Status Text" (called a comment everywhere else). + * + * If the character after the symbol table id is "," (comma) or 0x1d, we have telemetry. + * (Is this obsoleted by the base-91 telemetry?) + * + * ` Two 2-character hexadecimal numbers. (Channels 1 & 3) + * ' Five 2-character hexadecimal numbers. + * + * Anything left over is a comment which can contain various types of information. + * + * If present, the MIC-E compressed altitude must be first. + * It is three base-91 characters followed by "}". + * Examples: "4T} "4T} ]"4T} + * + * We can also have frequency specification -- http://www.aprs.org/info/freqspec.tx + * + * Warning: Some Kenwood radios add CR at the end, in apparent violation of the spec. + * Watch out so it doesn't get included when looking for equipment type suffix. * * Mic-E TEST EXAMPLES -- http://www.aprs.org/aprs12/mic-e-examples.txt * - * Examples: `b9Z!4y>/>"4N}Paul's_TH-D7 + * Examples: Observed on the air. + * + * KB1HNZ-9>TSSP5P,W1IMD,WIDE1,KQ1L-8,N3LLO-3,WIDE2*:`b6,l}#>/]"48}449.225MHz<0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff>=<0x0d> + * + * ` b6, l}# >/ ] "48} 449.225MHz ...... = <0x0d> + * mic-e long. cs sym prefix alt. freq comment suffix must-ignore + * Kenwood D710 + *--------------- + * + * N1JDU-9>ECCU8Y,W1MHL*,WIDE2-1:'cZ<0x7f>l#H>/]Go fly a kite!<0x0d> * - * TODO: Destination SSID can contain generic digipeater path. + * ' cZ<0x7f> l#H >/ ] ..... <0x0d> + * mic-e long. cs sym prefix comment no-suffix must-ignore + * Kenwood D700 + *--------------- + * + * KC1HHO-7>T2PX5R,WA1PLE-4,WIDE1*,WIDE2-1:`c_snp(k/`"4B}official relay station NTS_(<0x0d> + * + * ` c_s np( k/ ` "4B} ....... _( <0x0d> + * mic-e long. cs sym prefix alt comment suffix must-ignore + * FT2D + *--------------- + * + * N1CMD-12>T3PQ1Y,KA1GJU-3,WIDE1,WA1PLE-4*:`cP#l!Fk/'"7H}|!%&-']|!w`&!|3 + * + * ` cP# l!F k/ ' "7H} |!%&-']| !w`&! |3 + * mic-e long. cs sym prefix alt base91telemetry DAO suffix + * TinyTrack3 + *--------------- + * + * W1STJ-3>T2UR4X,WA1PLE-4,WIDE1*,WIDE2-1:`c@&l#.-/`"5,}146.685MHz T100 -060 146.520 Simplex or Voice Alert_%<0x0d> + * + * ` c@& l#. -/ ` "5,} 146.685MHz T100 -060 .............. _% <0x0d> + * mic-e long. cs sym prefix alt frequency-specification comment suffix must-ignore + * FTM-400DR + *--------------- + * + * + * + * + * TODO: Destination SSID can contain generic digipeater path. (?) * * Bugs: Doesn't handle ambiguous position. "space" treated as zero. * Invalid data results in a message but latitude is not set to unknown. @@ -1507,14 +1643,17 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int // This does not change very often but I'm wondering if we could parse // http://www.aprs.org/aprs12/mic-e-types.txt similar to how we use tocalls.txt. +// TODO: Use https://github.com/aprsorg/aprs-deviceid rather than hardcoding. + if (isT(*pfirst)) { // "legacy" formats. - + if (*pfirst == ' ' ) { strlcpy (A->g_mfr, "Original MIC-E", sizeof(A->g_mfr)); pfirst++; } else if (*pfirst == '>' && *plast == '=') { strlcpy (A->g_mfr, "Kenwood TH-D72", sizeof(A->g_mfr)); pfirst++; plast--; } else if (*pfirst == '>' && *plast == '^') { strlcpy (A->g_mfr, "Kenwood TH-D74", sizeof(A->g_mfr)); pfirst++; plast--; } + else if (*pfirst == '>' && *plast == '&') { strlcpy (A->g_mfr, "Kenwood TH-D75", sizeof(A->g_mfr)); pfirst++; plast--; } else if (*pfirst == '>' ) { strlcpy (A->g_mfr, "Kenwood TH-D7A", sizeof(A->g_mfr)); pfirst++; } else if (*pfirst == ']' && *plast == '=') { strlcpy (A->g_mfr, "Kenwood TM-D710", sizeof(A->g_mfr)); pfirst++; plast--; } @@ -1540,6 +1679,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int // ' should be used for trackers (not message capable). + else if (*pfirst == '\'' && *(plast-1) == '(' && *plast == '5') { strlcpy (A->g_mfr, "Anytone D578UV", sizeof(A->g_mfr)); pfirst++; plast-=2; } else if (*pfirst == '\'' && *(plast-1) == '(' && *plast == '8') { strlcpy (A->g_mfr, "Anytone D878UV", sizeof(A->g_mfr)); pfirst++; plast-=2; } else if (*pfirst == '\'' && *(plast-1) == '|' && *plast == '3') { strlcpy (A->g_mfr, "Byonics TinyTrack3", sizeof(A->g_mfr)); pfirst++; plast-=2; } @@ -1635,6 +1775,10 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int * * Cases: :BLNxxxxxx: ... Bulletin. * :NWSxxxxxx: ... National Weather Service Bulletin. + * http://www.aprs.org/APRS-docs/WX.TXT + * :SKYxxxxxx: ... Need reference. + * :CWAxxxxxx: ... Need reference. + * :BOMxxxxxx: ... Australian version. * * :xxxxxxxxx:PARM. Telemetry metadata, parameter name * :xxxxxxxxx:UNIT. Telemetry metadata, unit/label @@ -1768,19 +1912,36 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q strlcpy (A->g_comment, p->message, sizeof(A->g_comment)); } +#warning = double check. + + // Weather bulletins have addressee starting with NWS, SKY, CWA, or BOM. + // The protocol spec and http://www.aprs.org/APRS-docs/WX.TXT state that + // the 3 letter prefix must be followed by a dash. + // However, https://www.aprs-is.net/WX/ also lists the underscore + // alternative for the compressed format. Xastir implements this. + else if (strlen(addressee) >= 3 && strncmp(addressee,"NWS",3) == 0) { - // NWS-xxxxx if (strlen(addressee) >=4 && addressee[3] == '-') { - snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "NWS bulletin with identifier \"%s\"", addressee+4); + snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Weather bulletin with identifier \"%s\"", addressee+4); + } + else if (strlen(addressee) >=4 && addressee[3] == '_') { + snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Compressed Weather bulletin with identifier \"%s\"", addressee+4); } else { - snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "NWS bulletin with identifier \"%s\", missing - after NWS", addressee+3); + snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Weather bulletin is missing - or _ after %.3s", addressee); } A->g_message_subtype = message_subtype_nws; strlcpy (A->g_comment, p->message, sizeof(A->g_comment)); } + else if (strlen(addressee) >= 3 && (strncmp(addressee,"SKY",3) == 0 || strncmp(addressee,"CWA",3) == 0 || strncmp(addressee,"BOM",3) == 0)) { + // SKY... or CWA... https://www.aprs-is.net/WX/ + snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Weather bulletin with identifier \"%s\"", addressee+4); + A->g_message_subtype = message_subtype_nws; + strlcpy (A->g_comment, p->message, sizeof(A->g_comment)); + } + /* * Special message formats contain telemetry metadata. @@ -1794,22 +1955,22 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q */ else if (strncmp(p->message,"PARM.",5) == 0) { - snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Parameter Name Message for \"%s\"", addressee); + snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Parameter Name for \"%s\"", addressee); A->g_message_subtype = message_subtype_telem_parm; telemetry_name_message (addressee, p->message+5); } else if (strncmp(p->message,"UNIT.",5) == 0) { - snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Unit/Label Message for \"%s\"", addressee); + snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Unit/Label for \"%s\"", addressee); A->g_message_subtype = message_subtype_telem_unit; telemetry_unit_label_message (addressee, p->message+5); } else if (strncmp(p->message,"EQNS.",5) == 0) { - snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Equation Coefficients Message for \"%s\"", addressee); + snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Equation Coefficients for \"%s\"", addressee); A->g_message_subtype = message_subtype_telem_eqns; telemetry_coefficents_message (addressee, p->message+5, quiet); } else if (strncmp(p->message,"BITS.",5) == 0) { - snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Bit Sense/Project Name Message for \"%s\"", addressee); + snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Bit Sense/Project Name for \"%s\"", addressee); A->g_message_subtype = message_subtype_telem_bits; telemetry_bit_sense_message (addressee, p->message+5, quiet); } diff --git a/src/decode_aprs.h b/src/decode_aprs.h index e9d5b443..acaae2d6 100644 --- a/src/decode_aprs.h +++ b/src/decode_aprs.h @@ -24,7 +24,8 @@ typedef struct decode_aprs_s { int g_quiet; /* Suppress error messages when decoding. */ - char g_src[AX25_MAX_ADDR_LEN]; + char g_src[AX25_MAX_ADDR_LEN]; // In the case of a packet encapsulated by a 3rd party + // header, this is the encapsulated source. char g_dest[AX25_MAX_ADDR_LEN]; @@ -66,6 +67,24 @@ typedef struct decode_aprs_s { /* Also for Directed Station Query which is a */ /* special case of message. */ + // This is so pfilter.c:filt_t does not need to duplicate the same work. + + int g_has_thirdparty_header; + enum packet_type_e { + packet_type_none=0, + packet_type_position, + packet_type_weather, + packet_type_object, + packet_type_item, + packet_type_message, + packet_type_query, + packet_type_capabilities, + packet_type_status, + packet_type_telemetry, + packet_type_userdefined, + packet_type_nws + } g_packet_type; + enum message_subtype_e { message_subtype_invalid = 0, message_subtype_message, message_subtype_ack, diff --git a/src/igate.c b/src/igate.c index 8bdf109b..11f28963 100644 --- a/src/igate.c +++ b/src/igate.c @@ -1749,9 +1749,34 @@ static void * satgate_delay_thread (void *arg) * *--------------------------------------------------------------------*/ -// TODO: Use of "message" here is confusing because that term already -// has a special meaning for APRS. This could be an APRS message or -// some other APRS data type. Payload is already used. +#warning - clean up + +// It is unforunate that the : data type indicator (DTI) was overloaded with +// so many different meanings. Simply looking at the DTI is not adequate for +// determining whether a packet is a message. +// We need to exclude the other special cases of telemetry metadata, +// bulletins, and weather bulletins. + +static int is_message_message (char *infop) +{ + if (*infop != ':') return (0); + if (strlen(infop) < 11) return (0); // too short for : addressee : + if (strlen(infop) >= 16) { + if (strncmp(infop+10, ":PARM.", 6) == 0) return (0); + if (strncmp(infop+10, ":UNIT.", 6) == 0) return (0); + if (strncmp(infop+10, ":EQNS.", 6) == 0) return (0); + if (strncmp(infop+10, ":BITS.", 6) == 0) return (0); + } + if (strlen(infop) >= 4) { + if (strncmp(infop+1, "BLN", 3) == 0) return (0); + if (strncmp(infop+1, "NWS", 3) == 0) return (0); + if (strncmp(infop+1, "SKY", 3) == 0) return (0); + if (strncmp(infop+1, "CWA", 3) == 0) return (0); + if (strncmp(infop+1, "BOM", 3) == 0) return (0); + } + return (1); // message, including ack, rej +} + static void maybe_xmit_packet_from_igate (char *message, int to_chan) { @@ -1843,7 +1868,7 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan) * If we recently transmitted a 'message' from some station, * send the position of the message sender when it comes along later. * - * Some refer to this as a courtesy posit report but I don't + * Some refer to this as a "courtesy posit report" but I don't * think that is an official term. * * If we have a position report, look up the sender and see if we should @@ -1988,10 +2013,7 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan) #endif stats_rf_xmit_packets++; // Any type of packet. - // TEMP TEST: metadata temporarily allowed during testing. - - if (*pinfo == ':' && ! is_telem_metadata(pinfo)) { - // temp test // if (*pinfo == ':') { + if (is_message_message(pinfo)) { // We transmitted a "message." Telemetry metadata is excluded. // Remember to pass along address of the sender later. @@ -2449,6 +2471,26 @@ void ig_to_tx_remember (packet_t pp, int chan, int bydigi) } } + +#warning remove + +static int is_message_overload (char *infop) +{ + if (*infop != ':') return (0); + if (strlen(infop) < 16) return (0); + if (strncmp(infop+10, ":PARM.", 6) == 0) return (1); + if (strncmp(infop+10, ":UNIT.", 6) == 0) return (1); + if (strncmp(infop+10, ":EQNS.", 6) == 0) return (1); + if (strncmp(infop+10, ":BITS.", 6) == 0) return (1); + if (strncmp(infop+1, "BLN", 3) == 0) return (1); + if (strncmp(infop+1, "NWS", 3) == 0) return (1); + if (strncmp(infop+1, "SKY", 3) == 0) return (1); + if (strncmp(infop+1, "CWA", 3) == 0) return (1); + if (strncmp(infop+1, "BOM", 3) == 0) return (1); + return (0); +} + + static int ig_to_tx_allow (packet_t pp, int chan) { unsigned short crc = ax25_dedupe_crc(pp); @@ -2481,7 +2523,7 @@ static int ig_to_tx_allow (packet_t pp, int chan) /* We have a duplicate within some time period. */ - if (*pinfo == ':' && ! is_telem_metadata((char*)pinfo)) { + if (is_message_message((char*)pinfo)) { /* I think I want to avoid the duplicate suppression for "messages." */ /* Suppose we transmit a message from station X and it doesn't get an ack back. */ @@ -2530,7 +2572,7 @@ static int ig_to_tx_allow (packet_t pp, int chan) /* the normal limit for them. */ increase_limit = 1; - if (*pinfo == ':' && ! is_telem_metadata((char*)pinfo)) { + if (is_message_message((char*)pinfo)) { increase_limit = 3; } diff --git a/src/mheard.c b/src/mheard.c index 5c20f303..f11c68f0 100644 --- a/src/mheard.c +++ b/src/mheard.c @@ -336,6 +336,52 @@ void mheard_save_rf (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, r */ hops = ax25_get_heard(pp) - AX25_SOURCE; +/* + * Consider the following scenario: + * + * (1) We hear AA1PR-9 by a path of 4 digipeaters. + * Looking closer, it's probably only two because there are left over WIDE1-0 and WIDE2-0. + * + * Digipeater WIDE2 (probably N3LLO-3) audio level = 72(19/15) [NONE] _|||||___ + * [0.3] AA1PR-9>APY300,K1EQX-7,WIDE1,N3LLO-3,WIDE2*,ARISS::ANSRVR :cq hotg vt aprsthursday{01<0x0d> + * ----- ----- + * + * (2) APRS-IS sends a response to us. + * + * [ig>tx] ANSRVR>APWW11,KJ4ERJ-15*,TCPIP*,qAS,KJ4ERJ-15::AA1PR-9 :N:HOTG 161 Messages Sent{JL} + * + * (3) Here is our analysis of whether it should be sent to RF. + * + * Was message addressee AA1PR-9 heard in the past 180 minutes, with 2 or fewer digipeater hops? + * No, AA1PR-9 was last heard over the radio with 4 digipeater hops 0 minutes ago. + * + * The wrong hop count caused us to drop a packet that should have been transmitted. + * We could put in a hack to not count the "WIDEn-0" addresses. + * That is not correct because other prefixes could be used and we don't know + * what they are for other digipeaters. + * I think the best solution is to simply ignore the hop count. + * Maybe next release will have a major cleanup. + */ + + // HACK - Reduce hop count by number of used WIDEn-0 addresses. + + if (hops > 1) { + for (int k = 0; k < ax25_get_num_repeaters(pp); k++) { + char digi[AX25_MAX_ADDR_LEN]; + ax25_get_addr_no_ssid (pp, AX25_REPEATER_1 + k, digi); + int ssid = ax25_get_ssid (pp, AX25_REPEATER_1 + k); + int used = ax25_get_h (pp, AX25_REPEATER_1 + k); + + //text_color_set(DW_COLOR_DEBUG); + //dw_printf ("Examining %s-%d used=%d.\n", digi, ssid, used); + + if (used && strlen(digi) == 5 && strncmp(digi, "WIDE", 4) == 0 && isdigit(digi[4]) && ssid == 0) { + hops--; + //text_color_set(DW_COLOR_DEBUG); + //dw_printf ("Decrease hop count to %d for problematic %s.\n", hops, digi); + } + } + } mptr = mheard_ptr(source); if (mptr == NULL) { @@ -571,7 +617,7 @@ void mheard_save_is (char *ptext) * 8 for RF_CNT. * * time_limit - Include only stations heard within this many minutes. - * Typically 30 or 60. + * Typically 180. * * Returns: Number to be used in the statistics report. * @@ -649,7 +695,7 @@ int mheard_count (int max_hops, int time_limit) * callsign - Callsign for station. * * time_limit - Include only stations heard within this many minutes. - * Typically 30 or 60. + * Typically 180. * * max_hops - Include only stations heard with this number of * digipeater hops or less. For reporting, we might use: diff --git a/src/pfilter.c b/src/pfilter.c index 08922f52..b7ad63ee 100644 --- a/src/pfilter.c +++ b/src/pfilter.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2015, 2016 John Langner, WB2OSZ +// Copyright (C) 2015, 2016, 2023 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -546,7 +546,8 @@ static int parse_filter_spec (pfstate_t *pf) /* b - budlist */ else if (pf->token_str[0] == 'b' && ispunct(pf->token_str[1])) { - /* Budlist - source address */ + /* Budlist - AX.25 source address */ + /* Could be different than source encapsulated by 3rd party header. */ char addr[AX25_MAX_ADDR_LEN]; ax25_get_addr_with_ssid (pf->pp, AX25_SOURCE, addr); result = filt_bodgu (pf, addr); @@ -572,7 +573,7 @@ static int parse_filter_spec (pfstate_t *pf) else if (pf->token_str[0] == 'd' && ispunct(pf->token_str[1])) { int n; - // loop on all digipeaters + // Loop on all AX.25 digipeaters. result = 0; for (n = AX25_REPEATER_1; result == 0 && n < ax25_get_num_addr (pf->pp); n++) { // Consider only those with the H (has-been-used) bit set. @@ -599,7 +600,7 @@ static int parse_filter_spec (pfstate_t *pf) else if (pf->token_str[0] == 'v' && ispunct(pf->token_str[1])) { int n; - // loop on all digipeaters (mnemonic Via) + // loop on all AX.25 digipeaters (mnemonic Via) result = 0; for (n = AX25_REPEATER_1; result == 0 && n < ax25_get_num_addr (pf->pp); n++) { // This is different than the previous "d" filter. @@ -623,10 +624,15 @@ static int parse_filter_spec (pfstate_t *pf) } } -/* g - Addressee of message. */ +/* g - Addressee of message. e.g. "BLN*" for bulletins. */ else if (pf->token_str[0] == 'g' && ispunct(pf->token_str[1])) { - if (ax25_get_dti(pf->pp) == ':') { + if (pf->decoded.g_message_subtype == message_subtype_message || + pf->decoded.g_message_subtype == message_subtype_ack || + pf->decoded.g_message_subtype == message_subtype_rej || + pf->decoded.g_message_subtype == message_subtype_bulletin || + pf->decoded.g_message_subtype == message_subtype_nws || + pf->decoded.g_message_subtype == message_subtype_directed_query) { result = filt_bodgu (pf, pf->decoded.g_addressee); if (s_debug >= 2) { @@ -643,7 +649,7 @@ static int parse_filter_spec (pfstate_t *pf) } } -/* u - unproto (destination) */ +/* u - unproto (AX.25 destination) */ else if (pf->token_str[0] == 'u' && ispunct(pf->token_str[1])) { /* Probably want to exclude mic-e types */ @@ -668,7 +674,7 @@ static int parse_filter_spec (pfstate_t *pf) } } -/* t - type: position, weather, etc. */ +/* t - packet type: position, weather, telemetry, etc. */ else if (pf->token_str[0] == 't' && ispunct(pf->token_str[1])) { @@ -728,7 +734,7 @@ static int parse_filter_spec (pfstate_t *pf) (void) ax25_get_info (pf->pp, (unsigned char **)(&infop)); text_color_set(DW_COLOR_DEBUG); - if (*infop == ':' && ! is_telem_metadata(infop)) { + if (pf->decoded.g_packet_type == packet_type_message) { dw_printf (" %s returns %s for message to %s\n", pf->token_str, bool2text(result), pf->decoded.g_addressee); } else { @@ -832,31 +838,17 @@ static int filt_bodgu (pfstate_t *pf, char *arg) * 0 = no * -1 = error detected * - * Description: The filter is based the type filtering described here: + * Description: The filter is loosely based the type filtering described here: * http://www.aprs-is.net/javAPRSFilter.aspx * - * Most of these simply check the first byte of the information part. - * Trying to detect NWS information is a little trickier. + * Mostly use g_packet_type and g_message_subtype from decode_aprs. + * + * References: * http://www.aprs-is.net/WX/ - * http://wxsvr.aprs.net.au/protocol-new.html + * http://wxsvr.aprs.net.au/protocol-new.html (has disappeared) * *------------------------------------------------------------------------------*/ -/* Telemetry metadata is a special case of message. */ -/* We want to categorize it as telemetry rather than message. */ - -int is_telem_metadata (char *infop) -{ - if (*infop != ':') return (0); - if (strlen(infop) < 16) return (0); - if (strncmp(infop+10, ":PARM.", 6) == 0) return (1); - if (strncmp(infop+10, ":UNIT.", 6) == 0) return (1); - if (strncmp(infop+10, ":EQNS.", 6) == 0) return (1); - if (strncmp(infop+10, ":BITS.", 6) == 0) return (1); - return (0); -} - - static int filt_t (pfstate_t *pf) { char src[AX25_MAX_ADDR_LEN]; @@ -873,108 +865,60 @@ static int filt_t (pfstate_t *pf) switch (*f) { case 'p': /* Position */ - if (*infop == '!') return (1); - if (*infop == '/') return (1); - if (*infop == '=') return (1); - if (*infop == '@') return (1); - if (*infop == '\'') return (1); // MIC-E - if (*infop == '`') return (1); // MIC-E - - // What if we have "_" symbol code for weather? - // Still consider as position. - // The same packet can match more than one type here. + if (pf->decoded.g_packet_type == packet_type_position) return(1); break; case 'o': /* Object */ - if (*infop == ';') return (1); + if (pf->decoded.g_packet_type == packet_type_object) return(1); break; case 'i': /* Item */ - if (*infop == ')') return (1); + if (pf->decoded.g_packet_type == packet_type_item) return(1); break; - case 'm': /* Message */ - if (*infop == ':' && ! is_telem_metadata(infop)) return (1); + case 'm': // Any "message." + if (pf->decoded.g_packet_type == packet_type_message) return(1); break; case 'q': /* Query */ - if (*infop == '?') return (1); + if (pf->decoded.g_packet_type == packet_type_query) return(1); break; case 'c': /* station Capabilities - my extension */ /* Most often used for IGate statistics. */ - if (*infop == '<') return (1); + if (pf->decoded.g_packet_type == packet_type_capabilities) return(1); break; case 's': /* Status */ - if (*infop == '>') return (1); + if (pf->decoded.g_packet_type == packet_type_status) return(1); break; - case 't': /* Telemetry */ - if (*infop == 'T') return (1); - if (is_telem_metadata(infop)) return (1); + case 't': /* Telemetry data or metadata */ + if (pf->decoded.g_packet_type == packet_type_telemetry) return(1); break; case 'u': /* User-defined */ - if (*infop == '{') return (1); + if (pf->decoded.g_packet_type == packet_type_userdefined) return(1); break; - case 'h': /* third party Header - my extension */ - if (*infop == '}') return (1); + case 'h': /* has third party Header - my extension */ + if (pf->decoded.g_has_thirdparty_header) return (1); break; case 'w': /* Weather */ - if (*infop == '*') return (1); // Peet Bros - if (*infop == '_') return (1); // Weather report, no position. - if (strncmp(infop, "!!", 2) == 0) return(1); // Ultimeter 2000. - - /* '$' is normally raw GPS. Check for special case. */ - - if (strncmp(infop, "$ULTW", 5) == 0) return (1); - - /* Positions !=/@ with symbol code _ are weather. */ - - if (strchr("!=/@", *infop) != NULL && - pf->decoded.g_symbol_code == '_') return (1); + if (pf->decoded.g_packet_type == packet_type_weather) return(1); + /* Positions !=/@ with symbol code _ are weather. */ /* Object with _ symbol is also weather. APRS protocol spec page 66. */ + // Can't use *infop because it would not work with 3rd party header. - if (*infop == ';' && - pf->decoded.g_symbol_code == '_') return (1); - -// TODO: need more test cases at end for new weather cases. - + if ((pf->decoded.g_packet_type == packet_type_position || + pf->decoded.g_packet_type == packet_type_object) && pf->decoded.g_symbol_code == '_') return (1); break; case 'n': /* NWS format */ -/* - * This is the interesting case. - * The source must be exactly 6 upper case letters, no SSID. - */ - if (strlen(src) != 6) break; - if (! isupper(src[0])) break; - if (! isupper(src[1])) break; - if (! isupper(src[2])) break; - if (! isupper(src[3])) break; - if (! isupper(src[4])) break; - if (! isupper(src[5])) break; -/* - * We can have a "message" with addressee starting with NWS, SKY, or BOM (Australian version.) - */ - if (strncmp(infop, ":NWS", 4) == 0) return (1); - if (strncmp(infop, ":SKY", 4) == 0) return (1); - if (strncmp(infop, ":BOM", 4) == 0) return (1); -/* - * Or we can have an object. - * It's not exactly clear how to distinguish this from other objects. - * It looks like the first 3 characters of the source should be the same - * as the first 3 characters of the addressee. - */ - if (infop[0] == ';' && - infop[1] == src[0] && - infop[2] == src[1] && - infop[3] == src[2]) return (1); + if (pf->decoded.g_packet_type == packet_type_nws) return(1); break; default: @@ -990,6 +934,7 @@ static int filt_t (pfstate_t *pf) + /*------------------------------------------------------------------------------ * * Name: filt_r @@ -1327,8 +1272,36 @@ static int filt_s (pfstate_t *pf) * * IMHO, the rules here are too restrictive. * - * (1) The APRS-IS would send a "message" to my IGate only if the addressee + * The APRS-IS would send a "message" to my IGate only if the addressee * has been heard nearby recently. 180 minutes, I believe. + * Why would I not want to transmit it? + * + * Discussion: In retrospect, I think this is far too complicated. + * In a future release, I think at options other than time should be removed. + * Messages have more value than most packets. Why reduce the chance of successful delivery? + * + * Consider the following scenario: + * + * (1) We hear AA1PR-9 by a path of 4 digipeaters. + * Looking closer, it's probably only two because there are left over WIDE1-0 and WIDE2-0. + * + * Digipeater WIDE2 (probably N3LLO-3) audio level = 72(19/15) [NONE] _|||||___ + * [0.3] AA1PR-9>APY300,K1EQX-7,WIDE1,N3LLO-3,WIDE2*,ARISS::ANSRVR :cq hotg vt aprsthursday{01<0x0d> + * + * (2) APRS-IS sends a response to us. + * + * [ig>tx] ANSRVR>APWW11,KJ4ERJ-15*,TCPIP*,qAS,KJ4ERJ-15::AA1PR-9 :N:HOTG 161 Messages Sent{JL} + * + * (3) Here is our analysis of whether it should be sent to RF. + * + * Was message addressee AA1PR-9 heard in the past 180 minutes, with 2 or fewer digipeater hops? + * No, AA1PR-9 was last heard over the radio with 4 digipeater hops 0 minutes ago. + * + * The wrong hop count caused us to drop a packet that should have been transmitted. + * We could put in a hack to not count the "WIDE*-0" addresses. + * That is not correct because other prefixes could be used and we don't know + * what they are for other digipeaters. + * I think the best solution is to simply ignore the hop count. * *------------------------------------------------------------------------------*/ @@ -1357,9 +1330,9 @@ static int filt_i (pfstate_t *pf) double km = G_UNKNOWN; - char src[AX25_MAX_ADDR_LEN]; - char *infop = NULL; - int info_len; + //char src[AX25_MAX_ADDR_LEN]; + //char *infop = NULL; + //int info_len; //char *f; //char addressee[AX25_MAX_ADDR_LEN]; @@ -1433,20 +1406,7 @@ static int filt_i (pfstate_t *pf) * Get source address and info part. * Addressee has already been extracted into pf->decoded.g_addressee. */ - - memset (src, 0, sizeof(src)); - ax25_get_addr_with_ssid (pf->pp, AX25_SOURCE, src); - info_len = ax25_get_info (pf->pp, (unsigned char **)(&infop)); - - if (infop == NULL) return (0); - if (info_len < 1) return (0); - -// Determine packet type. We are interested only in "message." -// Telemetry metadata is not considered a message. - - if (*infop != ':') return (0); - if (is_telem_metadata(infop)) return (0); - + if (pf->decoded.g_packet_type != packet_type_message) return(0); #if defined(PFTEST) || defined(DIGITEST) // TODO: test functionality too, not just syntax. @@ -1486,7 +1446,7 @@ static int filt_i (pfstate_t *pf) * the past minute, rather than the usual 180 minutes for the addressee. */ - was_heard = mheard_was_recently_nearby ("source", src, 1, 0, G_UNKNOWN, G_UNKNOWN, G_UNKNOWN); + was_heard = mheard_was_recently_nearby ("source", pf->decoded.g_src, 1, 0, G_UNKNOWN, G_UNKNOWN, G_UNKNOWN); if (was_heard) return (0); @@ -1640,24 +1600,29 @@ int main () pftest (112, "t/t", "WM1X>APU25N:@210147z4235.39N/07106.58W_359/000g000t027r000P000p000h89b10234/WX REPORT {UIV32N}<0x0d>", 0); pftest (113, "t/w", "WM1X>APU25N:@210147z4235.39N/07106.58W_359/000g000t027r000P000p000h89b10234/WX REPORT {UIV32N}<0x0d>", 1); - /* Telemetry metadata is a special case of message. */ + /* Telemetry metadata should not be classified as message. */ pftest (114, "t/t", "KJ4SNT>APMI04::KJ4SNT :PARM.Vin,Rx1h,Dg1h,Eff1h,Rx10m,O1,O2,O3,O4,I1,I2,I3,I4", 1); pftest (115, "t/m", "KJ4SNT>APMI04::KJ4SNT :PARM.Vin,Rx1h,Dg1h,Eff1h,Rx10m,O1,O2,O3,O4,I1,I2,I3,I4", 0); pftest (116, "t/t", "KB1GKN-10>APRX27,UNCAN,WIDE1*:T#491,4.9,0.3,25.0,0.0,1.0,00000000", 1); + /* Bulletins should not be considered to be messages. Was bug in 1.6. */ + pftest (117, "t/m", "A>B::W1AW :test", 1); + pftest (118, "t/m", "A>B::BLN :test", 0); + pftest (119, "t/m", "A>B::NWS :test", 0); - pftest (120, "t/p", "CWAPID>APRS::NWS-TTTTT:DDHHMMz,ADVISETYPE,zcs{seq#", 0); + // https://www.aprs-is.net/WX/ + pftest (121, "t/p", "CWAPID>APRS::NWS-TTTTT:DDHHMMz,ADVISETYPE,zcs{seq#", 0); pftest (122, "t/p", "CWAPID>APRS::SKYCWA :DDHHMMz,ADVISETYPE,zcs{seq#", 0); pftest (123, "t/p", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", 0); pftest (124, "t/n", "CWAPID>APRS::NWS-TTTTT:DDHHMMz,ADVISETYPE,zcs{seq#", 1); pftest (125, "t/n", "CWAPID>APRS::SKYCWA :DDHHMMz,ADVISETYPE,zcs{seq#", 1); - pftest (126, "t/n", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", 1); + //pftest (126, "t/n", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", 1); pftest (127, "t/", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", 0); pftest (128, "t/c", "S0RCE>DEST:DEST:DEST:}thirdpartyheaderwhatever", 1); - pftest (131, "t/c", "S0RCE>DEST:}thirdpartyheaderwhatever", 0); + pftest (130, "t/h", "S0RCE>DEST:}WB2OSZ-5>APDW12,DIGI1,DIGI2*,DIGI3,DIGI4:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1); + pftest (131, "t/c", "S0RCE>DEST:}WB2OSZ-5>APDW12,DIGI1,DIGI2*,DIGI3,DIGI4:!4237.14NS07120.83W#PHG7140Chelmsford MA", 0); pftest (140, "r/42.6/-71.3/10", "WB2OSZ-5>APDW12,WIDE1-1,WIDE2-1:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1); pftest (141, "r/42.6/-71.3/10", "WA1PLE-5>APWW10,W1MHL,N8VIM,WIDE2*:@022301h4208.75N/07115.16WoAPRS-IS for Win32", 0); @@ -1712,13 +1677,13 @@ int main () pftest (203, "t/w t/w", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", -1); pftest (204, "r/42.6/-71.3", "WA1PLE-5>APWW10,W1MHL,N8VIM,WIDE2*:@022301h4208.75N/07115.16WoAPRS-IS for Win32", -1); - pftest (220, "i/30/8/42.6/-71.3/50", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1); - pftest (222, "i/30/8/42.6/-71.3/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1); - pftest (223, "i/30/8/42.6/-71.3", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1); - pftest (224, "i/30/8/42.6/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1); - pftest (225, "i/30/8/42.6", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1); - pftest (226, "i/30/8/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1); - pftest (227, "i/30/8", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1); + pftest (210, "i/30/8/42.6/-71.3/50", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1); + pftest (212, "i/30/8/42.6/-71.3/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1); + pftest (213, "i/30/8/42.6/-71.3", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1); + pftest (214, "i/30/8/42.6/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1); + pftest (215, "i/30/8/42.6", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1); + pftest (216, "i/30/8/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1); + pftest (217, "i/30/8", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1); // FIXME: behaves differently on Windows and Linux. Why? // On Windows we have our own version of strsep because it's not in the MS library. @@ -1726,7 +1691,12 @@ int main () //pftest (228, "i/30/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1); pftest (229, "i/30", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1); - pftest (230, "i/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1); + pftest (230, "i/30", "X>X:}WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1); + pftest (231, "i/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1); + + // Besure bulletins and telemetry metadata don't get included. + pftest (234, "i/30", "KJ4SNT>APMI04::KJ4SNT :PARM.Vin,Rx1h,Dg1h,Eff1h,Rx10m,O1,O2,O3,O4,I1,I2,I3,I4", 0); + pftest (235, "i/30", "A>B::BLN :test", 0); pftest (240, "s/", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1); pftest (241, "s/'/O/-/#/_", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1); @@ -1736,12 +1706,36 @@ int main () pftest (245, "s//", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1); pftest (246, "s///", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1); + // Third party header - done properly in 1.7. + // Packet filter t/h is no longer a mutually exclusive packet type. + // Now it is an independent attribute and the encapsulated part is evaluated. + + pftest (250, "o/home", "A>B:}WB2OSZ>APDW12,WIDE1-1,WIDE2-1:;home *111111z4237.14N/07120.83W-Chelmsford MA", 1); + pftest (251, "t/p", "A>B:}W1WRA-7>TRSY3T,WIDE1-1,WIDE2-1:`c-:l!hK\\>\"4b}=<0x0d>", 1); + pftest (252, "i/180", "A>B:}WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1); + pftest (253, "t/m", "A>B:}WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1); + pftest (254, "r/42.6/-71.3/10", "A>B:}WB2OSZ-5>APDW12,WIDE1-1,WIDE2-1:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1); + pftest (254, "r/42.6/-71.3/10", "A>B:}WA1PLE-5>APWW10,W1MHL,N8VIM,WIDE2*:@022301h4208.75N/07115.16WoAPRS-IS for Win32", 0); + pftest (255, "t/h", "KB1GKN-10>APRX27,UNCAN,WIDE1*:T#491,4.9,0.3,25.0,0.0,1.0,00000000", 0); + pftest (256, "t/h", "A>B:}KB1GKN-10>APRX27,UNCAN,WIDE1*:T#491,4.9,0.3,25.0,0.0,1.0,00000000", 1); + pftest (258, "t/t", "A>B:}KB1GKN-10>APRX27,UNCAN,WIDE1*:T#491,4.9,0.3,25.0,0.0,1.0,00000000", 1); + pftest (259, "t/t", "A>B:}KJ4SNT>APMI04::KJ4SNT :PARM.Vin,Rx1h,Dg1h,Eff1h,Rx10m,O1,O2,O3,O4,I1,I2,I3,I4", 1); + + pftest (270, "g/BLN*", "WB2OSZ>APDW17::BLN1xxxxx:bulletin text", 1); + pftest (271, "g/BLN*", "A>B:}WB2OSZ>APDW17::BLN1xxxxx:bulletin text", 1); + pftest (272, "g/BLN*", "A>B:}WB2OSZ>APDW17::W1AW :xxxx", 0); + + pftest (273, "g/NWS*", "WB2OSZ>APDW17::NWS-xxxxx:weather bulletin", 1); + pftest (274, "g/NWS*", "A>B:}WB2OSZ>APDW17::NWS-xxxxx:weather bulletin", 1); + pftest (275, "g/NWS*", "A>B:}WB2OSZ>APDW17::W1AW :xxxx", 0); + +// TODO: add b/ with 3rd party header. -// TODO: to be continued... +// TODO: to be continued... directed query ... if (error_count > 0) { text_color_set (DW_COLOR_ERROR); - dw_printf ("\nPacket Filtering Test - FAILED!\n"); + dw_printf ("\nPacket Filtering Test - FAILED! %d errors\n", error_count); exit (EXIT_FAILURE); } text_color_set (DW_COLOR_REC); From fed79a7978a7e11f193e6ee33b903613ac82cd5b Mon Sep 17 00:00:00 2001 From: wb2osz Date: Mon, 17 Jul 2023 02:28:16 +0100 Subject: [PATCH 21/36] Mention IL2P transmit for channels besides first. --- CHANGES.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index d842b19a..92975bf3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,7 +13,11 @@ - New variable speed option for gen_packets. For example, "-v 5,0.1" would generate packets from 5% too slow to 5% too fast with increments of 0.1. Some implementations might have imprecise timing. Use this to test how well TNCs tolerate sloppy timing. -- Improved Layer 2 Protocol [(IL2P)](https://en.wikipedia.org/wiki/FX.25_Forward_Error_Correction). Use "-I 1" on command line to enable transmit for first channel. Compatible with Nino TNC for 1200 and 9600 bps. +- Improved Layer 2 Protocol [(IL2P)](https://en.wikipedia.org/wiki/FX.25_Forward_Error_Correction). Compatible with Nino TNC for 1200 and 9600 bps. Use "-I 1" on command line to enable transmit for first channel. For more general case, add to config file (simplified version, see User Guide for more details): + + > After: "CHANNEL 1" (or other channel) + > + > Add: "IL2PTX 1" - Limited support for CM108/CM119 GPIO PTT on Windows. From c5ad945c3c15a65ecbe208b76b469edcd6e39cb7 Mon Sep 17 00:00:00 2001 From: Andrew Rodland Date: Mon, 17 Jul 2023 13:21:22 -0400 Subject: [PATCH 22/36] Fix IL2PTX config parsing (#483) We were checking `*t` for end of string instead of `*c`, but `t` never changes, so we would run off of the end of the buffer and output a very large number of config errors before eventually crashing. --- src/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.c b/src/config.c index de84b047..f3edc15a 100644 --- a/src/config.c +++ b/src/config.c @@ -2380,7 +2380,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, p_audio_config->achan[channel].il2p_invert_polarity = 0; while ((t = split(NULL,0)) != NULL) { - for (char *c = t; *t != '\0'; c++) { + for (char *c = t; *c != '\0'; c++) { switch (*c) { case '+': p_audio_config->achan[channel].il2p_invert_polarity = 0; From 9d2ded2eec7b9c71ae891356f9f35722b2cf574e Mon Sep 17 00:00:00 2001 From: wb2osz Date: Mon, 7 Aug 2023 23:47:48 +0100 Subject: [PATCH 23/36] Add TH-D75 and another seen on APRS Thursday. --- data/tocalls.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/tocalls.txt b/data/tocalls.txt index 692d6b40..169c9868 100644 --- a/data/tocalls.txt +++ b/data/tocalls.txt @@ -4,6 +4,7 @@ APRS TO-CALL VERSION NUMBERS 14 Dec 2021 WB4APR +07 Jun 23 Added APK005 for Kenwood TH-D75 14 Dec 21 Added APATAR ATA-R APRS Digipeater by TA7W/OH2UDS and TA6AEU 26 Sep 21 Added APRRDZ EPS32 https://github.com/dl9rdz/rdz_ttgo_sonde 18 Sep 21 Added APCSS for AMSAT Cubesat Simulator https://cubesatsim.org @@ -156,6 +157,7 @@ yourselves and keep me informed. APK APK0xx Kenwood TH-D7's APK003 Kenwood TH-D72 APK004 Kenwood TH-D74 + APK005 Kenwood TH-D75 APK1xx Kenwood D700's APK102 Kenwood D710 APKRAM KRAMstuff.com - Mark. G7LEU @@ -278,6 +280,7 @@ yourselves and keep me informed. APZ247 for UPRS NR0Q APZ0xx Xastir (old versions. See APX) APZMAJ Martyn M1MAJ DeLorme inReach Tracker + APZMDM github/codec2_talkie - product code not registered APZMDR for HaMDR trackers - hessu * hes.iki.fi] APZPAD Smart Palm APZTKP TrackPoint, Nick N0LP (Balloon tracking)(depricated) From 790c8ab72391c480e92e510d69c082faa6ecceff Mon Sep 17 00:00:00 2001 From: wb2osz Date: Mon, 7 Aug 2023 23:51:08 +0100 Subject: [PATCH 24/36] Additional documentation location. --- doc/README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/doc/README.md b/doc/README.md index 40aa77d6..9f44684e 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,4 +1,4 @@ -# Documentation for Dire Wolf # +# Documentation for Dire Wolf # Click on the document name to view in your web browser or the link following to download the PDF file. @@ -154,6 +154,14 @@ and a couple things that can be done about it. Here, we take a closer look at some of the frames on the TNC Test CD in hopes of gaining some insights into why some are easily decoded and others are more difficult. There are a lot of ugly signals out there. Many can be improved by decreasing the transmit volume. Others are just plain weird and you have to wonder how they are being generated. + +## Additional Documentation for Dire Wolf Software TNC # + + +When there was little documentation, it was all added to the source code repository [https://github.com/wb2osz/direwolf/tree/master/doc](https://github.com/wb2osz/direwolf/tree/master/doc) + +The growing number of documentation files and revisions are making the source code repository very large which means long download times. Additional documentation, not tied to a specific release, is now being added to [https://github.com/wb2osz/direwolf-doc](https://github.com/wb2osz/direwolf-doc) + ## Questions? Experiences to share? ## Here are some good places to ask questions and share your experiences: From 80bbf5a553e63494b5d115bca1cef07b77249eea Mon Sep 17 00:00:00 2001 From: wb2osz Date: Tue, 8 Aug 2023 00:02:44 +0100 Subject: [PATCH 25/36] FIX_BITS default to 0. --- src/audio.h | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/audio.h b/src/audio.h index 87d6c9c7..cb5ca94e 100644 --- a/src/audio.h +++ b/src/audio.h @@ -151,6 +151,17 @@ struct audio_s { struct achan_param_s { + // Currently, we have a fixed mapping from audio sources to channel. + // + // ADEVICE CHANNEL (mono) (stereo) + // 0 0 0, 1 + // 1 2 2, 3 + // 2 4 4, 5 + // + // A future feauture might allow the user to specify a different audio source. + // This would allow multiple modems (with associated channel) to share an audio source. + // int audio_source; // Default would be [0,1,2,3,4,5] + // What else should be moved out of structure and enlarged when NETTNC is implemented. ??? char mycall[AX25_MAX_ADDR_LEN]; /* Call associated with this radio channel. */ /* Could all be the same or different. */ @@ -415,7 +426,8 @@ struct audio_s { #define DEFAULT_BITS_PER_SAMPLE 16 -#define DEFAULT_FIX_BITS RETRY_INVERT_SINGLE +#define DEFAULT_FIX_BITS RETRY_NONE // Interesting research project but even a single bit fix up + // will occasionally let corrupted packets through. /* * Standard for AFSK on VHF FM. @@ -445,11 +457,11 @@ struct audio_s { */ #define DEFAULT_DWAIT 0 -#define DEFAULT_SLOTTIME 10 +#define DEFAULT_SLOTTIME 10 // *10mS = 100mS #define DEFAULT_PERSIST 63 -#define DEFAULT_TXDELAY 30 -#define DEFAULT_TXTAIL 10 -#define DEFAULT_FULLDUP 0 +#define DEFAULT_TXDELAY 30 // *10mS = 300mS +#define DEFAULT_TXTAIL 10 // *10mS = 100mS +#define DEFAULT_FULLDUP 0 // false = half duplex /* * Note that we have two versions of these in audio.c and audio_win.c. From dfc063f905213f41790e339b3159afbda72262ed Mon Sep 17 00:00:00 2001 From: wb2osz Date: Tue, 8 Aug 2023 00:14:47 +0100 Subject: [PATCH 26/36] Minor clarifications. --- src/decode_aprs.c | 32 +++++++++++++++++++------------- src/decode_aprs.h | 5 +++-- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/decode_aprs.c b/src/decode_aprs.c index fc6443e9..1d3ec1d2 100644 --- a/src/decode_aprs.c +++ b/src/decode_aprs.c @@ -17,6 +17,7 @@ // along with this program. If not, see . // +// TODO: Better error messages for examples here: http://lists.tapr.org/pipermail/aprssig_lists.tapr.org/2023-July/date.html /*------------------------------------------------------------------ * @@ -339,7 +340,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr else { aprs_raw_nmea (A, pinfo, info_len); - A->g_packet_type = packet_type_position; + A->g_packet_type = packet_type_position; } break; @@ -382,7 +383,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr break; case message_subtype_bulletin: - default: + default: break; case message_subtype_telem_parm: @@ -549,14 +550,16 @@ void decode_aprs_print (decode_aprs_t *A) { //dw_printf ("DEBUG decode_aprs_print stemp3=%s mfr=%s\n", stemp, A->g_mfr); if (strlen(A->g_mfr) > 0) { - if (strcmp(A->g_dest, "APRS") == 0 || strcmp(A->g_dest, "BEACON") == 0) { + if (strcmp(A->g_dest, "APRS") == 0 || + strcmp(A->g_dest, "BEACON") == 0 || + strcmp(A->g_dest, "ID") == 0) { strlcat (stemp, "\nUse of \"", sizeof(stemp)); strlcat (stemp, A->g_dest, sizeof(stemp)); strlcat (stemp, "\" in the destination field is obsolete.", sizeof(stemp)); strlcat (stemp, " You can help to improve the quality of APRS signals.", sizeof(stemp)); strlcat (stemp, "\nTell the sender (", sizeof(stemp)); strlcat (stemp, A->g_src, sizeof(stemp)); - strlcat (stemp, ") to use the proper product code from", sizeof(stemp)); + strlcat (stemp, ") to use the proper product identifier from", sizeof(stemp)); strlcat (stemp, " http://www.aprs.org/aprs11/tocalls.txt", sizeof(stemp)); } else { @@ -582,7 +585,7 @@ void decode_aprs_print (decode_aprs_t *A) { /* http://eng.usna.navy.mil/~bruninga/aprs/aprs11.html */ /* "The Antenna Gain in the PHG format on page 28 is in dBi." */ - snprintf (phg, sizeof(phg), ", %d W height=%d %ddBi %s", A->g_power, A->g_height, A->g_gain, A->g_directivity); + snprintf (phg, sizeof(phg), ", %d W height(HAAT)=%dft=%.0fm %ddBi %s", A->g_power, A->g_height, DW_FEET_TO_METERS(A->g_height), A->g_gain, A->g_directivity); strlcat (stemp, phg, sizeof(stemp)); } @@ -1147,7 +1150,7 @@ static void aprs_raw_nmea (decode_aprs_t *A, unsigned char *info, int ilen) * * Description: * - * AX.25 Destination Address Field - + * AX.25 Destination Address Field - * * The 6-byte Destination Address field contains * the following encoded information: @@ -1213,7 +1216,7 @@ static void aprs_raw_nmea (decode_aprs_t *A, unsigned char *info, int ilen) * It is three base-91 characters followed by "}". * Examples: "4T} "4T} ]"4T} * - * We can also have frequency specification -- http://www.aprs.org/info/freqspec.tx + * We can also have frequency specification -- http://www.aprs.org/info/freqspec.txt * * Warning: Some Kenwood radios add CR at the end, in apparent violation of the spec. * Watch out so it doesn't get included when looking for equipment type suffix. @@ -1247,7 +1250,7 @@ static void aprs_raw_nmea (decode_aprs_t *A, unsigned char *info, int ilen) * * ` cP# l!F k/ ' "7H} |!%&-']| !w`&! |3 * mic-e long. cs sym prefix alt base91telemetry DAO suffix - * TinyTrack3 + * TinyTrack3 *--------------- * * W1STJ-3>T2UR4X,WA1PLE-4,WIDE1*,WIDE2-1:`c@&l#.-/`"5,}146.685MHz T100 -060 146.520 Simplex or Voice Alert_%<0x0d> @@ -1912,7 +1915,6 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q strlcpy (A->g_comment, p->message, sizeof(A->g_comment)); } -#warning = double check. // Weather bulletins have addressee starting with NWS, SKY, CWA, or BOM. // The protocol spec and http://www.aprs.org/APRS-docs/WX.TXT state that @@ -1994,10 +1996,12 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q text_color_set(DW_COLOR_ERROR); dw_printf("ERROR: \"%s\" must be lower case \"ack\"\n", p->message); } - strlcpy (A->g_message_number, p->message + 3, sizeof(A->g_message_number)); - if (strlen(A->g_message_number) == 0) { + else { + strlcpy (A->g_message_number, p->message + 3, sizeof(A->g_message_number)); + if (strlen(A->g_message_number) == 0) { text_color_set(DW_COLOR_ERROR); dw_printf("ERROR: Message number is missing after \"ack\".\n"); + } } // Xastir puts a carriage return on the end. @@ -2018,10 +2022,12 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q text_color_set(DW_COLOR_ERROR); dw_printf("ERROR: \"%s\" must be lower case \"rej\"\n", p->message); } - strlcpy (A->g_message_number, p->message + 3, sizeof(A->g_message_number)); - if (strlen(A->g_message_number) == 0) { + else { + strlcpy (A->g_message_number, p->message + 3, sizeof(A->g_message_number)); + if (strlen(A->g_message_number) == 0) { text_color_set(DW_COLOR_ERROR); dw_printf("ERROR: Message number is missing after \"rej\".\n"); + } } // Xastir puts a carriage return on the end. diff --git a/src/decode_aprs.h b/src/decode_aprs.h index acaae2d6..94a9fd6b 100644 --- a/src/decode_aprs.h +++ b/src/decode_aprs.h @@ -111,8 +111,9 @@ typedef struct decode_aprs_s { int g_power; /* Transmitter power in watts. */ int g_height; /* Antenna height above average terrain, feet. */ + // TODO: rename to g_height_ft - int g_gain; /* Antenna gain in dB. */ + int g_gain; /* Antenna gain in dBi. */ char g_directivity[12]; /* Direction of max signal strength */ @@ -120,7 +121,7 @@ typedef struct decode_aprs_s { float g_altitude_ft; /* Feet above median sea level. */ /* I used feet here because the APRS specification */ - /* has units of feet for alititude. Meters would be */ + /* has units of feet for altitude. Meters would be */ /* more natural to the other 96% of the world. */ char g_mfr[80]; /* Manufacturer or application. */ From 2434e5f13be129b8aa6b7f100eeebbacbf461b55 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Tue, 8 Aug 2023 17:34:06 +0100 Subject: [PATCH 27/36] Minor cleanups. --- src/config.c | 3 ++- src/direwolf.c | 6 +++--- src/igate.c | 22 ---------------------- src/log.c | 2 +- src/pfilter.c | 5 +++++ src/recv.c | 8 +++++--- systemd/direwolf.service | 3 +++ 7 files changed, 19 insertions(+), 30 deletions(-) diff --git a/src/config.c b/src/config.c index f3edc15a..b1b08290 100644 --- a/src/config.c +++ b/src/config.c @@ -5775,8 +5775,9 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_ else if (strcasecmp(keyword, "POWER") == 0) { b->power = atoi(value); } - else if (strcasecmp(keyword, "HEIGHT") == 0) { + else if (strcasecmp(keyword, "HEIGHT") == 0) { // This is in feet. b->height = atoi(value); + // TODO: ability to add units suffix, e.g. 10m } else if (strcasecmp(keyword, "GAIN") == 0) { b->gain = atoi(value); diff --git a/src/direwolf.c b/src/direwolf.c index 1a92968a..8735193b 100644 --- a/src/direwolf.c +++ b/src/direwolf.c @@ -300,8 +300,8 @@ int main (int argc, char *argv[]) text_color_init(t_opt); text_color_set(DW_COLOR_INFO); - //dw_printf ("Dire Wolf version %d.%d (%s) Beta Test 4\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); - dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "G", __DATE__); + dw_printf ("Dire Wolf version %d.%d (%s) BETA TEST 4\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); + //dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "G", __DATE__); //dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION); @@ -387,7 +387,7 @@ int main (int argc, char *argv[]) dw_printf ("\n"); dw_printf ("Dire Wolf requires only privileges available to ordinary users.\n"); dw_printf ("Running this as root is an unnecessary security risk.\n"); - SLEEP_SEC(1); + //SLEEP_SEC(1); } } #endif diff --git a/src/igate.c b/src/igate.c index 11f28963..d77d78ca 100644 --- a/src/igate.c +++ b/src/igate.c @@ -1749,7 +1749,6 @@ static void * satgate_delay_thread (void *arg) * *--------------------------------------------------------------------*/ -#warning - clean up // It is unforunate that the : data type indicator (DTI) was overloaded with // so many different meanings. Simply looking at the DTI is not adequate for @@ -1825,9 +1824,6 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan) *gt = '\0'; } -// FIXME NO! - ///////ax25_get_addr_with_ssid (pp3, AX25_SOURCE, src); - /* * Drop if path contains: * NOGATE or RFONLY - means IGate should not pass them. @@ -2472,24 +2468,6 @@ void ig_to_tx_remember (packet_t pp, int chan, int bydigi) } -#warning remove - -static int is_message_overload (char *infop) -{ - if (*infop != ':') return (0); - if (strlen(infop) < 16) return (0); - if (strncmp(infop+10, ":PARM.", 6) == 0) return (1); - if (strncmp(infop+10, ":UNIT.", 6) == 0) return (1); - if (strncmp(infop+10, ":EQNS.", 6) == 0) return (1); - if (strncmp(infop+10, ":BITS.", 6) == 0) return (1); - if (strncmp(infop+1, "BLN", 3) == 0) return (1); - if (strncmp(infop+1, "NWS", 3) == 0) return (1); - if (strncmp(infop+1, "SKY", 3) == 0) return (1); - if (strncmp(infop+1, "CWA", 3) == 0) return (1); - if (strncmp(infop+1, "BOM", 3) == 0) return (1); - return (0); -} - static int ig_to_tx_allow (packet_t pp, int chan) { diff --git a/src/log.c b/src/log.c index c8b2f825..d7ef544b 100644 --- a/src/log.c +++ b/src/log.c @@ -224,7 +224,7 @@ void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_ now = time(NULL); // Get current time. (void)gmtime_r (&now, &tm); - +// FIXME: https://github.com/wb2osz/direwolf/issues/473 if (g_daily_names) { diff --git a/src/pfilter.c b/src/pfilter.c index b7ad63ee..35767a67 100644 --- a/src/pfilter.c +++ b/src/pfilter.c @@ -1303,6 +1303,11 @@ static int filt_s (pfstate_t *pf) * what they are for other digipeaters. * I think the best solution is to simply ignore the hop count. * + * Release 1.7: I got overly ambitious and now realize this is just giving people too much + * "rope to hang themselves," drop messages unexpectedly, and accidentally break messaging. + * Change documentation to mention only the time limit. + * The other functionality will be undocumented and maybe disappear over time. + * *------------------------------------------------------------------------------*/ static int filt_i (pfstate_t *pf) diff --git a/src/recv.c b/src/recv.c index 9873bbd6..a6b4afb1 100644 --- a/src/recv.c +++ b/src/recv.c @@ -207,7 +207,7 @@ static void * recv_adev_thread (void *arg) int eof; /* This audio device can have one (mono) or two (stereo) channels. */ - /* Find number of the first channel. */ + /* Find number of the first channel and number of channels. */ int first_chan = ADEVFIRSTCHAN(a); int num_chan = save_pa->adev[a].num_channels; @@ -234,6 +234,8 @@ static void * recv_adev_thread (void *arg) if (audio_sample >= 256 * 256) eof = 1; + // Future? provide more flexible mapping. + // i.e. for each valid channel where audio_source[] is first_chan+c. multi_modem_process_sample(first_chan + c, audio_sample); @@ -262,14 +264,14 @@ static void * recv_adev_thread (void *arg) aprs_tt_button (first_chan + c, tt); } } - } + } // for c is just 0 or 0 then 1 /* When a complete frame is accumulated, */ /* dlq_rec_frame, is called. */ /* recv_process, below, drains the queue. */ - } + } // while !eof on audio stream // What should we do now? // Seimply terminate the application? diff --git a/systemd/direwolf.service b/systemd/direwolf.service index d4109a04..c3380fac 100644 --- a/systemd/direwolf.service +++ b/systemd/direwolf.service @@ -1,6 +1,7 @@ [Unit] Description=Direwolf Sound Card-based AX.25 TNC After=sound.target +After=network.target [Service] EnvironmentFile=/etc/sysconfig/direwolf @@ -22,3 +23,5 @@ ReadWritePaths=/var/log/direwolf [Install] WantedBy=multi-user.target DefaultInstance=1 + +# alternate version: https://www.f4fxl.org/start-direwolf-at-boot-the-systemd-way/ From a87b72e040e3255d484900c804a7cdb57399eb88 Mon Sep 17 00:00:00 2001 From: Brent Petit Date: Tue, 8 Aug 2023 12:48:53 -0500 Subject: [PATCH 28/36] Handle slow Hamlib init. This change adds a retry loop to the (#484) rig_open call. If the rig_open continues to fail after 5 tries, exit. --- src/ptt.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/ptt.c b/src/ptt.c index d5e28160..5187f1df 100644 --- a/src/ptt.c +++ b/src/ptt.c @@ -991,6 +991,8 @@ void ptt_init (struct audio_s *audio_config_p) for (ot = 0; ot < NUM_OCTYPES; ot++) { if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_HAMLIB) { if (ot == OCTYPE_PTT) { + int err = -1; + int tries = 0; /* For "AUTO" model, try to guess what is out there. */ @@ -1055,13 +1057,24 @@ void ptt_init (struct audio_s *audio_config_p) rig[ch][ot]->state.rigport.parm.serial.parity = RIG_PARITY_NONE; rig[ch][ot]->state.rigport.parm.serial.handshake = RIG_HANDSHAKE_NONE; } - int err = rig_open(rig[ch][ot]); + tries = 0; + do { + // Try up to 5 times, Hamlib can take a moment to finish init + err = rig_open(rig[ch][ot]); + if (++tries > 5) { + break; + } else if (err != RIG_OK) { + text_color_set(DW_COLOR_INFO); + dw_printf ("Retrying Hamlib Rig open...\n"); + sleep (5); + } + } while (err != RIG_OK); if (err != RIG_OK) { text_color_set(DW_COLOR_ERROR); dw_printf ("Hamlib Rig open error %d: %s\n", err, rigerror(err)); rig_cleanup (rig[ch][ot]); rig[ch][ot] = NULL; - continue; + exit (1); } /* Successful. Later code should check for rig[ch][ot] not NULL. */ From 7a8e4320ac7248326428a95d68a83b313231db20 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Wed, 16 Aug 2023 15:00:10 +0100 Subject: [PATCH 29/36] Issue 486. Maybe. --- src/direwolf.c | 2 +- src/rrbb.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- src/rrbb.h | 4 ++++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/direwolf.c b/src/direwolf.c index 8735193b..3e7dca27 100644 --- a/src/direwolf.c +++ b/src/direwolf.c @@ -300,7 +300,7 @@ int main (int argc, char *argv[]) text_color_init(t_opt); text_color_set(DW_COLOR_INFO); - dw_printf ("Dire Wolf version %d.%d (%s) BETA TEST 4\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); + dw_printf ("Dire Wolf version %d.%d (%s) BETA TEST 5\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); //dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "G", __DATE__); //dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION); diff --git a/src/rrbb.c b/src/rrbb.c index 2047d69f..e787dae5 100644 --- a/src/rrbb.c +++ b/src/rrbb.c @@ -51,8 +51,8 @@ #define MAGIC2 0x56788765 -static int new_count = 0; -static int delete_count = 0; +volatile static int new_count = 0; +volatile static int delete_count = 0; /*********************************************************************************** @@ -425,6 +425,50 @@ alevel_t rrbb_get_audio_level (rrbb_t b) +/*********************************************************************************** + * + * Name: rrbb_set_speed_error + * + * Purpose: Set speed error of the received frame. + * + * Inputs: b Handle for bit array. + * speed_error In percentage. + * + ***********************************************************************************/ + +void rrbb_set_speed_error (rrbb_t b, float speed_error) +{ + assert (b != NULL); + assert (b->magic1 == MAGIC1); + assert (b->magic2 == MAGIC2); + + b->speed_error = speed_error; +} + + +/*********************************************************************************** + * + * Name: rrbb_get_speed_error + * + * Purpose: Get speed error of the received frame. + * + * Inputs: b Handle for bit array. + * + * Returns: speed error in percentage. + * + ***********************************************************************************/ + +float rrbb_get_speed_error (rrbb_t b) +{ + assert (b != NULL); + assert (b->magic1 == MAGIC1); + assert (b->magic2 == MAGIC2); + + return (b->speed_error); +} + + + /*********************************************************************************** * * Name: rrbb_get_is_scrambled @@ -488,6 +532,7 @@ int rrbb_get_prev_descram (rrbb_t b) } + /* end rrbb.c */ diff --git a/src/rrbb.h b/src/rrbb.h index 4b283726..894a448f 100644 --- a/src/rrbb.h +++ b/src/rrbb.h @@ -33,6 +33,7 @@ typedef struct rrbb_s { int slice; /* Which slicer. */ alevel_t alevel; /* Received audio level at time of frame capture. */ + float speed_error; /* Received data speed error as percentage. */ unsigned int len; /* Current number of samples in array. */ int is_scrambled; /* Is data scrambled G3RUH / K9NG style? */ @@ -84,6 +85,9 @@ int rrbb_get_slice (rrbb_t b); void rrbb_set_audio_level (rrbb_t b, alevel_t alevel); alevel_t rrbb_get_audio_level (rrbb_t b); +void rrbb_set_speed_error (rrbb_t b, float speed_error); +float rrbb_get_speed_error (rrbb_t b); + int rrbb_get_is_scrambled (rrbb_t b); int rrbb_get_descram_state (rrbb_t b); int rrbb_get_prev_descram (rrbb_t b); From a08d0939b5427d868d48e5f2c810510b16b3f16f Mon Sep 17 00:00:00 2001 From: wb2osz Date: Mon, 11 Sep 2023 00:07:36 +0100 Subject: [PATCH 30/36] Add FEC type to station heard line. --- src/atest.c | 42 ++++++++++++++++++++++-------------------- src/direwolf.c | 35 +++++++++++++++++++++++------------ src/dlq.c | 8 ++++---- src/dlq.h | 7 +++++-- src/hdlc_rec2.h | 3 ++- src/igate.c | 4 ++-- src/il2p_test.c | 2 +- src/multi_modem.c | 32 ++++++++++++++++++-------------- src/multi_modem.h | 4 ++-- src/recv.c | 2 +- 10 files changed, 80 insertions(+), 59 deletions(-) diff --git a/src/atest.c b/src/atest.c index 733e8c4f..c5f4ec50 100644 --- a/src/atest.c +++ b/src/atest.c @@ -757,7 +757,7 @@ int audio_get (int a) * This is called when we have a good frame. */ -void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, int is_fx25, retry_t retries, char *spectrum) +void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, fec_type_t fec_type, retry_t retries, char *spectrum) { char stemp[500]; @@ -826,29 +826,31 @@ void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alev strlcat (heard, ")", sizeof(heard)); } - if (my_audio_config.achan[chan].fix_bits == RETRY_NONE && my_audio_config.achan[chan].passall == 0) { - dw_printf ("%s audio level = %s %s\n", heard, alevel_text, spectrum); - } - else if (is_fx25) { - dw_printf ("%s audio level = %s %s\n", heard, alevel_text, spectrum); - } - else { - assert (retries >= RETRY_NONE && retries <= RETRY_MAX); - dw_printf ("%s audio level = %s [%s] %s\n", heard, alevel_text, retry_text[(int)retries], spectrum); + switch (fec_type) { + + case fec_type_fx25: + dw_printf ("%s audio level = %s FX.25 %s\n", heard, alevel_text, spectrum); + break; + + case fec_type_il2p: + dw_printf ("%s audio level = %s IL2P %s\n", heard, alevel_text, spectrum); + break; + + case fec_type_none: + default: + if (my_audio_config.achan[chan].fix_bits == RETRY_NONE && my_audio_config.achan[chan].passall == 0) { + // No fix_bits or passall specified. + dw_printf ("%s audio level = %s %s\n", heard, alevel_text, spectrum); + } + else { + assert (retries >= RETRY_NONE && retries <= RETRY_MAX); // validate array index. + dw_printf ("%s audio level = %s [%s] %s\n", heard, alevel_text, retry_text[(int)retries], spectrum); + } + break; } #endif -//#if defined(EXPERIMENT_G) || defined(EXPERIMENT_H) -// int j; -// -// for (j=0; j= 0 && chan < MAX_TOTAL_CHANS); // TOTAL for virtual channels assert (subchan >= -2 && subchan < MAX_SUBCHANS); @@ -1197,12 +1199,21 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev strlcpy (display_retries, "", sizeof(display_retries)); - if (is_fx25) { - ; - } - else if (audio_config.achan[chan].fix_bits != RETRY_NONE || audio_config.achan[chan].passall) { - assert (retries >= RETRY_NONE && retries <= RETRY_MAX); - snprintf (display_retries, sizeof(display_retries), " [%s] ", retry_text[(int)retries]); + switch (fec_type) { + case fec_type_fx25: + strlcpy (display_retries, " FX.25 ", sizeof(display_retries)); + break; + case fec_type_il2p: + strlcpy (display_retries, " IL2P ", sizeof(display_retries)); + break; + case fec_type_none: + default: + // Possible fix_bits indication. + if (audio_config.achan[chan].fix_bits != RETRY_NONE || audio_config.achan[chan].passall) { + assert (retries >= RETRY_NONE && retries <= RETRY_MAX); + snprintf (display_retries, sizeof(display_retries), " [%s] ", retry_text[(int)retries]); + } + break; } ax25_format_addrs (pp, stemp); @@ -1567,7 +1578,7 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev * However, if it used FEC mode (FX.25. IL2P), we have much higher level of * confidence that it is correct. */ - if (ax25_is_aprs(pp) && ( retries == RETRY_NONE || is_fx25) ) { + if (ax25_is_aprs(pp) && ( retries == RETRY_NONE || fec_type == fec_type_fx25 || fec_type == fec_type_il2p) ) { igate_send_rec_packet (chan, pp); } @@ -1588,7 +1599,7 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev * However, if it used FEC mode (FX.25. IL2P), we have much higher level of * confidence that it is correct. */ - if (ax25_is_aprs(pp) && ( retries == RETRY_NONE || is_fx25) ) { + if (ax25_is_aprs(pp) && ( retries == RETRY_NONE || fec_type == fec_type_fx25 || fec_type == fec_type_il2p) ) { digipeater (chan, pp); } @@ -1598,7 +1609,7 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev * Use only those with correct CRC (or using FEC.) */ - if (retries == RETRY_NONE || is_fx25) { + if (retries == RETRY_NONE || fec_type == fec_type_fx25 || fec_type == fec_type_il2p) { cdigipeater (chan, pp); } diff --git a/src/dlq.c b/src/dlq.c index 698df5a2..f56b8649 100644 --- a/src/dlq.c +++ b/src/dlq.c @@ -215,10 +215,10 @@ void dlq_init (void) * display of audio level line. * Use -2 to indicate DTMF message.) * - * is_fx25 - Was it from FX.25? Need to know because + * fec_type - Was it from FX.25 or IL2P? Need to know because * meaning of retries is different. * - * retries - Level of bit correction used. + * retries - Level of correction used. * * spectrum - Display of how well multiple decoders did. * @@ -228,7 +228,7 @@ void dlq_init (void) * *--------------------------------------------------------------------*/ -void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, int is_fx25, retry_t retries, char *spectrum) +void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, fec_type_t fec_type, retry_t retries, char *spectrum) { struct dlq_item_s *pnew; @@ -278,7 +278,7 @@ void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alev pnew->subchan = subchan; pnew->pp = pp; pnew->alevel = alevel; - pnew->is_fx25 = is_fx25; + pnew->fec_type = fec_type; pnew->retries = retries; if (spectrum == NULL) strlcpy(pnew->spectrum, "", sizeof(pnew->spectrum)); diff --git a/src/dlq.h b/src/dlq.h index f07d3303..fdac1c0c 100644 --- a/src/dlq.h +++ b/src/dlq.h @@ -33,10 +33,13 @@ typedef struct cdata_s { + /* Types of things that can be in queue. */ typedef enum dlq_type_e {DLQ_REC_FRAME, DLQ_CONNECT_REQUEST, DLQ_DISCONNECT_REQUEST, DLQ_XMIT_DATA_REQUEST, DLQ_REGISTER_CALLSIGN, DLQ_UNREGISTER_CALLSIGN, DLQ_OUTSTANDING_FRAMES_REQUEST, DLQ_CHANNEL_BUSY, DLQ_SEIZE_CONFIRM, DLQ_CLIENT_CLEANUP} dlq_type_t; +typedef enum fec_type_e {fec_type_none=0, fec_type_fx25=1, fec_type_il2p=2} fec_type_t; + /* A queue item. */ @@ -68,7 +71,7 @@ typedef struct dlq_item_s { alevel_t alevel; /* Audio level. */ - int is_fx25; /* Was it from FX.25? */ + fec_type_t fec_type; // Type of FEC for received signal: none, FX.25, or IL2P. retry_t retries; /* Effort expended to get a valid CRC. */ /* Bits changed for regular AX.25. */ @@ -106,7 +109,7 @@ void dlq_init (void); -void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, int is_fx25, retry_t retries, char *spectrum); +void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, fec_type_t fec_type, retry_t retries, char *spectrum); void dlq_connect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client, int pid); diff --git a/src/hdlc_rec2.h b/src/hdlc_rec2.h index 5141f3ac..01ef3238 100644 --- a/src/hdlc_rec2.h +++ b/src/hdlc_rec2.h @@ -6,6 +6,7 @@ #include "ax25_pad.h" /* for packet_t, alevel_t */ #include "rrbb.h" #include "audio.h" /* for struct audio_s */ +#include "dlq.h" // for fec_type_t definition. @@ -62,6 +63,6 @@ int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, int slice, /* Provided by the top level application to process a complete frame. */ -void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t level, int is_fx25, retry_t retries, char *spectrum); +void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t level, fec_type_t fec_type, retry_t retries, char *spectrum); #endif diff --git a/src/igate.c b/src/igate.c index d77d78ca..7f84228a 100644 --- a/src/igate.c +++ b/src/igate.c @@ -1564,9 +1564,9 @@ static void * igate_recv_thread (void *arg) // See what happens with -2 and follow up on this. // Do we need something else here? int slice = 0; - int is_fx25 = 0; + fec_type_t fec_type = fec_type_none; char spectrum[] = "APRS-IS"; - dlq_rec_frame (ichan, subchan, slice, pp3, alevel, is_fx25, RETRY_NONE, spectrum); + dlq_rec_frame (ichan, subchan, slice, pp3, alevel, fec_type, RETRY_NONE, spectrum); } else { text_color_set(DW_COLOR_ERROR); diff --git a/src/il2p_test.c b/src/il2p_test.c index c983daff..17995259 100644 --- a/src/il2p_test.c +++ b/src/il2p_test.c @@ -943,7 +943,7 @@ void tone_gen_put_bit (int chan, int data) // This is called when a complete frame has been deserialized. -void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, int is_fx25) +void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, fec_type_t fec_type) { if (rec_count < 0) return; // Skip check before serdes test. diff --git a/src/multi_modem.c b/src/multi_modem.c index d02b8c2a..d2382f1a 100644 --- a/src/multi_modem.c +++ b/src/multi_modem.c @@ -116,7 +116,8 @@ static struct audio_s *save_audio_config_p; static struct { packet_t packet_p; alevel_t alevel; - int is_fx25; // 1 for FX.25, 0 for regular AX.25. + float speed_error; + fec_type_t fec_type; // Type of FEC: none(0), fx25, il2p retry_t retries; // For the old "fix bits" strategy, this is the // number of bits that were modified to get a good CRC. // It would be 0 to something around 4. @@ -306,14 +307,14 @@ void multi_modem_process_sample (int chan, int audio_sample) * display of audio level line. * Use -2 to indicate DTMF message.) * retries - Level of correction used. - * is_fx25 - 1 for FX.25, 0 for normal AX.25. + * fec_type - none(0), fx25, il2p * * Description: Add to list of candidates. Best one will be picked later. * *--------------------------------------------------------------------*/ -void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries, int is_fx25) +void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries, fec_type_t fec_type) { packet_t pp; @@ -346,12 +347,12 @@ void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned c pp = ax25_from_frame (fbuf, flen, alevel); } - multi_modem_process_rec_packet (chan, subchan, slice, pp, alevel, retries, is_fx25); + multi_modem_process_rec_packet (chan, subchan, slice, pp, alevel, retries, fec_type); } // TODO: Eliminate function above and move code elsewhere? -void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, int is_fx25) +void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, fec_type_t fec_type) { if (pp == NULL) { text_color_set(DW_COLOR_ERROR); @@ -386,7 +387,7 @@ void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t ax25_delete (pp); } else { - dlq_rec_frame (chan, subchan, slice, pp, alevel, is_fx25, retries, ""); + dlq_rec_frame (chan, subchan, slice, pp, alevel, fec_type, retries, ""); } return; } @@ -406,7 +407,7 @@ void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t candidate[chan][subchan][slice].packet_p = pp; candidate[chan][subchan][slice].alevel = alevel; - candidate[chan][subchan][slice].is_fx25 = is_fx25; + candidate[chan][subchan][slice].fec_type = fec_type; candidate[chan][subchan][slice].retries = retries; candidate[chan][subchan][slice].age = 0; candidate[chan][subchan][slice].crc = ax25_m_m_crc(pp); @@ -443,6 +444,9 @@ static void pick_best_candidate (int chan) int best_n, best_score; char spectrum[MAX_SUBCHANS*MAX_SLICERS+1]; int n, j, k; + if (save_audio_config_p->achan[chan].num_slicers < 1) { + save_audio_config_p->achan[chan].num_slicers = 1; + } int num_bars = save_audio_config_p->achan[chan].num_slicers * save_audio_config_p->achan[chan].num_subchan; memset (spectrum, 0, sizeof(spectrum)); @@ -456,7 +460,7 @@ static void pick_best_candidate (int chan) if (candidate[chan][j][k].packet_p == NULL) { spectrum[n] = '_'; } - else if (candidate[chan][j][k].is_fx25) { + else if (candidate[chan][j][k].fec_type != fec_type_none) { // FX.25 or IL2P // FIXME: using retries both as an enum and later int too. if ((int)(candidate[chan][j][k].retries) <= 9) { spectrum[n] = '0' + candidate[chan][j][k].retries; @@ -464,7 +468,7 @@ static void pick_best_candidate (int chan) else { spectrum[n] = '+'; } - } + } // AX.25 below else if (candidate[chan][j][k].retries == RETRY_NONE) { spectrum[n] = '|'; } @@ -481,8 +485,8 @@ static void pick_best_candidate (int chan) candidate[chan][j][k].score = 0; } else { - if (candidate[chan][j][k].is_fx25) { - candidate[chan][j][k].score = 9000 - 100 * candidate[chan][j][k].retries; + if (candidate[chan][j][k].fec_type != fec_type_none) { + candidate[chan][j][k].score = 9000 - 100 * candidate[chan][j][k].retries; // has FEC } else { /* Originally, this produced 0 for the PASSALL case. */ @@ -550,9 +554,9 @@ static void pick_best_candidate (int chan) candidate[chan][j][k].packet_p); } else { - dw_printf ("%d.%d.%d: ptr=%p, is_fx25=%d, retry=%d, age=%3d, crc=%04x, score=%d %s\n", chan, j, k, + dw_printf ("%d.%d.%d: ptr=%p, fec_type=%d, retry=%d, age=%3d, crc=%04x, score=%d %s\n", chan, j, k, candidate[chan][j][k].packet_p, - candidate[chan][j][k].is_fx25, + (int)(candidate[chan][j][k].fec_type), (int)(candidate[chan][j][k].retries), candidate[chan][j][k].age, candidate[chan][j][k].crc, @@ -611,7 +615,7 @@ static void pick_best_candidate (int chan) dlq_rec_frame (chan, j, k, candidate[chan][j][k].packet_p, candidate[chan][j][k].alevel, - candidate[chan][j][k].is_fx25, + candidate[chan][j][k].fec_type, (int)(candidate[chan][j][k].retries), spectrum); diff --git a/src/multi_modem.h b/src/multi_modem.h index de3061ed..51c3cde5 100644 --- a/src/multi_modem.h +++ b/src/multi_modem.h @@ -17,8 +17,8 @@ void multi_modem_process_sample (int c, int audio_sample); int multi_modem_get_dc_average (int chan); // Deprecated. Replace with ...packet -void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries, int is_fx25); +void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries, fec_type_t fec_type); -void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, int is_fx25); +void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, fec_type_t fec_type); #endif diff --git a/src/recv.c b/src/recv.c index a6b4afb1..49040e55 100644 --- a/src/recv.c +++ b/src/recv.c @@ -339,7 +339,7 @@ void recv_process (void) * - Digipeater. */ - app_process_rec_packet (pitem->chan, pitem->subchan, pitem->slice, pitem->pp, pitem->alevel, pitem->is_fx25, pitem->retries, pitem->spectrum); + app_process_rec_packet (pitem->chan, pitem->subchan, pitem->slice, pitem->pp, pitem->alevel, pitem->fec_type, pitem->retries, pitem->spectrum); /* From ba0313ca7861a9f36b65ea57fdf9a3d133e6f335 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Mon, 11 Sep 2023 00:08:58 +0100 Subject: [PATCH 31/36] Add FTM-500D to recognized device identifers. --- src/decode_aprs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/decode_aprs.c b/src/decode_aprs.c index 1d3ec1d2..d96274bc 100644 --- a/src/decode_aprs.c +++ b/src/decode_aprs.c @@ -1674,6 +1674,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '0') { strlcpy (A->g_mfr, "Yaesu FT3D", sizeof(A->g_mfr)); pfirst++; plast-=2; } else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '3') { strlcpy (A->g_mfr, "Yaesu FT5D", sizeof(A->g_mfr)); pfirst++; plast-=2; } else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '1') { strlcpy (A->g_mfr, "Yaesu FTM-300D", sizeof(A->g_mfr)); pfirst++; plast-=2; } + else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '5') { strlcpy (A->g_mfr, "Yaesu FTM-500D", sizeof(A->g_mfr)); pfirst++; plast-=2; } else if (*pfirst == '`' && *(plast-1) == ' ' && *plast == 'X') { strlcpy (A->g_mfr, "AP510", sizeof(A->g_mfr)); pfirst++; plast-=2; } @@ -1799,7 +1800,8 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int * :xxxxxxxxx: ... {mm}aa Message with new style message number and ack. * * - * Reference: See new message id style: http://www.aprs.org/aprs11/replyacks.txt + * Reference: http://www.aprs.org/txt/messages101.txt + * http://www.aprs.org/aprs11/replyacks.txt <-- New (1999) adding ack to outgoing message. * *------------------------------------------------------------------*/ From 877d1c7707ac11df9d32b821e3c6dfd66e561e09 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sat, 23 Sep 2023 02:48:28 +0100 Subject: [PATCH 32/36] Fix build on Alpine Linux. issues 150, 319, 344. --- external/misc/CMakeLists.txt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/external/misc/CMakeLists.txt b/external/misc/CMakeLists.txt index 16125d0a..4dd4ec44 100644 --- a/external/misc/CMakeLists.txt +++ b/external/misc/CMakeLists.txt @@ -32,9 +32,15 @@ if(LINUX) ) endif() - add_library(misc STATIC - ${misc_SOURCES} - ) + # add_library doesn't like to get an empty source file list. + + if($misc_SOURCES) + add_library(misc STATIC + ${misc_SOURCES} + ) + else() + set(MISC_LIBRARIES "" CACHE INTERNAL "") + endif() elseif(WIN32 OR CYGWIN) # windows From 3c73a6b2b21d358d8e8a4434582fdb4e7a8f27af Mon Sep 17 00:00:00 2001 From: wb2osz Date: Fri, 22 Sep 2023 22:43:12 -0400 Subject: [PATCH 33/36] Revert "Fix build on Alpine Linux. issues 150, 319, 344." This reverts commit 877d1c7707ac11df9d32b821e3c6dfd66e561e09. --- external/misc/CMakeLists.txt | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/external/misc/CMakeLists.txt b/external/misc/CMakeLists.txt index 4dd4ec44..16125d0a 100644 --- a/external/misc/CMakeLists.txt +++ b/external/misc/CMakeLists.txt @@ -32,15 +32,9 @@ if(LINUX) ) endif() - # add_library doesn't like to get an empty source file list. - - if($misc_SOURCES) - add_library(misc STATIC - ${misc_SOURCES} - ) - else() - set(MISC_LIBRARIES "" CACHE INTERNAL "") - endif() + add_library(misc STATIC + ${misc_SOURCES} + ) elseif(WIN32 OR CYGWIN) # windows From ab834f338bdd0580def6a18a81dbf9cbf7e7f18e Mon Sep 17 00:00:00 2001 From: wb2osz Date: Mon, 25 Sep 2023 03:17:15 +0100 Subject: [PATCH 34/36] Second attempt to fix build on Alpine Linux. issues 150, 319, 344. --- external/misc/CMakeLists.txt | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/external/misc/CMakeLists.txt b/external/misc/CMakeLists.txt index 16125d0a..685b89ad 100644 --- a/external/misc/CMakeLists.txt +++ b/external/misc/CMakeLists.txt @@ -32,9 +32,22 @@ if(LINUX) ) endif() - add_library(misc STATIC - ${misc_SOURCES} - ) + # Add_library doesn't like to get an empty source file list. + # I tried several variations on this theme to test whether the list + # was not empty and was not successful in getting it to work + # on both Alpine and RPi. + #if("${misc_SOURCES}") + # This is less elegant and less maintainable but it works. + + if ((NOT HAVE_STRLCPY) OR (NOT HAVE_STRLCAT)) + add_library(misc STATIC + ${misc_SOURCES} + ) + else() + set(MISC_LIBRARIES "" CACHE INTERNAL "") + endif() + + elseif(WIN32 OR CYGWIN) # windows From 2157d2a7f99e5fce7438a3c804e1c4c119746717 Mon Sep 17 00:00:00 2001 From: ars-ka0s <26339355+ars-ka0s@users.noreply.github.com> Date: Wed, 18 Jan 2023 18:57:40 -0600 Subject: [PATCH 35/36] Add text color options with black background. --- src/textcolor.c | 62 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/src/textcolor.c b/src/textcolor.c index dea90f09..7ddf74a2 100644 --- a/src/textcolor.c +++ b/src/textcolor.c @@ -89,7 +89,13 @@ #if __WIN32__ +// For Windows platform: +// -t 0 disables color +// -t 1 enables with white background (default) +// -t 2 enables with black background + #define BACKGROUND_WHITE (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY) +#define FOREGROUND_WHITE (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY) #else /* Linux, BSD, Mac OSX */ @@ -133,18 +139,28 @@ // In recent tests, background is always gray, not white like it should be. -#define MAX_T 4 +// Alternative 5: + +// This uses the 8-color foreground colors like alternative 2, but with a reset background color. +// For people that prefer dark backgrounds or use terminals with color theming. + + +// Alternative 6: + +// This uses the RGB color setting like alternative 1, but with a black background. -static const char *t_background_white[MAX_T+1] = { "", "\e[48;2;255;255;255m", "\e[48;2;255;255;255m", "\e[5;47m", "\e[1;47m" }; +#define MAX_T 6 -static const char *t_black[MAX_T+1] = { "", "\e[38;2;0;0;0m", "\e[0;30m" "\e[48;2;255;255;255m", "\e[0;30m" "\e[5;47m", "\e[0;30m" "\e[1;47m" }; -static const char *t_red[MAX_T+1] = { "", "\e[38;2;255;0;0m", "\e[1;31m" "\e[48;2;255;255;255m", "\e[1;31m" "\e[5;47m", "\e[1;31m" "\e[1;47m" }; -static const char *t_green[MAX_T+1] = { "", "\e[38;2;0;255;0m", "\e[1;32m" "\e[48;2;255;255;255m", "\e[1;32m" "\e[5;47m", "\e[1;32m" "\e[1;47m" }; -static const char *t_dark_green[MAX_T+1]= { "", "\e[38;2;0;192;0m", "\e[0;32m" "\e[48;2;255;255;255m", "\e[0;32m" "\e[5;47m", "\e[0;32m" "\e[1;47m" }; -static const char *t_yellow[MAX_T+1] = { "", "\e[38;2;255;255;0m", "\e[1;33m" "\e[48;2;255;255;255m", "\e[1;33m" "\e[5;47m", "\e[1;33m" "\e[1;47m" }; -static const char *t_blue[MAX_T+1] = { "", "\e[38;2;0;0;255m", "\e[1;34m" "\e[48;2;255;255;255m", "\e[1;34m" "\e[5;47m", "\e[1;34m" "\e[1;47m" }; -static const char *t_magenta[MAX_T+1] = { "", "\e[38;2;255;0;255m", "\e[1;35m" "\e[48;2;255;255;255m", "\e[1;35m" "\e[5;47m", "\e[1;35m" "\e[1;47m" }; -static const char *t_cyan[MAX_T+1] = { "", "\e[38;2;0;255;255m", "\e[0;36m" "\e[48;2;255;255;255m", "\e[0;36m" "\e[5;47m", "\e[0;36m" "\e[1;47m" }; +static const char *t_background_white[MAX_T+1] = { "", "\e[48;2;255;255;255m", "\e[48;2;255;255;255m", "\e[5;47m", "\e[1;47m", "\e[0;49m" , "\e[48;2;0;0;0m" }; + +static const char *t_black[MAX_T+1] = { "", "\e[38;2;0;0;0m", "\e[0;30m" "\e[48;2;255;255;255m", "\e[0;30m" "\e[5;47m", "\e[0;30m" "\e[1;47m", "\e[0;49m" "\e[0;39m", "\e[38;2;255;255;255m" }; +static const char *t_red[MAX_T+1] = { "", "\e[38;2;255;0;0m", "\e[1;31m" "\e[48;2;255;255;255m", "\e[1;31m" "\e[5;47m", "\e[1;31m" "\e[1;47m", "\e[0;49m" "\e[1;31m", "\e[38;2;255;0;0m"}; +static const char *t_green[MAX_T+1] = { "", "\e[38;2;0;255;0m", "\e[1;32m" "\e[48;2;255;255;255m", "\e[1;32m" "\e[5;47m", "\e[1;32m" "\e[1;47m", "\e[0;49m" "\e[1;32m", "\e[38;2;0;255;0m"}; +static const char *t_dark_green[MAX_T+1]= { "", "\e[38;2;0;192;0m", "\e[0;32m" "\e[48;2;255;255;255m", "\e[0;32m" "\e[5;47m", "\e[0;32m" "\e[1;47m", "\e[0;49m" "\e[0;32m", "\e[38;2;0;192;0m"}; +static const char *t_yellow[MAX_T+1] = { "", "\e[38;2;255;255;0m", "\e[1;33m" "\e[48;2;255;255;255m", "\e[1;33m" "\e[5;47m", "\e[1;33m" "\e[1;47m", "\e[0;49m" "\e[1;33m", "\e[38;2;255;255;0m"}; +static const char *t_blue[MAX_T+1] = { "", "\e[38;2;0;0;255m", "\e[1;34m" "\e[48;2;255;255;255m", "\e[1;34m" "\e[5;47m", "\e[1;34m" "\e[1;47m", "\e[0;49m" "\e[1;34m", "\e[38;2;0;0;255m"}; +static const char *t_magenta[MAX_T+1] = { "", "\e[38;2;255;0;255m", "\e[1;35m" "\e[48;2;255;255;255m", "\e[1;35m" "\e[5;47m", "\e[1;35m" "\e[1;47m", "\e[0;49m" "\e[1;35m", "\e[38;2;255;0;255m"}; +static const char *t_cyan[MAX_T+1] = { "", "\e[38;2;0;255;255m", "\e[0;36m" "\e[48;2;255;255;255m", "\e[0;36m" "\e[5;47m", "\e[0;36m" "\e[1;47m", "\e[0;49m" "\e[0;36m", "\e[38;2;0;255;255m"}; /* Clear from cursor to end of screen. */ @@ -179,6 +195,7 @@ void text_color_init (int enable_color) #if __WIN32__ +g_enable_color = enable_color; if (g_enable_color != 0) { @@ -189,6 +206,10 @@ void text_color_init (int enable_color) COORD coord; DWORD nwritten; + if (g_enable_color > 1) { + attr = 0; + } + h = GetStdHandle(STD_OUTPUT_HANDLE); if (h != NULL && h != INVALID_HANDLE_VALUE) { @@ -210,7 +231,7 @@ void text_color_init (int enable_color) for (t = 0; t <= MAX_T; t++) { text_color_init (t); printf ("-t %d", t); - if (t) printf (" [white background] "); + if (t < 5) printf (" [white background] "); printf ("\n"); printf ("%sBlack ", t_black[t]); printf ("%sRed ", t_red[t]); @@ -254,34 +275,41 @@ void text_color_set ( enum dw_color_e c ) return; } + WORD info_fg = 0; + WORD background = BACKGROUND_WHITE; + if (g_enable_color > 1) { + info_fg = FOREGROUND_WHITE; + background = 0; + } + switch (c) { default: case DW_COLOR_INFO: - attr = BACKGROUND_WHITE; + attr = info_fg | background; break; case DW_COLOR_ERROR: - attr = FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_WHITE; + attr = FOREGROUND_RED | FOREGROUND_INTENSITY | background; break; case DW_COLOR_REC: // Release 1.6. Dark green, same as for debug. // Bright green is too hard to see with white background, // attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_WHITE; - attr = FOREGROUND_GREEN | BACKGROUND_WHITE; + attr = FOREGROUND_GREEN | background; break; case DW_COLOR_DECODED: - attr = FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_WHITE; + attr = FOREGROUND_BLUE | FOREGROUND_INTENSITY | background; break; case DW_COLOR_XMIT: - attr = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_WHITE; + attr = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY | background; break; case DW_COLOR_DEBUG: - attr = FOREGROUND_GREEN | BACKGROUND_WHITE; + attr = FOREGROUND_GREEN | background; break; } From 5fe904f97600e39180066db6379c3e9519e106b8 Mon Sep 17 00:00:00 2001 From: ars-ka0s <26339355+ars-ka0s@users.noreply.github.com> Date: Wed, 18 Jan 2023 21:19:00 -0600 Subject: [PATCH 36/36] Update changelog. --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 92975bf3..a55a44bb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -33,6 +33,8 @@ > > Add: "FX25TX 1" (or 16 or 32 or 64) +- Two additional color modes added to the -t command line option. 5 uses default background color and basic color commands. This should obey most terminal emulator color themes. 6 uses the RGB color mode and is stark black background. -t 2 on Windows implements a dark background mode as well. + ### Bugs Fixed: ###