diff --git a/CHANGES.md b/CHANGES.md index 589cd5a2..f979b541 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,9 @@ ### New Features: ### + +- 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. - Limited support for CM109/CM119 GPIO PTT on Windows. @@ -17,7 +20,7 @@ - The BEACON configuration now recognizes the SOURCE= option. This replaces the AX.25 source address rather than using the MYCALL value for the channel. This is useful for sending more than 5 analog telemetry channels. Use two, or more, source addresses with up to 5 analog channels each. -- For more flexibility, the FX.25 transmit property can now be set individually by channel, rather than having a global setting for all channels. The -X on the command line applies only to channel 0. For other channels you need to add a new line to the configuration file. +- For more flexibility, the FX.25 transmit property can now be set individually by channel, rather than having a global setting for all channels. The -X on the command line applies only to channel 0. For other channels you need to add a new line to the configuration file. You can specify a specific number of parity bytes (16, 32, 64) or 1 to choose automatically based on packet size. > After: "CHANNEL 1" (or other channel) > diff --git a/README.md b/README.md index fc08c69a..0bf92daf 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Why waste $200 and settle for mediocre receive performance from a 1980's technol ![](tnc-test-cd-results.png) -Dire Wolf now includes [FX.25](https://en.wikipedia.org/wiki/FX.25_Forward_Error_Correction) which adds Forward Error Correction (FEC) in a way that is completely compatible with existing systems. If both ends are capable of FX.25, your information will continue to get through under conditions where regular AX.25 is completely useless. +Dire Wolf now includes [FX.25](https://en.wikipedia.org/wiki/FX.25_Forward_Error_Correction) which adds Forward Error Correction (FEC) in a way that is completely compatible with existing systems. If both ends are capable of FX.25, your information will continue to get through under conditions where regular AX.25 is completely useless. This was originally developed for satellites and is now seeing widespread use on HF. ![](fx25.png) @@ -80,7 +80,21 @@ It can also be used as a virtual TNC for other applications such as [APRSIS32](h -- **Standard 300, 1200 & 9600 bps modems and more.** +- **Modems:** + + 300 bps AFSK for HF + + 1200 bps AFSK most common for VHF/UHF + + 2400 & 4800 bps PSK + + 9600 bps GMSK/G3RUH + + AIS reception + + EAS SAME reception + + - **DTMF ("Touch Tone") Decoding and Encoding.** diff --git a/cmake/modules/FindAvahi.cmake b/cmake/modules/FindAvahi.cmake index 4a3cdd0b..9dc27618 100644 --- a/cmake/modules/FindAvahi.cmake +++ b/cmake/modules/FindAvahi.cmake @@ -9,7 +9,7 @@ if(AVAHI_CLIENT_LIBRARY) set(AVAHI_CLIENT_FOUND TRUE) endif() -FIND_PACKAGE_HANDLE_STANDARD_ARGS(AVAHI DEFAULT_MSG AVAHI_COMMON_FOUND AVAHI_CLIENT_FOUND) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Avahi DEFAULT_MSG AVAHI_COMMON_FOUND AVAHI_CLIENT_FOUND) if (AVAHI_FOUND) set(AVAHI_INCLUDE_DIRS ${AVAHI_UI_INCLUDE_DIR}) diff --git a/cmake/modules/Findhamlib.cmake b/cmake/modules/Findhamlib.cmake index 2086a98f..16ca5685 100644 --- a/cmake/modules/Findhamlib.cmake +++ b/cmake/modules/Findhamlib.cmake @@ -52,7 +52,7 @@ find_library(HAMLIB_LIBRARY ) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(HAMLIB +find_package_handle_standard_args(hamlib DEFAULT_MSG HAMLIB_LIBRARY HAMLIB_INCLUDE_DIR diff --git a/cmake/modules/Findudev.cmake b/cmake/modules/Findudev.cmake index 38ba2e2f..c8c4b624 100644 --- a/cmake/modules/Findudev.cmake +++ b/cmake/modules/Findudev.cmake @@ -65,7 +65,7 @@ find_path(UDEV_INCLUDE_DIR ) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(UDEV +find_package_handle_standard_args(udev DEFAULT_MSG UDEV_LIBRARY UDEV_INCLUDE_DIR diff --git a/conf/generic.conf b/conf/generic.conf index 8630ed55..887dc229 100644 --- a/conf/generic.conf +++ b/conf/generic.conf @@ -369,10 +369,11 @@ %W% %C%# %C%# It is sometimes possible to recover frames with a bad FCS. -%C%# This applies to all channels. +%C%# This is not a global setting. +%C%# It applies only the the most recent CHANNEL specified. %C%# -%C%# 0 [NONE] - Don't try to repair. -%C%# 1 [SINGLE] - Attempt to fix single bit error. (default) +%C%# 0 - Don't try to repair. +%C%# 1 - Attempt to fix single bit error. (default) %C%# ... see User Guide for more values and in-depth discussion. %C%# %C% diff --git a/man/gen_packets.1 b/man/gen_packets.1 index a4042885..ba782fe1 100644 --- a/man/gen_packets.1 +++ b/man/gen_packets.1 @@ -100,9 +100,13 @@ Send output to .wav file. 8 bit audio rather than 16. .TP -.B "-2" +.BI "-2" 2 channels of audio rather than 1. +.TP +.BI "-v" "max[,incr]" +Variable speed with specified maximum error and optional increment. + .SH EXAMPLES .P diff --git a/src/agwlib.c b/src/agwlib.c index 33d490c8..2c03adaa 100644 --- a/src/agwlib.c +++ b/src/agwlib.c @@ -365,7 +365,7 @@ static void * tnc_listen_thread (void *arg) } /* - * Call to/from fields are 10 bytes but contents must not exceeed 9 characters. + * Call to/from fields are 10 bytes but contents must not exceed 9 characters. * It's not guaranteed that unused bytes will contain 0 so we * don't issue error message in this case. */ diff --git a/src/atest.c b/src/atest.c index 5f2dd054..aec626f2 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 John Langner, WB2OSZ +// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2019, 2021, 2022 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 @@ -23,11 +23,11 @@ * * Name: atest.c * - * Purpose: Test fixture for the AFSK demodulator. + * Purpose: Test fixture for the Dire Wolf demodulators. * * Inputs: Takes audio from a .WAV file instead of the audio device. * - * Description: This can be used to test the AFSK demodulator under + * Description: This can be used to test the demodulators under * controlled and reproducible conditions for tweaking. * * For example @@ -68,6 +68,7 @@ #include #include #include +#include #define ATEST_C 1 @@ -107,7 +108,7 @@ struct wav_header { /* .WAV file header. */ /* 8 bit samples are unsigned bytes */ /* in range of 0 .. 255. */ - /* 16 bit samples are signed short */ + /* 16 bit samples are little endian signed short */ /* in range of -32768 .. +32767. */ static struct { @@ -765,7 +766,7 @@ void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alev unsigned char *pinfo; int info_len; int h; - char heard[AX25_MAX_ADDR_LEN]; + char heard[2 * AX25_MAX_ADDR_LEN + 20]; char alevel_text[AX25_ALEVEL_TO_TEXT_SIZE]; packets_decoded_one++; @@ -810,6 +811,23 @@ void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alev } ax25_alevel_to_text (alevel, alevel_text); + /* As suggested by KJ4ERJ, if we are receiving from */ + /* WIDEn-0, it is quite likely (but not guaranteed), that */ + /* we are actually hearing the preceding station in the path. */ + + if (h >= AX25_REPEATER_2 && + strncmp(heard, "WIDE", 4) == 0 && + isdigit(heard[4]) && + heard[5] == '\0') { + + char probably_really[AX25_MAX_ADDR_LEN]; + ax25_get_addr_with_ssid(pp, h-1, probably_really); + + strlcat (heard, " (probably ", sizeof(heard)); + strlcat (heard, probably_really, sizeof(heard)); + 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); } @@ -877,7 +895,7 @@ void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alev -#if 1 // temp experiment TODO: remove this. +#if 0 // temp experiment #include "decode_aprs.h" #include "log.h" @@ -886,7 +904,7 @@ void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alev decode_aprs_t A; - decode_aprs (&A, pp, 0, 0); + decode_aprs (&A, pp, 0, NULL); // Temp experiment to see how different systems set the RR bits in the source and destination. // log_rr_bits (&A, pp); diff --git a/src/audio.c b/src/audio.c index b8cf6b11..5335db57 100644 --- a/src/audio.c +++ b/src/audio.c @@ -1532,7 +1532,7 @@ int audio_flush (int a) * (3) Call this function, which might or might not wait long enough. * (4) Add (1) and (2) resulting in when PTT should be turned off. * (5) Take difference between current time and desired PPT off time - * and wait for additoinal time if required. + * and wait for additional time if required. * *----------------------------------------------------------------*/ diff --git a/src/audio.h b/src/audio.h index 78327a73..87d6c9c7 100644 --- a/src/audio.h +++ b/src/audio.h @@ -72,7 +72,7 @@ struct audio_s { struct adev_param_s { - /* Properites of the sound device. */ + /* Properties of the sound device. */ int defined; /* Was device defined? */ /* First one defaults to yes. */ @@ -102,7 +102,7 @@ struct audio_s { /* This is the probability, in per cent, of randomly corrupting it. */ /* Normally this is 0. 25 would mean corrupt it 25% of the time. */ - int recv_error_rate; /* Similar but the % probablity of dropping a received frame. */ + int recv_error_rate; /* Similar but the % probability of dropping a received frame. */ float recv_ber; /* Receive Bit Error Rate (BER). */ /* Probability of inverting a bit coming out of the modem. */ diff --git a/src/audio_portaudio.c b/src/audio_portaudio.c index 77c4ee35..cb6ccf10 100644 --- a/src/audio_portaudio.c +++ b/src/audio_portaudio.c @@ -213,7 +213,21 @@ static int pa_devNN(char *deviceStr, char *_devName, size_t length, int *_devNo) while(*cPtr) { cVal = *cPtr++; if(cVal == ':') break; - if(((cVal >= ' ') && (cVal <= '~')) && (count < length)) { + + // See Issue 417. + // Originally this copied only printable ASCII characters (space thru ~). + // That is a problem for some locales that use UTF-8 characters in the device name. + // original: if(((cVal >= ' ') && (cVal <= '~')) && (count < length)) { + + // At first I was thinking we should keep the test for < ' ' but then I + // remembered that char type can be signed or unsigned depending on implementation. + // If characters are signed then a value above 0x7f would be considered negative. + + // It seems to me that the test for buffer full is off by one. + // count could reach length, leaving no room for a nul terminator. + // Compare has been changed so count is limited to length minus 1. + + if(count < length - 1) { _devName[count++] = cVal; } @@ -1149,7 +1163,7 @@ int audio_put (int a, int c) static double start = 0, end = 0, diff = 0; if(adev[a].outbuf_len == 0) - start = dtime_now(); + start = dtime_monotonic(); #endif if(c >= 0) { @@ -1178,7 +1192,7 @@ int audio_put (int a, int c) #ifdef __TIMED__ count += frames; if(c < 0) { // When the Ax25 frames are flushed. - end = dtime_now(); + end = dtime_monotonic(); diff = end - start; if(count) dw_printf ("Transfer Time:%3.9f No of Frames:%d Per frame:%3.9f speed:%f\n", @@ -1246,7 +1260,7 @@ int audio_flush (int a) * (3) Call this function, which might or might not wait long enough. * (4) Add (1) and (2) resulting in when PTT should be turned off. * (5) Take difference between current time and desired PPT off time - * and wait for additoinal time if required. + * and wait for additional time if required. * *----------------------------------------------------------------*/ diff --git a/src/audio_stats.c b/src/audio_stats.c index b6549cab..7b94b1ee 100644 --- a/src/audio_stats.c +++ b/src/audio_stats.c @@ -65,7 +65,6 @@ #include "audio_stats.h" #include "textcolor.h" -#include "dtime_now.h" #include "demod.h" /* for alevel_t & demod_get_audio_level() */ diff --git a/src/audio_win.c b/src/audio_win.c index 1ba64bba..85a1548b 100644 --- a/src/audio_win.c +++ b/src/audio_win.c @@ -84,7 +84,7 @@ static struct audio_s *save_audio_config_p; */ /* - * Originally, we had an abitrary buf time of 40 mS. + * Originally, we had an arbitrary buf time of 40 mS. * * For mono, the buffer size was rounded up from 3528 to 4k so * it was really about 50 mS per buffer or about 20 per second. @@ -1074,7 +1074,7 @@ int audio_flush (int a) * (3) Call this function, which might or might not wait long enough. * (4) Add (1) and (2) resulting in when PTT should be turned off. * (5) Take difference between current time and desired PPT off time - * and wait for additoinal time if required. + * and wait for additional time if required. * *----------------------------------------------------------------*/ diff --git a/src/ax25_link.c b/src/ax25_link.c index 09e71359..ab2875d9 100644 --- a/src/ax25_link.c +++ b/src/ax25_link.c @@ -347,7 +347,7 @@ typedef struct ax25_dlsm_s { // Sometimes the flow chart has SAT instead of SRT. // I think that is a typographical error. - float t1v; // How long to wait for an acknowlegement before resending. + float t1v; // How long to wait for an acknowledgement before resending. // Value used when starting timer T1, in seconds. // "FRACK" parameter in some implementations. // Typically it might be 3 seconds after frame has been @@ -6049,7 +6049,7 @@ static void check_need_for_response (ax25_dlsm_t *S, ax25_frame_type_t frame_typ * * Outputs: S->srt New smoothed roundtrip time. * - * S->t1v How long to wait for an acknowlegement before resending. + * S->t1v How long to wait for an acknowledgement before resending. * Value used when starting timer T1, in seconds. * Here it is dynamically adjusted. * diff --git a/src/ax25_pad.c b/src/ax25_pad.c index b5d47639..0f075808 100644 --- a/src/ax25_pad.c +++ b/src/ax25_pad.c @@ -1866,7 +1866,7 @@ packet_t ax25_get_nextp (packet_t this_p) * * Inputs: this_p - Current packet object. * - * release_time - Time as returned by dtime_now(). + * release_time - Time as returned by dtime_monotonic(). * *------------------------------------------------------------------------------*/ @@ -2923,7 +2923,9 @@ int ax25_alevel_to_text (alevel_t alevel, char text[AX25_ALEVEL_TO_TEXT_SIZE]) snprintf (text, AX25_ALEVEL_TO_TEXT_SIZE, "%d(%+d/%+d)", alevel.rec, alevel.mark, alevel.space); } - else if (alevel.mark == -1 && alevel.space == -1) { /* PSK - single number. */ + else if ((alevel.mark == -1 && alevel.space == -1) || /* PSK */ + (alevel.mark == -99 && alevel.space == -99)) { /* v. 1.7 "B" FM demodulator. */ + // ?? Where does -99 come from? snprintf (text, AX25_ALEVEL_TO_TEXT_SIZE, "%d", alevel.rec); } diff --git a/src/config.c b/src/config.c index 194d96a7..2588a96e 100644 --- a/src/config.c +++ b/src/config.c @@ -2864,6 +2864,9 @@ void config_init (char *fname, struct audio_s *p_audio_config, /* * CFILTER from-chan to-chan filter_specification_expression + * + * Why did I put this here? + * What would be a useful use case? Perhaps block by source or destination? */ else if (strcasecmp(t, "CFILTER") == 0) { @@ -4987,7 +4990,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, text_color_set(DW_COLOR_ERROR); dw_printf ("Config file, line %d: Old style 'BEACON' has been replaced with new commands.\n", line); - dw_printf ("Use PBEACON, OBEACON, or CBEACON instead.\n"); + dw_printf ("Use PBEACON, OBEACON, TBEACON, or CBEACON instead.\n"); } @@ -5002,6 +5005,9 @@ void config_init (char *fname, struct audio_s *p_audio_config, * New style with keywords for options. */ +// TODO: maybe add proportional pathing so multiple beacon timing does not need to be manually constructed? +// http://www.aprs.org/newN/ProportionalPathing.txt + else if (strcasecmp(t, "PBEACON") == 0 || strcasecmp(t, "OBEACON") == 0 || strcasecmp(t, "TBEACON") == 0 || @@ -5455,7 +5461,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, for (j=0; jchan_medium[j] == MEDIUM_RADIO || p_audio_config->chan_medium[j] == MEDIUM_NETTNC) { if (p_digi_config->filter_str[MAX_CHANS][j] == NULL) { - p_digi_config->filter_str[MAX_CHANS][j] = strdup("i/60"); + p_digi_config->filter_str[MAX_CHANS][j] = strdup("i/180"); } } } @@ -5822,6 +5828,10 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_ /* * Process symbol now that we have any later overlay. + * + * FIXME: Someone who used this was surprised to end up with Solar Powser (S-). + * overlay=S symbol="/-" + * We should complain if overlay used with symtab other than \. */ if (strlen(temp_symbol) > 0) { diff --git a/src/config.h b/src/config.h index 5ab2ae9a..360ac492 100644 --- a/src/config.h +++ b/src/config.h @@ -214,7 +214,7 @@ struct misc_config_s { char symbol; /* Symbol code. */ float power; /* For PHG. */ - float height; + float height; /* HAAT in feet */ float gain; /* Original protocol spec was unclear. */ /* Addendum 1.1 clarifies it is dBi not dBd. */ diff --git a/src/decode_aprs.c b/src/decode_aprs.c index 26e87cc7..d00ceb62 100644 --- a/src/decode_aprs.c +++ b/src/decode_aprs.c @@ -140,10 +140,11 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen); * * quiet - Suppress error messages. * - * third_party - True when parsing a third party header. + * third_party_src - Specify when parsing a third party header. * (decode_aprs is called recursively.) * This is mostly found when an IGate transmits a message * that came via APRS-IS. + * NULL when not third party payload. * * Outputs: A-> g_symbol_table, g_symbol_code, * g_lat, g_lon, @@ -156,11 +157,10 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen); * *------------------------------------------------------------------*/ -void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, int third_party) +void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_src) { - //dw_printf ("DEBUG decode_aprs quiet=%d, third_party=%d\n", quiet, third_party); + //dw_printf ("DEBUG decode_aprs quiet=%d, third_party=%p\n", quiet, third_party_src); - //char dest[AX25_MAX_ADDR_LEN]; unsigned char *pinfo; int info_len; @@ -174,10 +174,10 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, int third_party) A->g_quiet = quiet; if (isprint(*pinfo)) { - snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Unknown APRS Data Type Indicator \"%c\"", *pinfo); + snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "ERROR!!! Unknown APRS Data Type Indicator \"%c\"", *pinfo); } else { - snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "ERROR!!! Unknown APRS Data Type Indicator: unprintable 0x%02x", *pinfo); + snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "ERROR!!! Unknown APRS Data Type Indicator: unprintable 0x%02x", *pinfo); } A->g_symbol_table = '/'; /* Default to primary table. */ @@ -229,7 +229,12 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, int third_party) packet_t pp_payload = ax25_from_text ((char*)pinfo+1, 0); if (pp_payload != NULL) { - decode_aprs (A, pp_payload, quiet, 1); // 1 means used recursively + char payload_src[AX25_MAX_ADDR_LEN]; + memset(payload_src, 0, sizeof(payload_src)); + memcpy(payload_src, (char*)pinfo+1, sizeof(payload_src)-1); + char *q = strchr(payload_src, '>'); + if (q != NULL) *q = '\0'; + decode_aprs (A, pp_payload, quiet, payload_src); // 1 means used recursively ax25_delete (pp_payload); return; } @@ -243,8 +248,12 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, int third_party) /* * Extract source and destination including the SSID. */ - - ax25_get_addr_with_ssid (pp, AX25_SOURCE, A->g_src); + if (third_party_src != NULL) { + strlcpy (A->g_src, third_party_src, sizeof(A->g_src)); + } + else { + ax25_get_addr_with_ssid (pp, AX25_SOURCE, A->g_src); + } ax25_get_addr_with_ssid (pp, AX25_DESTINATION, A->g_dest); //dw_printf ("DEBUG decode_aprs source=%s, dest=%s\n", A->g_src, A->g_dest); @@ -303,7 +312,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, int third_party) if (strncmp((char*)pinfo, "!!", 2) == 0) { - aprs_ultimeter (A, (char*)pinfo, info_len); + aprs_ultimeter (A, (char*)pinfo, info_len); // TODO: produce obsolete error. } else { @@ -312,7 +321,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, int third_party) break; - //case '#': /* Peet Bros U-II Weather station */ + //case '#': /* Peet Bros U-II Weather station */ // TODO: produce obsolete error. //case '*': /* Peet Bros U-II Weather station */ //break; @@ -320,7 +329,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, int third_party) if (strncmp((char*)pinfo, "$ULTW", 5) == 0) { - aprs_ultimeter (A, (char*)pinfo, info_len); + aprs_ultimeter (A, (char*)pinfo, info_len); // TODO: produce obsolete error. } else { @@ -536,7 +545,13 @@ void decode_aprs_print (decode_aprs_t *A) { snprintf (rng, sizeof(rng), ", range=%.1f", A->g_range); strlcat (stemp, rng, sizeof(stemp)); } - text_color_set(DW_COLOR_DECODED); + + if (strncmp(stemp, "ERROR", 5) == 0) { + text_color_set(DW_COLOR_ERROR); + } + else { + text_color_set(DW_COLOR_DECODED); + } dw_printf("%s\n", stemp); /* @@ -556,7 +571,6 @@ void decode_aprs_print (decode_aprs_t *A) { * Any example was checked for each hemihemisphere using * http://www.amsat.org/cgi-bin/gridconv */ -// FIXME soften language about upper case. if (strlen(A->g_maidenhead) > 0) { @@ -1361,7 +1375,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int } } -/* 6th character of destintation indicates east / west. */ +/* 6th character of destination indicates east / west. */ /* * Example of apparently invalid encoding. 6th character missing. @@ -1565,7 +1579,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int * Purpose: Decode "Message Format." * The word message is used loosely all over the place, but it has a very specific meaning here. * - * Inputs: info - Pointer to Information field. + * Inputs: info - Pointer to Information field. Be careful not to modify it here! * ilen - Information field length. * quiet - suppress error messages. * @@ -1609,7 +1623,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int * *------------------------------------------------------------------*/ -static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int quiet) +static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int quiet) { struct aprs_message_s { @@ -1619,7 +1633,7 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q char message[256-1-9-1]; /* Officially up to 67 characters for message text. */ /* Relaxing seemingly arbitrary restriction here; it doesn't need to fit on a punched card. */ /* Wouldn't surprise me if others did not pay attention to the limit. */ - /* Optional { followed by 1-5 alphanumeric characters for message number */ + /* Optional '{' followed by 1-5 alphanumeric characters for message number */ /* If the first character is '?' it is a Directed Station Query. */ } *p; @@ -1751,6 +1765,16 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q text_color_set(DW_COLOR_ERROR); dw_printf("ERROR: Message number is missing after \"ack\".\n"); } + + // Xastir puts a carriage return on the end. + char *p = strchr(A->g_message_number, '\r'); + if (p != NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf("The APRS protocol specification says nothing about a possible carriage return after the\n"); + dw_printf("message id. Adding CR might prevent proper interoperability with with other applications.\n"); + *p = '\0'; + } + if (strlen(A->g_message_number) >= 3 && A->g_message_number[2] == '}') A->g_message_number[2] = '\0'; snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "\"%s\" ACKnowledged message number \"%s\" from \"%s\"", A->g_src, A->g_message_number, addressee); A->g_message_subtype = message_subtype_ack; @@ -1765,6 +1789,16 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q text_color_set(DW_COLOR_ERROR); dw_printf("ERROR: Message number is missing after \"rej\".\n"); } + + // Xastir puts a carriage return on the end. + char *p = strchr(A->g_message_number, '\r'); + if (p != NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf("The APRS protocol specification says nothing about a possible carriage return after the\n"); + dw_printf("message id. Adding CR might prevent proper interoperability with with other applications.\n"); + *p = '\0'; + } + if (strlen(A->g_message_number) >= 3 && A->g_message_number[2] == '}') A->g_message_number[2] = '\0'; snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "\"%s\" REJected message number \"%s\" from \"%s\"", A->g_src, A->g_message_number, addressee); A->g_message_subtype = message_subtype_ack; @@ -1788,13 +1822,23 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q // Look for message number. char *pno = strchr(p->message, '{'); if (pno != NULL) { - *pno = '\0'; - int mlen = strlen(pno+1); + strlcpy (A->g_message_number, pno+1, sizeof(A->g_message_number)); + + // Xastir puts a carriage return on the end. + char *p = strchr(A->g_message_number, '\r'); + if (p != NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf("The APRS protocol specification says nothing about a possible carriage return after the\n"); + dw_printf("message id. Adding CR might prevent proper interoperability with with other applications.\n"); + *p = '\0'; + } + + int mlen = strlen(A->g_message_number); if (mlen < 1 || mlen > 5) { text_color_set(DW_COLOR_ERROR); - dw_printf("Message number \"%s\" has length outside range of 1 to 5.\n", pno+1); + dw_printf("Message number \"%s\" has length outside range of 1 to 5.\n", A->g_message_number); } - strlcpy (A->g_message_number, pno+1, sizeof(A->g_message_number)); + // TODO: Complain if not alphanumeric. char ack[8] = ""; @@ -1824,6 +1868,11 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q /* No location so don't use process_comment () */ strlcpy (A->g_comment, p->message, sizeof(A->g_comment)); + // Remove message number when displaying message text. + pno = strchr(A->g_comment, '{'); + if (pno != NULL) { + *pno = '\0'; + } } } @@ -3752,9 +3801,14 @@ time_t get_timestamp (decode_aprs_t *A, char *p) * It is composed of: * a pair of letters in range A to R. * a pair of digits in range of 0 to 9. - * a pair of letters in range of A to X. + * an optional pair of letters in range of A to X. * - * The APRS spec says that all letters must be transmitted in upper case. + * The spec says: + * "All letters must be transmitted in upper case. + * Letters may be received in upper case or lower case." + * + * Typically the second set of letters is written in lower case. + * An earlier version incorrectly produced an error if lower case found. * * * Examples from APRS spec: @@ -3775,25 +3829,10 @@ int get_maidenhead (decode_aprs_t *A, char *p) /* We have 4 characters matching the rule. */ - if (islower(p[0]) || islower(p[1])) { - if ( ! A->g_quiet) { - text_color_set(DW_COLOR_ERROR); - dw_printf("Warning: Lower case letter in Maidenhead locator. Specification requires upper case.\n"); - } - } - if (toupper(p[4]) >= 'A' && toupper(p[4]) <= 'X' && toupper(p[5]) >= 'A' && toupper(p[5]) <= 'X') { /* We have 6 characters matching the rule. */ - - if (islower(p[4]) || islower(p[5])) { - if ( ! A->g_quiet) { - text_color_set(DW_COLOR_ERROR); - dw_printf("Warning: Lower case letter in Maidenhead locator. Specification requires upper case.\n"); - } - } - return 6; } @@ -4649,7 +4688,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen) int a = A->g_comment[match[0].rm_so+2]; int o = A->g_comment[match[0].rm_so+3]; - dw_printf("DAO start=%d, end=%d\n", (int)(match[0].rm_so), (int)(match[0].rm_eo)); + //dw_printf("DAO start=%d, end=%d\n", (int)(match[0].rm_so), (int)(match[0].rm_eo)); /* @@ -5035,7 +5074,9 @@ int main (int argc, char *argv[]) /* Try to process it. */ text_color_set(DW_COLOR_REC); - dw_printf("\n%s\n", stuff); + dw_printf("\n"); + ax25_safe_print (stuff, -1, 0); + dw_printf("\n"); // Do we have monitor format, KISS, or AX.25 frame? @@ -5144,7 +5185,7 @@ int main (int argc, char *argv[]) ax25_safe_print ((char *)pinfo, info_len, 1); // Display non-ASCII to hexadecimal. dw_printf ("\n"); - decode_aprs (&A, pp, 0, 0); // Extract information into structure. + decode_aprs (&A, pp, 0, NULL); // Extract information into structure. decode_aprs_print (&A); // Now print it in human readable format. @@ -5165,7 +5206,7 @@ int main (int argc, char *argv[]) if (pp != NULL) { decode_aprs_t A; - decode_aprs (&A, pp, 0, 0); // Extract information into structure. + decode_aprs (&A, pp, 0, NULL); // Extract information into structure. decode_aprs_print (&A); // Now print it in human readable format. diff --git a/src/decode_aprs.h b/src/decode_aprs.h index 324be2b9..f25d1e91 100644 --- a/src/decode_aprs.h +++ b/src/decode_aprs.h @@ -77,7 +77,7 @@ typedef struct decode_aprs_s { message_subtype_directed_query } g_message_subtype; /* Various cases of the overloaded "message." */ - char g_message_number[8]; /* Message number. Should be 1 - 5 alphanumeric characters if used. */ + char g_message_number[12]; /* Message number. Should be 1 - 5 alphanumeric characters if used. */ /* Addendum 1.1 has new format {mm} or {mm}aa with only two */ /* characters for message number and an ack riding piggyback. */ @@ -142,9 +142,9 @@ typedef struct decode_aprs_s { -extern void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, int third_party); +extern void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_src); extern void decode_aprs_print (decode_aprs_t *A); -#endif \ No newline at end of file +#endif diff --git a/src/demod.c b/src/demod.c index 482c1076..9f94dd8d 100644 --- a/src/demod.c +++ b/src/demod.c @@ -832,7 +832,7 @@ int demod_init (struct audio_s *pa) * * Name: demod_get_sample * - * Purpose: Get one audio sample fromt the specified sound input source. + * Purpose: Get one audio sample from the specified sound input source. * * Inputs: a - Index for audio device. 0 = first. * diff --git a/src/demod_afsk.c b/src/demod_afsk.c index f32137c5..b4d6c295 100644 --- a/src/demod_afsk.c +++ b/src/demod_afsk.c @@ -309,10 +309,6 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq, D->lp_window = BP_WINDOW_TRUNCATED; } - D->agc_fast_attack = 0.820; - D->agc_slow_decay = 0.000214; - D->agc_fast_attack = 0.45; - D->agc_slow_decay = 0.000195; D->agc_fast_attack = 0.70; D->agc_slow_decay = 0.000090; @@ -372,10 +368,16 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq, // For scaling phase shift into normallized -1 to +1 range for mark and space. D->u.afsk.normalize_rpsam = 1.0 / (0.5 * abs(mark_freq - space_freq) * 2 * M_PI / samples_per_sec); + // New "B" demodulator does not use AGC but demod.c needs this to derive "quick" and + // "sluggish" values for overall signal amplitude. That probably should be independent + // of these values. + D->agc_fast_attack = 0.70; + D->agc_slow_decay = 0.000090; + D->pll_locked_inertia = 0.74; D->pll_searching_inertia = 0.50; - D->alevel_mark_peak = -1; // FIXME: disable display + D->alevel_mark_peak = -1; // Disable received signal (m/s) display. D->alevel_space_peak = -1; break; @@ -868,6 +870,7 @@ static void nudge_pll (int chan, int subchan, int slice, float demod_out, struct { D->slicer[slice].prev_d_c_pll = D->slicer[slice].data_clock_pll; + // Perform the add as unsigned to avoid signed overflow error. D->slicer[slice].data_clock_pll = (signed)((unsigned)(D->slicer[slice].data_clock_pll) + (unsigned)(D->pll_step_per_sample)); @@ -901,7 +904,15 @@ static void nudge_pll (int chan, int subchan, int slice, float demod_out, struct #endif + +#if 1 hdlc_rec_bit (chan, subchan, slice, demod_out > 0, 0, quality); +#else // TODO: new feature to measure data speed error. +// Maybe hdlc_rec_bit could provide indication when frame starts. + hdlc_rec_bit_new (chan, subchan, slice, demod_out > 0, 0, quality, + &(D->slicer[slice].pll_nudge_total), &(D->slicer[slice].pll_symbol_count)); + D->slicer[slice].pll_symbol_count++; +#endif pll_dcd_each_symbol2 (D, chan, subchan, slice); } @@ -912,12 +923,14 @@ static void nudge_pll (int chan, int subchan, int slice, float demod_out, struct pll_dcd_signal_transition2 (D, slice, D->slicer[slice].data_clock_pll); +// TODO: signed int before = (signed int)(D->slicer[slice].data_clock_pll); // Treat as signed. if (D->slicer[slice].data_detect) { D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_locked_inertia); } else { D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_searching_inertia); } +// TODO: D->slicer[slice].pll_nudge_total += (int64_t)((signed int)(D->slicer[slice].data_clock_pll)) - (int64_t)before; } /* diff --git a/src/direwolf.c b/src/direwolf.c index 77cd27c4..3ad404e6 100644 --- a/src/direwolf.c +++ b/src/direwolf.c @@ -1426,7 +1426,7 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev // we still want to decode it for logging and other processing. // Just be quiet about errors if "-qd" is set. - decode_aprs (&A, pp, q_d_opt, 0); + decode_aprs (&A, pp, q_d_opt, NULL); if ( ! q_d_opt ) { @@ -1554,10 +1554,14 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev } else { -/* Send to Internet server if option is enabled. */ -/* Consider only those with correct CRC. */ - - if (ax25_is_aprs(pp) && retries == RETRY_NONE) { +/* + * Send to the IGate processing. + * Use only those with correct CRC; We don't want to spread corrupted data! + * Our earlier "fix bits" hack could allow corrupted information to get thru. + * 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) ) { igate_send_rec_packet (chan, pp); } @@ -1572,24 +1576,23 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev /* - * APRS digipeater. + * Send to APRS digipeater. * Use only those with correct CRC; We don't want to spread corrupted data! + * Our earlier "fix bits" hack could allow corrupted information to get thru. + * However, if it used FEC mode (FX.25. IL2P), we have much higher level of + * confidence that it is correct. */ - -// TODO: Should also use anything received with FX.25 because it is known to be good. -// Our earlier "fix bits" hack could allow corrupted information to get thru. - - if (ax25_is_aprs(pp) && retries == RETRY_NONE) { + if (ax25_is_aprs(pp) && ( retries == RETRY_NONE || is_fx25) ) { digipeater (chan, pp); } /* * Connected mode digipeater. - * Use only those with correct CRC. + * Use only those with correct CRC (or using FEC.) */ - if (retries == RETRY_NONE) { + if (retries == RETRY_NONE || is_fx25) { cdigipeater (chan, pp); } diff --git a/src/direwolf.h b/src/direwolf.h index 8351e1a9..3a47398c 100644 --- a/src/direwolf.h +++ b/src/direwolf.h @@ -38,9 +38,7 @@ #endif /* - * Previously, we could handle only a single audio device. - * This meant we could have only two radio channels. - * In version 1.2, we relax this restriction and allow more audio devices. + * Maximum number of audio devices. * Three is probably adequate for standard version. * Larger reasonable numbers should also be fine. * diff --git a/src/dwgpsd.c b/src/dwgpsd.c index cc2f801a..1bc9d47a 100644 --- a/src/dwgpsd.c +++ b/src/dwgpsd.c @@ -57,17 +57,36 @@ -// An incompatibility was introduced with version 7 -// and again with 9 and again with 10. +// An API incompatibility was introduced with API version 7. +// and again with 9. +// and again with 10. +// We deal with it by using a bunch of conditional code such as: +// #if GPSD_API_MAJOR_VERSION >= 9 -// release lib version API Raspberry Pi OS -// 3.22 28 11 bullseye -// 3.23 29 12 -#if GPSD_API_MAJOR_VERSION < 5 || GPSD_API_MAJOR_VERSION > 12 -#error libgps API version might be incompatible. +// release lib version API Raspberry Pi OS Testing status +// 3.22 28 11 bullseye OK. +// 3.23 29 12 OK. +// 3.25 30 14 OK, Jan. 2023 + + +// Previously the compilation would fail if the API version was later +// than the last one tested. Now it is just a warning because it changes so +// often but more recent versions have not broken backward compatibility. + +#define MAX_TESTED_VERSION 14 + +#if (GPSD_API_MAJOR_VERSION < 5) || (GPSD_API_MAJOR_VERSION > MAX_TESTED_VERSION) +#pragma message "Your version of gpsd might be incompatible with this application." +#pragma message "The libgps application program interface (API) often" +#pragma message "changes to be incompatible with earlier versions." +// I could not figure out how to do value substitution here. +#pragma message "You have libgpsd API version GPSD_API_MAJOR_VERSION." +#pragma message "The last that has been tested is MAX_TESTED_VERSION." +#pragma message "Even if this builds successfully, it might not run properly." #endif + /* * Information for interface to gpsd daemon. */ @@ -167,6 +186,22 @@ static void * read_gpsd_thread (void *arg); * can't find it there. Solution is to define environment variable: * * export LD_LIBRARY_PATH=/use/local/lib + * + * January 2023: Now using 64 bit Raspberry Pi OS, bullseye. + * See https://gitlab.com/gpsd/gpsd/-/blob/master/build.adoc + * Try to install in proper library place so we don't have to mess with LD_LIBRARY_PATH. + * + * (Remove any existing gpsd first so we are not mixing mismatched pieces.) + * + * sudo apt-get install libncurses5-dev + * sudo apt-get install gtk+-3.0 + * + * git clone https://gitlab.com/gpsd/gpsd.git gpsd-gitlab + * cd gpsd-gitlab + * scons prefix=/usr libdir=lib/aarch64-linux-gnu + * [ scons check ] + * sudo scons udev-install + * */ diff --git a/src/encode_aprs.c b/src/encode_aprs.c index a4597f74..20992bf7 100644 --- a/src/encode_aprs.c +++ b/src/encode_aprs.c @@ -551,6 +551,15 @@ int encode_position (int messaging, int compressed, double lat, double lon, int int result_len = 0; if (compressed) { + +// Thought: +// https://groups.io/g/direwolf/topic/92718535#6886 +// When speed is zero, we could put the altitude in the compressed +// position rather than having /A=999999. +// However, the resolution would be decreased and that could be important +// when hiking in hilly terrain. It would also be confusing to +// flip back and forth between two different representations. + aprs_compressed_pos_t *p = (aprs_compressed_pos_t *)presult; p->dti = messaging ? '=' : '!'; diff --git a/src/fsk_demod_state.h b/src/fsk_demod_state.h index bf8d23b5..c9b26c23 100644 --- a/src/fsk_demod_state.h +++ b/src/fsk_demod_state.h @@ -2,6 +2,8 @@ #ifndef FSK_DEMOD_STATE_H +#include // int64_t + #include "rpack.h" #include "audio.h" // for enum modem_t @@ -43,8 +45,9 @@ typedef struct cic_s { } cic_t; -#define MAX_FILTER_SIZE 404 /* 401 is needed for profile A, 300 baud & 44100. Revisit someday. */ - +#define MAX_FILTER_SIZE 480 /* 401 is needed for profile A, 300 baud & 44100. Revisit someday. */ + // Size comes out to 417 for 1200 bps with 48000 sample rate + // v1.7 - Was 404. Bump up to 480. struct demodulator_state_s { @@ -216,6 +219,12 @@ struct demodulator_state_s signed int prev_d_c_pll; // Previous value of above, before // incrementing, to detect overflows. + int pll_symbol_count; // Number symbols during time nudge_total is accumulated. + int64_t pll_nudge_total; // Sum of DPLL nudge amounts. + // Both of these are cleared at start of frame. + // At end of frame, we can see if incoming + // baud rate is a little off. + int prev_demod_data; // Previous data bit detected. // Used to look for transitions. float prev_demod_out_f; @@ -358,7 +367,7 @@ struct demodulator_state_s // Add a sample to the total when putting it in our array of recent samples. // Subtract it from the total when it gets pushed off the end. // We can also eliminate the need to shift them all down by using a circular buffer. - // This only works with integers because float would have cummulated round off errors. + // This only works with integers because float would have cumulated round off errors. cic_t cic_center1; cic_t cic_above; diff --git a/src/fx25_init.c b/src/fx25_init.c index 2844fb93..47423254 100644 --- a/src/fx25_init.c +++ b/src/fx25_init.c @@ -371,6 +371,7 @@ int fx25_pick_mode (int fx_mode, int dlen) // The PRUG FX.25 TNC has additional modes that will handle larger frames // by using multiple RS blocks. This is a future possibility but needs // to be coordinated with other FX.25 developers so we maintain compatibility. +// See https://web.tapr.org/meetings/DCC_2020/JE1WAZ/DCC-2020-PRUG-FINAL.pptx static const int prefer[6] = { 0x04, 0x03, 0x06, 0x09, 0x05, 0x01 }; for (int k = 0; k < 6; k++) { diff --git a/src/gen_packets.c b/src/gen_packets.c index ba58abbd..97286119 100644 --- a/src/gen_packets.c +++ b/src/gen_packets.c @@ -56,7 +56,15 @@ * gen_packets -n 100 -o z2.wav * atest z2.wav * - * + * Variable speed. e.g. 95% to 105% of normal speed. + * Required parameter is max % below and above normal. + * Optionally specify step other than 0.1%. + * Used to test how tolerant TNCs are to senders not + * not using exactly the right baud rate. + * + * gen_packets -v 5 + * gen_packets -v 5,0.5 + * *------------------------------------------------------------------*/ @@ -67,6 +75,7 @@ #include #include #include +#include #include "audio.h" #include "ax25_pad.h" @@ -100,6 +109,7 @@ static float g_noise_level = 0; static int g_morse_wpm = 0; /* Send morse code at this speed. */ + static struct audio_s modem; @@ -179,6 +189,9 @@ int main(int argc, char **argv) int X_opt = 0; // send FX.25 int I_opt = -1; // send IL2P rather than AX.25, normal polarity int i_opt = -1; // send IL2P rather than AX.25, inverted polarity + double variable_speed_max_error = 0; // both in percent + double variable_speed_increment = 0.1; + /* * Set up default values for the modem. @@ -230,7 +243,7 @@ int main(int argc, char **argv) /* ':' following option character means arg is required. */ - c = getopt_long(argc, argv, "gjJm:s:a:b:B:r:n:N:o:z:82M:X:I:i:", + c = getopt_long(argc, argv, "gjJm:s:a:b:B:r:n:N:o:z:82M:X:I:i:v:", long_options, &option_index); if (c == -1) break; @@ -469,6 +482,16 @@ int main(int argc, char **argv) i_opt = atoi(optarg); break; + case 'v': // Variable speed data + an - this percentage + // optional comma and increment. + + variable_speed_max_error = fabs(atof(optarg)); + char *q = strchr(optarg, ','); + if (q != NULL) { + variable_speed_increment = fabs(atof(q+1)); + } + break; + case '?': /* Unknown option message was already printed. */ @@ -479,7 +502,7 @@ int main(int argc, char **argv) /* Should not be here. */ text_color_set(DW_COLOR_ERROR); - dw_printf("?? getopt returned character code 0%o ??\n", c); + dw_printf("?? getopt returned character code 0%o ??\n", (unsigned)c); usage (argv); } } @@ -647,9 +670,35 @@ int main(int argc, char **argv) */ text_color_set(DW_COLOR_INFO); dw_printf ("built in message...\n"); - - if (packet_count > 0) { +// +// Generate packets with variable speed. +// This overrides any other number of packets or adding noise. +// + + + if (variable_speed_max_error != 0) { + + int normal_speed = modem.achan[0].baud; + + text_color_set(DW_COLOR_INFO); + dw_printf ("Variable speed.\n"); + + for (double speed_error = - variable_speed_max_error; + speed_error <= variable_speed_max_error + 0.001; + speed_error += variable_speed_increment) { + + // Baud is int so we get some roundoff. Make it real? + modem.achan[0].baud = (int)round(normal_speed * (1. + speed_error / 100.)); + gen_tone_init (&modem, amplitude/2, 1); + + char stemp[256]; + snprintf (stemp, sizeof(stemp), "WB2OSZ-15>TEST:, speed %+0.1f%% The quick brown fox jumps over the lazy dog!", speed_error); + send_packet (stemp); + } + } + + else if (packet_count > 0) { /* * Generate packets with increasing noise level. @@ -730,6 +779,7 @@ static void usage (char **argv) dw_printf (" -o Send output to .wav file.\n"); dw_printf (" -8 8 bit audio rather than 16.\n"); dw_printf (" -2 2 channels (stereo) audio rather than one channel.\n"); + dw_printf (" -v max[,incr] Variable speed with specified maximum error and increment.\n"); // dw_printf (" -z Number of leading zero bits before frame.\n"); // dw_printf (" Default is 12 which is .01 seconds at 1200 bits/sec.\n"); diff --git a/src/igate.c b/src/igate.c index bb63e367..dea0cba6 100644 --- a/src/igate.c +++ b/src/igate.c @@ -203,6 +203,8 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S #if ITEST +// TODO: Add to automated tests. + /* For unit testing. */ int main (int argc, char *argv[]) @@ -326,7 +328,7 @@ static int stats_uplink_packets; /* Number of packets passed along to the IGate /* server after filtering. */ static int stats_uplink_bytes; /* Total number of bytes sent to IGate server */ - /* including login, packets, and hearbeats. */ + /* including login, packets, and heartbeats. */ static int stats_downlink_bytes; /* Total number of bytes from IGate server including */ /* packets, heartbeats, other messages. */ @@ -1219,7 +1221,7 @@ static void send_packet_to_server (packet_t pp, int chan) * Name: send_msg_to_server * * Purpose: Send something to the IGate server. - * This one function should be used for login, hearbeats, + * This one function should be used for login, heartbeats, * and packets. * * Inputs: imsg - Message. We will add CR/LF here. @@ -1713,7 +1715,14 @@ static void * satgate_delay_thread (void *arg) * K1RI-2>APWW10,WIDE1-1,WIDE2-1,qAS,K1RI:/221700h/9AmAT3PQ3S,WIDE1-1,WIDE2-1,qAR,W1TG-1:`c)@qh\>/"50}TinyTrak4 Mobile * - * Notice how the final address in the header might not + * This is interesting because the source is not a valid AX.25 address. + * Non-RF stations can have 2 alphanumeric characters for SSID. + * In this example, the WHO-IS server is responding to a message. + * + * WHO-IS>APJIW4,TCPIP*,qAC,AE5PL-JF::ZL1JSH-9 :Charles Beadfield/New Zealand{583 + * + * + * Notice how the final digipeater address, in the header, might not * be a valid AX.25 address. We see a 9 character address * (with no ssid) and an ssid of two letters. * We don't care because we end up discarding them before @@ -1728,29 +1737,37 @@ 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. + static void maybe_xmit_packet_from_igate (char *message, int to_chan) { - packet_t pp3; - char payload[AX25_MAX_PACKET_LEN]; /* what is max len? */ - char src[AX25_MAX_ADDR_LEN]; /* Source address. */ - - char *pinfo = NULL; - int info_len; int n; assert (to_chan >= 0 && to_chan < MAX_CHANS); - /* - * Try to parse it into a packet object. - * This will contain "q constructs" and we might see an address - * with two alphnumeric characters in the SSID so we must use - * the non-strict parsing. + * Try to parse it into a packet object; we need this for the packet filtering. + * + * We use the non-strict option because there the via path can have: + * - station names longer than 6. + * - alphanumeric SSID. + * - lower case for "q constructs. + * We don't care about any of those because the via path will be discarded anyhow. + * + * The other issue, that I did not think of originally, is that the "source" + * address might not conform to AX.25 restrictions when it originally came + * from a non-RF source. For example an APRS "message" might be sent to the + * "WHO-IS" server, and the reply message would have that for the source address. + * + * Originally, I used the source address from the packet object but that was + * missing the alphanumeric SSID. This needs to be done differently. * - * Bug: Up to 8 digipeaters are allowed in radio format. - * There is a potential of finding a larger number here. + * Potential Bug: Up to 8 digipeaters are allowed in radio format. + * Is there a possibility of finding a larger number here? */ - pp3 = ax25_from_text(message, 0); + packet_t pp3 = ax25_from_text(message, 0); if (pp3 == NULL) { text_color_set(DW_COLOR_ERROR); dw_printf ("Tx IGate: Could not parse message from server.\n"); @@ -1758,7 +1775,21 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan) return; } - ax25_get_addr_with_ssid (pp3, AX25_SOURCE, src); +// Issue 408: The source address might not be valid AX.25 because it +// came from a non-RF station. e.g. some server responding to a message. +// We need to take source address from original rather than extracting it +// from the packet object. + + char src[AX25_MAX_ADDR_LEN]; /* Source address. */ + memset (src, 0, sizeof(src)); + memcpy (src, message, sizeof(src)-1); + char *gt = strchr(src, '>'); + if (gt != NULL) { + *gt = '\0'; + } + +// FIXME NO! + ///////ax25_get_addr_with_ssid (pp3, AX25_SOURCE, src); /* * Drop if path contains: @@ -1770,8 +1801,8 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan) ax25_get_addr_with_ssid (pp3, n + AX25_REPEATER_1, via); - if (strcmp(via, "qAX") == 0 || - strcmp(via, "TCPXX") == 0 || + if (strcmp(via, "qAX") == 0 || // qAX deprecated. http://www.aprs-is.net/q.aspx + strcmp(via, "TCPXX") == 0 || // TCPXX deprecated. strcmp(via, "RFONLY") == 0 || strcmp(via, "NOGATE") == 0) { @@ -1807,7 +1838,8 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan) // TODO: Not quite this simple. Should have a function to check for position. // $ raw gps could be a position. @ could be weather data depending on symbol. - info_len = ax25_get_info (pp3, (unsigned char **)(&pinfo)); + char *pinfo = NULL; + int info_len = ax25_get_info (pp3, (unsigned char **)(&pinfo)); int msp_special_case = 0; @@ -1837,12 +1869,6 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan) // Previously there was a debug message here about the packet being dropped by filtering. // This is now handled better by the "-df" command line option for filtering details. - // TODO: clean up - remove these lines. - //if (s_debug >= 1) { - // text_color_set(DW_COLOR_INFO); - // dw_printf ("Packet from IGate to channel %d was rejected by filter: %s\n", to_chan, save_digi_config_p->filter_str[MAX_CHANS][to_chan]); - //} - ax25_delete (pp3); return; } @@ -1851,13 +1877,15 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan) /* - * Remove the VIA path. + * We want to discard the via path, as received from the APRS-IS, then + * replace it with TCPIP and our own call, marked as used. + * * * For example, we might get something like this from the server. - * K1USN-1>APWW10,TCPIP*,qAC,N5JXS-F1:T#479,100,048,002,500,000,10000000<0x0d><0x0a> + * K1USN-1>APWW10,TCPIP*,qAC,N5JXS-F1:T#479,100,048,002,500,000,10000000 * - * We want to reduce it to this before wrapping it as third party traffic. - * K1USN-1>APWW10:T#479,100,048,002,500,000,10000000<0x0d><0x0a> + * We want to transform it to this before wrapping it as third party traffic. + * K1USN-1>APWW10,TCPIP,mycall*:T#479,100,048,002,500,000,10000000 */ /* @@ -1885,36 +1913,23 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan) * * What is the ",I" construct? * Do we care here? - * Is is something new and improved that we should be using in the other direction? + * Is it something new and improved that we should be using in the other direction? */ - while (ax25_get_num_repeaters(pp3) > 0) { - ax25_remove_addr (pp3, AX25_REPEATER_1); - } - + char payload[AX25_MAX_PACKET_LEN]; -/* - * Replace the VIA path with TCPIP and my call. - * Mark my call as having been used. - */ - ax25_set_addr (pp3, AX25_REPEATER_1, "TCPIP"); - ax25_set_h (pp3, AX25_REPEATER_1); - ax25_set_addr (pp3, AX25_REPEATER_2, save_audio_config_p->achan[to_chan].mycall); - ax25_set_h (pp3, AX25_REPEATER_2); + char dest[AX25_MAX_ADDR_LEN]; /* Destination field. */ + ax25_get_addr_with_ssid (pp3, AX25_DESTINATION, dest); + snprintf (payload, sizeof(payload), "%s>%s,TCPIP,%s*:%s", + src, dest, save_audio_config_p->achan[to_chan].mycall, pinfo); -/* - * Convert to text representation. - */ - memset (payload, 0, sizeof(payload)); - ax25_format_addrs (pp3, payload); - info_len = ax25_get_info (pp3, (unsigned char **)(&pinfo)); - (void)(info_len); - strlcat (payload, pinfo, sizeof(payload)); #if DEBUGx text_color_set(DW_COLOR_DEBUG); - dw_printf ("Tx IGate: payload=%s\n", payload); + dw_printf ("Tx IGate: DEBUG payload=%s\n", payload); #endif + + /* * Encapsulate for sending over radio if no reason to drop it. @@ -1931,19 +1946,13 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan) */ if (ig_to_tx_allow (pp3, to_chan)) { char radio [2400]; - packet_t pradio; - snprintf (radio, sizeof(radio), "%s>%s%d%d%s:}%s", save_audio_config_p->achan[to_chan].mycall, APP_TOCALL, MAJOR_VERSION, MINOR_VERSION, save_igate_config_p->tx_via, payload); - pradio = ax25_from_text (radio, 1); - - /* Oops. Didn't have a check for NULL here. */ - /* Could this be the cause of rare and elusive crashes in 1.2? */ - + packet_t pradio = ax25_from_text (radio, 1); if (pradio != NULL) { #if ITEST diff --git a/src/il2p_header.c b/src/il2p_header.c index 9a1e9ea4..0ab34a01 100644 --- a/src/il2p_header.c +++ b/src/il2p_header.c @@ -437,7 +437,7 @@ packet_t il2p_decode_header_type_1 (unsigned char *hdr, int num_sym_changed) // However, I have seen cases, where the error rate is very high, where the RS decoder // thinks it found a valid code block by changing one symbol but it was the wrong one. // The result is trash. This shows up as address fields like 'R&G4"A' and 'TEW\ !'. -// I added a sanity check here to catch characters other than uppper case letters and digits. +// I added a sanity check here to catch characters other than upper case letters and digits. // The frame should be rejected in this case. The question is whether to discard it // silently or print a message so the user can see that something strange is happening? // My current thinking is that it should be silently ignored if the header has been diff --git a/src/il2p_payload.c b/src/il2p_payload.c index 67c79a98..d5fb4887 100644 --- a/src/il2p_payload.c +++ b/src/il2p_payload.c @@ -194,7 +194,7 @@ int il2p_encode_payload (unsigned char *payload, int payload_size, int max_fec, * Purpose: Extract original data from encoded payload. * * Inputs: received Array of bytes. Size is unknown but in practice it - * must not exceeed IL2P_MAX_ENCODED_SIZE. + * must not exceed IL2P_MAX_ENCODED_SIZE. * payload_size 0 to 1023. (IL2P_MAX_PAYLOAD_SIZE) * Expected result size based on header. * max_fec true for 16 parity symbols, false for automatic. diff --git a/src/kiss_frame.h b/src/kiss_frame.h index 47f3dfe5..941f5c01 100644 --- a/src/kiss_frame.h +++ b/src/kiss_frame.h @@ -84,6 +84,8 @@ struct kissport_status_s { // The default is a limit of 3 client applications at the same time. // You can increase the limit by changing the line below. // A larger number consumes more resources so don't go crazy by making it larger than needed. + // TODO: Should this be moved to direwolf.h so max number of audio devices + // client apps are in the same place? #define MAX_NET_CLIENTS 3 diff --git a/src/kissutil.c b/src/kissutil.c index 470d178d..fcd86088 100644 --- a/src/kissutil.c +++ b/src/kissutil.c @@ -67,7 +67,6 @@ #include "serial_port.h" #include "kiss_frame.h" #include "dwsock.h" -#include "dtime_now.h" #include "audio.h" // for DEFAULT_TXDELAY, etc. #include "dtime_now.h" diff --git a/src/mheard.c b/src/mheard.c index e751c365..5c20f303 100644 --- a/src/mheard.c +++ b/src/mheard.c @@ -434,6 +434,7 @@ void mheard_save_rf (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, r * N1HKO-10>APJI40,TCPIP*,qAC,N1HKO-JS:APWW10,WIDE1-1,WIDE2-1,qAS,K1RI:/221700h/9AmAT3PQ3S,WIDE1-1,WIDE2-1,qAR,W1TG-1:`c)@qh\>/"50}TinyTrak4 Mobile + * WHO-IS>APJIW4,TCPIP*,qAC,AE5PL-JF::WB2OSZ :C/Billerica Amateur Radio Society/MA/United States{XF}WO * * Notice how the final address in the header might not * be a valid AX.25 address. We see a 9 character address @@ -443,6 +444,7 @@ void mheard_save_rf (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, r * a clue about the journey taken but I don't think we care here. * * All we should care about here is the the source address. + * Note that the source address might not adhere to the AX.25 format. * * Description: * @@ -450,21 +452,25 @@ void mheard_save_rf (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, r void mheard_save_is (char *ptext) { - packet_t pp; time_t now = time(NULL); char source[AX25_MAX_ADDR_LEN]; - mheard_t *mptr; + +#if 1 +// It is possible that source won't adhere to the AX.25 restrictions. +// So we simply extract the source address, as text, from the beginning rather than +// using ax25_from_text() and ax25_get_addr_with_ssid(). + + memset (source, 0, sizeof(source)); + memcpy (source, ptext, sizeof(source)-1); + char *g = strchr(source, '>'); + if (g != NULL) *g = '\0'; + +#else /* - * Try to parse it into a packet object. - * This will contain "q constructs" and we might see an address - * with two alphnumeric characters in the SSID so we must use - * the non-strict parsing. - * - * Bug: Up to 8 digipeaters are allowed in radio format. - * There is a potential of finding a larger number here. + * Keep this here in case I want to revive it to get location. */ - pp = ax25_from_text(ptext, 0); + packet_t pp = ax25_from_text(ptext, 0); if (pp == NULL) { if (mheard_debug) { @@ -475,13 +481,17 @@ void mheard_save_is (char *ptext) return; } - ax25_get_addr_with_ssid (pp, AX25_SOURCE, source); + //////ax25_get_addr_with_ssid (pp, AX25_SOURCE, source); +#endif - mptr = mheard_ptr(source); + mheard_t *mptr = mheard_ptr(source); if (mptr == NULL) { int i; /* * Not heard before. Add it. + * Observation years later: + * Hmmmm. I wonder why I did not store the location if available. + * An earlier example has an APRSdroid station reporting location without using [ham] RF. */ if (mheard_debug) { @@ -537,7 +547,9 @@ void mheard_save_is (char *ptext) mheard_dump (); } +#if 0 ax25_delete (pp); +#endif } /* end mheard_save_is */ diff --git a/src/pfilter.c b/src/pfilter.c index 9ae3fe87..08922f52 100644 --- a/src/pfilter.c +++ b/src/pfilter.c @@ -235,7 +235,7 @@ int pfilter (int from_chan, int to_chan, char *filter, packet_t pp, int is_aprs) pfstate.is_aprs = is_aprs; if (is_aprs) { - decode_aprs (&pfstate.decoded, pp, 1, 0); + decode_aprs (&pfstate.decoded, pp, 1, NULL); } next_token(&pfstate); @@ -1278,7 +1278,8 @@ static int filt_s (pfstate_t *pf) * * Name: filt_i * - * Purpose: IGate messaging default behavior. + * Purpose: IGate messaging filter. + * This would make sense only for IS>RF direction. * * Inputs: pf - Pointer to current state information. * token_str should contain something of format: @@ -1307,7 +1308,7 @@ static int filt_s (pfstate_t *pf) * The rest is distanced, in kilometers, from given point. * * Examples: - * i/60/0 Heard in past 60 minutes directly. + * i/180/0 Heard in past 3 hours directly. * i/45 Past 45 minutes, default max digi hops. * i/180/3 Default time (3 hours), max 3 digi hops. * i/180/8/42.6/-71.3/50. @@ -1317,13 +1318,17 @@ static int filt_s (pfstate_t *pf) * The basic idea is that we want to transmit a "message" only if the * addressee has been heard recently and is not too far away. * + * That is so we can distinguish messages addressed to a specific + * station, and other sundry uses of the addressee field. + * * After passing along a "message" we will also allow the next * position report from the sender of the "message." * That is done somewhere else. We are not concerned with it here. * * IMHO, the rules here are too restrictive. * - * FIXME -explain + * (1) The APRS-IS would send a "message" to my IGate only if the addressee + * has been heard nearby recently. 180 minutes, I believe. * *------------------------------------------------------------------------------*/ diff --git a/src/ptt.c b/src/ptt.c index 3bad92c4..3afef5e4 100644 --- a/src/ptt.c +++ b/src/ptt.c @@ -357,6 +357,9 @@ static void get_access_to_gpio (const char *path) * We don't have permission. * Try a hack which requires that the user be set up to use sudo without a password. */ +// FIXME: I think this was a horrible work around for some early release that +// did not give gpio permission to the pi user. This should go. +// Provide recovery instructions when there is a permission failure. if (ptt_debug_level >= 2) { text_color_set(DW_COLOR_ERROR); // debug message but different color so it stands out. @@ -494,6 +497,13 @@ void export_gpio(int ch, int ot, int invert, int direction) * matching the pattern "gpio61_*". * * We are finally implementing the third choice. + */ + +/* + * Then we have the Odroid board with GPIO numbers starting around 480. + * Can we simply use those numbers? + * Apparently, the export names look like GPIOX.17 + * https://wiki.odroid.com/odroid-c4/hardware/expansion_connectors#gpio_map_for_wiringpi_library */ struct dirent **file_list; diff --git a/src/recv.c b/src/recv.c index f93f1d1e..9873bbd6 100644 --- a/src/recv.c +++ b/src/recv.c @@ -107,7 +107,6 @@ #include "recv.h" #include "dtmf.h" #include "aprs_tt.h" -#include "dtime_now.h" #include "ax25_link.h" diff --git a/src/server.c b/src/server.c index 1639b5f9..da20d0df 100644 --- a/src/server.c +++ b/src/server.c @@ -176,6 +176,7 @@ * You can increase the limit by changing the line below. * A larger number consumes more resources so don't go crazy by making it larger than needed. */ +// FIXME: Put in direwolf.h rather than in .c file. Change name to reflect AGW vs KISS. Update user guide 5.7. #define MAX_NET_CLIENTS 3 @@ -1420,7 +1421,7 @@ static THREAD_F cmd_listen_thread (void *arg) } /* - * Call to/from fields are 10 bytes but contents must not exceeed 9 characters. + * Call to/from fields are 10 bytes but contents must not exceed 9 characters. * It's not guaranteed that unused bytes will contain 0 so we * don't issue error message in this case. */ diff --git a/src/symbols.c b/src/symbols.c index 35dba807..c9f07e6e 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -681,7 +681,7 @@ void symbols_from_dest_or_src (char dti, char *src, char *dest, char *symtab, ch // The position and object formats all contain a proper symbol and table. // There doesn't seem to be much reason to have a symbol for something without -// a postion because it would not show up on a map. +// a position because it would not show up on a map. // This just seems to be a remnant of something used long ago and no longer needed. // The protocol spec mentions a "MIM tracker" but I can't find any references to it. diff --git a/src/tt_user.c b/src/tt_user.c index 46e44453..a73d6a46 100644 --- a/src/tt_user.c +++ b/src/tt_user.c @@ -882,7 +882,7 @@ static void xmit_object_report (int i, int first_time) * IGate. * * When transmitting over the radio, it gets sent multiple times, to help - * probablity of being heard, with increasing delays between. + * probability of being heard, with increasing delays between. * * The other methods are reliable so we only want to send it once. */ diff --git a/src/waypoint.c b/src/waypoint.c index 70ea3205..20c1cdbc 100644 --- a/src/waypoint.c +++ b/src/waypoint.c @@ -298,7 +298,7 @@ void waypoint_send_sentence (char *name_in, double dlat, double dlong, char symt dw_printf ("waypoint_send_sentence (\"%s\", \"%c%c\")\n", name_in, symtab, symbol); #endif -// Don't waste time if no destintations specified. +// Don't waste time if no destinations specified. if (s_waypoint_serial_port_fd == MYFDERROR && s_waypoint_udp_sock_fd == -1) { diff --git a/src/xmit.c b/src/xmit.c index 9494dac9..13bbaecb 100644 --- a/src/xmit.c +++ b/src/xmit.c @@ -600,7 +600,7 @@ static void * xmit_thread (void *arg) // I don't know if this in some official specification // somewhere, but it is generally agreed that APRS digipeaters // should send only one frame at a time rather than - // bunding multiple frames into a single transmission. + // bundling multiple frames into a single transmission. // Discussion here: http://lists.tapr.org/pipermail/aprssig_lists.tapr.org/2021-September/049034.html break;