From 81d11c5e93a86f472c460be0be9b7a2c94551aec Mon Sep 17 00:00:00 2001 From: Vladimir K Date: Sun, 5 Apr 2020 19:43:41 -0700 Subject: [PATCH 01/57] Add libgpiod support --- .gitignore | 2 +- CMakeLists.txt | 12 +++++- cmake/modules/FindCompiler.cmake | 4 +- cmake/modules/Findgpiod.cmake | 23 +++++++++++ src/CMakeLists.txt | 2 + src/audio.h | 4 +- src/config.c | 37 +++++++++++++++++ src/direwolf.c | 3 ++ src/ptt.c | 70 ++++++++++++++++++++++++++++++-- 9 files changed, 149 insertions(+), 8 deletions(-) create mode 100644 cmake/modules/Findgpiod.cmake diff --git a/.gitignore b/.gitignore index 659c845b..b917a7ab 100644 --- a/.gitignore +++ b/.gitignore @@ -109,5 +109,5 @@ $RECYCLE.BIN/ *.dSYM # cmake -build/ +build*/ tmp/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a1cb8e2..930f2718 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,8 +143,8 @@ elseif(APPLE) message(STATUS "RPATH support: ${CMAKE_MACOSX_RPATH}") elseif (WIN32) - if(NOT VS2015 AND NOT VS2017) - message(FATAL_ERROR "You must use Microsoft Visual Studio 2015 or 2017 as compiler") + if(NOT VS2015 AND NOT VS2017 AND NOT VS2019) + message(FATAL_ERROR "You must use Microsoft Visual Studio 2015 | 2017 | 2019 as compiler") endif() # compile with full multicore @@ -251,6 +251,14 @@ else() set(HAMLIB_LIBRARIES "") endif() +find_package(gpiod) +if(GPIOD_FOUND) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_GPIOD") +else() + set(GPIOD_INCLUDE_DIRS "") + set(GPIOD_LIBRARIES "") +endif() + if(LINUX) find_package(ALSA REQUIRED) if(ALSA_FOUND) diff --git a/cmake/modules/FindCompiler.cmake b/cmake/modules/FindCompiler.cmake index f339a73e..91e1b89c 100644 --- a/cmake/modules/FindCompiler.cmake +++ b/cmake/modules/FindCompiler.cmake @@ -5,7 +5,9 @@ elseif(NOT DEFINED C_GCC AND CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(C_GCC 1) elseif(NOT DEFINED C_MSVC AND CMAKE_CXX_COMPILER_ID MATCHES "MSVC") set(C_MSVC 1) - if(MSVC_VERSION GREATER 1910 AND MSVC_VERSION LESS 1919) + if(MSVC_VERSION GREATER 1919 AND MSVC_VERSION LESS 1926) + set(VS2019 ON) + elseif(MSVC_VERSION GREATER 1910 AND MSVC_VERSION LESS 1919) set(VS2017 ON) elseif(MSVC_VERSION GREATER 1899 AND MSVC_VERSION LESS 1910) set(VS2015 ON) diff --git a/cmake/modules/Findgpiod.cmake b/cmake/modules/Findgpiod.cmake new file mode 100644 index 00000000..bf5be305 --- /dev/null +++ b/cmake/modules/Findgpiod.cmake @@ -0,0 +1,23 @@ +# - Try to find libgpiod +# Once done this will define +# GPIOD_FOUND - System has libgpiod +# GPIOD_INCLUDE_DIRS - The libgpiod include directories +# GPIOD_LIBRARIES - The libraries needed to use libgpiod +# GPIOD_DEFINITIONS - Compiler switches required for using libgpiod + +find_package(PkgConfig) +pkg_check_modules(PC_GPIOD QUIET gpiod) + +find_path(GPIOD_INCLUDE_DIR gpiod.h) +find_library(GPIOD_LIBRARY NAMES gpiod) + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set GPIOD_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(gpiod DEFAULT_MSG + GPIOD_LIBRARY GPIOD_INCLUDE_DIR) + +mark_as_advanced(GPIOD_INCLUDE_DIR GPIOD_LIBRARY) + +set(GPIOD_LIBRARIES ${GPIOD_LIBRARY}) +set(GPIOD_INCLUDE_DIRS ${GPIOD_INCLUDE_DIR}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 46d3ac7a..4f8e647f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,6 +8,7 @@ include_directories( ${UDEV_INCLUDE_DIRS} ${PORTAUDIO_INCLUDE_DIRS} ${CUSTOM_GEOTRANZ_DIR} + ${GPIOD_INCLUDE_DIRS} ) if(WIN32 OR CYGWIN) @@ -127,6 +128,7 @@ target_link_libraries(direwolf ${ALSA_LIBRARIES} ${UDEV_LIBRARIES} ${PORTAUDIO_LIBRARIES} + ${GPIOD_LIBRARIES} ) if(WIN32 OR CYGWIN) diff --git a/src/audio.h b/src/audio.h index 53768f20..654d16fc 100644 --- a/src/audio.h +++ b/src/audio.h @@ -28,7 +28,8 @@ enum ptt_method_e { PTT_METHOD_NONE, /* VOX or no transmit. */ PTT_METHOD_SERIAL, /* Serial port RTS or DTR. */ - PTT_METHOD_GPIO, /* General purpose I/O, Linux only. */ + PTT_METHOD_GPIO, /* General purpose I/O using sysfs, deprecated after 2020, Linux only. */ + PTT_METHOD_GPIOD, /* General purpose I/O, using libgpiod, Linux only. */ PTT_METHOD_LPT, /* Parallel printer port, Linux only. */ PTT_METHOD_HAMLIB, /* HAMLib, Linux only. */ PTT_METHOD_CM108 }; /* GPIO pin of CM108/CM119/etc. Linux only. */ @@ -266,6 +267,7 @@ struct audio_s { /* the case for CubieBoard where it was longer. */ /* This is filled in by ptt_init so we don't have to */ /* recalculate it each time we access it. */ + /* Also GPIO chip name for GPIOD method. Looks like 'gpiochip4' */ /* This could probably be collapsed into ptt_device instead of being separate. */ diff --git a/src/config.c b/src/config.c index 8f9cb9f9..e40a6521 100644 --- a/src/config.c +++ b/src/config.c @@ -1743,6 +1743,43 @@ void config_init (char *fname, struct audio_s *p_audio_config, } p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_GPIO; #endif + } + else if (strcasecmp(t, "GPIOD") == 0) { +#if __WIN32__ + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file line %d: %s with GPIOD is only available on Linux.\n", line, otname); +#else +#if defined(USE_GPIOD) + t = split(NULL,0); + if (t == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file line %d: Missing GPIO chip for %s.\n", line, otname); + continue; + } + strlcpy(p_audio_config->achan[channel].octrl[ot].out_gpio_name, t, + sizeof(p_audio_config->achan[channel].octrl[ot].out_gpio_name)); + + t = split(NULL,0); + if (t == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf("Config file line %d: Missing GPIO number for %s.\n", line, otname); + continue; + } + + if (*t == '-') { + p_audio_config->achan[channel].octrl[ot].out_gpio_num = atoi(t+1); + p_audio_config->achan[channel].octrl[ot].ptt_invert = 1; + } + else { + p_audio_config->achan[channel].octrl[ot].out_gpio_num = atoi(t); + p_audio_config->achan[channel].octrl[ot].ptt_invert = 0; + } + p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_GPIOD; +#else + text_color_set(DW_COLOR_ERROR); + dw_printf ("GPIOD is not supported.\n"); +#endif /* USE_GPIOD*/ +#endif /* __WIN32__ */ } else if (strcasecmp(t, "LPT") == 0) { diff --git a/src/direwolf.c b/src/direwolf.c index 8d6e8a7c..0e41f4fc 100644 --- a/src/direwolf.c +++ b/src/direwolf.c @@ -302,6 +302,9 @@ int main (int argc, char *argv[]) #endif #if defined(USE_CM108) dw_printf (" cm108-ptt"); +#endif +#if defined(USE_GPIOD) + dw_printf (" libgpiod"); #endif dw_printf ("\n"); #endif diff --git a/src/ptt.c b/src/ptt.c index cf49bbab..05eeeda3 100644 --- a/src/ptt.c +++ b/src/ptt.c @@ -166,6 +166,10 @@ #include "cm108.h" #endif +#ifdef USE_GPIOD +#include +#endif + /* So we can have more common code for fd. */ typedef int HANDLE; #define INVALID_HANDLE_VALUE (-1) @@ -623,6 +627,31 @@ void export_gpio(int ch, int ot, int invert, int direction) get_access_to_gpio (gpio_value_path); } +#if defined(USE_GPIOD) +int gpiod_probe(const char *chip_name, int line_number) +{ + struct gpiod_chip *chip; + chip = gpiod_chip_open_by_name(chip_name); + if (chip == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Can't open GPIOD chip %s.\n", chip_name); + return -1; + } + + struct gpiod_line *line; + line = gpiod_chip_get_line(chip, line_number); + if (line == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Can't get GPIOD line %d.\n", line_number); + return -1; + } + if (ptt_debug_level >= 2) { + text_color_set(DW_COLOR_DEBUG); + dw_printf("GPIOD probe OK. Chip: %s line: %d\n", chip_name, line_number); + } + return 0; +} +#endif /* USE_GPIOD */ #endif /* not __WIN32__ */ @@ -639,7 +668,8 @@ void export_gpio(int ch, int ot, int invert, int direction) * ptt_method Method for PTT signal. * PTT_METHOD_NONE - not configured. Could be using VOX. * PTT_METHOD_SERIAL - serial (com) port. - * PTT_METHOD_GPIO - general purpose I/O. + * PTT_METHOD_GPIO - general purpose I/O (sysfs). + * PTT_METHOD_GPIOD - general purpose I/O (libgpiod). * PTT_METHOD_LPT - Parallel printer port. * PTT_METHOD_HAMLIB - HAMLib rig control. * PTT_METHOD_CM108 - GPIO pins of CM108 etc. USB Audio. @@ -718,12 +748,13 @@ void ptt_init (struct audio_s *audio_config_p) if (ptt_debug_level >= 2) { text_color_set(DW_COLOR_DEBUG); - dw_printf ("ch=%d, %s method=%d, device=%s, line=%d, gpio=%d, lpt_bit=%d, invert=%d\n", + dw_printf ("ch=%d, %s method=%d, device=%s, line=%d, name=%s, gpio=%d, lpt_bit=%d, invert=%d\n", ch, otnames[ot], audio_config_p->achan[ch].octrl[ot].ptt_method, audio_config_p->achan[ch].octrl[ot].ptt_device, audio_config_p->achan[ch].octrl[ot].ptt_line, + audio_config_p->achan[ch].octrl[ot].out_gpio_name, audio_config_p->achan[ch].octrl[ot].out_gpio_num, audio_config_p->achan[ch].octrl[ot].ptt_lpt_bit, audio_config_p->achan[ch].octrl[ot].ptt_invert); @@ -869,7 +900,28 @@ void ptt_init (struct audio_s *audio_config_p) if (using_gpio) { get_access_to_gpio ("/sys/class/gpio/export"); } - +#if defined(USE_GPIOD) + // GPIOD + for (ch = 0; ch < MAX_CHANS; ch++) { + if (save_audio_config_p->achan[ch].medium == MEDIUM_RADIO) { + for (int ot = 0; ot < NUM_OCTYPES; ot++) { + if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_GPIOD) { + const char *chip_name = audio_config_p->achan[ch].octrl[ot].out_gpio_name; + int line_number = audio_config_p->achan[ch].octrl[ot].out_gpio_num; + int rc = gpiod_probe(chip_name, line_number); + if (rc < 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Disable PTT for channel %d\n", ch); + audio_config_p->achan[ch].octrl[ot].ptt_method = PTT_METHOD_NONE; + } else { + // Set initial state off ptt_set will invert output signal if appropriate. + ptt_set (ot, ch, 0); + } + } + } + } + } +#endif /* USE_GPIOD */ /* * We should now be able to create the device nodes for * the pins we want to use. @@ -1226,6 +1278,18 @@ void ptt_set (int ot, int chan, int ptt_signal) close (fd); } + +#if defined(USE_GPIOD) + if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_GPIOD) { + const char *chip = save_audio_config_p->achan[chan].octrl[ot].out_gpio_name; + int line = save_audio_config_p->achan[chan].octrl[ot].out_gpio_num; + int rc = gpiod_ctxless_set_value(chip, line, ptt, false, "direwolf", NULL, NULL); + if (ptt_debug_level >= 1) { + text_color_set(DW_COLOR_DEBUG); + dw_printf("PTT_METHOD_GPIOD chip: %s line: %d ptt: %d rc: %d\n", chip, line, ptt, rc); + } + } +#endif /* USE_GPIOD */ #endif /* From 12abb8d91e4429d342e4057484bcdb03a23cc970 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Wed, 22 Nov 2023 21:29:05 +0000 Subject: [PATCH 02/57] dev branch is now 1.8 development. --- CMakeLists.txt | 2 +- src/direwolf.c | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 84aeb738..3c01045b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project(direwolf) # configure version set(direwolf_VERSION_MAJOR "1") -set(direwolf_VERSION_MINOR "7") +set(direwolf_VERSION_MINOR "8") set(direwolf_VERSION_PATCH "0") set(direwolf_VERSION_SUFFIX "Development") diff --git a/src/direwolf.c b/src/direwolf.c index e23aecb4..c6ed9d4d 100644 --- a/src/direwolf.c +++ b/src/direwolf.c @@ -186,7 +186,7 @@ static int d_u_opt = 0; /* "-d u" command line option to print UTF-8 also in h static int d_p_opt = 0; /* "-d p" option for dumping packets over radio. */ static int q_h_opt = 0; /* "-q h" Quiet, suppress the "heard" line with audio level. */ -static int q_d_opt = 0; /* "-q d" Quiet, suppress the printing of decoded of APRS packets. */ +static int q_d_opt = 0; /* "-q d" Quiet, suppress the printing of description of APRS packets. */ static int A_opt_ais_to_obj = 0; /* "-A" Convert received AIS to APRS "Object Report." */ @@ -302,24 +302,24 @@ 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 7\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); + dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "A", __DATE__); + //dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION); #if defined(ENABLE_GPSD) || defined(USE_HAMLIB) || defined(USE_CM108) || USE_AVAHI_CLIENT || USE_MACOS_DNSSD dw_printf ("Includes optional support for: "); -#if defined(ENABLE_GPSD) + #if defined(ENABLE_GPSD) dw_printf (" gpsd"); -#endif -#if defined(USE_HAMLIB) + #endif + #if defined(USE_HAMLIB) dw_printf (" hamlib"); -#endif -#if defined(USE_CM108) + #endif + #if defined(USE_CM108) dw_printf (" cm108-ptt"); -#endif -#if (USE_AVAHI_CLIENT|USE_MACOS_DNSSD) + #endif + #if (USE_AVAHI_CLIENT|USE_MACOS_DNSSD) dw_printf (" dns-sd"); -#endif + #endif dw_printf ("\n"); #endif @@ -1708,7 +1708,7 @@ static void usage (char **argv) dw_printf (" d d = APRStt (DTMF to APRS object translation).\n"); dw_printf (" -q Quiet (suppress output) options:\n"); dw_printf (" h h = Heard line with the audio level.\n"); - dw_printf (" d d = Decoding of APRS packets.\n"); + dw_printf (" d d = Description of APRS packets.\n"); dw_printf (" x x = Silence FX.25 information.\n"); dw_printf (" -t n Text colors. 0=disabled. 1=default. 2,3,4,... alternatives.\n"); dw_printf (" Use 9 to test compatibility with your terminal.\n"); From 6f0c1518c0186e6af9442d7e3276c44a6411f0e0 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Wed, 22 Nov 2023 21:34:41 +0000 Subject: [PATCH 03/57] More error checking. --- src/config.c | 86 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 14 deletions(-) diff --git a/src/config.c b/src/config.c index 1ad6c081..739eb2f6 100644 --- a/src/config.c +++ b/src/config.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2021 John Langner, WB2OSZ +// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 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 @@ -755,12 +755,13 @@ void config_init (char *fname, struct audio_s *p_audio_config, strlcpy (p_audio_config->adev[adevice].adevice_out, DEFAULT_ADEVICE, sizeof(p_audio_config->adev[adevice].adevice_out)); p_audio_config->adev[adevice].defined = 0; + p_audio_config->adev[adevice].copy_from = -1; p_audio_config->adev[adevice].num_channels = DEFAULT_NUM_CHANNELS; /* -2 stereo */ p_audio_config->adev[adevice].samples_per_sec = DEFAULT_SAMPLES_PER_SEC; /* -r option */ p_audio_config->adev[adevice].bits_per_sample = DEFAULT_BITS_PER_SAMPLE; /* -8 option for 8 instead of 16 bits */ } - p_audio_config->adev[0].defined = 1; + p_audio_config->adev[0].defined = 2; // 2 means it was done by default and not the user's config file. for (channel=0; channelmaxframe_extended = AX25_K_MAXFRAME_EXTENDED_DEFAULT; /* Max frames to send before ACK. mod 128 "Window" size. */ - p_misc_config->maxv22 = AX25_N2_RETRY_DEFAULT / 3; /* Max SABME before falling back to SABM. */ - p_misc_config->v20_addrs = NULL; /* Go directly to v2.0 for stations listed. */ + p_misc_config->maxv22 = AX25_N2_RETRY_DEFAULT / 3; /* Send SABME this many times before falling back to SABM. */ + p_misc_config->v20_addrs = NULL; /* Go directly to v2.0 for stations listed */ + /* without trying v2.2 first. */ p_misc_config->v20_count = 0; p_misc_config->noxid_addrs = NULL; /* Don't send XID to these stations. */ + /* Might work with a partial v2.2 implementation */ + /* on the other end. */ p_misc_config->noxid_count = 0; /* @@ -1012,7 +1016,11 @@ void config_init (char *fname, struct audio_s *p_audio_config, * ADEVICE plughw:1,0 -- same for in and out. * ADEVICE plughw:2,0 plughw:3,0 -- different in/out for a channel or channel pair. * ADEVICE1 udp:7355 default -- from Software defined radio (SDR) via UDP. - * + * + * New in 1.8: Ability to map to another audio device. + * This allows multiple modems (i.e. data speeds) on the same audio interface. + * + * ADEVICEn = n -- Copy from different already defined channel. */ /* Note that ALSA name can contain comma such as hw:1,0 */ @@ -1040,17 +1048,42 @@ void config_init (char *fname, struct audio_s *p_audio_config, exit(EXIT_FAILURE); } + // Do not allow same adevice to be defined more than once. + // Overriding the default for adevice 0 is ok. + // In that case definded was 2. That's why we check for 1, not just non-zero. + + if (p_audio_config->adev[adevice].defined == 1) { // 1 means defined by user. + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file: ADEVICE%d can't be defined more than once. Line %d.\n", adevice, line); + continue; + } + p_audio_config->adev[adevice].defined = 1; - - /* First channel of device is valid. */ - p_audio_config->chan_medium[ADEVFIRSTCHAN(adevice)] = MEDIUM_RADIO; - strlcpy (p_audio_config->adev[adevice].adevice_in, t, sizeof(p_audio_config->adev[adevice].adevice_in)); - strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out)); + // New case for release 1.8. - t = split(NULL,0); - if (t != NULL) { + if (strcmp(t, "=") == 0) { + t = split(NULL,0); + if (t != NULL) { + + } + +///////// to be continued.... FIXME + + } + else { + /* First channel of device is valid. */ + // This might be changed to UDP or STDIN when the device name is examined. + p_audio_config->chan_medium[ADEVFIRSTCHAN(adevice)] = MEDIUM_RADIO; + + strlcpy (p_audio_config->adev[adevice].adevice_in, t, sizeof(p_audio_config->adev[adevice].adevice_in)); strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out)); + + t = split(NULL,0); + if (t != NULL) { + // Different audio devices for receive and transmit. + strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out)); + } } } @@ -2173,7 +2206,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, else { p_audio_config->achan[channel].slottime = DEFAULT_SLOTTIME; text_color_set(DW_COLOR_ERROR); - dw_printf ("Line %d: Invalid delay time for persist algorithm. Using %d.\n", + dw_printf ("Line %d: Invalid delay time for persist algorithm. Using default %d.\n", line, p_audio_config->achan[channel].slottime); } } @@ -2197,7 +2230,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, else { p_audio_config->achan[channel].persist = DEFAULT_PERSIST; text_color_set(DW_COLOR_ERROR); - dw_printf ("Line %d: Invalid probability for persist algorithm. Using %d.\n", + dw_printf ("Line %d: Invalid probability for persist algorithm. Using default %d.\n", line, p_audio_config->achan[channel].persist); } } @@ -2216,6 +2249,19 @@ void config_init (char *fname, struct audio_s *p_audio_config, } n = atoi(t); if (n >= 0 && n <= 255) { + text_color_set(DW_COLOR_ERROR); + if (n == 0) { + dw_printf ("Line %d: Setting TXDELAY to 0 is a REALLY BAD idea if you want other stations to hear you.\n", + line); + dw_printf ("Line %d: See User Guide, \"Radio Channel - Transmit Timing\" for an explanation.\n", + line); + } + if (n >= 100) { + dw_printf ("Line %d: Keeping with tradition, going back to the 1980s, TXDELAY is in 10 millisecond units.\n", + line); + dw_printf ("Line %d: The value %d would be %.3f seconds which seems rather excessive. Are you sure you want that?\n", + line, n, (double)n * 10. / 1000.); + } p_audio_config->achan[channel].txdelay = n; } else { @@ -2240,6 +2286,18 @@ void config_init (char *fname, struct audio_s *p_audio_config, } n = atoi(t); if (n >= 0 && n <= 255) { + if (n == 0) { + dw_printf ("Line %d: Setting TXTAIL to 0 is a REALLY BAD idea if you want other stations to hear you.\n", + line); + dw_printf ("Line %d: See User Guide, \"Radio Channel - Transmit Timing\" for an explanation.\n", + line); + } + if (n >= 50) { + dw_printf ("Line %d: Keeping with tradition, going back to the 1980s, TXTAIL is in 10 millisecond units.\n", + line); + dw_printf ("Line %d: The value %d would be %.3f seconds which seems rather excessive. Are you sure you want that?\n", + line, n, (double)n * 10. / 1000.); + } p_audio_config->achan[channel].txtail = n; } else { From 6192661f3df331c7abffe921b62db80994e0930e Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sat, 25 Nov 2023 15:32:04 +0000 Subject: [PATCH 04/57] Compile error. --- src/audio.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/audio.h b/src/audio.h index cb5ca94e..ae1035d9 100644 --- a/src/audio.h +++ b/src/audio.h @@ -74,16 +74,23 @@ struct audio_s { /* Properties of the sound device. */ - int defined; /* Was device defined? */ - /* First one defaults to yes. */ + int defined; /* Was device defined? 0=no. >0 for yes. */ + /* First channel defaults to 2 for yes with default config. */ + /* 1 means it was defined by user. */ + + int copy_from; /* >=0 means copy contents from another audio device. */ + /* In this case we don't have device names, below. */ + /* Num channels, samples/sec, and bit/sample are copied from */ + /* original device and can't be changed. */ + /* -1 for normal case. */ char adevice_in[80]; /* Name of the audio input device (or file?). */ - /* TODO: Can be "-" to read from stdin. */ + /* Can be udp:nnn for UDP or "-" to read from stdin. */ char adevice_out[80]; /* Name of the audio output device (or file?). */ int num_channels; /* Should be 1 for mono or 2 for stereo. */ - int samples_per_sec; /* Audio sampling rate. Typically 11025, 22050, or 44100. */ + int samples_per_sec; /* Audio sampling rate. Typically 11025, 22050, 44100, or 48000. */ int bits_per_sample; /* 8 (unsigned char) or 16 (signed short). */ } adev[MAX_ADEVS]; From ad5dbaec73c1ac280f88177bc0aa635fc6e108e3 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sun, 26 Nov 2023 01:12:34 +0000 Subject: [PATCH 05/57] Refine ptt gpiod. --- src/config.c | 6 ++++-- src/ptt.c | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/config.c b/src/config.c index faf4e8ff..747d0e60 100644 --- a/src/config.c +++ b/src/config.c @@ -1852,7 +1852,8 @@ void config_init (char *fname, struct audio_s *p_audio_config, t = split(NULL,0); if (t == NULL) { text_color_set(DW_COLOR_ERROR); - dw_printf ("Config file line %d: Missing GPIO chip for %s.\n", line, otname); + dw_printf ("Config file line %d: Missing GPIO chip name for %s.\n", line, otname); + dw_printf ("Use the \"gpioinfo\" command to get a list of gpio chip names and corresponding I/O lines.\n"); continue; } strlcpy(p_audio_config->achan[channel].octrl[ot].out_gpio_name, t, @@ -1876,7 +1877,8 @@ void config_init (char *fname, struct audio_s *p_audio_config, p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_GPIOD; #else text_color_set(DW_COLOR_ERROR); - dw_printf ("GPIOD is not supported.\n"); + dw_printf ("Application was not built with optional support for GPIOD.\n"); + dw_printf ("Install packages gpiod and libgpiod-dev, remove 'build' subdirectory, then rebuild.\n"); #endif /* USE_GPIOD*/ #endif /* __WIN32__ */ } diff --git a/src/ptt.c b/src/ptt.c index 6177d6d7..f6020394 100644 --- a/src/ptt.c +++ b/src/ptt.c @@ -472,6 +472,20 @@ void export_gpio(int ch, int ot, int invert, int direction) text_color_set(DW_COLOR_ERROR); dw_printf ("Error writing \"%s\" to %s, errno=%d\n", stemp, gpio_export_path, e); dw_printf ("%s\n", strerror(e)); + + if (e == 22) { + // It appears that error 22 occurs when sysfs gpio is not available. + // (See https://github.com/wb2osz/direwolf/issues/503) + // + // The solution might be to use the new gpiod approach. + + dw_printf ("It looks like gpio with sysfs is not supported on this operating system.\n"); + dw_printf ("Rather than the following form, in the configuration file,\n); + dw_printf (" PTT GPIO %s\n", stemp); + dw_printf ("try using gpiod form instead. e.g.\n"); + dw_printf (" PTT GPIOD gpiochip0 %s\n", stemp); + dw_printf ("You can get a list of gpio chip names and corresponding I/O lines with \"gpioinfo\" command.\n"); + } exit (1); } } @@ -914,7 +928,7 @@ void ptt_init (struct audio_s *audio_config_p) #if defined(USE_GPIOD) // GPIOD for (ch = 0; ch < MAX_CHANS; ch++) { - if (save_audio_config_p->achan[ch].medium == MEDIUM_RADIO) { + if (save_audio_config_p->chan_medium[ch] == MEDIUM_RADIO) { for (int ot = 0; ot < NUM_OCTYPES; ot++) { if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_GPIOD) { const char *chip_name = audio_config_p->achan[ch].octrl[ot].out_gpio_name; From 5d35780498e6c14c56e1cdf998409fff7b5ba380 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sun, 26 Nov 2023 01:29:13 +0000 Subject: [PATCH 06/57] missing quote --- src/ptt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ptt.c b/src/ptt.c index f6020394..a75cb8de 100644 --- a/src/ptt.c +++ b/src/ptt.c @@ -480,7 +480,7 @@ void export_gpio(int ch, int ot, int invert, int direction) // The solution might be to use the new gpiod approach. dw_printf ("It looks like gpio with sysfs is not supported on this operating system.\n"); - dw_printf ("Rather than the following form, in the configuration file,\n); + dw_printf ("Rather than the following form, in the configuration file,\n"); dw_printf (" PTT GPIO %s\n", stemp); dw_printf ("try using gpiod form instead. e.g.\n"); dw_printf (" PTT GPIOD gpiochip0 %s\n", stemp); From b069d0f031eb2b92d189ecbc4e36f7d71739e321 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Thu, 21 Dec 2023 23:01:05 +0000 Subject: [PATCH 07/57] More config file checking. --- src/config.c | 61 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/src/config.c b/src/config.c index 747d0e60..de8d74d4 100644 --- a/src/config.c +++ b/src/config.c @@ -2204,6 +2204,9 @@ void config_init (char *fname, struct audio_s *p_audio_config, /* * DWAIT n - Extra delay for receiver squelch. n = 10 mS units. + * + * Why did I do this? Just add more to TXDELAY. + * Now undocumented in User Guide. Might disappear someday. */ else if (strcasecmp(t, "DWAIT") == 0) { @@ -2239,14 +2242,20 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } n = atoi(t); - if (n >= 0 && n <= 255) { + if (n >= 5 && n < 50) { + // 0 = User has no clue. This would be no delay. + // 10 = Default. + // 50 = Half second. User might think it is mSec and use 100. p_audio_config->achan[channel].slottime = n; } else { p_audio_config->achan[channel].slottime = DEFAULT_SLOTTIME; text_color_set(DW_COLOR_ERROR); - dw_printf ("Line %d: Invalid delay time for persist algorithm. Using default %d.\n", + dw_printf ("Line %d: Invalid delay time for persist algorithm. Using default %d.\n", line, p_audio_config->achan[channel].slottime); + dw_printf ("Read the Dire Wolf User Guide, \"Radio Channel - Transmit Timing\"\n"); + dw_printf ("section, to understand what this means.\n"); + dw_printf ("Why don't you just use the default?\n"); } } @@ -2263,14 +2272,17 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } n = atoi(t); - if (n >= 0 && n <= 255) { + if (n >= 5 && n <= 250) { p_audio_config->achan[channel].persist = n; } else { p_audio_config->achan[channel].persist = DEFAULT_PERSIST; text_color_set(DW_COLOR_ERROR); - dw_printf ("Line %d: Invalid probability for persist algorithm. Using default %d.\n", + dw_printf ("Line %d: Invalid probability for persist algorithm. Using default %d.\n", line, p_audio_config->achan[channel].persist); + dw_printf ("Read the Dire Wolf User Guide, \"Radio Channel - Transmit Timing\"\n"); + dw_printf ("section, to understand what this means.\n"); + dw_printf ("Why don't you just use the default?\n"); } } @@ -2289,17 +2301,21 @@ void config_init (char *fname, struct audio_s *p_audio_config, n = atoi(t); if (n >= 0 && n <= 255) { text_color_set(DW_COLOR_ERROR); - if (n == 0) { - dw_printf ("Line %d: Setting TXDELAY to 0 is a REALLY BAD idea if you want other stations to hear you.\n", - line); - dw_printf ("Line %d: See User Guide, \"Radio Channel - Transmit Timing\" for an explanation.\n", + if (n < 10) { + dw_printf ("Line %d: Setting TXDELAY this small is a REALLY BAD idea if you want other stations to hear you.\n", line); + dw_printf ("Read the Dire Wolf User Guide, \"Radio Channel - Transmit Timing\"\n"); + dw_printf ("section, to understand what this means.\n"); + dw_printf ("Why don't you just use the default rather than reducing reliability?\n"); } - if (n >= 100) { + else if (n >= 100) { dw_printf ("Line %d: Keeping with tradition, going back to the 1980s, TXDELAY is in 10 millisecond units.\n", line); dw_printf ("Line %d: The value %d would be %.3f seconds which seems rather excessive. Are you sure you want that?\n", line, n, (double)n * 10. / 1000.); + dw_printf ("Read the Dire Wolf User Guide, \"Radio Channel - Transmit Timing\"\n"); + dw_printf ("section, to understand what this means.\n"); + dw_printf ("Why don't you just use the default?\n"); } p_audio_config->achan[channel].txdelay = n; } @@ -2325,24 +2341,28 @@ void config_init (char *fname, struct audio_s *p_audio_config, } n = atoi(t); if (n >= 0 && n <= 255) { - if (n == 0) { - dw_printf ("Line %d: Setting TXTAIL to 0 is a REALLY BAD idea if you want other stations to hear you.\n", - line); - dw_printf ("Line %d: See User Guide, \"Radio Channel - Transmit Timing\" for an explanation.\n", + if (n < 5) { + dw_printf ("Line %d: Setting TXTAIL that small is a REALLY BAD idea if you want other stations to hear you.\n", line); + dw_printf ("Read the Dire Wolf User Guide, \"Radio Channel - Transmit Timing\"\n"); + dw_printf ("section, to understand what this means.\n"); + dw_printf ("Why don't you just use the default rather than reducing reliability?\n"); } - if (n >= 50) { + else if (n >= 50) { dw_printf ("Line %d: Keeping with tradition, going back to the 1980s, TXTAIL is in 10 millisecond units.\n", line); dw_printf ("Line %d: The value %d would be %.3f seconds which seems rather excessive. Are you sure you want that?\n", line, n, (double)n * 10. / 1000.); + dw_printf ("Read the Dire Wolf User Guide, \"Radio Channel - Transmit Timing\"\n"); + dw_printf ("section, to understand what this means.\n"); + dw_printf ("Why don't you just use the default?\n"); } p_audio_config->achan[channel].txtail = n; } else { p_audio_config->achan[channel].txtail = DEFAULT_TXTAIL; text_color_set(DW_COLOR_ERROR); - dw_printf ("Line %d: Invalid time for transmit timing. Using %d.\n", + dw_printf ("Line %d: Invalid time for transmit timing. Using %d.\n", line, p_audio_config->achan[channel].txtail); } } @@ -2891,7 +2911,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, dw_printf ("Config file: FILTER IG ... on line %d.\n", line); dw_printf ("Warning! Don't mess with IS>RF filtering unless you are an expert and have an unusual situation.\n"); dw_printf ("Warning! The default is fine for nearly all situations.\n"); - dw_printf ("Warning! Be sure to read carefully and understand Successful-APRS-Gateway-Operation.pdf .\n"); + dw_printf ("Warning! Be sure to read carefully and understand \"Successful-APRS-Gateway-Operation.pdf\" .\n"); dw_printf ("Warning! If you insist, be sure to add \" | i/180 \" so you don't break messaging.\n"); } else { @@ -2931,7 +2951,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, dw_printf ("Warning! Don't mess with RF>IS filtering unless you are an expert and have an unusual situation.\n"); dw_printf ("Warning! Expected behavior is for everything to go from RF to IS.\n"); dw_printf ("Warning! The default is fine for nearly all situations.\n"); - dw_printf ("Warning! Be sure to read carefully and understand Successful-APRS-Gateway-Operation.pdf .\n"); + dw_printf ("Warning! Be sure to read carefully and understand \"Successful-APRS-Gateway-Operation.pdf\" .\n"); } else { to_chan = isdigit(*t) ? atoi(t) : -999; @@ -4567,6 +4587,13 @@ void config_init (char *fname, struct audio_s *p_audio_config, if (t != NULL && strlen(t) > 0) { p_igate_config->t2_filter = strdup (t); + + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: Warning - IGFILTER is a rarely needed expert level feature.\n", line); + dw_printf ("If you don't have a special situation and a good understanding of\n"); + dw_printf ("how this works, you probably should not be messing with it.\n"); + dw_printf ("The default behavior is appropriate for most situations.\n"); + dw_printf ("Please read \"Successful-APRS-IGate-Operation.pdf\".\n"); } } From d679e06846e4f163dbf1a020e151065e5243d4ba Mon Sep 17 00:00:00 2001 From: wb2osz Date: Thu, 21 Dec 2023 23:15:21 +0000 Subject: [PATCH 08/57] Warnings about using VOX rather than wired PTT. --- src/ptt.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/ptt.c b/src/ptt.c index a75cb8de..af746626 100644 --- a/src/ptt.c +++ b/src/ptt.c @@ -1189,7 +1189,27 @@ void ptt_init (struct audio_s *audio_config_p) if (audio_config_p->chan_medium[ch] == MEDIUM_RADIO) { if(audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_method == PTT_METHOD_NONE) { text_color_set(DW_COLOR_INFO); - dw_printf ("Note: PTT not configured for channel %d. (Ignore this if using VOX.)\n", ch); + dw_printf ("\n"); + dw_printf ("Note: PTT not configured for channel %d. (OK if using VOX.)\n", ch); + dw_printf ("When using VOX, ensure that it adds very little delay (e.g. 10-20) milliseconds\n"); + dw_printf ("between the time that transmit audio ends and PTT is deactivated.\n"); + dw_printf ("For example, if using a SignaLink USB, turn the DLY control all the\n"); + dw_printf ("way counter clockwise.\n"); + dw_printf ("\n"); + dw_printf ("Using VOX built in to the radio is a VERY BAD idea. This is intended\n"); + dw_printf ("for voice operation, with gaps in the sound, and typically has a delay of about a\n"); + dw_printf ("half second between the time the audio stops and the transmitter is turned off.\n"); + dw_printf ("When using APRS your transmiter will be sending a quiet carrier for\n"); + dw_printf ("about a half second after your packet ends. This may interfere with the\n"); + dw_printf ("the next station to transmit. This is being inconsiderate.\n"); + dw_printf ("\n"); + dw_printf ("If you are trying to use VOX with connected mode packet, expect\n"); + dw_printf ("frustration and disappointment. Connected mode involves rapid responses\n"); + dw_printf ("which you will probably miss because your transmitter is still on when\n"); + dw_printf ("the response is being transmitted.\n"); + dw_printf ("\n"); + dw_printf ("Read the User Guide 'Transmit Timing' section for more details.\n"); + dw_printf ("\n"); } } } From 46f31d4453ed133c99db71a514deacebae19f877 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sat, 23 Dec 2023 15:57:03 +0000 Subject: [PATCH 09/57] Use tocalls.yaml rather than tocalls.txt which is no longer maintained. --- CHANGES.md | 6 + data/CMakeLists.txt | 8 +- data/tocalls.txt | 326 ---------- data/tocalls.yaml | 1481 +++++++++++++++++++++++++++++++++++++++++++ src/CMakeLists.txt | 3 + src/decode_aprs.c | 356 +---------- src/deviceid.c | 660 +++++++++++++++++++ src/deviceid.h | 6 + src/direwolf.c | 2 + test/CMakeLists.txt | 4 + 10 files changed, 2199 insertions(+), 653 deletions(-) delete mode 100644 data/tocalls.txt create mode 100644 data/tocalls.yaml create mode 100644 src/deviceid.c create mode 100644 src/deviceid.h diff --git a/CHANGES.md b/CHANGES.md index 4b78ca14..0903e9ea 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,12 @@ # Revision History # +## Version 1.8 -- Development Version + +### New Features: ### + +- [http://www.aprs.org/aprs11/tocalls.txt](http://www.aprs.org/aprs11/tocalls.txt) has been abandoned since the end of 2021. [https://github.com/aprsorg/aprs-deviceid](https://github.com/aprsorg/aprs-deviceid) is now considered to be the authoritative source of truth for the vendor/model encoding. + ## Version 1.7 -- October 2023 ## diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index 7972cc23..11a82a43 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -16,7 +16,7 @@ # # 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. +# a file called tocalls.yaml at application start up time. # # The original permanent symbols are built in but the "new" symbols, # using overlays, are often updated. These are also read from files. @@ -25,17 +25,17 @@ include(ExternalProject) -set(TOCALLS_TXT "tocalls.txt") +set(TOCALLS_YAML "tocalls.yaml") set(SYMBOLS-NEW_TXT "symbols-new.txt") set(SYMBOLSX_TXT "symbolsX.txt") set(CUSTOM_BINARY_DATA_DIR "${CMAKE_BINARY_DIR}/data") # we can also move to a separate cmake file and use file(download) # see conf/install_conf.cmake as example -file(COPY "${CUSTOM_DATA_DIR}/${TOCALLS_TXT}" DESTINATION "${CUSTOM_BINARY_DATA_DIR}") +file(COPY "${CUSTOM_DATA_DIR}/${TOCALLS_YAML}" DESTINATION "${CUSTOM_BINARY_DATA_DIR}") 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}") -install(FILES "${CUSTOM_BINARY_DATA_DIR}/${TOCALLS_TXT}" DESTINATION ${INSTALL_DATA_DIR}) +install(FILES "${CUSTOM_BINARY_DATA_DIR}/${TOCALLS_YAML}" 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}) diff --git a/data/tocalls.txt b/data/tocalls.txt deleted file mode 100644 index 169c9868..00000000 --- a/data/tocalls.txt +++ /dev/null @@ -1,326 +0,0 @@ - -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 -16 Sep 21 Added APY05D for Yaesu FT5D series -04 Sep 21 APLOxx LoRa KISS TNC/Tracker https://github.com/SQ9MDD/TTGO-T-Beam-LoRa-APRS -24 Aug 21 Added APLSxx SARIMESH http://www.sarimesh.net -22 Aug 21 Added APE2Ax for VA3NNW's Email-2-APRS ap -30 Jun 21 Added APCNxx for carNET by DG5OAW -14 Jun 21 Added APN2xx for NOSaprs JNOS 2.0 - VE4KLM -24 Apr 21 Added APMPAD for DF1JSL's WXBot clone and extension -20 Apr 21 Added APLCxx for APRScube by DL3DCW -19 Apr 21 Added APVMxx for DRCC-DVM Voice (Digital Radio China Club) -13 Apr 21 Added APIxxx for all Dstar ICOMS (APRS via DPRS) -23 MAr 20 Added APW9xx For 9A9Y Weather Tracker -16 Feb 21 Added API970 for I com 9700 - -2020 Added APHBLx,APIZCI,APLGxx,APLTxx,APNVxx,APY300,APESPG,APESPW - APGDTx,APOSWx,APOSBx,APBT62,APCLUB,APMQxx -2019 Added APTPNx,APJ8xx,APBSDx,APNKMX,APAT51,APMGxx,APTCMA, - APATxx,APQTHx,APLIGx -2018 added APRARX,APELKx,APGBLN,APBKxx,APERSx,APTCHE -2017 Added APHWxx,APDVxx,APPICO,APBMxx,APP6xx,APTAxx,APOCSG,APCSMS, - APPMxx,APOFF,APDTMF,APRSON,APDIGI,APSAT,APTBxx,APIExx, - APSFxx -2016 added APYSxx,APINxx,APNICx,APTKPT,APK004,APFPRS,APCDS0,APDNOx -2015 Added APSTPO,APAND1,APDRxx,APZ247,APHTxx,APMTxx,APZMAJ - APB2MF,APR2MF,APAVT5 - - - - -In APRS, the AX.25 Destination address is not used for packet -routing as is normally done in AX.25. So APRS uses it for two -things. The initial APxxxx is used as a group identifier to make -APRS packets instanantly recognizable on shared channels. Most -applicaitons ignore all non APRS packets. The remaining 4 xxxx -bytes of the field are available to indicate the software version -number or application. The following applications have requested -a TOCALL number series: - -Authors with similar alphabetic requirements are encouraged to share -their address space with other software. Work out agreements amongst -yourselves and keep me informed. - - - - - APn 3rd digit is a number - AP1WWX TAPR T-238+ WX station - AP1MAJ Martyn M1MAJ DeLorme inReach Tracker - AP4Rxy APRS4R software interface - APnnnD Painter Engineering uSmartDigi D-Gate DSTAR Gateway - APnnnU Painter Engineering uSmartDigi Digipeater - APA APAFxx AFilter. - APAGxx AGATE - APAGWx SV2AGW's AGWtracker - APALxx Alinco DR-620/635 internal TNC digis. "Hachi" ,JF1AJE - APAXxx AFilterX. - APAHxx AHub - APAND1 APRSdroid (pre-release) http://aprsdroid.org/ - APAMxx Altus Metrum GPS trackers - APATAR ATA-R APRS Digipeater by TA7W/OH2UDS and TA6AEU - APAT8x for Anytone. 81 for 878 HT - APAT51 for Anytone AT-D578UV APRS mobile radio - APAVT5 SainSonic AP510 which is a 1watt tracker - APAWxx AGWPE - APB APBxxx Beacons or Rabbit TCPIP micros? - APB2MF DL2MF - MF2APRS Radiosonde for balloons - APBLxx BigRedBee BeeLine - APBLO MOdel Rocketry K7RKT - APBKxx PY5BK Bravo Tracker in Brazil - APBPQx John G8BPQ Digipeater/IGate - APBMxx BrandMeister DMR Server for R3ABM - APBSDx HamBSD https://hambsd.org/ - APBT62 BTech DMR 6x2 - APC APCxxx Cellular applications - APCBBx VE7UDP Blackberry Applications - APCDS0 Leon Lessing ZS6LMG's cell tracker - APCLEY EYTraker GPRS/GSM tracker by ZS6EY - APCLEZ Telit EZ10 GSM application ZS6CEY - APCLUB Brazil APRS network - APCLWX EYWeather GPRS/GSM WX station by ZS6EY - APCNxx for carNET by DG5OAW - APCSMS for Cosmos (used for sending commands @USNA) - APCSS for AMSAT cubesats https://cubesatsim.org - APCWP8 John GM7HHB, WinphoneAPRS - APCYxx Cybiko applications - APD APD4xx UP4DAR platform - APDDxx DV-RPTR Modem and Control Center Software - APDFxx Automatic DF units - APDGxx D-Star Gateways by G4KLX ircDDB - APDHxx WinDV (DUTCH*Star DV Node for Windows) - APDInn DIXPRS - Bela, HA5DI - APDIGI Used by PSAT2 to indicate the digi is ON - APDIGI digi ON for PSAT2 and QIKCOM-2 - APDKxx KI4LKF g2_ircddb Dstar gateway software - APDNOx APRSduino by DO3SWW - APDOxx ON8JL Standalone DStar Node - APDPRS D-Star originated posits - APDRxx APRSdroid Android App http://aprsdroid.org/ - APDSXX SP9UOB for dsDigi and ds-tracker - APDTxx APRStouch Tone (DTMF) - APDTMF digi off mode on QIKCOM2 and DTMF ON - APDUxx U2APRS by JA7UDE - APDVxx OE6PLD's SSTV with APRS status exchange - APDWxx DireWolf, WB2OSZ - APE APExxx Telemetry devices - APE2Ax VA3NNW's Email-2-APRS ap - APECAN Pecan Pico APRS Balloon Tracker - APELKx WB8ELK balloons - APERXQ Experimental tracker by PE1RXQ - APERSx Runner tracking by Jason,KG7YKZ - APESPG ESP SmartBeacon APRS-IS Client - APESPW ESP Weather Station APRS-IS Client - APF APFxxx Firenet - APFGxx Flood Gage (KP4DJT) - APFIxx for APRS.FI OH7LZB, Hessu - APFPRS for FreeDV by Jeroen PE1RXQ - APG APGxxx Gates, etc - APGOxx for AA3NJ PDA application - APGBLN for NW5W's GoBalloon - APGDTx for VK4FAST's Graphic Data Terminal - APH APHKxx for LA1BR tracker/digipeater - APHAXn SM2APRS by PY2UEP - APHBLx for DMR Gateway by Eric - KF7EEL - APHTxx HMTracker by IU0AAC - APHWxx for use in "HamWAN - API API282 for ICOM IC-2820 - API31 for ICOM ID-31 - API410 for ICOM ID-4100 - API51 for ICOM ID-51 - API510 for ICOM ID-5100 - API710 for ICOM IC-7100 - API80 for ICOM IC-80 - API880 for ICOM ID-880 - API910 for ICOM IC-9100 - API92 for ICOM IC-92 - API970 for ICOM 9700 - APICQx for ICQ - APICxx HA9MCQ's Pic IGate - APIExx W7KMV's PiAPRS system - APINxx PinPoint by AB0WV - APIZCI hymTR IZCI Tracker by TA7W/OH2UDS and TA6AEU - APJ APJ8xx Jordan / KN4CRD JS8Call application - APJAxx JavAPRS - APJExx JeAPRS - APJIxx jAPRSIgate - APJSxx javAPRSSrvr - APJYnn KA2DDO Yet another APRS system - 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 - APL APLCxx APRScube by DL3DCW - APLGxx LoRa Gateway/Digipeater OE5BPA - APLIGx LightAPRS - TA2MUN and TA9OHC - APLOxx LoRa KISS TNC/Tracker - APLQRU Charlie - QRU Server - APLMxx WA0TQG transceiver controller - APLSxx SARIMESH ( http://www.sarimesh.net ) - APLTxx LoRa Tracker - OE5BPA - APM APMxxx MacAPRS, - APMGxx PiCrumbs and MiniGate - Alex, AB0TJ - APMIxx SQ3PLX http://microsat.com.pl/ - APMPAD DF1JSL's WXBot clone and extension - APMQxx Ham Radio of Things WB2OSZ - APMTxx LZ1PPL for tracker - APN APNxxx Network nodes, digis, etc - APN2xx NOSaprs for JNOS 2.0 - VE4KLM - APN3xx Kantronics KPC-3 rom versions - APN9xx Kantronics KPC-9612 Roms - APNAxx WB6ZSU's APRServe - APNDxx DIGI_NED - APNICx SQ5EKU http://sq5eku.blogspot.com/ - APNK01 Kenwood D700 (APK101) type - APNK80 KAM version 8.0 - APNKMP KAM+ - APNKMX KAM-XL - APNMxx MJF TNC roms - APNPxx Paccom TNC roms - APNTxx SV2AGW's TNT tnc as a digi - APNUxx UIdigi - APNVxx SQ8L's VP digi and Nodes - APNXxx TNC-X (K6DBG) - APNWxx SQ3FYK.com WX/Digi and SQ3PLX http://microsat.com.pl/ - APO APRSpoint - APOFF Used by PSAT and PSAT2 to indicate the digi is OFF - APOLUx for OSCAR satellites for AMSAT-LU by LU9DO - APOAxx OpenAPRS - Greg Carter - APOCSG For N0AGI's APRS to POCSAG project - APOD1w Open Track with 1 wire WX - APOSBx openSPOT3 by HA2NON at sharkrf.com - APOSWx openSPOT2 - APOTxx Open Track - APOU2k Open Track for Ultimeter - APOZxx www.KissOZ.dk Tracker. OZ1EKD and OZ7HVO - APP APP6xx for APRSlib - APPICx DB1NTO' PicoAPRS - APPMxx DL1MX's RTL-SDR pytohon Igate - APPTxx KetaiTracker by JF6LZE, Takeki (msg capable) - APQ APQxxx Earthquake data - APQTHx W8WJB's QTH.app - APR APR8xx APRSdos versions 800+ - APR2MF DL2MF - MF2APRS Radiosonde WX reporting - APRARX VK5QI's radiosonde tracking - APRDxx APRSdata, APRSdr - APRGxx aprsg igate software, OH2GVE - APRHH2 HamHud 2 - APRKxx APRStk - APRNOW W5GGW ipad application - APRRTx RPC electronics - APRS Generic, (obsolete. Digis should use APNxxx instead) - APRSON Used by PSAT to indicate the DIGI is ON - APRXxx >40 APRSmax - APRXxx <39 for OH2MQK's igate - APRTLM used in MIM's and Mic-lites, etc - APRtfc APRStraffic - APRSTx APRStt (Touch tone) - APS APSxxx APRS+SA, etc - APSARx ZL4FOX's SARTRACK - APSAT digi ON for QIKCOM-1 - APSCxx aprsc APRS-IS core server (OH7LZB, OH2MQK) - APSFxx F5OPV embedded devices - was APZ40 - APSK63 APRS Messenger -over-PSK63 - APSK25 APRS Messenger GMSK-250 - APSMSx Paul Dufresne's SMSGTE - SMS Gateway - APSTMx for W7QO's Balloon trackers - APSTPO for N0AGI Satellite Tracking and Operations - APT APT2xx Tiny Track II - APT3xx Tiny Track III - APTAxx K4ATM's tiny track - APTBxx TinyAPRS by BG5HHP Was APTAxx till Sep 2017 - APTCHE PU3IKE in Brazil TcheTracker/Tcheduino - APTCMA CAPI tracker - PU1CMA Brazil - APTIGR TigerTrack - APTKPT TrackPoint N0LP - APTPNx TARPN Packet Node Tracker by KN4ORB http://tarpn.net/ - APTTxx Tiny Track - APTWxx Byons WXTrac - APTVxx for ATV/APRN and SSTV applications - APU APU1xx UIview 16 bit applications - APU2xx UIview 32 bit apps - APU3xx UIview terminal program - APUDRx NW Digital Radio's UDR (APRS/Dstar) - APV APVxxx Voice over Internet applications - APVMxx DRCC-DVM Digital Voice (Digital Radio China Club) - APVRxx for IRLP - APVLxx for I-LINK - APVExx for ECHO link - APW APWxxx WinAPRS, etc - APW9xx 9A9Y Weather Tracker - APWAxx APRSISCE Android version - APWSxx DF4IAN's WS2300 WX station - APWMxx APRSISCE KJ4ERJ - APWWxx APRSISCE win32 version - APX APXnnn Xastir - APXRnn Xrouter - APY APYxxx Yaesu Radios - APY008 Yaesu VX-8 series - APY01D Yaesu FT1D series - APY02D Yaesu FT2D series - APY03D Yaesu FT3D series - APY05D Yaesu FT5D series - APY100 Yaesu FTM-100D series - APY300 Yaesu FTM-300D series - APY350 Yaesu FTM-350 series - APY400 Yaesu FTM-400D series - APZ APZxxx Experimental - APZ200 old versions of JNOS - 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) - APZWIT MAP27 radio (Mountain Rescue) EI7IG - APZWKR GM1WKR NetSked application - - - - - - -REGISTERED TOCALL ALTNETS: --------------------------- - -ALTNETS are uses of the AX-25 tocall to distinguish specialized -traffic that may be flowing on the APRS-IS, but that are not intended -to be part of normal APRS distribution to all normal APRS software -operating in normal (default) modes. Proper APRS software that -honors this design are supposed to IGNORE all ALTNETS unless the -particular operator has selected an ALTNET to monitor for. - -An example is when testing; an author may want to transmit objects -all over his map for on-air testing, but does not want these to -clutter everyone's maps or databases. He could use the ALTNET of -"TEST" and client APRS software that respects the ALTNET concept -should ignore these packets. - -An ALTNET is defined to be ANY AX.25 TOCALL that is NOT one of the -normal APRS TOCALL's. The normal TOCALL's that APRS is supposed to -process are: ALL, BEACON, CQ, QST, GPSxxx and of course APxxxx. - -The following is a list of ALTNETS that may be of interest to other -users. This list is by no means complete, since ANY combination of -characters other than APxxxx are considered an ALTNET. But this list -can give consisntecy to ALTNETS that may be using the global APRS-IS -and need some special recognition. Here are some ideas: - - - - SATERN - Salvation Army Altnet - AFMARS - Airforce Mars - AMARS - Army Mars - \ No newline at end of file diff --git a/data/tocalls.yaml b/data/tocalls.yaml new file mode 100644 index 00000000..adf26a1f --- /dev/null +++ b/data/tocalls.yaml @@ -0,0 +1,1481 @@ +# +# This is a machine-readable index of APRS device and software +# identification strings. For easy manual editing and validation, the +# master file is in YAML format. A conversion tool and pre-converted +# versions in XML and JSON are also provided for environments where those +# are more convenient to parse. +# +# This list is maintained by Hessu, OH7LZB, for the aprs.fi service. +# It is licensed under the CC BY-SA 2.0 license, so you're free to use +# it in any of your applications. For free. Just mention the source +# somewhere in the small print. +# http://creativecommons.org/licenses/by-sa/2.0/ +# + +--- + +# +# English shown names and descriptions for device classes +# +classes: + - class: wx + shown: Weather station + description: Dedicated weather station + + - class: tracker + shown: Tracker + description: Tracker device + + - class: rig + shown: Rig + description: Mobile or desktop radio + + - class: ht + shown: HT + description: Hand-held radio + + - class: app + shown: Mobile app + description: Mobile phone or tablet app + + - class: software + shown: Software + description: Desktop software + + - class: digi + shown: Digipeater + description: Digipeater software + + - class: igate + shown: iGate + description: iGate software + + - class: dstar + shown: D-Star + description: D-Star radio + + - class: satellite + shown: Satellite + description: Satellite-based station + + - class: service + shown: Service + description: Software running as a web service + +# +# mic-e device identifier index for new-style 2-character device +# suffixes. The first prefix byte indicates messaging capability. +# +mice: + - suffix: "_ " + vendor: Yaesu + model: VX-8 + class: ht + + - suffix: "_\"" + vendor: Yaesu + model: FTM-350 + class: rig + + - suffix: "_#" + vendor: Yaesu + model: VX-8G + class: ht + + - suffix: "_$" + vendor: Yaesu + model: FT1D + class: ht + + - suffix: "_(" + vendor: Yaesu + model: FT2D + class: ht + + - suffix: "_0" + vendor: Yaesu + model: FT3D + class: ht + + - suffix: "_3" + vendor: Yaesu + model: FT5D + class: ht + + - suffix: "_1" + vendor: Yaesu + model: FTM-300D + class: rig + + - suffix: "_)" + vendor: Yaesu + model: FTM-100D + class: rig + + - suffix: "_%" + vendor: Yaesu + model: FTM-400DR + class: rig + + - suffix: "(5" + vendor: Anytone + model: D578UV + class: ht + + - suffix: "(8" + vendor: Anytone + model: D878UV + class: ht + + - suffix: "|3" + vendor: Byonics + model: TinyTrak3 + class: tracker + + - suffix: "|4" + vendor: Byonics + model: TinyTrak4 + class: tracker + + - suffix: "^v" + vendor: HinzTec + model: anyfrog + + - suffix: "*v" + vendor: KissOZ + model: Tracker + class: tracker + +# +# mic-e legacy devices, with an unique comment suffix and prefix +# + +micelegacy: + - prefix: ">" + vendor: Kenwood + model: TH-D7A + class: ht + features: + - messaging + + - prefix: ">" + suffix: "=" + vendor: Kenwood + model: TH-D72 + class: ht + features: + - messaging + + - prefix: ">" + suffix: "^" + vendor: Kenwood + model: TH-D74 + class: ht + features: + - messaging + + - prefix: ">" + suffix: "&" + vendor: Kenwood + model: TH-D75 + class: ht + features: + - messaging + + - prefix: "]" + vendor: Kenwood + model: TM-D700 + class: rig + features: + - messaging + + - prefix: "]" + suffix: "=" + vendor: Kenwood + model: TM-D710 + class: rig + features: + - messaging + +# +# TOCALL index +# +tocalls: + - tocall: AP1WWX + vendor: TAPR + model: T-238+ + class: wx + + - tocall: AP4R?? + vendor: Open Source + model: APRS4R + class: software + + - tocall: APAEP1 + vendor: Paraguay Space Agency (AEP) + model: "EIRUAPRSDIGIS&FV1" + class: satellite + + - tocall: APAF?? + model: AFilter + + - tocall: APAG?? + model: AGate + + - tocall: APAGW + vendor: SV2AGW + model: AGWtracker + class: software + os: Windows + + - tocall: APAGW? + vendor: SV2AGW + model: AGWtracker + class: software + os: Windows + + - tocall: APAH?? + model: AHub + + - tocall: APAM?? + vendor: Altus Metrum + model: AltOS + class: tracker + + - tocall: APAND? + vendor: Open Source + model: APRSdroid + os: Android + class: app + + - tocall: APAT51 + vendor: Anytone + model: AT-D578 + class: rig + + - tocall: APAT81 + vendor: Anytone + model: AT-D878 + class: ht + + - tocall: APAT?? + vendor: Anytone + + - tocall: APATAR + vendor: TA7W/OH2UDS Baris Dinc and TA6AEU + model: ATA-R APRS Digipeater + class: digi + + - tocall: APAVT5 + vendor: SainSonic + model: AP510 + class: tracker + + - tocall: APAW?? + vendor: SV2AGW + model: AGWPE + class: software + os: Windows + + - tocall: APAX?? + model: AFilterX + + - tocall: APB2MF + vendor: Mike, DL2MF + model: MF2APRS Radiosonde tracking tool + class: software + os: Windows + + - tocall: APBK?? + vendor: PY5BK + model: Bravo Tracker + class: tracker + + - tocall: APBL?? + vendor: BigRedBee + model: BeeLine GPS + class: tracker + + - tocall: APBM?? + vendor: R3ABM + model: BrandMeister DMR + + - tocall: APBPQ? + vendor: John Wiseman, G8BPQ + model: BPQ32 + class: software + os: Windows + + - tocall: APBSD? + vendor: hambsd.org + model: HamBSD + + - tocall: APBT62 + vendor: BTech + model: DMR 6x2 + + - tocall: APC??? + vendor: Rob Wittner, KZ5RW + model: APRS/CE + class: app + + - tocall: APCDS0 + vendor: ZS6LMG + model: cell tracker + class: tracker + + - tocall: APCLEY + vendor: ZS6EY + model: EYTraker + class: tracker + + - tocall: APCLEZ + vendor: ZS6EY + model: Telit EZ10 GSM application + class: tracker + + - tocall: APCLUB + model: Brazil APRS network + + - tocall: APCLWX + vendor: ZS6EY + model: EYWeather + class: wx + + - tocall: APCN?? + vendor: DG5OAW + model: carNET + + - tocall: APCSMS + vendor: USNA + model: Cosmos + + - tocall: APCSS + vendor: AMSAT + model: CubeSatSim CubeSat Simulator + + - tocall: APCTLK + vendor: Open Source + model: Codec2Talkie + class: app + + - tocall: APCWP8 + vendor: GM7HHB + model: WinphoneAPRS + class: app + + - tocall: APDF?? + model: Automatic DF units + + - tocall: APDG?? + vendor: Jonathan, G4KLX + model: ircDDB Gateway + class: dstar + + - tocall: APDI?? + vendor: Bela, HA5DI + model: DIXPRS + class: software + + - tocall: APDNO? + vendor: DO3SWW + model: APRSduino + class: tracker + os: embedded + + - tocall: APDPRS + vendor: unknown + model: D-Star APDPRS + class: dstar + + - tocall: APDR?? + vendor: Open Source + model: APRSdroid + os: Android + class: app + + - tocall: APDS?? + vendor: SP9UOB + model: dsDIGI + os: embedded + + - tocall: APDST? + vendor: SP9UOB + model: dsTracker + os: embedded + + - tocall: APDT?? + vendor: unknown + model: APRStouch Tone (DTMF) + + - tocall: APDU?? + vendor: JA7UDE + model: U2APRS + class: app + os: Android + + - tocall: APDV?? + vendor: OE6PLD + model: SSTV with APRS + class: software + + - tocall: APDW?? + vendor: WB2OSZ + model: DireWolf + + - tocall: APDnnn + vendor: Open Source + model: aprsd + class: software + os: Linux/Unix + + - tocall: APE2A? + vendor: NoseyNick, VA3NNW + model: Email-2-APRS gateway + class: software + os: Linux/Unix + + - tocall: APE??? + model: Telemetry devices + + - tocall: APECAN + vendor: KT5TK/DL7AD + model: Pecan Pico APRS Balloon Tracker + class: tracker + + - tocall: APELK? + vendor: WB8ELK + model: Balloon tracker + class: tracker + + - tocall: APERS? + vendor: Jason, KG7YKZ + model: Runner tracking + class: tracker + + - tocall: APERXQ + vendor: PE1RXQ + model: PE1RXQ APRS Tracker + class: tracker + + - tocall: APESP? + vendor: LY3PH + model: APRS-ESP + os: embedded + + - tocall: APFG?? + vendor: KP4DJT + model: Flood Gage + class: software + + - tocall: APFI?? + vendor: aprs.fi + class: app + + - tocall: APFII? + model: iPhone/iPad app + vendor: aprs.fi + os: ios + class: app + + - tocall: APGBLN + vendor: NW5W + model: GoBalloon + class: tracker + + - tocall: APGO?? + vendor: AA3NJ + model: APRS-Go + class: app + + - tocall: APHAX? + vendor: PY2UEP + model: SM2APRS SondeMonitor + class: software + os: Windows + + - tocall: APHBL? + vendor: KF7EEL + model: HBLink D-APRS Gateway + class: software + + - tocall: APHH? + vendor: Steven D. Bragg, KA9MVA + model: HamHud + class: tracker + + - tocall: APHK?? + vendor: LA1BR + model: Digipeater/tracker + + - tocall: APHMEY + vendor: Tapio Heiskanen, OH2TH + model: APRS-IS Client for Athom Homey + contact: oh2th@iki.fi + + - tocall: APHPIA + vendor: HP3ICC + model: Arduino APRS + + - tocall: APHPIB + vendor: HP3ICC + model: Python APRS Beacon + + - tocall: APHPIW + vendor: HP3ICC + model: Python APRS WX + + - tocall: APHT?? + vendor: IU0AAC + model: HMTracker + class: tracker + + - tocall: APHW?? + vendor: HamWAN + + - tocall: API282 + vendor: Icom + model: IC-2820 + class: dstar + + - tocall: API31 + vendor: Icom + model: IC-31 + class: dstar + + - tocall: API410 + vendor: Icom + model: IC-4100 + class: dstar + + - tocall: API51 + vendor: Icom + model: IC-51 + class: dstar + + - tocall: API510 + vendor: Icom + model: IC-5100 + class: dstar + + - tocall: API710 + vendor: Icom + model: IC-7100 + class: dstar + + - tocall: API80 + vendor: Icom + model: IC-80 + class: dstar + + - tocall: API880 + vendor: Icom + model: IC-880 + class: dstar + + - tocall: API910 + vendor: Icom + model: IC-9100 + class: dstar + + - tocall: API92 + vendor: Icom + model: IC-92 + class: dstar + + - tocall: API970 + vendor: Icom + model: IC-9700 + class: dstar + + - tocall: API??? + vendor: Icom + model: unknown + class: dstar + + - tocall: APIC?? + vendor: HA9MCQ + model: PICiGATE + + - tocall: APIE?? + vendor: W7KMV + model: PiAPRS + + - tocall: APIN?? + vendor: AB0WV + model: PinPoint + + - tocall: APIZCI + vendor: TA7W/OH2UDS and TA6AEU + model: hymTR IZCI Tracker + class: tracker + os: embedded + + - tocall: APJ8?? + vendor: KN4CRD + model: JS8Call + class: software + + - tocall: APJA?? + vendor: K4HG & AE5PL + model: JavAPRS + + - tocall: APJE?? + vendor: Gregg Wonderly, W5GGW + model: JeAPRS + + - tocall: APJI?? + vendor: Peter Loveall, AE5PL + model: jAPRSIgate + class: software + + - tocall: APJID2 + vendor: Peter Loveall, AE5PL + model: D-Star APJID2 + class: dstar + + - tocall: APJS?? + vendor: Peter Loveall, AE5PL + model: javAPRSSrvr + + - tocall: APJY?? + vendor: KA2DDO + model: YAAC + class: software + + - tocall: APK003 + vendor: Kenwood + model: TH-D72 + class: ht + + - tocall: APK004 + vendor: Kenwood + model: TH-D74 + class: ht + + - tocall: APK005 + vendor: Kenwood + model: TH-D75 + class: ht + + - tocall: APK0?? + vendor: Kenwood + model: TH-D7 + class: ht + + - tocall: APK1?? + vendor: Kenwood + model: TM-D700 + class: rig + + - tocall: APKHTW + vendor: Kip, W3SN + model: Tempest Weather Bridge + class: wx + os: embedded + contact: w3sn@moxracing.33mail.com + + - tocall: APKRAM + vendor: kramstuff.com + model: Ham Tracker + class: app + os: ios + + - tocall: APLC?? + vendor: DL3DCW + model: APRScube + + - tocall: APLDI? + vendor: David, OK2DDS + model: LoRa IGate/Digipeater + class: digi + + - tocall: APLDM? + vendor: David, OK2DDS + model: LoRa Meteostation + class: wx + + - tocall: APLETK + vendor: DL5TKL + model: T-Echo + class: tracker + os: embedded + contact: cfr34k-git@tkolb.de + + - tocall: APLG?? + vendor: OE5BPA + model: LoRa Gateway/Digipeater + class: digi + + - tocall: APLIG? + vendor: TA2MUN/TA9OHC + model: LightAPRS Tracker + class: tracker + + - tocall: APLM?? + vendor: WA0TQG + class: software + + - tocall: APLO?? + vendor: SQ9MDD + model: LoRa KISS TNC/Tracker + class: tracker + + - tocall: APLP0? + vendor: SQ9P + model: fajne digi + class: digi + os: embedded + contact: sq9p.peter@gmail.com + + - tocall: APLP1? + vendor: SQ9P + model: LORA/FSK/AFSK fajny tracker + class: tracker + os: embedded + contact: sq9p.peter@gmail.com + + - tocall: APLRG? + vendor: Ricardo, CD2RXU + model: ESP32 LoRa iGate + class: igate + os: embedded + contact: richonguzman@gmail.com + + - tocall: APLRT? + vendor: Ricardo, CD2RXU + model: ESP32 LoRa Tracker + class: tracker + os: embedded + contact: richonguzman@gmail.com + + - tocall: APLS?? + vendor: SARIMESH + model: SARIMESH + class: software + + - tocall: APLT?? + vendor: OE5BPA + model: LoRa Tracker + class: tracker + + - tocall: APLU0? + vendor: SP9UP + model: ESP32/SX12xx LoRa iGate / Digi + class: digi + os: embedded + contact: wajdzik.m@gmail.com + + - tocall: APLU1? + vendor: SP9UP + model: ESP32/SX12xx LoRa Tracker + class: tracker + os: embedded + contact: wajdzik.m@gmail.com + + - tocall: APMG?? + vendor: Alex, AB0TJ + model: PiCrumbs and MiniGate + class: software + + - tocall: APMI01 + vendor: Microsat + os: embedded + model: WX3in1 + + - tocall: APMI02 + vendor: Microsat + os: embedded + model: WXEth + + - tocall: APMI03 + vendor: Microsat + os: embedded + model: PLXDigi + + - tocall: APMI04 + vendor: Microsat + os: embedded + model: WX3in1 Mini + + - tocall: APMI05 + vendor: Microsat + os: embedded + model: PLXTracker + + - tocall: APMI06 + vendor: Microsat + os: embedded + model: WX3in1 Plus 2.0 + + - tocall: APMI?? + vendor: Microsat + os: embedded + + - tocall: APMON? + vendor: Amon Schumann, DL9AS + model: APRS Balloon Tracker + class: tracker + os: embedded + + - tocall: APMPAD + vendor: DF1JSL + model: Multi-Purpose APRS Daemon + class: service + contact: joerg.schultze.lutter@gmail.com + features: + - messaging + + - tocall: APMQ?? + vendor: WB2OSZ + model: Ham Radio of Things + + - tocall: APMT?? + vendor: LZ1PPL + model: Micro APRS Tracker + class: tracker + + - tocall: APN102 + vendor: Gregg Wonderly, W5GGW + model: APRSNow + class: app + os: ipad + + - tocall: APN2?? + vendor: VE4KLM + model: NOSaprs for JNOS 2.0 + + - tocall: APN3?? + vendor: Kantronics + model: KPC-3 + + - tocall: APN9?? + vendor: Kantronics + model: KPC-9612 + + - tocall: APNCM + vendor: Keith Kaiser, WA0TJT + model: Net Control Manager + class: software + os: browser + contact: wa0tjt@gmail.com + + - tocall: APND?? + vendor: PE1MEW + model: DIGI_NED + + - tocall: APNIC4 + vendor: SQ5EKU + model: BidaTrak + class: tracker + os: embedded + + - tocall: APNJS? + vendor: Julien Sansonnens, HB9HRD + model: Web messaging service + class: service + contact: julien.owls@gmail.com + features: + - messaging + + - tocall: APNK01 + vendor: Kenwood + model: TM-D700 + class: rig + features: + - messaging + + - tocall: APNK80 + vendor: Kantronics + model: KAM + + - tocall: APNKMP + vendor: Kantronics + model: KAM+ + + - tocall: APNKMX + vendor: Kantronics + model: KAM-XL + + - tocall: APNM?? + vendor: MFJ + model: TNC + + - tocall: APNP?? + vendor: PacComm + model: TNC + + - tocall: APNT?? + vendor: SV2AGW + model: TNT TNC as a digipeater + class: digi + + - tocall: APNU?? + vendor: IW3FQG + model: UIdigi + class: digi + + - tocall: APNV0? + vendor: SQ8L + model: VP-Digi + os: embedded + + - tocall: APNV1? + vendor: SQ8L + model: VP-Node + os: embedded + + - tocall: APNV?? + vendor: SQ8L + + - tocall: APNW?? + vendor: SQ3FYK + model: WX3in1 + os: embedded + + - tocall: APNX?? + vendor: K6DBG + model: TNC-X + + - tocall: APOA?? + vendor: OpenAPRS + model: app + class: app + os: ios + + - tocall: APOCSG + vendor: N0AGI + model: POCSAG + + - tocall: APOG7? + vendor: OpenGD77 + model: OpenGD77 + os: embedded + contact: Roger VK3KYY/G4KYF + + - tocall: APOLU? + vendor: AMSAT-LU + model: Oscar + class: satellite + + - tocall: APOSAT + vendor: Mike, NA7Q + model: Open Source Satellite Gateway + class: service + contact: mike.ph4@gmail.com + + - tocall: APOSMS + vendor: Mike, NA7Q + model: Open Source SMS Gateway + class: service + contact: mike.ph4@gmail.com + features: + - messaging + + - tocall: APOT?? + vendor: Argent Data Systems + model: OpenTracker + class: tracker + + - tocall: APOVU? + vendor: K J Somaiya Institute + model: BeliefSat + + - tocall: APOZ?? + vendor: OZ1EKD, OZ7HVO + model: KissOZ + class: tracker + + - tocall: APP6?? + model: APRSlib + + - tocall: APPCO? + vendor: RadCommSoft, LLC + model: PicoAPRSTracker + class: tracker + os: embedded + contact: ab4mw@radcommsoft.com + + - tocall: APPIC? + vendor: DB1NTO + model: PicoAPRS + class: tracker + + - tocall: APPM?? + vendor: DL1MX + model: rtl-sdr Python iGate + class: software + + - tocall: APPRIS + vendor: DF1JSL + model: Apprise APRS plugin + class: service + contact: joerg.schultze.lutter@gmail.com + features: + - messaging + + - tocall: APPT?? + vendor: JF6LZE + model: KetaiTracker + class: tracker + + - tocall: APQTH? + vendor: Weston Bustraan, W8WJB + model: QTH.app + class: software + os: macOS + features: + - messaging + + - tocall: APR2MF + vendor: Mike, DL2MF + model: MF2wxAPRS Tinkerforge gateway + class: wx + os: Windows + + - tocall: APR8?? + vendor: Bob Bruninga, WB4APR + model: APRSdos + class: software + + - tocall: APRARX + vendor: Open Source + model: radiosonde_auto_rx + class: software + os: Linux/Unix + + - tocall: APRFG? + vendor: RF.Guru + contact: info@rf.guru + + - tocall: APRFGB + vendor: RF.Guru + model: APRS LoRa Pager + os: embedded + contact: info@rf.guru + + - tocall: APRFGD + vendor: RF.Guru + model: APRS Digipeater + class: digi + os: embedded + contact: info@rf.guru + + - tocall: APRFGH + vendor: RF.Guru + model: Hotspot + class: rig + os: embedded + contact: info@rf.guru + + - tocall: APRFGI + vendor: RF.Guru + model: LoRa APRS iGate + class: igate + os: embedded + contact: info@rf.guru + + - tocall: APRFGL + vendor: RF.Guru + model: Lora APRS Digipeater + class: digi + os: embedded + contact: info@rf.guru + + - tocall: APRFGM + vendor: RF.Guru + model: Mobile Radio + class: rig + os: embedded + contact: info@rf.guru + + - tocall: APRFGP + vendor: RF.Guru + model: Portable Radio + class: ht + os: embedded + contact: info@rf.guru + + - tocall: APRFGR + vendor: RF.Guru + model: Repeater + class: rig + os: embedded + contact: info@rf.guru + + - tocall: APRFGT + vendor: RF.Guru + model: LoRa APRS Tracker + class: tracker + os: embedded + contact: info@rf.guru + + - tocall: APRFGW + vendor: RF.Guru + model: LoRa APRS Weather Station + class: wx + os: embedded + contact: info@rf.guru + + - tocall: APRG?? + vendor: OH2GVE + model: aprsg + class: software + os: Linux/Unix + + - tocall: APRHH? + vendor: Steven D. Bragg, KA9MVA + model: HamHud + class: tracker + + - tocall: APRNOW + vendor: Gregg Wonderly, W5GGW + model: APRSNow + class: app + os: ipad + + - tocall: APRPR? + vendor: Robert DM4RW, Peter DL6MAA + model: Teensy RPR TNC + class: tracker + os: embedded + contact: dm4rw@skywaves.de + + - tocall: APRRDZ + model: rdzTTGOsonde + vendor: DL9RDZ + class: tracker + + - tocall: APRRF? + vendor: Jean-Francois Huet F1EVM + model: Tracker for RRF + class: tracker + os: embedded + contact: f1evm@f1evm.fr + features: + - messaging + + - tocall: APRRT? + vendor: RPC Electronics + model: RTrak + class: tracker + + - tocall: APRS + vendor: Unknown + model: Unknown + + - tocall: APRX?? + vendor: Kenneth W. Finnegan, W6KWF + model: Aprx + class: igate + os: Linux/Unix + + - tocall: APS??? + vendor: Brent Hildebrand, KH2Z + model: APRS+SA + class: software + + - tocall: APSAR + vendor: ZL4FOX + model: SARTrack + class: software + os: Windows + + - tocall: APSC?? + vendor: OH2MQK, OH7LZB + model: aprsc + class: software + + - tocall: APSF?? + vendor: F5OPV, SFCP_LABS + model: embedded APRS devices + os: embedded + + - tocall: APSFLG + vendor: F5OPV, SFCP_LABS + model: LoRa/APRS Gateway + class: digi + os: embedded + + - tocall: APSFRP + vendor: F5OPV, SFCP_LABS + model: VHF/UHF Repeater + os: embedded + + - tocall: APSFTL + vendor: F5OPV, SFCP_LABS + model: LoRa/APRS Telemetry Reporter + os: embedded + + - tocall: APSFWX + vendor: F5OPV, SFCP_LABS + model: embedded Weather Station + class: wx + os: embedded + + - tocall: APSK63 + vendor: Chris Moulding, G4HYG + model: APRS Messenger + class: software + os: Windows + + - tocall: APSMS? + vendor: Paul Dufresne + model: SMS gateway + class: software + + - tocall: APSRF? + vendor: SoftRF + model: Ham Edition + class: tracker + os: embedded + + - tocall: APSTM? + vendor: W7QO + model: Balloon tracker + class: tracker + + - tocall: APSTPO + vendor: N0AGI + model: Satellite Tracking and Operations + class: software + + - tocall: APT2?? + vendor: Byonics + model: TinyTrak2 + class: tracker + + - tocall: APT3?? + vendor: Byonics + model: TinyTrak3 + class: tracker + + - tocall: APT4?? + vendor: Byonics + model: TinyTrak4 + class: tracker + + - tocall: APTB?? + vendor: BG5HHP + model: TinyAPRS + + - tocall: APTCHE + vendor: PU3IKE + model: TcheTracker, Tcheduino + class: tracker + + - tocall: APTCMA + vendor: Cleber, PU1CMA + model: CAPI Tracker + class: tracker + + - tocall: APTEMP + vendor: KL7AF + model: APRS-Tempest Weather Gateway + class: wx + os: Linux/Unix + contact: kl7af@foghaven.net + + - tocall: APTKJ? + vendor: W9JAJ + model: ATTiny APRS Tracker + os: embedded + + - tocall: APTNG? + vendor: Filip YU1TTN + model: Tango Tracker + class: tracker + + - tocall: APTPN? + vendor: KN4ORB + model: TARPN Packet Node Tracker + class: tracker + + - tocall: APTR?? + vendor: Motorola + model: MotoTRBO + + - tocall: APTT* + vendor: Byonics + model: TinyTrak + class: tracker + + - tocall: APTW?? + vendor: Byonics + model: WXTrak + class: wx + + - tocall: APU1?? + vendor: Roger Barker, G4IDE + model: UI-View16 + class: software + os: Windows + + - tocall: APU2* + vendor: Roger Barker, G4IDE + model: UI-View32 + class: software + os: Windows + + - tocall: APUDR? + vendor: NW Digital Radio + model: UDR + + - tocall: APVE?? + vendor: unknown + model: EchoLink + + - tocall: APVM?? + vendor: Digital Radio China Club + model: DRCC-DVM + class: igate + + - tocall: APVR?? + vendor: unknown + model: IRLP + + - tocall: APW9?? + vendor: Mile Strk, 9A9Y + model: WX Katarina + class: wx + os: embedded + features: + - messaging + + - tocall: APWA?? + vendor: KJ4ERJ + model: APRSISCE + class: software + os: Android + + - tocall: APWEE? + vendor: Tom Keffer and Matthew Wall + model: WeeWX Weather Software + class: software + os: Linux/Unix + + - tocall: APWM?? + vendor: KJ4ERJ + model: APRSISCE + class: software + os: Windows Mobile + features: + - messaging + - item-in-msg + + - tocall: APWW?? + vendor: KJ4ERJ + model: APRSIS32 + class: software + os: Windows + features: + - messaging + - item-in-msg + + - tocall: APWnnn + vendor: Sproul Brothers + model: WinAPRS + class: software + os: Windows + + - tocall: APX??? + vendor: Open Source + model: Xastir + class: software + os: Linux/Unix + + - tocall: APXR?? + vendor: G8PZT + model: Xrouter + + - tocall: APY01D + vendor: Yaesu + model: FT1D + class: ht + + - tocall: APY02D + vendor: Yaesu + model: FT2D + class: ht + + - tocall: APY05D + vendor: Yaesu + model: FT5D + class: ht + + - tocall: APY300 + vendor: Yaesu + model: FTM-300D + class: rig + + - tocall: APY400 + vendor: Yaesu + model: FTM-400 + class: rig + + - tocall: APYS?? + vendor: W2GMD + model: Python APRS + class: software + + - tocall: APZ18 + vendor: IW3FQG + model: UIdigi + class: digi + + - tocall: APZ186 + vendor: IW3FQG + model: UIdigi + class: digi + + - tocall: APZ19 + vendor: IW3FQG + model: UIdigi + class: digi + + - tocall: APZ247 + model: UPRS + vendor: NR0Q + + - tocall: APZG?? + vendor: OH2GVE + model: aprsg + class: software + os: Linux/Unix + + - tocall: APZMAJ + vendor: M1MAJ + model: DeLorme inReach Tracker + + - tocall: APZMDR + vendor: Open Source + model: HaMDR + class: tracker + os: embedded + + - tocall: APZTKP + vendor: Nick Hanks, N0LP + model: TrackPoint + class: tracker + os: embedded + + - tocall: APZWKR + vendor: GM1WKR + model: NetSked + class: software + + - tocall: APnnnD + vendor: Painter Engineering + model: uSmartDigi D-Gate + class: dstar + + - tocall: APnnnU + vendor: Painter Engineering + model: uSmartDigi Digipeater + class: digi + + - tocall: PSKAPR + vendor: Open Source + model: PSKmail + class: software + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5320a163..19dada4a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,6 +33,7 @@ list(APPEND direwolf_SOURCES beacon.c config.c decode_aprs.c + deviceid.c dedupe.c demod_9600.c demod_afsk.c @@ -171,6 +172,7 @@ endif() # decode_aprs list(APPEND decode_aprs_SOURCES decode_aprs.c + deviceid.c ais.c kiss_frame.c ax25_pad.c @@ -355,6 +357,7 @@ list(APPEND atest_SOURCES ax25_pad.c ax25_pad2.c decode_aprs.c + deviceid.c dwgpsnmea.c dwgps.c dwgpsd.c diff --git a/src/decode_aprs.c b/src/decode_aprs.c index ab933278..54c2839d 100644 --- a/src/decode_aprs.c +++ b/src/decode_aprs.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2017, 2022 John Langner, WB2OSZ +// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2017, 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 @@ -56,7 +56,7 @@ #include "decode_aprs.h" #include "telemetry.h" #include "ais.h" - +#include "deviceid.h" #define TRUE 1 #define FALSE 0 @@ -124,7 +124,6 @@ static double get_longitude_9 (char *p, int quiet); static time_t get_timestamp (decode_aprs_t *A, char *p); static int get_maidenhead (decode_aprs_t *A, char *p); static int data_extension_comment (decode_aprs_t *A, char *pdext); -static void decode_tocall (decode_aprs_t *A, char *dest); //static void get_symbol (decode_aprs_t *A, char dti, char *src, char *dest); static void process_comment (decode_aprs_t *A, char *pstart, int clen); @@ -292,7 +291,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr } /* - * Application might be in the destination field for most message types. + * Device/Application is in the destination field for most packet types. * MIC-E format has part of location in the destination field. */ @@ -303,7 +302,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr break; default: - decode_tocall (A, A->g_dest); + deviceid_decode_dest (A->g_dest, A->g_mfr, sizeof(A->g_mfr)); break; } @@ -1392,7 +1391,6 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int int cust_msg = 0; const char *std_text[8] = {"Emergency", "Priority", "Special", "Committed", "Returning", "In Service", "En Route", "Off Duty" }; const char *cust_text[8] = {"Emergency", "Custom-6", "Custom-5", "Custom-4", "Custom-3", "Custom-2", "Custom-1", "Custom-0" }; - unsigned char *pfirst, *plast; strlcpy (A->g_data_type_desc, "MIC-E", sizeof(A->g_data_type_desc)); @@ -1622,111 +1620,32 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int A->g_course = n; -/* Now try to pick out manufacturer and other optional items. */ -/* The telemetry field, in the original spec, is no longer used. */ - - strlcpy (A->g_mfr, "Unknown manufacturer", sizeof(A->g_mfr)); - - pfirst = info + sizeof(struct aprs_mic_e_s); - plast = info + ilen - 1; - -/* Carriage return character at the end is not mentioned in spec. */ -/* Remove if found because it messes up extraction of manufacturer. */ -/* Don't drop trailing space because that is used for Yaesu VX-8. */ -/* As I recall, the IGate function trims trailing spaces. */ -/* That would be bad for this particular model. Maybe I'm mistaken? */ - - - if (*plast == '\r') plast--; - -#define isT(c) ((c) == ' ' || (c) == '>' || (c) == ']' || (c) == '`' || (c) == '\'') - -// Last Updated Dec. 2021 - -// 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)) { +// The rest is a comment which can have other information cryptically embedded. +// Remove any trailing CR, which I would argue, violates the protocol spec. +// It is essential to keep trailing spaces. e.g. VX-8 suffix is "_ " -// "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--; } - else if (*pfirst == ']' ) { strlcpy (A->g_mfr, "Kenwood TM-D700", sizeof(A->g_mfr)); pfirst++; } - -// ` should be used for message capable devices. - - else if (*pfirst == '`' && *(plast-1) == '_' && *plast == ' ') { strlcpy (A->g_mfr, "Yaesu VX-8", sizeof(A->g_mfr)); pfirst++; plast-=2; } - else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '"') { strlcpy (A->g_mfr, "Yaesu FTM-350", sizeof(A->g_mfr)); pfirst++; plast-=2; } - else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '#') { strlcpy (A->g_mfr, "Yaesu VX-8G", sizeof(A->g_mfr)); pfirst++; plast-=2; } - else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '$') { strlcpy (A->g_mfr, "Yaesu FT1D", sizeof(A->g_mfr)); pfirst++; plast-=2; } - else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '%') { strlcpy (A->g_mfr, "Yaesu FTM-400DR", sizeof(A->g_mfr)); pfirst++; plast-=2; } - else if (*pfirst == '`' && *(plast-1) == '_' && *plast == ')') { strlcpy (A->g_mfr, "Yaesu FTM-100D", sizeof(A->g_mfr)); pfirst++; plast-=2; } - else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '(') { strlcpy (A->g_mfr, "Yaesu FT2D", sizeof(A->g_mfr)); pfirst++; plast-=2; } - 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; } - - else if (*pfirst == '`' && *(plast-1) == '(' && *plast == '5') { strlcpy (A->g_mfr, "Anytone D578UV", sizeof(A->g_mfr)); pfirst++; plast-=2; } - else if (*pfirst == '`' ) { strlcpy (A->g_mfr, "Generic Mic-Emsg", sizeof(A->g_mfr)); pfirst++; } - -// ' should be used for trackers (not message capable). + char mcomment[256]; + strlcpy (mcomment, info + sizeof(struct aprs_mic_e_s), sizeof(mcomment)); + if (mcomment[strlen(mcomment)-1] == '\r') { + mcomment[strlen(mcomment)-1] = '\0'; + } - 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; } +/* Now try to pick out manufacturer and other optional items. */ +/* The telemetry field, in the original spec, is no longer used. */ - else if (*pfirst == '\'' && *(plast-1) == '|' && *plast == '3') { strlcpy (A->g_mfr, "Byonics TinyTrack3", sizeof(A->g_mfr)); pfirst++; plast-=2; } - else if (*pfirst == '\'' && *(plast-1) == '|' && *plast == '4') { strlcpy (A->g_mfr, "Byonics TinyTrack4", sizeof(A->g_mfr)); pfirst++; plast-=2; } + char trimmed[256]; // Comment with vendor/model removed. + deviceid_decode_mice (mcomment, trimmed, sizeof(trimmed), A->g_mfr, sizeof(A->g_mfr)); - else if (*pfirst == '\'' && *(plast-1) == ':' && *plast == '4') { strlcpy (A->g_mfr, "SCS GmbH & Co. P4dragon DR-7400 modems", sizeof(A->g_mfr)); pfirst++; plast-=2; } - else if (*pfirst == '\'' && *(plast-1) == ':' && *plast == '8') { strlcpy (A->g_mfr, "SCS GmbH & Co. P4dragon DR-7800 modems", sizeof(A->g_mfr)); pfirst++; plast-=2; } - else if (*pfirst == '\'' ) { strlcpy (A->g_mfr, "Generic McTrackr", sizeof(A->g_mfr)); pfirst++; } - else if ( *(plast-1) == '\\' ) { strlcpy (A->g_mfr, "Hamhud ?", sizeof(A->g_mfr)); pfirst++; plast-=2; } - else if ( *(plast-1) == '/' ) { strlcpy (A->g_mfr, "Argent ?", sizeof(A->g_mfr)); pfirst++; plast-=2; } - else if ( *(plast-1) == '^' ) { strlcpy (A->g_mfr, "HinzTec anyfrog", sizeof(A->g_mfr)); pfirst++; plast-=2; } - else if ( *(plast-1) == '*' ) { strlcpy (A->g_mfr, "APOZxx www.KissOZ.dk Tracker. OZ1EKD and OZ7HVO", sizeof(A->g_mfr)); pfirst++; plast-=2; } - else if ( *(plast-1) == '~' ) { strlcpy (A->g_mfr, "Unknown OTHER", sizeof(A->g_mfr)); pfirst++; plast-=2; } - } +// Possible altitude. 3 characters followed by } -/* - * An optional altitude is next. - * It is three base-91 digits followed by "}". - * The TM-D710A might have encoding bug. This was observed: - * - * KJ4ETP-9>SUUP9Q,KE4OTZ-3,WIDE1*,WIDE2-1,qAR,KI4HDU-2:`oV$n6:>/]"7&}162.475MHz clintserman@gmail= - * N 35 50.9100, W 083 58.0800, 25 MPH, course 230, alt 945 ft, 162.475MHz - * - * KJ4ETP-9>SUUP6Y,GRNTOP-3*,WIDE2-1,qAR,KI4HDU-2:`oU~nT >/]<0x9a>xt}162.475MHz clintserman@gmail= - * Invalid character in MIC-E altitude. Must be in range of '!' to '{'. - * N 35 50.6900, W 083 57.9800, 29 MPH, course 204, alt 3280843 ft, 162.475MHz - * - * KJ4ETP-9>SUUP6Y,N4NEQ-3,K4EGA-1,WIDE2*,qAS,N5CWH-1:`oU~nT >/]?xt}162.475MHz clintserman@gmail= - * N 35 50.6900, W 083 57.9800, 29 MPH, course 204, alt 808497 ft, 162.475MHz - * - * KJ4ETP-9>SUUP2W,KE4OTZ-3,WIDE1*,WIDE2-1,qAR,KI4HDU-2:`oV2o"J>/]"7)}162.475MHz clintserman@gmail= - * N 35 50.2700, W 083 58.2200, 35 MPH, course 246, alt 955 ft, 162.475MHz - * - * Note the <0x9a> which is outside of the 7-bit ASCII range. Clearly very wrong. - */ - if (plast > pfirst && pfirst[3] == '}') { + if (strlen(trimmed) >=4 && trimmed[3] == '}') { - A->g_altitude_ft = DW_METERS_TO_FEET((pfirst[0]-33)*91*91 + (pfirst[1]-33)*91 + (pfirst[2]-33) - 10000); + A->g_altitude_ft = DW_METERS_TO_FEET((trimmed[0]-33)*91*91 + (trimmed[1]-33)*91 + (trimmed[2]-33) - 10000); - if ( ! isdigit91(pfirst[0]) || ! isdigit91(pfirst[1]) || ! isdigit91(pfirst[2])) + if ( ! isdigit91(trimmed[0]) || ! isdigit91(trimmed[1]) || ! isdigit91(trimmed[2])) { if ( ! A->g_quiet) { text_color_set(DW_COLOR_ERROR); @@ -1736,12 +1655,13 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int A->g_altitude_ft = G_UNKNOWN; } - pfirst += 4; + process_comment (A, mcomment+4, strlen(mcomment) - 4); + return; } - process_comment (A, (char*)pfirst, (int)(plast - pfirst) + 1); + process_comment (A, mcomment, strlen(mcomment)); -} +} // end aprs_mic_e /*------------------------------------------------------------------ @@ -4251,221 +4171,6 @@ static int data_extension_comment (decode_aprs_t *A, char *pdext) } -/*------------------------------------------------------------------ - * - * Function: decode_tocall - * - * Purpose: Extract application from the destination. - * - * Inputs: dest - Destination address. - * Don't care if SSID is present or not. - * - * Outputs: A->g_mfr - * - * Description: For maximum flexibility, we will read the - * data file at run time rather than compiling it in. - * - * For the most recent version, download from: - * - * http://www.aprs.org/aprs11/tocalls.txt - * - * Windows version: File must be in current working directory. - * - * Linux version: Search order is current working directory then - * /usr/local/share/direwolf - * /usr/share/direwolf/tocalls.txt - * - * Mac: Like Linux and then - * /opt/local/share/direwolf - * - *------------------------------------------------------------------*/ - -// If I was more ambitious, this would dynamically allocate enough -// storage based on the file contents. Just stick in a constant for -// now. This takes an insignificant amount of space and -// I don't anticipate tocalls.txt growing that quickly. -// Version 1.4 - add message if too small instead of silently ignoring the rest. - -// Dec. 2016 tocalls.txt has 153 destination addresses. - -#define MAX_TOCALLS 250 - -static struct tocalls_s { - unsigned char len; - char prefix[7]; - char *description; -} tocalls[MAX_TOCALLS]; - -static int num_tocalls = 0; - -// Make sure the array is null terminated. -// If search order is changed, do the same in symbols.c for consistency. - -static const char *search_locations[] = { - (const char *) "tocalls.txt", // CWD - (const char *) "data/tocalls.txt", // Windows with CMake - (const char *) "../data/tocalls.txt", // ? -#ifndef __WIN32__ - (const char *) "/usr/local/share/direwolf/tocalls.txt", - (const char *) "/usr/share/direwolf/tocalls.txt", -#endif -#if __APPLE__ - // https://groups.yahoo.com/neo/groups/direwolf_packet/conversations/messages/2458 - // Adding the /opt/local tree since macports typically installs there. Users might want their - // INSTALLDIR (see Makefile.macosx) to mirror that. If so, then we need to search the /opt/local - // path as well. - (const char *) "/opt/local/share/direwolf/tocalls.txt", -#endif - (const char *) NULL // Important - Indicates end of list. -}; - -static int tocall_cmp (const void *px, const void *py) -{ - const struct tocalls_s *x = (struct tocalls_s *)px; - const struct tocalls_s *y = (struct tocalls_s *)py; - - if (x->len != y->len) return (y->len - x->len); - return (strcmp(x->prefix, y->prefix)); -} - -static void decode_tocall (decode_aprs_t *A, char *dest) -{ - FILE *fp = 0; - int n = 0; - static int first_time = 1; - char stuff[100]; - char *p = NULL; - char *r = NULL; - - //dw_printf("debug: decode_tocall(\"%s\")\n", dest); - -/* - * Extract the calls and descriptions from the file. - * - * Use only lines with exactly these formats: - * - * APN Network nodes, digis, etc - * APWWxx APRSISCE win32 version - * | | | - * 00000000001111111111 - * 01234567890123456789... - * - * Matching will be with only leading upper case and digits. - */ - -// TODO: Look for this in multiple locations. -// For example, if application was installed in /usr/local/bin, -// we might want to put this in /usr/local/share/aprs - -// If search strategy changes, be sure to keep symbols_init in sync. - - if (first_time) { - - n = 0; - fp = NULL; - do { - if(search_locations[n] == NULL) break; - fp = fopen(search_locations[n++], "r"); - } while (fp == NULL); - - if (fp != NULL) { - - while (fgets(stuff, sizeof(stuff), fp) != NULL && num_tocalls < MAX_TOCALLS) { - - p = stuff + strlen(stuff) - 1; - while (p >= stuff && (*p == '\r' || *p == '\n')) { - *p-- = '\0'; - } - - // dw_printf("debug: %s\n", stuff); - - if (stuff[0] == ' ' && - stuff[4] == ' ' && - stuff[5] == ' ' && - stuff[6] == 'A' && - stuff[7] == 'P' && - stuff[12] == ' ' && - stuff[13] == ' ' ) { - - p = stuff + 6; - r = tocalls[num_tocalls].prefix; - while (isupper((int)(*p)) || isdigit((int)(*p))) { - *r++ = *p++; - } - *r = '\0'; - if (strlen(tocalls[num_tocalls].prefix) > 2) { - tocalls[num_tocalls].description = strdup(stuff+14); - tocalls[num_tocalls].len = strlen(tocalls[num_tocalls].prefix); - // dw_printf("debug %d: %d '%s' -> '%s'\n", num_tocalls, tocalls[num_tocalls].len, tocalls[num_tocalls].prefix, tocalls[num_tocalls].description); - - num_tocalls++; - } - } - else if (stuff[0] == ' ' && - stuff[1] == 'A' && - stuff[2] == 'P' && - isupper((int)(stuff[3])) && - stuff[4] == ' ' && - stuff[5] == ' ' && - stuff[6] == ' ' && - stuff[12] == ' ' && - stuff[13] == ' ' ) { - - p = stuff + 1; - r = tocalls[num_tocalls].prefix; - while (isupper((int)(*p)) || isdigit((int)(*p))) { - *r++ = *p++; - } - *r = '\0'; - if (strlen(tocalls[num_tocalls].prefix) > 2) { - tocalls[num_tocalls].description = strdup(stuff+14); - tocalls[num_tocalls].len = strlen(tocalls[num_tocalls].prefix); - // dw_printf("debug %d: %d '%s' -> '%s'\n", num_tocalls, tocalls[num_tocalls].len, tocalls[num_tocalls].prefix, tocalls[num_tocalls].description); - - num_tocalls++; - } - } - if (num_tocalls == MAX_TOCALLS) { // oops. might have discarded some. - text_color_set(DW_COLOR_ERROR); - dw_printf("MAX_TOCALLS needs to be larger than %d to handle contents of 'tocalls.txt'.\n", MAX_TOCALLS); - } - } - fclose(fp); - -/* - * Sort by decreasing length so the search will go - * from most specific to least specific. - * Example: APY350 or APY008 would match those specific - * models before getting to the more generic APY. - */ - - qsort (tocalls, num_tocalls, sizeof(struct tocalls_s), tocall_cmp); - - } - else { - if ( ! A->g_quiet) { - text_color_set(DW_COLOR_ERROR); - dw_printf("Warning: Could not open 'tocalls.txt'.\n"); - dw_printf("System types in the destination field will not be decoded.\n"); - } - } - - first_time = 0; - - //for (n=0; n '%s'\n", n, tocalls[n].len, tocalls[n].prefix, tocalls[n].description); - //} - } - - - for (n=0; ng_mfr, tocalls[n].description, sizeof(A->g_mfr)); - return; - } - } - -} /* end decode_tocall */ @@ -4513,7 +4218,7 @@ static void substr_se (char *dest, const char *src, int start, int endp1) * clen - Length of comment or -1 to take it all. * * Outputs: A->g_telemetry - Base 91 telemetry |ss1122| - * A->g_altitude_ft - from /A=123456 + * A->g_altitude_ft - from /A=123456 or /A=-12345 * A->g_lat - Might be adjusted from !DAO! * A->g_lon - Might be adjusted from !DAO! * A->g_aprstt_loc - Private extension to !DAO! @@ -4543,6 +4248,10 @@ static void substr_se (char *dest, const char *src, int start, int endp1) * Protocol reference, end of chapter 6. * * /A=123456 Altitude + * /A=-12345 Enhancement - There are many places on the earth's + * surface but the APRS spec has no provision for negative + * numbers. I propose having 5 digits for a consistent + * field width. 6 would be excessive. * * What can appear in a comment? * @@ -4708,7 +4417,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen) dw_printf("%s:%d: %s\n", __FILE__, __LINE__, emsg); } - e = regcomp (&alt_re, "/A=[0-9][0-9][0-9][0-9][0-9][0-9]", REG_EXTENDED); + e = regcomp (&alt_re, "/A=[0-9-][0-9][0-9][0-9][0-9][0-9]", REG_EXTENDED); if (e) { regerror (e, &alt_re, emsg, sizeof(emsg)); dw_printf("%s:%d: %s\n", __FILE__, __LINE__, emsg); @@ -5068,7 +4777,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen) } /* - * Altitude in feet. /A=123456 + * Altitude in feet. /A=123456 or /A=-12345 */ if (regexec (&alt_re, A->g_comment, MAXMATCH, match, 0) == 0) @@ -5186,7 +4895,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen) * * Function: main * - * Purpose: Main program for standalone test program. + * Purpose: Main program for standalone application to parse and explain APRS packets. * * Inputs: stdin for raw data to decode. * This is in the usual display format either from @@ -5332,6 +5041,7 @@ int main (int argc, char *argv[]) // If you don't like the text colors, use 0 instead of 1 here. text_color_init(1); text_color_set(DW_COLOR_INFO); + deviceid_init(); while (fgets(stuff, sizeof(stuff), stdin) != NULL) { diff --git a/src/deviceid.c b/src/deviceid.c new file mode 100644 index 00000000..594b20ea --- /dev/null +++ b/src/deviceid.c @@ -0,0 +1,660 @@ +// +// This file is part of Dire Wolf, an amateur radio packet TNC. +// +// Copyright (C) 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 +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + + +/*------------------------------------------------------------------ + * + * File: deviceid.c + * + * Purpose: Determine the device identifier from the destination field, + * or from prefix/suffix for MIC-E format. + * + * Description: Orginally this used the tocalls.txt file and was part of decode_aprs.c. + * For release 1.8, we use tocalls.yaml and this is split into a separate file. + * + *------------------------------------------------------------------*/ + +//#define TEST 1 // Standalone test. $ gcc -DTEST deviceid.c && ./a.out + + +#if TEST +#define HAVE_STRLCPY 1 // prevent defining in direwolf.h +#define HAVE_STRLCAT 1 +#endif + +#include "direwolf.h" + +#include +#include +#include +#include + +#include "deviceid.h" +#include "textcolor.h" + + +static void unquote (int line, char *pin, char *pout); +static int tocall_cmp (const void *px, const void *py); +static int mice_cmp (const void *px, const void *py); + +/*------------------------------------------------------------------ + * + * Function: main + * + * Purpose: A little self-test used during development. + * + * Description: Read the yaml file. Decipher a few typical values. + * + *------------------------------------------------------------------*/ + +#if TEST +// So we don't need to link with any other files. +#define dw_printf printf +void text_color_set(dw_color_t) { return; } +void strlcpy(char *dst, char *src, size_t dlen) { + strcpy (dst, src); +} +void strlcat(char *dst, char *src, size_t dlen) { + strcat (dst, src); +} + + +int main (int argc, char *argv[]) +{ + char device[80]; + char comment_out[80]; + + deviceid_init (); + + dw_printf ("\n"); + dw_printf ("Testing ...\n"); + +// MIC-E Legacy (really Kenwood). + + deviceid_decode_mice (">Comment", comment_out, sizeof(comment_out), device, sizeof(device)); + dw_printf ("%s %s\n", comment_out, device); + assert (strcmp(comment_out, "Comment") == 0); + assert (strcmp(device, "Kenwood TH-D7A") == 0); + + deviceid_decode_mice (">Comment^", comment_out, sizeof(comment_out), device, sizeof(device)); + dw_printf ("%s %s\n", comment_out, device); + assert (strcmp(comment_out, "Comment") == 0); + assert (strcmp(device, "Kenwood TH-D74") == 0); + + deviceid_decode_mice ("]Comment", comment_out, sizeof(comment_out), device, sizeof(device)); + dw_printf ("%s %s\n", comment_out, device); + assert (strcmp(comment_out, "Comment") == 0); + assert (strcmp(device, "Kenwood TM-D700") == 0); + + deviceid_decode_mice ("]Comment=", comment_out, sizeof(comment_out), device, sizeof(device)); + dw_printf ("%s %s\n", comment_out, device); + assert (strcmp(comment_out, "Comment") == 0); + assert (strcmp(device, "Kenwood TM-D710") == 0); + + +// Modern MIC-E. + + deviceid_decode_mice ("`Comment_\"", comment_out, sizeof(comment_out), device, sizeof(device)); + dw_printf ("%s %s\n", comment_out, device); + assert (strcmp(comment_out, "Comment") == 0); + assert (strcmp(device, "Yaesu FTM-350") == 0); + + deviceid_decode_mice ("`Comment_ ", comment_out, sizeof(comment_out), device, sizeof(device)); + dw_printf ("%s %s\n", comment_out, device); + assert (strcmp(comment_out, "Comment") == 0); + assert (strcmp(device, "Yaesu VX-8") == 0); + + deviceid_decode_mice ("'Comment|3", comment_out, sizeof(comment_out), device, sizeof(device)); + dw_printf ("%s %s\n", comment_out, device); + assert (strcmp(comment_out, "Comment") == 0); + assert (strcmp(device, "Byonics TinyTrak3") == 0); + + deviceid_decode_mice ("Comment", comment_out, sizeof(comment_out), device, sizeof(device)); + dw_printf ("%s %s\n", comment_out, device); + assert (strcmp(comment_out, "Comment") == 0); + assert (strcmp(device, "UNKNOWN vendor/model") == 0); + + +// Tocall + + deviceid_decode_dest ("APDW18", device, sizeof(device)); + dw_printf ("%s\n", device); + assert (strcmp(device, "WB2OSZ DireWolf") == 0); + + deviceid_decode_dest ("APD123", device, sizeof(device)); + dw_printf ("%s\n", device); + assert (strcmp(device, "Open Source aprsd") == 0); + + // null for Vendor. + deviceid_decode_dest ("APAX", device, sizeof(device)); + dw_printf ("%s\n", device); + assert (strcmp(device, "AFilterX") == 0); + + deviceid_decode_dest ("APA123", device, sizeof(device)); + dw_printf ("%s\n", device); + assert (strcmp(device, "UNKNOWN vendor/model") == 0); + + dw_printf ("\n"); + dw_printf ("Success!\n"); + + exit (EXIT_SUCCESS); +} + +#endif // TEST + + + +// Structures to hold mapping from encoded form to vendor and model. +// The .yaml file has two separate sections for MIC-E but they can +// both be handled as a single more general case. + +struct mice { + char prefix[4]; // The legacy form has 1 prefix character. + // The newer form has none. (more accurately ` or ') + char suffix[4]; // The legacy form has 0 or 1. + // The newer form has 2. + char *vendor; + char *model; +}; + +struct tocalls { + char tocall[8]; // Up to 6 characters. Some may have wildcards at the end. + // Most often they are trailing "??" or "?" or "???" in one case. + // Sometimes there is trailing "nnn". Does that imply digits only? + // Sometimes we see a trailing "*". Is "*" different than "?"? + // There are a couple bizzare cases like APnnnD which can + // create an ambigious situation. APMPAD, APRFGD, APY0[125]D. + // Screw them if they can't follow the rules. I'm not putting in a special case. + char *vendor; + char *model; +}; + + +static struct mice *pmice = NULL; // Pointer to array. +static int mice_count = 0; // Number of allocated elements. +static int mice_index = -1; // Current index for filling in. + +static struct tocalls *ptocalls = NULL; // Pointer to array. +static int tocalls_count = 0; // Number of allocated elements. +static int tocalls_index = -1; // Current index for filling in. + + + + +/*------------------------------------------------------------------ + * + * Function: deviceid_init + * + * Purpose: Called once at startup to read the tocalls.yaml file which was obtained from + * https://github.com/aprsorg/aprs-deviceid . + * + * Inputs: tocalls.yaml with OS specific directory search list. + * + * Outputs: static variables listed above. + * + * Description: For maximum flexibility, we will read the + * data file at run time rather than compiling it in. + * + *------------------------------------------------------------------*/ + +// Make sure the array is null terminated. +// If search order is changed, do the same in symbols.c for consistency. +// fopen is perfectly happy with / in file path when running on Windows. + +static const char *search_locations[] = { + (const char *) "tocalls.yaml", // Current working directory + (const char *) "data/tocalls.yaml", // Windows with CMake + (const char *) "../data/tocalls.yaml", // Source tree +#ifndef __WIN32__ + (const char *) "/usr/local/share/direwolf/tocalls.yaml", + (const char *) "/usr/share/direwolf/tocalls.yaml", +#endif +#if __APPLE__ + // https://groups.yahoo.com/neo/groups/direwolf_packet/conversations/messages/2458 + // Adding the /opt/local tree since macports typically installs there. Users might want their + // INSTALLDIR (see Makefile.macosx) to mirror that. If so, then we need to search the /opt/local + // path as well. + (const char *) "/opt/local/share/direwolf/tocalls.yaml", +#endif + (const char *) NULL // Important - Indicates end of list. +}; + + +void deviceid_init(void) +{ + FILE *fp = NULL; + for (int n = 0; search_locations[n] != NULL && fp == NULL; n++) { + dw_printf ("Trying %s\n", search_locations[n]); + fp = fopen(search_locations[n], "r"); +#if TEST + if (fp != NULL) { + dw_printf ("Opened %s\n", search_locations[n]); + } +#endif + }; + + if (fp == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf("Could not open any of these file locations:\n"); + for (int n = 0; search_locations[n] != NULL; n++) { + dw_printf (" %s\n", search_locations[n]); + } + dw_printf("It won't be possible to extract device identifiers from packets.\n"); + return; + }; + +// Read file first time to get number of items. +// Allocate required space. +// Rewind. +// Read file second time to gather data. + + enum { no_section, mice_section, tocalls_section} section = no_section; + char stuff[80]; + + for (int pass = 1; pass <=2; pass++) { + int line = 0; // Line number within file. + + while (fgets(stuff, sizeof(stuff), fp)) { + line++; + + // Remove trailing CR/LF or spaces. + char *p = stuff + strlen(stuff) - 1; + while (p >= (char*)stuff && (*p == '\r' || *p == '\n' || *p == ' ')) { + *p-- = '\0'; + } + + // Ignore comment lines. + if (stuff[0] == '#') { + continue; + } + +#if TEST + //dw_printf ("%d: %s\n", line, stuff); +#endif + // This is not very robust; everything better be in exactly the right format. + + if (strncmp(stuff, "mice:", strlen("mice:")) == 0) { + section = mice_section; +#if TEST + dw_printf ("Pass %d, line %d, MIC-E section\n", pass, line); +#endif + } + else if (strncmp(stuff, "micelegacy:", strlen("micelegacy:")) == 0) { + section = mice_section; // treat both same. +#if TEST + dw_printf ("Pass %d, line %d, Legacy MIC-E section\n", pass, line); +#endif + } + else if (strncmp(stuff, "tocalls:", strlen("tocalls:")) == 0) { + section = tocalls_section; +#if TEST + dw_printf ("Pass %d, line %d, TOCALLS section\n", pass, line); +#endif + } + + // The first property of an item is preceded by " - ". + + if (pass == 1 && strncmp(stuff, " - ", 3) == 0) { + switch (section) { + case no_section: break; + case mice_section: mice_count++; break; + case tocalls_section: tocalls_count++; break; + } + } + + if (pass == 2) { + switch (section) { + case no_section: + break; + + case mice_section: + if (strncmp(stuff, " - ", 3) == 0) { + mice_index++; + assert (mice_index >= 0 && mice_index < mice_count); + } + if (strncmp(stuff+3, "prefix: ", strlen("prefix: ")) == 0) { + unquote (line, stuff+3+8, pmice[mice_index].prefix); + } + else if (strncmp(stuff+3, "suffix: ", strlen("suffix: ")) == 0) { + unquote (line, stuff+3+8, pmice[mice_index].suffix); + } + else if (strncmp(stuff+3, "vendor: ", strlen("vendor: ")) == 0) { + pmice[mice_index].vendor = strdup(stuff+3+8); + } + else if (strncmp(stuff+3, "model: ", strlen("model: ")) == 0) { + pmice[mice_index].model = strdup(stuff+3+7); + } + break; + + case tocalls_section: + if (strncmp(stuff, " - ", 3) == 0) { + tocalls_index++; + assert (tocalls_index >= 0 && tocalls_index < tocalls_count); + } + if (strncmp(stuff+3, "tocall: ", strlen("tocall: ")) == 0) { + // Remove trailing wildcard characters ? * n + char *r = stuff + strlen(stuff) - 1; + while (r >= (char*)stuff && (*r == '?' || *r == '*' || *r == 'n')) { + *r-- = '\0'; + } + + strlcpy (ptocalls[tocalls_index].tocall, stuff+3+8, sizeof(ptocalls[tocalls_index].tocall)); + + // Remove trailing CR/LF or spaces. + char *p = stuff + strlen(stuff) - 1; + while (p >= (char*)stuff && (*p == '\r' || *p == '\n' || *p == ' ')) { + *p-- = '\0'; + } + } + else if (strncmp(stuff+3, "vendor: ", strlen("vendor: ")) == 0) { + ptocalls[tocalls_index].vendor = strdup(stuff+3+8); + } + else if (strncmp(stuff+3, "model: ", strlen("model: ")) == 0) { + ptocalls[tocalls_index].model = strdup(stuff+3+7); + } + break; + } + } + } // while(fgets + + if (pass == 1) { +#if TEST + dw_printf ("deviceid sizes %d %d\n", mice_count, tocalls_count); +#endif + pmice = calloc(sizeof(struct mice), mice_count); + ptocalls = calloc(sizeof(struct tocalls), tocalls_count); + + rewind (fp); + section = no_section; + } + } // for pass = 1 or 2 + + fclose (fp); + + assert (mice_index == mice_count - 1); + assert (tocalls_index == tocalls_count - 1); + + +// MIC-E Legacy needs to be sorted so those with suffix come first. + + qsort (pmice, mice_count, sizeof(struct mice), mice_cmp); + + +// Sort tocalls by decreasing length so the search will go from most specific to least specific. +// Example: APY350 or APY008 would match those specific models before getting to the more generic APY. + + qsort (ptocalls, tocalls_count, sizeof(struct tocalls), tocall_cmp); + + +#if TEST + dw_printf ("MIC-E:\n"); + for (int i = 0; i < mice_count; i++) { + dw_printf ("%s %s %s\n", pmice[i].suffix, pmice[i].vendor, pmice[i].model); + } + dw_printf ("TOCALLS:\n"); + for (int i = 0; i < tocalls_count; i++) { + dw_printf ("%s %s %s\n", ptocalls[i].tocall, ptocalls[i].vendor, ptocalls[i].model); + } +#endif + + return; + +} // end deviceid_init + + +/*------------------------------------------------------------------ + * + * Function: unquote + * + * Purpose: Remove surrounding quotes and undo any escapes. + * + * Inputs: line - File line number for error message. + * + * in - String with quotes. Might contain \ escapes. + * + * Outputs: out - Quotes and escapes removed. + * Limited to 2 characters to avoid buffer overflow. + * + * Examples: in out + * "_#" _# + * "_\"" _" + * "=" = + * + *------------------------------------------------------------------*/ + +static void unquote (int line, char *pin, char *pout) +{ + int count = 0; + + *pout = '\0'; + if (*pin != '"') { + text_color_set(DW_COLOR_ERROR); + dw_printf("Missing leading \" for %s on line %d.\n", pin, line); + return; + } + + pin++; + while (*pin != '\0' && *pin != '\"' && count < 2) { + if (*pin == '\\') { + pin++; + } + *pout++ = *pin++; + count++; + } + *pout = '\0'; + + if (*pin != '"') { + text_color_set(DW_COLOR_ERROR); + dw_printf("Missing trailing \" or string too long on line %d.\n", line); + return; + } +} + +// Used to sort the tocalls by length. +// When length is equal, alphabetically. + +static int tocall_cmp (const void *px, const void *py) +{ + const struct tocalls *x = (struct tocalls *)px; + const struct tocalls *y = (struct tocalls *)py; + + if (strlen(x->tocall) != strlen(y->tocall)) { + return (strlen(y->tocall) - strlen(x->tocall)); + } + return (strcmp(x->tocall, y->tocall)); +} + +// Used to sort the suffixes by length. +// Longer at the top. +// Example check for >xxx^ before >xxx . + +static int mice_cmp (const void *px, const void *py) +{ + const struct mice *x = (struct mice *)px; + const struct mice *y = (struct mice *)py; + + return (strlen(y->suffix) - strlen(x->suffix)); +} + + + + + +/*------------------------------------------------------------------ + * + * Function: deviceid_decode_dest + * + * Purpose: Find vendor/model for destination address of form APxxxx. + * + * Inputs: dest - Destination address. No SSID. + * + * device_size - Amount of space available for result to avoid buffer overflow. + * + * Outputs: device - Vendor and model. + * + * Description: With the exception of MIC-E format, we expect to find the vendor/model in the + * AX.25 destination field. The form should be APxxxx. + * + * Search the list looking for the maximum length match. + * For example, + * APXR = Xrouter + * APX = Xastir + * + *------------------------------------------------------------------*/ + +void deviceid_decode_dest (char *dest, char *device, size_t device_size) +{ + *device = '\0'; + + if (ptocalls == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf("deviceid_decode_dest called without any deviceid data.\n"); + return; + } + + for (int n = 0; n < tocalls_count; n++) { + if (strncmp(dest, ptocalls[n].tocall, strlen(ptocalls[n].tocall)) == 0) { + + if (ptocalls[n].vendor != NULL) { + strlcpy (device, ptocalls[n].vendor, device_size); + } + + if (ptocalls[n].vendor != NULL && ptocalls[n].model != NULL) { + strlcat (device, " ", device_size); + } + + if (ptocalls[n].model != NULL) { + strlcat (device, ptocalls[n].model, device_size); + } + return; + } + } + + strlcpy (device, "UNKNOWN vendor/model", device_size); + +} // end deviceid_decode_dest + + +/*------------------------------------------------------------------ + * + * Function: deviceid_decode_mice + * + * Purpose: Find vendor/model for MIC-E comment. + * + * Inputs: comment - MIC-E comment that might have vendor/model encoded as + * a prefix and/or suffix. + * + * trimmed_size - Amount of space available for result to avoid buffer overflow. + * + * device_size - Amount of space available for result to avoid buffer overflow. + * + * Outputs: trimmed - Final comment with device vendor/model removed. + * + * device - Vendor and model. + * + * Description: This has a tortured history. + * + * The Kenwood TH-D7A put ">" at the beginning of the comment. + * The Kenwood TM-D700 put "]" at the beginning of the comment. + * Later Kenwood models also added a single suffix character + * using a character very unlikely to appear at the end of a comment. + * + * The later convention, used by everyone else, is to have a prefix of ` or ' + * and a suffix of two characters. The suffix characters need to be + * something very unlikely to be found at the end of a comment. + * + * A receiving device is expected to remove those extra characters + * before displaying the comment. + * + * References: http://www.aprs.org/aprs12/mic-e-types.txt + * http://www.aprs.org/aprs12/mic-e-examples.txt + * + *------------------------------------------------------------------*/ + +// The strncmp documentation doesn't mention behavior if length is zero. +// Do our own just to be safe. + +static inline int strncmp_z (char *a, char *b, size_t len) +{ + int result = 0; + if (len > 0) { + result = strncmp(a, b, len); + } + //dw_printf ("Comparing '%s' and '%s' len %d result %d\n", a, b, len, result); + return result; +} + +void deviceid_decode_mice (char *comment, char *trimmed, size_t trimmed_size, char *device, size_t device_size) +{ + *device = '\0'; + + if (ptocalls == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf("deviceid_decode_mice called without any deviceid data.\n"); + return; + } + + +// The Legacy format has an explicit prefix in the table. +// For others, it must be ` or ' to indicate whether messaging capable. + + for (int n = 0; n < mice_count; n++) { + if ((strlen(pmice[n].prefix) != 0 && // Legacy + strncmp_z(comment, // prefix from table + pmice[n].prefix, + strlen(pmice[n].prefix)) == 0 && + strncmp_z(comment + strlen(comment) - strlen(pmice[n].suffix), //suffix + pmice[n].suffix, + strlen(pmice[n].suffix)) == 0) || + + (strlen(pmice[n].prefix) == 0 && // Later + (comment[0] == '`' || comment[0] == '\'') && // prefix ` or ' + strncmp_z(comment + strlen(comment) - strlen(pmice[n].suffix), //suffix + pmice[n].suffix, + strlen(pmice[n].suffix)) == 0) ) { + + if (pmice[n].vendor != NULL) { + strlcpy (device, pmice[n].vendor, device_size); + } + + if (pmice[n].vendor != NULL && pmice[n].model != NULL) { + strlcat (device, " ", device_size); + } + + if (pmice[n].model != NULL) { + strlcat (device, pmice[n].model, device_size); + } + + // Remove any prefix/suffix and return what remains. + + strlcpy (trimmed, comment + 1, trimmed_size); + trimmed[strlen(comment) - 1 - strlen(pmice[n].suffix)] = '\0'; + + return; + } + } + + +// Not found. + + strlcpy (device, "UNKNOWN vendor/model", device_size); + +} // end deviceid_decode_mice + +// end deviceid.c diff --git a/src/deviceid.h b/src/deviceid.h new file mode 100644 index 00000000..d7a1b30e --- /dev/null +++ b/src/deviceid.h @@ -0,0 +1,6 @@ + +// deviceid.h + +void deviceid_init(void); +void deviceid_decode_dest (char *dest, char *device, size_t device_size); +void deviceid_decode_mice (char *comment, char *trimmed, size_t trimmed_size, char *device, size_t device_size); diff --git a/src/direwolf.c b/src/direwolf.c index c8bb3a1b..2dfa58d3 100644 --- a/src/direwolf.c +++ b/src/direwolf.c @@ -129,6 +129,7 @@ #include "dwsock.h" #include "dns_sd_dw.h" #include "dlq.h" // for fec_type_t definition. +#include "deviceid.h" //static int idx_decoded = 0; @@ -985,6 +986,7 @@ int main (int argc, char *argv[]) * Files not supported at this time. * Can always "cat" the file and pipe it into stdin. */ + deviceid_init(); err = audio_open (&audio_config); if (err < 0) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 91e06a2c..da732ac8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -147,6 +147,7 @@ list(APPEND dtest_SOURCES ${CUSTOM_SRC_DIR}/tq.c ${CUSTOM_SRC_DIR}/textcolor.c ${CUSTOM_SRC_DIR}/decode_aprs.c + ${CUSTOM_SRC_DIR}/deviceid.c ${CUSTOM_SRC_DIR}/dwgpsnmea.c ${CUSTOM_SRC_DIR}/dwgps.c ${CUSTOM_SRC_DIR}/dwgpsd.c @@ -232,6 +233,7 @@ list(APPEND pftest_SOURCES ${CUSTOM_SRC_DIR}/textcolor.c ${CUSTOM_SRC_DIR}/fcs_calc.c ${CUSTOM_SRC_DIR}/decode_aprs.c + ${CUSTOM_SRC_DIR}/deviceid.c ${CUSTOM_SRC_DIR}/dwgpsnmea.c ${CUSTOM_SRC_DIR}/dwgps.c ${CUSTOM_SRC_DIR}/dwgpsd.c @@ -522,6 +524,7 @@ if(OPTIONAL_TEST) ${CUSTOM_SRC_DIR}/pfilter.c ${CUSTOM_SRC_DIR}/telemetry.c ${CUSTOM_SRC_DIR}/decode_aprs.c + ${CUSTOM_SRC_DIR}/deviceid.c.c ${CUSTOM_SRC_DIR}/dwgpsnmea.c ${CUSTOM_SRC_DIR}/dwgps.c ${CUSTOM_SRC_DIR}/dwgpsd.c @@ -574,6 +577,7 @@ if(OPTIONAL_TEST) ${CUSTOM_SRC_DIR}/fcs_calc.c ${CUSTOM_SRC_DIR}/ax25_pad.c ${CUSTOM_SRC_DIR}/decode_aprs.c + ${CUSTOM_SRC_DIR}/deviceid.c ${CUSTOM_SRC_DIR}/dwgpsnmea.c ${CUSTOM_SRC_DIR}/dwgps.c ${CUSTOM_SRC_DIR}/dwgpsd.c From c9b7c61f2cb6457ba8000879e5f232ffe86f1969 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Fri, 26 Jan 2024 00:06:04 +0000 Subject: [PATCH 10/57] Issue 510 - List only valid channels for AGW G command. --- src/server.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/server.c b/src/server.c index 6b41af25..1cd3211d 100644 --- a/src/server.c +++ b/src/server.c @@ -1559,6 +1559,7 @@ static THREAD_F cmd_listen_thread (void *arg) case MEDIUM_RADIO: { + // Misleading if using stdin or udp. char stemp[100]; int a = ACHAN2ADEV(j); // If I was really ambitious, some description could be provided. @@ -1593,12 +1594,7 @@ static THREAD_F cmd_listen_thread (void *arg) break; default: - { - // could elaborate with hostname, etc. - char stemp[100]; - snprintf (stemp, sizeof(stemp), "Port%d INVALID CHANNEL;", j+1); - strlcat (reply.info, stemp, sizeof(reply.info)); - } + ; // Only list valid channels. break; } // switch From 78604808f857aa6b2eb193282f60f4d5fe2707fc Mon Sep 17 00:00:00 2001 From: wb2osz Date: Tue, 30 Jan 2024 17:19:46 +0000 Subject: [PATCH 11/57] Add hint for better operation. --- src/ax25_link.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ax25_link.c b/src/ax25_link.c index 3043a046..98d6c45e 100644 --- a/src/ax25_link.c +++ b/src/ax25_link.c @@ -4642,6 +4642,8 @@ static void dm_frame (ax25_dlsm_t *S, int f) if (f == 1) { text_color_set(DW_COLOR_INFO); dw_printf ("%s doesn't understand AX.25 v2.2. Trying v2.0 ...\n", S->addrs[PEERCALL]); + dw_printf ("You can avoid this failed attempt and speed up the\n"); + dw_printf ("process by putting \"V20 %s\" in the configuration file.\n", S->addrs[PEERCALL]); INIT_T1V_SRT; @@ -4930,6 +4932,8 @@ static void frmr_frame (ax25_dlsm_t *S) text_color_set(DW_COLOR_INFO); dw_printf ("%s doesn't understand AX.25 v2.2. Trying v2.0 ...\n", S->addrs[PEERCALL]); + dw_printf ("You can avoid this failed attempt and speed up the\n"); + dw_printf ("process by putting \"V20 %s\" in the configuration file.\n", S->addrs[PEERCALL]); INIT_T1V_SRT; From 4af7b22fa9096335b7c0ef6cf5232bd5bad27fd9 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Wed, 31 Jan 2024 23:50:00 +0000 Subject: [PATCH 12/57] AGW 'K' Monitor in Raw Format did not have 'Flag' field set with channel. --- src/server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.c b/src/server.c index 1cd3211d..9e4fa51c 100644 --- a/src/server.c +++ b/src/server.c @@ -820,7 +820,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl /* Stick in extra byte for the "TNC" to use. */ - agwpe_msg.data[0] = 0; + agwpe_msg.data[0] = chan << 4; // Was 0. Fixed in 1.8. memcpy (agwpe_msg.data + 1, fbuf, (size_t)flen); if (debug_client) { From 4d2d814ee1651cdba4f13de5e7fffc82bb6fbad5 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sun, 4 Feb 2024 22:40:40 +0000 Subject: [PATCH 13/57] Proper color for informational text. --- src/deviceid.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/deviceid.c b/src/deviceid.c index 594b20ea..221e65bd 100644 --- a/src/deviceid.c +++ b/src/deviceid.c @@ -240,7 +240,10 @@ void deviceid_init(void) { FILE *fp = NULL; for (int n = 0; search_locations[n] != NULL && fp == NULL; n++) { +#if TEST + text_color_set(DW_COLOR_INFO); dw_printf ("Trying %s\n", search_locations[n]); +#endif fp = fopen(search_locations[n], "r"); #if TEST if (fp != NULL) { From 5a54179c97b8f46a9fb315ee511cd026a559c353 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Fri, 16 Feb 2024 02:52:28 +0000 Subject: [PATCH 14/57] more work on mic-e device id. --- data/README.txt | 18 ++++++++ data/tocalls.yaml | 113 +++++++++++++++++++++++++++++++++++++++++++++- src/decode_aprs.c | 23 ++++------ src/deviceid.c | 9 +++- 4 files changed, 145 insertions(+), 18 deletions(-) create mode 100644 data/README.txt diff --git a/data/README.txt b/data/README.txt new file mode 100644 index 00000000..9d4da43c --- /dev/null +++ b/data/README.txt @@ -0,0 +1,18 @@ + +tocalls.yaml contains the encoding for the device/system/software +identifier which created the packet. +Knowing what generated the packet is very useful for troubleshooting. +TNCs, digipeaters, and IGates must not change this. + +For MIC-E format, well... it's complicated. +See Understanding-APRS-Packets.pdf. Too long to repeat here. + +For all other packet types, the AX.25 destination, or "tocall" field +contains a code for what generated the packet. +This is of the form AP????. For example, APDW18 for direwolf 1.8. + +The database of identifiers is currently maintained by Hessu, OH7LZB. + +You can update your local copy by running: + +wget https://raw.githubusercontent.com/aprsorg/aprs-deviceid/main/tocalls.yaml diff --git a/data/tocalls.yaml b/data/tocalls.yaml index adf26a1f..dfc5fd08 100644 --- a/data/tocalls.yaml +++ b/data/tocalls.yaml @@ -146,6 +146,11 @@ mice: model: Tracker class: tracker + - suffix: ":2" + vendor: SQ8L + model: VP-Tracker + class: tracker + # # mic-e legacy devices, with an unique comment suffix and prefix # @@ -237,6 +242,15 @@ tocalls: - tocall: APAH?? model: AHub + - tocall: APAIOR + vendor: J. Angelo Racoma DU2XXR/N2RAC + model: APRSPH net bot based on Ioreth + class: service + os: linux + contact: info@aprsph.net + features: + - messaging + - tocall: APAM?? vendor: Altus Metrum model: AltOS @@ -248,6 +262,12 @@ tocalls: os: Android class: app + - tocall: APAR?? + vendor: Øyvind, LA7ECA + model: Arctic Tracker + class: tracker + os: embedded + - tocall: APAT51 vendor: Anytone model: AT-D578 @@ -364,6 +384,12 @@ tocalls: model: WinphoneAPRS class: app + - tocall: APD5T? + vendor: Geoffrey, F4FXL + model: Open Source DStarGateway + class: dstar + contact: f4fxl@dstargateway.digital + - tocall: APDF?? model: Automatic DF units @@ -448,6 +474,19 @@ tocalls: model: Balloon tracker class: tracker + - tocall: APEML? + vendor: Leszek, SP9MLI + model: SP9MLI for WX, Telemetry + class: software + contact: sp9mli@gmail.com + + - tocall: APEP?? + vendor: Patrick EGLOFF, TK5EP + model: LoRa WX station + class: wx + os: embedded + contact: pegloff@gmail.com + - tocall: APERS? vendor: Jason, KG7YKZ model: Runner tracking @@ -525,6 +564,17 @@ tocalls: vendor: HP3ICC model: Python APRS WX + - tocall: APHRM? + vendor: Giovanni, IW1CGW + model: Meteo + class: wx + contact: iw1cgw@libero.it + + - tocall: APHRT? + vendor: Giovanni, IW1CGW + model: Telemetry + contact: iw1cgw@libero.it + - tocall: APHT?? vendor: IU0AAC model: HMTracker @@ -702,11 +752,29 @@ tocalls: os: embedded contact: cfr34k-git@tkolb.de + - tocall: APLFM? + vendor: DO1MA + model: FemtoAPRS + class: tracker + os: embedded + - tocall: APLG?? vendor: OE5BPA model: LoRa Gateway/Digipeater class: digi + - tocall: APLHI? + vendor: Giovanni, IW1CGW + model: LoRa IGate/Digipeater/Telemetry + class: digi + contact: iw1cgw@libero.it + + - tocall: APLHM? + vendor: Giovanni, IW1CGW + model: LoRa Meteostation + class: wx + contact: iw1cgw@libero.it + - tocall: APLIG? vendor: TA2MUN/TA9OHC model: LightAPRS Tracker @@ -736,14 +804,14 @@ tocalls: contact: sq9p.peter@gmail.com - tocall: APLRG? - vendor: Ricardo, CD2RXU + vendor: Ricardo, CA2RXU model: ESP32 LoRa iGate class: igate os: embedded contact: richonguzman@gmail.com - tocall: APLRT? - vendor: Ricardo, CD2RXU + vendor: Ricardo, CA2RXU model: ESP32 LoRa Tracker class: tracker os: embedded @@ -919,12 +987,18 @@ tocalls: vendor: SQ8L model: VP-Digi os: embedded + class: digi - tocall: APNV1? vendor: SQ8L model: VP-Node os: embedded + - tocall: APNV2? + vendor: SQ8L + model: VP-Tracker + class: tracker + - tocall: APNV?? vendor: SQ8L @@ -947,6 +1021,11 @@ tocalls: vendor: N0AGI model: POCSAG + - tocall: APODOT + vendor: Mike, NA7Q + model: Oregon Department of Transportion Traffic Alerts + class: service + - tocall: APOG7? vendor: OpenGD77 model: OpenGD77 @@ -958,6 +1037,12 @@ tocalls: model: Oscar class: satellite + - tocall: APOPYT + vendor: Mike, NA7Q + model: NA7Q Messenger + class: software + contact: mike.ph4@gmail.com + - tocall: APOSAT vendor: Mike, NA7Q model: Open Source Satellite Gateway @@ -1014,6 +1099,12 @@ tocalls: features: - messaging + - tocall: APPS?? + vendor: Øyvind, LA7ECA (for the Norwegian Radio Relay League) + model: Polaric Server + class: software + os: Linux + - tocall: APPT?? vendor: JF6LZE model: KetaiTracker @@ -1276,11 +1367,25 @@ tocalls: os: Linux/Unix contact: kl7af@foghaven.net + - tocall: APTHUR + model: APRSThursday weekly event mapbot daemon + contact: harihend1973@gmail.com + vendor: YD0BCX + class: service + os: linux/unix + features: + - messaging + - tocall: APTKJ? vendor: W9JAJ model: ATTiny APRS Tracker os: embedded + - tocall: APTLVC + vendor: TA5LVC + model: TR80 APRS Tracker + class: tracker + - tocall: APTNG? vendor: Filip YU1TTN model: Tango Tracker @@ -1418,6 +1523,10 @@ tocalls: model: Python APRS class: software + - tocall: "APZ*" + vendor: Unknown + model: Experimental + - tocall: APZ18 vendor: IW3FQG model: UIdigi diff --git a/src/decode_aprs.c b/src/decode_aprs.c index 54c2839d..08534f7a 100644 --- a/src/decode_aprs.c +++ b/src/decode_aprs.c @@ -1638,28 +1638,23 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int -// Possible altitude. 3 characters followed by } +// Possible altitude at beginning of remaining comment. +// Three base 91 characters followed by } - if (strlen(trimmed) >=4 && trimmed[3] == '}') { + if (strlen(trimmed) >=4 && + isdigit91(trimmed[0]) && + isdigit91(trimmed[1]) && + isdigit91(trimmed[2]) && + trimmed[3] == '}') { A->g_altitude_ft = DW_METERS_TO_FEET((trimmed[0]-33)*91*91 + (trimmed[1]-33)*91 + (trimmed[2]-33) - 10000); - if ( ! isdigit91(trimmed[0]) || ! isdigit91(trimmed[1]) || ! isdigit91(trimmed[2])) - { - if ( ! A->g_quiet) { - text_color_set(DW_COLOR_ERROR); - dw_printf("Invalid character in MIC-E altitude. Must be in range of '!' to '{'.\n"); - dw_printf("Bogus altitude of %.0f changed to unknown.\n", A->g_altitude_ft); - } - A->g_altitude_ft = G_UNKNOWN; - } - - process_comment (A, mcomment+4, strlen(mcomment) - 4); + process_comment (A, trimmed+4, strlen(trimmed) - 4); return; } - process_comment (A, mcomment, strlen(mcomment)); + process_comment (A, trimmed, strlen(trimmed)); } // end aprs_mic_e diff --git a/src/deviceid.c b/src/deviceid.c index 221e65bd..de910e50 100644 --- a/src/deviceid.c +++ b/src/deviceid.c @@ -107,6 +107,11 @@ int main (int argc, char *argv[]) assert (strcmp(comment_out, "Comment") == 0); assert (strcmp(device, "Kenwood TM-D710") == 0); + deviceid_decode_mice ("]\"4V}=", comment_out, sizeof(comment_out), device, sizeof(device)); + dw_printf ("%s %s\n", comment_out, device); + assert (strcmp(comment_out, "\"4V}") == 0); + assert (strcmp(device, "Kenwood TM-D710") == 0); + // Modern MIC-E. @@ -622,13 +627,13 @@ void deviceid_decode_mice (char *comment, char *trimmed, size_t trimmed_size, ch strncmp_z(comment, // prefix from table pmice[n].prefix, strlen(pmice[n].prefix)) == 0 && - strncmp_z(comment + strlen(comment) - strlen(pmice[n].suffix), //suffix + strncmp_z(comment + strlen(comment) - strlen(pmice[n].suffix), // possible suffix pmice[n].suffix, strlen(pmice[n].suffix)) == 0) || (strlen(pmice[n].prefix) == 0 && // Later (comment[0] == '`' || comment[0] == '\'') && // prefix ` or ' - strncmp_z(comment + strlen(comment) - strlen(pmice[n].suffix), //suffix + strncmp_z(comment + strlen(comment) - strlen(pmice[n].suffix), // suffix pmice[n].suffix, strlen(pmice[n].suffix)) == 0) ) { From a508a76a52db55a4f73bc4e18e8f4f4174163b21 Mon Sep 17 00:00:00 2001 From: Rafael Gustavo da Cunha Pereira Pinto Date: Tue, 5 Mar 2024 08:58:50 -0300 Subject: [PATCH 15/57] Update codeql-analysis.yml to v2 --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7134f213..5eb1f566 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -42,7 +42,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -53,7 +53,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -70,4 +70,4 @@ jobs: make test - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 From 26013e10579d4e0e357ec5515d337385d5ec0b56 Mon Sep 17 00:00:00 2001 From: Rafael Gustavo da Cunha Pereira Pinto Date: Tue, 5 Mar 2024 09:01:48 -0300 Subject: [PATCH 16/57] Update codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 5eb1f566..b2492841 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -19,6 +19,7 @@ on: branches: [ dev ] schedule: - cron: '25 8 * * 4' + workflow_dispatch: jobs: analyze: From 2c3e987a266e4c066502c88ebcbca3c7ad980427 Mon Sep 17 00:00:00 2001 From: Rafael Gustavo da Cunha Pereira Pinto Date: Tue, 5 Mar 2024 09:07:45 -0300 Subject: [PATCH 17/57] Update CMakeLists.txt --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 84aeb738..182a9b4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(direwolf) From fc3d2e52c3dd5bd2794a0cdeb09e5a87abca2aa6 Mon Sep 17 00:00:00 2001 From: Rafael Gustavo da Cunha Pereira Pinto Date: Tue, 5 Mar 2024 09:18:16 -0300 Subject: [PATCH 18/57] Update codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b2492841..4239b3eb 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -39,11 +39,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -54,7 +54,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -71,4 +71,4 @@ jobs: make test - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 From 1325b97475ff10b4d592a9445ccb6ce0ca9801d7 Mon Sep 17 00:00:00 2001 From: Rafael Gustavo da Cunha Pereira Pinto Date: Tue, 5 Mar 2024 14:13:53 -0300 Subject: [PATCH 19/57] Update codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4239b3eb..021dbe55 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -46,6 +46,7 @@ jobs: uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} + setup-python-dependencies: false # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. From d462fda79f7c5bb2f585dba24b4599aed8ce2606 Mon Sep 17 00:00:00 2001 From: Rafael Gustavo da Cunha Pereira Pinto Date: Tue, 5 Mar 2024 14:18:29 -0300 Subject: [PATCH 20/57] Update codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 021dbe55..956bb39e 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -46,7 +46,7 @@ jobs: uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} - setup-python-dependencies: false + setup-python-dependencies: true # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. From 274fed556da38f1d8924b3c9db1bed0114fe9d1f Mon Sep 17 00:00:00 2001 From: Rafael Gustavo da Cunha Pereira Pinto Date: Wed, 6 Mar 2024 09:39:20 -0300 Subject: [PATCH 21/57] removing python from codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 956bb39e..edd6a96b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -33,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'cpp', 'python' ] + language: [ 'cpp' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://git.io/codeql-language-support From a2c88f320feeed3876d34a5a50c44ea43775c209 Mon Sep 17 00:00:00 2001 From: Rafael Gustavo da Cunha Pereira Pinto Date: Wed, 6 Mar 2024 09:40:16 -0300 Subject: [PATCH 22/57] Create codeql-analysis-python.yml --- .github/workflows/codeql-analysis-python.yml | 65 ++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 .github/workflows/codeql-analysis-python.yml diff --git a/.github/workflows/codeql-analysis-python.yml b/.github/workflows/codeql-analysis-python.yml new file mode 100644 index 00000000..35cf0404 --- /dev/null +++ b/.github/workflows/codeql-analysis-python.yml @@ -0,0 +1,65 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ dev ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ dev ] + schedule: + - cron: '25 8 * * 4' + workflow_dispatch: + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'python' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://git.io/codeql-language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + setup-python-dependencies: true + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 From 327aba27ce8591e4583db9099240e867c30ad2ca Mon Sep 17 00:00:00 2001 From: Rafael Gustavo da Cunha Pereira Pinto Date: Wed, 6 Mar 2024 09:40:45 -0300 Subject: [PATCH 23/57] Update codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index edd6a96b..d445a168 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -9,7 +9,7 @@ # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # -name: "CodeQL" +name: "CodeQL - CPP" on: push: From 921f3be253040cc65fcd83fcfce0fdc81f1fd282 Mon Sep 17 00:00:00 2001 From: Rafael Gustavo da Cunha Pereira Pinto Date: Wed, 6 Mar 2024 09:41:05 -0300 Subject: [PATCH 24/57] Update codeql-analysis-python.yml --- .github/workflows/codeql-analysis-python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis-python.yml b/.github/workflows/codeql-analysis-python.yml index 35cf0404..15a4c0b6 100644 --- a/.github/workflows/codeql-analysis-python.yml +++ b/.github/workflows/codeql-analysis-python.yml @@ -9,7 +9,7 @@ # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # -name: "CodeQL" +name: "CodeQL - Python" on: push: From 1424cc942b9a5fb1e583fe33a207b7a6c00efebb Mon Sep 17 00:00:00 2001 From: Rafael Gustavo da Cunha Pereira Pinto Date: Wed, 6 Mar 2024 09:43:44 -0300 Subject: [PATCH 25/57] Update codeql-analysis-python.yml --- .github/workflows/codeql-analysis-python.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/codeql-analysis-python.yml b/.github/workflows/codeql-analysis-python.yml index 15a4c0b6..a47a8f8d 100644 --- a/.github/workflows/codeql-analysis-python.yml +++ b/.github/workflows/codeql-analysis-python.yml @@ -19,7 +19,6 @@ on: branches: [ dev ] schedule: - cron: '25 8 * * 4' - workflow_dispatch: jobs: analyze: From 93572513d4879f97a3d3a2ee993354fb2b04e658 Mon Sep 17 00:00:00 2001 From: Rafael Gustavo da Cunha Pereira Pinto Date: Wed, 6 Mar 2024 09:43:59 -0300 Subject: [PATCH 26/57] Update codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d445a168..a86300f3 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -19,7 +19,6 @@ on: branches: [ dev ] schedule: - cron: '25 8 * * 4' - workflow_dispatch: jobs: analyze: From e41a7f2278c0512719bc98b71bb4ed855abb9ffc Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sat, 9 Mar 2024 18:50:58 +0000 Subject: [PATCH 27/57] update comment --- src/server.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/server.c b/src/server.c index 9e4fa51c..866b58ae 100644 --- a/src/server.c +++ b/src/server.c @@ -1780,11 +1780,17 @@ static THREAD_F cmd_listen_thread (void *arg) // 00=Port 1 // 16=Port 2 // - // I don't know what that means; we already a port number in the header. + // The seems to be redundant; we already a port number in the header. // Anyhow, the original code here added one to cmd.data to get the // first byte of the frame. Unfortunately, it did not subtract one from // cmd.hdr.data_len so we ended up sending an extra byte. + // TODO: Right now I just use the port (channel) number in the header. + // What if the second one is inconsistent? + // - Continue to ignore port number at beginning of data? + // - Use second one instead? + // - Error message if a mismatch? + memset (&alevel, 0xff, sizeof(alevel)); pp = ax25_from_frame ((unsigned char *)cmd.data+1, data_len - 1, alevel); From 6be6f686a907077f2cd6165ab14ea17dc87a581d Mon Sep 17 00:00:00 2001 From: wb2osz Date: Wed, 13 Mar 2024 01:09:28 +0100 Subject: [PATCH 28/57] Latest device identifiers. --- data/tocalls.yaml | 51 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/data/tocalls.yaml b/data/tocalls.yaml index dfc5fd08..0df04785 100644 --- a/data/tocalls.yaml +++ b/data/tocalls.yaml @@ -107,10 +107,20 @@ mice: model: FTM-300D class: rig + - suffix: "_2" + vendor: Yaesu + model: FTM-200D + class: rig + + - suffix: "_4" + vendor: Yaesu + model: FTM-500D + class: rig + - suffix: "_)" vendor: Yaesu model: FTM-100D - class: rig + class: rig - suffix: "_%" vendor: Yaesu @@ -735,6 +745,20 @@ tocalls: vendor: DL3DCW model: APRScube + - tocall: APLDG? + vendor: Eddie, 9W2LWK + model: LoRAIGate + class: igate + os: embedded + contact: 9w2lwk@gmail.com + + - tocall: APLDH? + vendor: Eddie, 9W2LWK + model: LoraTracker + class: tracker + os: embedded + contact: 9w2lwk@gmail.com + - tocall: APLDI? vendor: David, OK2DDS model: LoRa IGate/Digipeater @@ -752,6 +776,13 @@ tocalls: os: embedded contact: cfr34k-git@tkolb.de + - tocall: APLFG? + vendor: Gabor, HG3FUG + model: LoRa WX station + class: wx + os: embedded + contact: hg3fug@fazi.hu + - tocall: APLFM? vendor: DO1MA model: FemtoAPRS @@ -841,6 +872,12 @@ tocalls: os: embedded contact: wajdzik.m@gmail.com + - tocall: APMAIL + vendor: Mike, NA7Q + model: APRS Mailbox + class: service + contact: mike.ph4@gmail.com + - tocall: APMG?? vendor: Alex, AB0TJ model: PiCrumbs and MiniGate @@ -1508,6 +1545,11 @@ tocalls: model: FT5D class: ht + - tocall: APY200 + vendor: Yaesu + model: FTM-200D + class: rig + - tocall: APY300 vendor: Yaesu model: FTM-300D @@ -1517,7 +1559,12 @@ tocalls: vendor: Yaesu model: FTM-400 class: rig - + + - tocall: APY500 + vendor: Yaesu + model: FTM-500D + class: rig + - tocall: APYS?? vendor: W2GMD model: Python APRS From f233bfb7836f638488ad5ca657396fb578305bb4 Mon Sep 17 00:00:00 2001 From: MrColdboot <33491243+MrColdboot@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:13:59 -0400 Subject: [PATCH 29/57] Update deprecated CMake command `exec_program()` (#526) Per the cmake documentation: > *Changed in version 3.28:* This command is available only if policy CMP0153 > is not set to NEW. Port projects to the execute_process() command. > > *Deprecated since version 3.0:* Use the execute_process() command instead. To avoid warnings or future errors, calls to `exec_program()` have been replaced with the recommended `execute_process()` command. --- cmake/include/uninstall.cmake.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/include/uninstall.cmake.in b/cmake/include/uninstall.cmake.in index 2037e365..8ddc56a6 100644 --- a/cmake/include/uninstall.cmake.in +++ b/cmake/include/uninstall.cmake.in @@ -7,10 +7,10 @@ string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") - exec_program( - "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + execute_process( + COMMAND "@CMAKE_COMMAND@" -E remove "$ENV{DESTDIR}${file}" OUTPUT_VARIABLE rm_out - RETURN_VALUE rm_retval + RESULT_VARIABLE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") From 75d910c743dc838d8e3d5f4cba3fd30e6d2f45c6 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Mon, 6 May 2024 19:17:42 +0100 Subject: [PATCH 30/57] Issues 527, 528: AGW protocol compatible with NET/ROM. --- src/ax25_pad.c | 53 ++++++++++++++++++++++++++++++++++++++++ src/ax25_pad.h | 2 ++ src/server.c | 66 ++++++++++++++++++++++++++------------------------ 3 files changed, 90 insertions(+), 31 deletions(-) diff --git a/src/ax25_pad.c b/src/ax25_pad.c index 0f075808..4a526386 100644 --- a/src/ax25_pad.c +++ b/src/ax25_pad.c @@ -2579,6 +2579,59 @@ int ax25_get_c2 (packet_t this_p) } +/*------------------------------------------------------------------ + * + * Function: ax25_set_pid + * + * Purpose: Set protocol ID in packet. + * + * Inputs: this_p - pointer to packet object. + * + * pid - usually 0xF0 for APRS or 0xCF for NET/ROM. + * + * AX.25: "The Protocol Identifier (PID) field appears in information + * frames (I and UI) only. It identifies which kind of + * Layer 3 protocol, if any, is in use." + * + *------------------------------------------------------------------*/ + +void ax25_set_pid (packet_t this_p, int pid) +{ + assert (this_p->magic1 == MAGIC); + assert (this_p->magic2 == MAGIC); + + // Some applications set this to 0 which is an error. + // Change 0 to 0xF0 meaning no layer 3 protocol. + + if (pid == 0) { + pid = AX25_PID_NO_LAYER_3; + } + + // Sanity check: is it I or UI frame? + + if (this_p->frame_len == 0) return; + + ax25_frame_type_t frame_type; + cmdres_t cr; // command or response. + char description[64]; + int pf; // Poll/Final. + int nr, ns; // Sequence numbers. + + frame_type = ax25_frame_type (this_p, &cr, description, &pf, &nr, &ns); + + if (frame_type != frame_type_I && frame_type != frame_type_U_UI) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("ax25_set_pid(0x%2x): Packet type is not I or UI.\n", pid); + return; + } + + // TODO: handle 2 control byte case. + if (this_p->num_addr >= 2) { + this_p->frame_data[ax25_get_pid_offset(this_p)] = pid; + } +} + + /*------------------------------------------------------------------ * * Function: ax25_get_pid diff --git a/src/ax25_pad.h b/src/ax25_pad.h index cdb84c65..6d3d5cb2 100644 --- a/src/ax25_pad.h +++ b/src/ax25_pad.h @@ -66,6 +66,7 @@ #define AX25_UI_FRAME 3 /* Control field value. */ #define AX25_PID_NO_LAYER_3 0xf0 /* protocol ID used for APRS */ +#define AX25_PID_NETROM 0xcf /* protocol ID used for NET/ROM */ #define AX25_PID_SEGMENTATION_FRAGMENT 0x08 #define AX25_PID_ESCAPE_CHARACTER 0xff @@ -427,6 +428,7 @@ extern int ax25_is_null_frame (packet_t this_p); extern int ax25_get_control (packet_t this_p); extern int ax25_get_c2 (packet_t this_p); +extern void ax25_set_pid (packet_t this_p, int pid); extern int ax25_get_pid (packet_t this_p); extern int ax25_get_frame_len (packet_t this_p); diff --git a/src/server.c b/src/server.c index 866b58ae..2cc108b2 100644 --- a/src/server.c +++ b/src/server.c @@ -379,7 +379,7 @@ static void debug_print (fromto_t fromto, int client, struct agwpe_s *pmsg, int case 'C': strlcpy (datakind, "AX.25 Connection Received", sizeof(datakind)); break; case 'D': strlcpy (datakind, "Connected AX.25 Data", sizeof(datakind)); break; case 'd': strlcpy (datakind, "Disconnected", sizeof(datakind)); break; - case 'M': strlcpy (datakind, "Monitored Connected Information", sizeof(datakind)); break; + case 'I': strlcpy (datakind, "Monitored Connected Information", sizeof(datakind)); break; case 'S': strlcpy (datakind, "Monitored Supervisory Information", sizeof(datakind)); break; case 'U': strlcpy (datakind, "Monitored Unproto Information", sizeof(datakind)); break; case 'T': strlcpy (datakind, "Monitoring Own Information", sizeof(datakind)); break; @@ -1717,6 +1717,7 @@ static THREAD_F cmd_listen_thread (void *arg) packet_t pp; + int pid = cmd.hdr.pid; strlcpy (stemp, cmd.hdr.call_from, sizeof(stemp)); strlcat (stemp, ">", sizeof(stemp)); strlcat (stemp, cmd.hdr.call_to, sizeof(stemp)); @@ -1730,33 +1731,41 @@ static THREAD_F cmd_listen_thread (void *arg) strlcat (stemp, p, sizeof(stemp)); p += 10; } + // At this point, p now points to info part after digipeaters. + + // Issue 527: NET/ROM routing broadcasts are binary info so we can't treat as string. + // Originally, I just appended the information part. + // That was fine until NET/ROM, with binary data, came along. + // Now we set the information field after creating the packet object. + strlcat (stemp, ":", sizeof(stemp)); - strlcat (stemp, p, sizeof(stemp)); + strlcat (stemp, " ", sizeof(stemp)); //text_color_set(DW_COLOR_DEBUG); //dw_printf ("Transmit '%s'\n", stemp); pp = ax25_from_text (stemp, 1); - if (pp == NULL) { text_color_set(DW_COLOR_ERROR); dw_printf ("Failed to create frame from AGW 'V' message.\n"); + break; } - else { - /* This goes into the low priority queue because it is an original. */ + ax25_set_info (pp, (unsigned char*)p, data_len - ndigi * 10); + // Issue 527: NET/ROM routing broadcasts use PID 0xCF which was not preserved here. + ax25_set_pid (pp, pid); - /* Note that the protocol has no way to set the "has been used" */ - /* bits in the digipeater fields. */ + /* This goes into the low priority queue because it is an original. */ - /* This explains why the digipeating option is grayed out in */ - /* xastir when using the AGW interface. */ - /* The current version uses only the 'V' message, not 'K' for transmitting. */ + /* Note that the protocol has no way to set the "has been used" */ + /* bits in the digipeater fields. */ - tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp); + /* This explains why the digipeating option is grayed out in */ + /* xastir when using the AGW interface. */ + /* The current version uses only the 'V' message, not 'K' for transmitting. */ - } + tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp); } break; @@ -1890,7 +1899,7 @@ static THREAD_F cmd_listen_thread (void *arg) unsigned char num_digi; /* Expect to be in range 1 to 7. Why not up to 8? */ char dcall[7][10]; } -#if 1 + // October 2017. gcc ??? complained: // warning: dereferencing pointer 'v' does break strict-aliasing rules // Try adding this attribute to get rid of the warning. @@ -1898,7 +1907,6 @@ static THREAD_F cmd_listen_thread (void *arg) // Let me know. Maybe we could put in a compiler version check here. __attribute__((__may_alias__)) -#endif *v = (struct via_info *)cmd.data; char callsigns[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN]; @@ -2007,19 +2015,7 @@ static THREAD_F cmd_listen_thread (void *arg) { int pid = cmd.hdr.pid; - (void)(pid); - /* The AGW protocol spec says, */ - /* "AX.25 PID 0x00 or 0xF0 for AX.25 0xCF NETROM and others" */ - - /* BUG: In theory, the AX.25 PID octet should be set from this. */ - /* All examples seen (above) have 0. */ - /* The AX.25 protocol spec doesn't list 0 as a valid value. */ - /* We always send 0xf0, meaning no layer 3. */ - /* Maybe we should have an ax25_set_pid function for cases when */ - /* it is neither 0 nor 0xf0. */ - char stemp[AX25_MAX_PACKET_LEN]; - packet_t pp; strlcpy (stemp, cmd.hdr.call_from, sizeof(stemp)); strlcat (stemp, ">", sizeof(stemp)); @@ -2027,21 +2023,29 @@ static THREAD_F cmd_listen_thread (void *arg) cmd.data[data_len] = '\0'; + // Issue 527: NET/ROM routing broadcasts are binary info so we can't treat as string. + // Originally, I just appended the information part as a text string. + // That was fine until NET/ROM, with binary data, came along. + // Now we set the information field after creating the packet object. + strlcat (stemp, ":", sizeof(stemp)); - strlcat (stemp, cmd.data, sizeof(stemp)); + strlcat (stemp, " ", sizeof(stemp)); //text_color_set(DW_COLOR_DEBUG); //dw_printf ("Transmit '%s'\n", stemp); - pp = ax25_from_text (stemp, 1); + packet_t pp = ax25_from_text (stemp, 1); if (pp == NULL) { text_color_set(DW_COLOR_ERROR); dw_printf ("Failed to create frame from AGW 'M' message.\n"); } - else { - tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp); - } + + ax25_set_info (pp, (unsigned char*)cmd.data, data_len); + // Issue 527: NET/ROM routing broadcasts use PID 0xCF which was not preserved here. + ax25_set_pid (pp, pid); + + tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp); } break; From cae46801c4b50df4cdb085a7d9916846adfc6e8a Mon Sep 17 00:00:00 2001 From: wb2osz Date: Wed, 15 May 2024 20:12:55 +0100 Subject: [PATCH 31/57] Check for RELAY,WIDE,TRACE, Check for RFONLY,NOGATE in wrong place. --- src/decode_aprs.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/decode_aprs.c b/src/decode_aprs.c index 08534f7a..71eb9469 100644 --- a/src/decode_aprs.c +++ b/src/decode_aprs.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2017, 2022, 2023 John Langner, WB2OSZ +// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2017, 2022, 2023, 2024 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 @@ -206,9 +206,36 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr A->g_footprint_lon = G_UNKNOWN; A->g_footprint_radius = G_UNKNOWN; -// TODO: Complain if obsolete WIDE or RELAY is found in via path. -// TODO: complain if unused WIDEn is see in path. +// Check for RFONLY or NOGATE in the destination field. +// Actual cases observed. +// W1KU-4>APDW15,W1IMD,WIDE1,KQ1L-8,N3LLO-3,WIDE2*:}EB1EBT-9>NOGATE,TCPIP,W1KU-4*::DF1AKR-9 :73{4 +// NE1CU-10>RFONLY,KB1AEV-15,N3LLO-3,WIDE2*:}W1HS-11>APMI06,TCPIP,NE1CU-10*:T#050,190,039,008,095,20403,00000000 + + char atemp[AX25_MAX_ADDR_LEN]; + ax25_get_addr_no_ssid (pp, AX25_DESTINATION, atemp); + if ( ! quiet) { + if (strcmp("RFONLY", atemp) == 0 || strcmp("NOGATE", atemp) == 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf("RFONLY and NOGATE must not appear in the destination address field.\n"); + dw_printf("They should appear only at the end of the digi via path.\n"); + } + } + +// Complain if obsolete WIDE or RELAY is found in via path. + + for (int i = 0; i < ax25_get_num_repeaters(pp); i++) { + ax25_get_addr_no_ssid (pp, AX25_REPEATER_1 + i, atemp); + if ( ! quiet) { + if (strcmp("RELAY", atemp) == 0 || strcmp("WIDE", atemp) == 0 || strcmp("TRACE", atemp) == 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf("RELAY, TRACE, and WIDE (not WIDEn) are obsolete.\n"); + dw_printf("Modern digipeaters will not recoginize these.\n"); + } + } + } + +// TODO: complain if unused WIDEn-0 is see in path. // There is a report of UIDIGI decrementing ssid 1 to 0 and not marking it used. // http://lists.tapr.org/pipermail/aprssig_lists.tapr.org/2022-May/049397.html From c05669a82ba7bf9cb10634a8a7c248ea3219b35e Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sun, 16 Jun 2024 01:30:57 +0100 Subject: [PATCH 32/57] Allow longer Windows PTT HID name. --- src/cm108.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cm108.c b/src/cm108.c index ff3ff792..787e7bb9 100644 --- a/src/cm108.c +++ b/src/cm108.c @@ -260,8 +260,9 @@ static void substr_se (char *dest, const char *src, int start, int endp1) // 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. +// Then we have this https://groups.io/g/direwolf/message/9622 where 127 is not enough. -#define MAXX_HIDRAW_NAME_LEN 128 +#define MAXX_HIDRAW_NAME_LEN 150 /* * Result of taking inventory of USB soundcards and USB HIDs. From ae888b0a8d7261491017512fc06a0ba44cbbfc34 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Fri, 19 Jul 2024 00:37:38 +0100 Subject: [PATCH 33/57] New NCHANNEL feature. --- CHANGES.md | 3 + conf/generic.conf | 2 +- src/CMakeLists.txt | 1 + src/agwlib.c | 2 +- src/appserver.c | 2 +- src/aprs_tt.c | 8 +- src/atest.c | 11 ++- src/audio.c | 2 +- src/audio.h | 27 ++++-- src/audio_portaudio.c | 2 +- src/audio_win.c | 20 +++- src/ax25_link.c | 39 ++++++-- src/ax25_link.h | 2 +- src/ax25_pad.c | 18 +++- src/beacon.c | 14 +-- src/cdigipeater.c | 18 ++-- src/cdigipeater.h | 14 ++- src/config.c | 206 +++++++++++++++++++++++++++--------------- src/config.h | 2 +- src/decode_aprs.c | 2 +- src/demod.c | 18 ++-- src/demod_9600.c | 9 +- src/demod_afsk.c | 2 +- src/demod_psk.c | 118 ++++++++++++++++-------- src/digipeater.c | 24 ++--- src/digipeater.h | 16 ++-- src/direwolf.c | 62 ++++++++++--- src/direwolf.h | 7 +- src/dlq.c | 12 +-- src/dtmf.c | 11 ++- src/dwsock.c | 2 +- src/encode_aprs.c | 12 ++- src/fsk_demod_state.h | 2 +- src/fx25_rec.c | 6 +- src/fx25_send.c | 4 +- src/gen_packets.c | 2 +- src/gen_tone.c | 28 +++--- src/hdlc_rec.c | 57 +++++++++--- src/hdlc_rec.h | 10 ++ src/hdlc_rec2.c | 10 +- src/hdlc_send.c | 6 +- src/igate.c | 26 +++--- src/il2p_rec.c | 9 +- src/il2p_send.c | 2 +- src/kiss_frame.c | 18 ++-- src/multi_modem.c | 21 +++-- src/pfilter.c | 18 ++-- src/ptt.c | 34 +++---- src/recv.c | 3 +- src/rrbb.c | 4 +- src/server.c | 12 +-- src/telemetry.c | 2 +- src/tnctest.c | 24 ++--- src/tq.c | 66 +++++++++----- src/tt_user.c | 6 +- src/xmit.c | 30 +++--- 56 files changed, 697 insertions(+), 391 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0903e9ea..69a1a857 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,9 @@ ### New Features: ### + +- New NCHANNEL feature to map a channel number to an external network TCP KISS TNC. See xxx for example of a bridge to LoRa APRS. See [APRS-LoRa-VHF-APRS-Bridge.pdf](https://github.com/wb2osz/direwolf-doc/blob/main/APRS-LoRa-VHF-APRS-Bridge.pdf) for explanation. + - [http://www.aprs.org/aprs11/tocalls.txt](http://www.aprs.org/aprs11/tocalls.txt) has been abandoned since the end of 2021. [https://github.com/aprsorg/aprs-deviceid](https://github.com/aprsorg/aprs-deviceid) is now considered to be the authoritative source of truth for the vendor/model encoding. ## Version 1.7 -- October 2023 ## diff --git a/conf/generic.conf b/conf/generic.conf index 9a19d8a2..4fb63f6b 100644 --- a/conf/generic.conf +++ b/conf/generic.conf @@ -274,7 +274,7 @@ %C%#DTMF %C% %C%# Push to Talk (PTT) can be confusing because there are so many different cases. -%C%# Radio-Interface-Guide.pdf in https://github.com/wb2osz/direwolf-doc +%C%# https://github.com/wb2osz/direwolf-doc/blob/main/Radio-Interface-Guide.pdf %C%# goes into detail about the various options. %C% %L%# If using a C-Media CM108/CM119 or similar USB Audio Adapter, diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 19dada4a..f376b7d5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -79,6 +79,7 @@ list(APPEND direwolf_SOURCES morse.c multi_modem.c waypoint.c + nettnc.c serial_port.c pfilter.c ptt.c diff --git a/src/agwlib.c b/src/agwlib.c index 2c03adaa..cee4f992 100644 --- a/src/agwlib.c +++ b/src/agwlib.c @@ -357,7 +357,7 @@ static void * tnc_listen_thread (void *arg) /* * Take some precautions to guard against bad data which could cause problems later. */ - if (cmd.hdr.portx < 0 || cmd.hdr.portx >= MAX_CHANS) { + if (cmd.hdr.portx < 0 || cmd.hdr.portx >= MAX_TOTAL_CHANS) { text_color_set(DW_COLOR_ERROR); dw_printf ("Invalid channel number, %d, in command '%c', from network TNC.\n", cmd.hdr.portx, cmd.hdr.datakind); diff --git a/src/appserver.c b/src/appserver.c index b0ef7d87..bc2e2818 100644 --- a/src/appserver.c +++ b/src/appserver.c @@ -597,7 +597,7 @@ void agw_cb_G_port_information (int num_chan_avail, char *chan_descriptions[]) if (strncasecmp(p, "Port", 4) == 0 && isdigit(p[4])) { int chan = atoi(p+4) - 1; // "Port1" is our channel 0. - if (chan >= 0 && chan < MAX_CHANS) { + if (chan >= 0 && chan < MAX_TOTAL_CHANS) { char *desc = p + 4; while (*desc != '\0' && (*desc == ' ' || isdigit(*desc))) { diff --git a/src/aprs_tt.c b/src/aprs_tt.c index 7b125759..a2d35ec6 100644 --- a/src/aprs_tt.c +++ b/src/aprs_tt.c @@ -95,8 +95,8 @@ #define MAX_MSG_LEN 100 -static char msg_str[MAX_CHANS][MAX_MSG_LEN+1]; -static int msg_len[MAX_CHANS]; +static char msg_str[MAX_RADIO_CHANS][MAX_MSG_LEN+1]; +static int msg_len[MAX_RADIO_CHANS]; static int parse_fields (char *msg); static int parse_callsign (char *e); @@ -185,7 +185,7 @@ void aprs_tt_init (struct tt_config_s *p, int debug) // TODO: Keep ptr instead of making a copy. memcpy (&tt_config, p, sizeof(struct tt_config_s)); #endif - for (c=0; c= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); //if (button != '.') { diff --git a/src/atest.c b/src/atest.c index c5f4ec50..a24ed727 100644 --- a/src/atest.c +++ b/src/atest.c @@ -231,7 +231,7 @@ int main (int argc, char *argv[]) my_audio_config.adev[0].bits_per_sample = DEFAULT_BITS_PER_SAMPLE; - for (channel=0; channeladev[a].bits_per_sample == 0) pa->adev[a].bits_per_sample = DEFAULT_BITS_PER_SAMPLE; - for (chan=0; chanachan[chan].mark_freq == 0) pa->achan[chan].mark_freq = DEFAULT_MARK_FREQ; diff --git a/src/audio.h b/src/audio.h index 4fc05708..92fd944e 100644 --- a/src/audio.h +++ b/src/audio.h @@ -16,7 +16,7 @@ #include #endif -#include "direwolf.h" /* for MAX_CHANS used throughout the application. */ +#include "direwolf.h" /* for MAX_RADIO_CHANS and MAX_TOTAL_CHANS used throughout the application. */ #include "ax25_pad.h" /* for AX25_MAX_ADDR_LEN */ #include "version.h" @@ -59,7 +59,7 @@ typedef enum retry_e { enum medium_e { MEDIUM_NONE = 0, // Channel is not valid for use. MEDIUM_RADIO, // Internal modem for radio. MEDIUM_IGATE, // Access IGate as ordinary channel. - MEDIUM_NETTNC }; // Remote network TNC. (possible future) + MEDIUM_NETTNC }; // Remote network TNC. (new in 1.8) typedef enum sanity_e { SANITY_APRS, SANITY_AX25, SANITY_NONE } sanity_t; @@ -139,10 +139,19 @@ struct audio_s { /* originally a "channel" was always connected to an internal modem. */ /* In version 1.6, this is generalized so that a channel (as seen by client application) */ /* can be connected to something else. Initially, this will allow application */ - /* access to the IGate. Later we might have network TNCs or other internal functions. */ + /* access to the IGate. In version 1.8 we add network KISS TNC. */ + + // Watch out for maximum number of channels. + // MAX_CHANS - Originally, this was 6 for internal modem adio channels. Has been phased out. + // After adding virtual channels (IGate, network TNC), this is split into two different numbers: + // MAX_RADIO_CHANNELS - For internal modems. + // MAX_TOTAL_CHANNELS - limited by KISS channels/ports. Needed for digipeating, filtering, etc. // Properties for all channels. + char mycall[MAX_TOTAL_CHANS][AX25_MAX_ADDR_LEN]; /* Call associated with this radio channel. */ + /* Could all be the same or different. */ + enum medium_e chan_medium[MAX_TOTAL_CHANS]; // MEDIUM_NONE for invalid. // MEDIUM_RADIO for internal modem. (only possibility earlier) @@ -154,6 +163,14 @@ struct audio_s { /* Redundant but it makes things quicker and simpler */ /* than always searching thru above. */ + // Applies only to network TNC type channels. + + char nettnc_addr[MAX_TOTAL_CHANS][80]; // Network TNC address: hostname or IP addr. + + int nettnc_port[MAX_TOTAL_CHANS]; // Network TNC TCP port. + + + /* Properties for each radio channel, common to receive and transmit. */ /* Can be different for each radio channel. */ @@ -171,8 +188,6 @@ struct audio_s { // 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. */ enum modem_t { MODEM_AFSK, MODEM_BASEBAND, MODEM_SCRAMBLE, MODEM_QPSK, MODEM_8PSK, MODEM_OFF, MODEM_16_QAM, MODEM_64_QAM, MODEM_AIS, MODEM_EAS } modem_type; @@ -381,7 +396,7 @@ struct audio_s { int fulldup; /* Full Duplex. */ - } achan[MAX_CHANS]; + } achan[MAX_RADIO_CHANS]; #ifdef USE_HAMLIB int rigs; /* Total number of configured rigs */ diff --git a/src/audio_portaudio.c b/src/audio_portaudio.c index cb6ccf10..92ba2cb3 100644 --- a/src/audio_portaudio.c +++ b/src/audio_portaudio.c @@ -578,7 +578,7 @@ int audio_open (struct audio_s *pa) if (pa->adev[a].bits_per_sample == 0) pa->adev[a].bits_per_sample = DEFAULT_BITS_PER_SAMPLE; - for (chan = 0; chan < MAX_CHANS; chan++) { + for (chan = 0; chan < MAX_RADIO_CHANS; chan++) { if (pa->achan[chan].mark_freq == 0) pa->achan[chan].mark_freq = DEFAULT_MARK_FREQ; diff --git a/src/audio_win.c b/src/audio_win.c index 85a1548b..a133648a 100644 --- a/src/audio_win.c +++ b/src/audio_win.c @@ -270,7 +270,7 @@ int audio_open (struct audio_s *pa) A->g_audio_in_type = AUDIO_IN_TYPE_SOUNDCARD; - for (chan=0; chan achan[chan].mark_freq == 0) pa -> achan[chan].mark_freq = DEFAULT_MARK_FREQ; @@ -660,7 +660,13 @@ int audio_open (struct audio_s *pa) */ case AUDIO_IN_TYPE_STDIN: - setmode (STDIN_FILENO, _O_BINARY); + // https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setmode?view=msvc-170 + + int err = _setmode (_fileno(stdin), _O_BINARY); + if (err == -1) { + text_color_set (DW_COLOR_ERROR); + dw_printf ("Could not set stdin to binary mode. Unlikely to get desired result.\n"); + } A->stream_next= 0; A->stream_len = 0; @@ -888,7 +894,7 @@ int audio_get (int a) while (A->stream_next >= A->stream_len) { int res; - res = read(STDIN_FILENO, A->stream_data, 1024); + res = read(STDIN_FILENO, A->stream_data, sizeof(A->stream_data)); if (res <= 0) { text_color_set(DW_COLOR_INFO); dw_printf ("\nEnd of file on stdin. Exiting.\n"); @@ -903,9 +909,13 @@ int audio_get (int a) A->stream_len = res; A->stream_next = 0; } - return (A->stream_data[A->stream_next++] & 0xff); + sample = A->stream_data[A->stream_next] & 0xff; + A->stream_next++; + return (sample); + break; - } + + } // end switch audio in type return (-1); diff --git a/src/ax25_link.c b/src/ax25_link.c index 98d6c45e..50495cd8 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, 2023 John Langner, WB2OSZ +// Copyright (C) 2016, 2017, 2018, 2023, 2024 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 @@ -679,11 +679,13 @@ static struct misc_config_s *g_misc_config_p; * Inputs: pconfig - misc. configuration from config file or command line. * Beacon stuff ended up here. * + * debug - debug level. + * * Outputs: Remember required information for future use. That's all. * *--------------------------------------------------------------------*/ -void ax25_link_init (struct misc_config_s *pconfig) +void ax25_link_init (struct misc_config_s *pconfig, int debug) { /* @@ -691,6 +693,31 @@ void ax25_link_init (struct misc_config_s *pconfig) */ g_misc_config_p = pconfig; + if (debug >= 1) { // Only single level so far. + + s_debug_protocol_errors = 1; // Less serious Protocol errors. + + s_debug_client_app = 1; // Interaction with client application. + // dl_connect_request, dl_data_request, dl_data_indication, etc. + + s_debug_radio = 1; // Received frames and channel busy status. + // lm_data_indication, lm_channel_busy + + s_debug_variables = 1; // Variables, state changes. + + s_debug_retry = 1; // Related to lost I frames, REJ, SREJ, timeout, resending. + + s_debug_link_handle = 1; // Create data link state machine or pick existing one, + // based on my address, peer address, client app index, and radio channel. + + s_debug_stats = 1; // Statistics when connection is closed. + + s_debug_misc = 1; // Anything left over that might be interesting. + + s_debug_timers = 1; // Timer details. + } + + } /* end ax25_link_init */ @@ -2013,14 +2040,14 @@ static void dl_data_indication (ax25_dlsm_t *S, int pid, char *data, int len) * *------------------------------------------------------------------------------*/ -static int dcd_status[MAX_CHANS]; -static int ptt_status[MAX_CHANS]; +static int dcd_status[MAX_RADIO_CHANS]; +static int ptt_status[MAX_RADIO_CHANS]; void lm_channel_busy (dlq_item_t *E) { int busy; - assert (E->chan >= 0 && E->chan < MAX_CHANS); + assert (E->chan >= 0 && E->chan < MAX_RADIO_CHANS); assert (E->activity == OCTYPE_PTT || E->activity == OCTYPE_DCD); assert (E->status == 1 || E->status == 0); @@ -2104,7 +2131,7 @@ void lm_channel_busy (dlq_item_t *E) void lm_seize_confirm (dlq_item_t *E) { - assert (E->chan >= 0 && E->chan < MAX_CHANS); + assert (E->chan >= 0 && E->chan < MAX_RADIO_CHANS); ax25_dlsm_t *S; diff --git a/src/ax25_link.h b/src/ax25_link.h index 40fa401b..52caceed 100644 --- a/src/ax25_link.h +++ b/src/ax25_link.h @@ -43,7 +43,7 @@ // Call once at startup time. -void ax25_link_init (struct misc_config_s *pconfig); +void ax25_link_init (struct misc_config_s *pconfig, int debug); diff --git a/src/ax25_pad.c b/src/ax25_pad.c index 4a526386..57fd79d2 100644 --- a/src/ax25_pad.c +++ b/src/ax25_pad.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011 , 2013, 2014, 2015, 2019 John Langner, WB2OSZ +// Copyright (C) 2011 , 2013, 2014, 2015, 2019, 2024 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 @@ -355,12 +355,26 @@ void ax25_delete (packet_t this_p) * The SSID can be 2 alphanumeric characters, not just 1 to 15. * * We can just truncate the name because we will only - * end up discarding it. TODO: check on this. + * end up discarding it. TODO: check on this. WRONG! FIXME * * Returns: Pointer to new packet object in the current implementation. * * Outputs: Use the "get" functions to retrieve information in different ways. * + * Evolution: Originally this was written to handle only valid RF packets. + * There are other places where the rules are not as strict. + * Using decode_aprs with raw data seen on aprs.fi. e.g. + * EL-CA2JOT>RXTLM-1,TCPIP,qAR,CA2JOT::EL-CA2JOT:UNIT.... + * EA4YR>APBM1S,TCPIP*,qAS,BM2142POS:@162124z... + * * Source addr might not comply to RF format. + * * The q-construct has lower case. + * * Tier-2 server name might not comply to RF format. + * We have the same issue with the encapsulated part of a third-party packet. + * WB2OSZ-5>APDW17,WIDE1-1,WIDE2-1:}WHO-IS>APJIW4,TCPIP,WB2OSZ-5*::WB2OSZ-7 :ack0 + * + * We need a way to keep and retrieve the original name. + * This gets a little messy because the packet object is in the on air frame format. + * *------------------------------------------------------------------------------*/ #if AX25MEMDEBUG diff --git a/src/beacon.c b/src/beacon.c index 69a72701..b868f228 100644 --- a/src/beacon.c +++ b/src/beacon.c @@ -162,14 +162,14 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct int chan = g_misc_config_p->beacon[j].sendto_chan; if (chan < 0) chan = 0; /* For IGate, use channel 0 call. */ - if (chan >= MAX_CHANS) chan = 0; // For ICHANNEL, use channel 0 call. + if (chan >= MAX_TOTAL_CHANS) chan = 0; // For ICHANNEL, use channel 0 call. if (g_modem_config_p->chan_medium[chan] == MEDIUM_RADIO || g_modem_config_p->chan_medium[chan] == MEDIUM_NETTNC) { - if (strlen(g_modem_config_p->achan[chan].mycall) > 0 && - strcasecmp(g_modem_config_p->achan[chan].mycall, "N0CALL") != 0 && - strcasecmp(g_modem_config_p->achan[chan].mycall, "NOCALL") != 0) { + if (strlen(g_modem_config_p->mycall[chan]) > 0 && + strcasecmp(g_modem_config_p->mycall[chan], "N0CALL") != 0 && + strcasecmp(g_modem_config_p->mycall[chan], "NOCALL") != 0) { switch (g_misc_config_p->beacon[j].btype) { @@ -809,10 +809,10 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo) if (g_modem_config_p->chan_medium[bp->sendto_chan] == MEDIUM_IGATE) { // ICHANNEL uses chan 0 mycall. // TODO: Maybe it should be allowed to have own. - strlcpy (mycall, g_modem_config_p->achan[0].mycall, sizeof(mycall)); + strlcpy (mycall, g_modem_config_p->mycall[0], sizeof(mycall)); } else { - strlcpy (mycall, g_modem_config_p->achan[bp->sendto_chan].mycall, sizeof(mycall)); + strlcpy (mycall, g_modem_config_p->mycall[bp->sendto_chan], sizeof(mycall)); } if (strlen(mycall) == 0 || strcmp(mycall, "NOCALL") == 0) { @@ -900,7 +900,7 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo) case BEACON_OBJECT: - encode_object (bp->objname, bp->compress, 0, bp->lat, bp->lon, bp->ambiguity, + encode_object (bp->objname, bp->compress, 1, bp->lat, bp->lon, bp->ambiguity, bp->symtab, bp->symbol, bp->power, bp->height, bp->gain, bp->dir, G_UNKNOWN, G_UNKNOWN, /* course, speed */ diff --git a/src/cdigipeater.c b/src/cdigipeater.c index 06128b20..844af470 100644 --- a/src/cdigipeater.c +++ b/src/cdigipeater.c @@ -76,7 +76,7 @@ static struct cdigi_config_s *save_cdigi_config_p; * Maintain count of packets digipeated for each combination of from/to channel. */ -static int cdigi_count[MAX_CHANS][MAX_CHANS]; +static int cdigi_count[MAX_RADIO_CHANS][MAX_RADIO_CHANS]; int cdigipeater_get_count (int from_chan, int to_chan) { return (cdigi_count[from_chan][to_chan]); @@ -132,7 +132,9 @@ void cdigipeater (int from_chan, packet_t pp) // Connected mode is allowed only for channels with internal modem. // It probably wouldn't matter for digipeating but let's keep that rule simple and consistent. - if ( from_chan < 0 || from_chan >= MAX_CHANS || save_audio_config_p->chan_medium[from_chan] != MEDIUM_RADIO) { + if ( from_chan < 0 || from_chan >= MAX_RADIO_CHANS || + (save_audio_config_p->chan_medium[from_chan] != MEDIUM_RADIO && + save_audio_config_p->chan_medium[from_chan] != MEDIUM_NETTNC) ) { text_color_set(DW_COLOR_ERROR); dw_printf ("cdigipeater: Did not expect to receive on invalid channel %d.\n", from_chan); return; @@ -145,13 +147,13 @@ void cdigipeater (int from_chan, packet_t pp) * Might not have a benefit here. */ - for (to_chan=0; to_chanenabled[from_chan][to_chan]) { if (to_chan == from_chan) { packet_t result; - result = cdigipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall, - save_audio_config_p->achan[to_chan].mycall, + result = cdigipeat_match (from_chan, pp, save_audio_config_p->mycall[from_chan], + save_audio_config_p->mycall[to_chan], save_cdigi_config_p->has_alias[from_chan][to_chan], &(save_cdigi_config_p->alias[from_chan][to_chan]), to_chan, save_cdigi_config_p->cfilter_str[from_chan][to_chan]); @@ -168,13 +170,13 @@ void cdigipeater (int from_chan, packet_t pp) * Second pass: Look at packets being digipeated to different channel. */ - for (to_chan=0; to_chanenabled[from_chan][to_chan]) { if (to_chan != from_chan) { packet_t result; - result = cdigipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall, - save_audio_config_p->achan[to_chan].mycall, + result = cdigipeat_match (from_chan, pp, save_audio_config_p->mycall[from_chan], + save_audio_config_p->mycall[to_chan], save_cdigi_config_p->has_alias[from_chan][to_chan], &(save_cdigi_config_p->alias[from_chan][to_chan]), to_chan, save_cdigi_config_p->cfilter_str[from_chan][to_chan]); diff --git a/src/cdigipeater.h b/src/cdigipeater.h index 69a4b8c7..89b0302a 100644 --- a/src/cdigipeater.h +++ b/src/cdigipeater.h @@ -5,7 +5,7 @@ #include "regex.h" -#include "direwolf.h" /* for MAX_CHANS */ +#include "direwolf.h" /* for MAX_RADIO_CHANS */ #include "ax25_pad.h" /* for packet_t */ #include "audio.h" /* for radio channel properties */ @@ -23,17 +23,21 @@ struct cdigi_config_s { /* * Rules for each of the [from_chan][to_chan] combinations. */ - int enabled[MAX_CHANS][MAX_CHANS]; // Is it enabled for from/to pair? - int has_alias[MAX_CHANS][MAX_CHANS]; // If there was no alias in the config file, +// For APRS digipeater, we use MAX_TOTAL_CHANS because we use external TNCs. +// Connected mode packet must use internal modems we we use MAX_RADIO_CHANS. + + int enabled[MAX_RADIO_CHANS][MAX_RADIO_CHANS]; // Is it enabled for from/to pair? + + int has_alias[MAX_RADIO_CHANS][MAX_RADIO_CHANS]; // If there was no alias in the config file, // the structure below will not be set up // properly and an attempt to use it could // result in a crash. (fixed v1.5) // Not needed for [APRS] DIGIPEAT because // the alias is mandatory there. - regex_t alias[MAX_CHANS][MAX_CHANS]; + regex_t alias[MAX_RADIO_CHANS][MAX_RADIO_CHANS]; - char *cfilter_str[MAX_CHANS][MAX_CHANS]; + char *cfilter_str[MAX_RADIO_CHANS][MAX_RADIO_CHANS]; // NULL or optional Packet Filter strings such as "t/m". }; diff --git a/src/config.c b/src/config.c index de8d74d4..69fa80e2 100644 --- a/src/config.c +++ b/src/config.c @@ -763,7 +763,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, p_audio_config->adev[0].defined = 2; // 2 means it was done by default and not the user's config file. - for (channel=0; channelchan_medium[channel] = MEDIUM_NONE; /* One or both channels will be */ @@ -1221,7 +1221,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ /* - * CHANNEL n - Set channel for channel-specific commands. + * CHANNEL n - Set channel for channel-specific commands. Only for modem/radio channels. */ else if (strcasecmp(t, "CHANNEL") == 0) { @@ -1233,7 +1233,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } n = atoi(t); - if (n >= 0 && n < MAX_CHANS) { + if (n >= 0 && n < MAX_RADIO_CHANS) { channel = n; @@ -1253,7 +1253,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, } else { text_color_set(DW_COLOR_ERROR); - dw_printf ("Line %d: Channel number must in range of 0 to %d.\n", line, MAX_CHANS-1); + dw_printf ("Line %d: Channel number must in range of 0 to %d.\n", line, MAX_RADIO_CHANS-1); } } @@ -1274,7 +1274,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } int ichan = atoi(t); - if (ichan >= MAX_CHANS && ichan < MAX_TOTAL_CHANS) { + if (ichan >= MAX_RADIO_CHANS && ichan < MAX_TOTAL_CHANS) { if (p_audio_config->chan_medium[ichan] == MEDIUM_NONE) { @@ -1286,15 +1286,73 @@ void config_init (char *fname, struct audio_s *p_audio_config, } else { text_color_set(DW_COLOR_ERROR); - dw_printf ("Line %d: ICHANNEL can't use %d because it is already in use.\n", line, ichan); + dw_printf ("Line %d: ICHANNEL can't use channel %d because it is already in use.\n", line, ichan); } } else { text_color_set(DW_COLOR_ERROR); - dw_printf ("Line %d: ICHANNEL number must in range of %d to %d.\n", line, MAX_CHANS, MAX_TOTAL_CHANS-1); + dw_printf ("Line %d: ICHANNEL number must in range of %d to %d.\n", line, MAX_RADIO_CHANS, MAX_TOTAL_CHANS-1); } } +/* + * NCHANNEL chan addr port - Define Network TNC virtual channel. + * + * This allows a client application to talk to to an external TNC over TCP KISS + * by using a channel number outside the normal range for modems. + * This does not change the current channel number used by MODEM, PTT, etc. + * + * chan = direwolf channel. + * addr = hostname or IP address of network TNC. + * port = KISS TCP port on network TNC. + * + * Future: Might allow selection of channel on the network TNC. + * For now, ignore incoming and set to 0 for outgoing. + * + * FIXME: Can't set mycall for nchannel. + */ + + else if (strcasecmp(t, "NCHANNEL") == 0) { + t = split(NULL,0); + if (t == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: Missing virtual channel number for NCHANNEL command.\n", line); + continue; + } + int nchan = atoi(t); + if (nchan >= MAX_RADIO_CHANS && nchan < MAX_TOTAL_CHANS) { + + if (p_audio_config->chan_medium[nchan] == MEDIUM_NONE) { + + p_audio_config->chan_medium[nchan] = MEDIUM_NETTNC; + } + else { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: NCHANNEL can't use channel %d because it is already in use.\n", line, nchan); + } + } + else { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: NCHANNEL number must in range of %d to %d.\n", line, MAX_RADIO_CHANS, MAX_TOTAL_CHANS-1); + } + + t = split(NULL,0); + if (t == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: Missing network TNC address for NCHANNEL command.\n", line); + continue; + } + strlcpy (p_audio_config->nettnc_addr[nchan], t, sizeof(p_audio_config->nettnc_addr[nchan])); + + t = split(NULL,0); + if (t == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: Missing network TNC TCP port for NCHANNEL command.\n", line); + continue; + } + p_audio_config->nettnc_port[nchan] = atoi(t); + } + /* * MYCALL station */ @@ -1330,14 +1388,14 @@ void config_init (char *fname, struct audio_s *p_audio_config, int c; - for (c = 0; c < MAX_CHANS; c++) { + for (c = 0; c < MAX_TOTAL_CHANS; c++) { if (c == channel || - strlen(p_audio_config->achan[c].mycall) == 0 || - strcasecmp(p_audio_config->achan[c].mycall, "NOCALL") == 0 || - strcasecmp(p_audio_config->achan[c].mycall, "N0CALL") == 0) { + strlen(p_audio_config->mycall[c]) == 0 || + strcasecmp(p_audio_config->mycall[c], "NOCALL") == 0 || + strcasecmp(p_audio_config->mycall[c], "N0CALL") == 0) { - strlcpy (p_audio_config->achan[c].mycall, t, sizeof(p_audio_config->achan[c].mycall)); + strlcpy (p_audio_config->mycall[c], t, sizeof(p_audio_config->mycall[c])); } } } @@ -2552,10 +2610,10 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } from_chan = atoi(t); - if (from_chan < 0 || from_chan >= MAX_CHANS) { + if (from_chan < 0 || from_chan >= MAX_TOTAL_CHANS) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: FROM-channel must be in range of 0 to %d on line %d.\n", - MAX_CHANS-1, line); + MAX_TOTAL_CHANS-1, line); continue; } @@ -2582,10 +2640,10 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } to_chan = atoi(t); - if (to_chan < 0 || to_chan >= MAX_CHANS) { + if (to_chan < 0 || to_chan >= MAX_TOTAL_CHANS) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: TO-channel must be in range of 0 to %d on line %d.\n", - MAX_CHANS-1, line); + MAX_TOTAL_CHANS-1, line); continue; } @@ -2713,10 +2771,10 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } from_chan = atoi(t); - if (from_chan < 0 || from_chan >= MAX_CHANS) { + if (from_chan < 0 || from_chan >= MAX_RADIO_CHANS) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: FROM-channel must be in range of 0 to %d on line %d.\n", - MAX_CHANS-1, line); + MAX_RADIO_CHANS-1, line); continue; } @@ -2742,10 +2800,10 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } to_chan = atoi(t); - if (to_chan < 0 || to_chan >= MAX_CHANS) { + if (to_chan < 0 || to_chan >= MAX_RADIO_CHANS) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: TO-channel must be in range of 0 to %d on line %d.\n", - MAX_CHANS-1, line); + MAX_RADIO_CHANS-1, line); continue; } if (p_audio_config->chan_medium[to_chan] != MEDIUM_RADIO) { @@ -2787,10 +2845,10 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } from_chan = atoi(t); - if (from_chan < 0 || from_chan >= MAX_CHANS) { + if (from_chan < 0 || from_chan >= MAX_RADIO_CHANS) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: FROM-channel must be in range of 0 to %d on line %d.\n", - MAX_CHANS-1, line); + MAX_RADIO_CHANS-1, line); continue; } @@ -2820,10 +2878,10 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } to_chan = atoi(t); - if (to_chan < 0 || to_chan >= MAX_CHANS) { + if (to_chan < 0 || to_chan >= MAX_RADIO_CHANS) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: TO-channel must be in range of 0 to %d on line %d.\n", - MAX_CHANS-1, line); + MAX_RADIO_CHANS-1, line); continue; } if (p_audio_config->chan_medium[to_chan] != MEDIUM_RADIO) { @@ -2906,7 +2964,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } if (*t == 'i' || *t == 'I') { - from_chan = MAX_CHANS; + from_chan = MAX_TOTAL_CHANS; text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: FILTER IG ... on line %d.\n", line); dw_printf ("Warning! Don't mess with IS>RF filtering unless you are an expert and have an unusual situation.\n"); @@ -2916,10 +2974,10 @@ void config_init (char *fname, struct audio_s *p_audio_config, } else { from_chan = isdigit(*t) ? atoi(t) : -999; - if (from_chan < 0 || from_chan >= MAX_CHANS) { + if (from_chan < 0 || from_chan >= MAX_TOTAL_CHANS) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: Filter FROM-channel must be in range of 0 to %d or \"IG\" on line %d.\n", - MAX_CHANS-1, line); + MAX_TOTAL_CHANS-1, line); continue; } @@ -2945,7 +3003,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } if (*t == 'i' || *t == 'I') { - to_chan = MAX_CHANS; + to_chan = MAX_TOTAL_CHANS; text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: FILTER ... IG ... on line %d.\n", line); dw_printf ("Warning! Don't mess with RF>IS filtering unless you are an expert and have an unusual situation.\n"); @@ -2955,10 +3013,10 @@ void config_init (char *fname, struct audio_s *p_audio_config, } else { to_chan = isdigit(*t) ? atoi(t) : -999; - if (to_chan < 0 || to_chan >= MAX_CHANS) { + if (to_chan < 0 || to_chan >= MAX_TOTAL_CHANS) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: Filter TO-channel must be in range of 0 to %d or \"IG\" on line %d.\n", - MAX_CHANS-1, line); + MAX_TOTAL_CHANS-1, line); continue; } if (p_audio_config->chan_medium[to_chan] != MEDIUM_RADIO && @@ -3020,10 +3078,10 @@ void config_init (char *fname, struct audio_s *p_audio_config, } from_chan = isdigit(*t) ? atoi(t) : -999; - if (from_chan < 0 || from_chan >= MAX_CHANS) { + if (from_chan < 0 || from_chan >= MAX_RADIO_CHANS) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: Filter FROM-channel must be in range of 0 to %d on line %d.\n", - MAX_CHANS-1, line); + MAX_RADIO_CHANS-1, line); continue; } @@ -3045,10 +3103,10 @@ void config_init (char *fname, struct audio_s *p_audio_config, } to_chan = isdigit(*t) ? atoi(t) : -999; - if (to_chan < 0 || to_chan >= MAX_CHANS) { + if (to_chan < 0 || to_chan >= MAX_RADIO_CHANS) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: Filter TO-channel must be in range of 0 to %d on line %d.\n", - MAX_CHANS-1, line); + MAX_RADIO_CHANS-1, line); continue; } if (p_audio_config->chan_medium[to_chan] != MEDIUM_RADIO) { @@ -4205,10 +4263,10 @@ void config_init (char *fname, struct audio_s *p_audio_config, } r = atoi(t); - if (r < 0 || r > MAX_CHANS-1) { + if (r < 0 || r > MAX_RADIO_CHANS-1) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: DTMF receive channel must be in range of 0 to %d on line %d.\n", - MAX_CHANS-1, line); + MAX_RADIO_CHANS-1, line); continue; } @@ -4236,9 +4294,9 @@ void config_init (char *fname, struct audio_s *p_audio_config, if (isdigit(*p)) { x = *p - '0'; - if (x < 0 || x > MAX_CHANS-1) { + if (x < 0 || x > MAX_TOTAL_CHANS-1) { text_color_set(DW_COLOR_ERROR); - dw_printf ("Config file: Transmit channel must be in range of 0 to %d on line %d.\n", MAX_CHANS-1, line); + dw_printf ("Config file: Transmit channel must be in range of 0 to %d on line %d.\n", MAX_TOTAL_CHANS-1, line); x = -1; } else if (p_audio_config->chan_medium[x] != MEDIUM_RADIO && @@ -4528,10 +4586,10 @@ void config_init (char *fname, struct audio_s *p_audio_config, } n = atoi(t); - if (n < 0 || n > MAX_CHANS-1) { + if (n < 0 || n > MAX_TOTAL_CHANS-1) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: Transmit channel must be in range of 0 to %d on line %d.\n", - MAX_CHANS-1, line); + MAX_TOTAL_CHANS-1, line); continue; } p_igate_config->tx_chan = n; @@ -4797,9 +4855,9 @@ void config_init (char *fname, struct audio_s *p_audio_config, t = split(NULL,0); if (t != NULL) { chan = atoi(t); - if (chan < 0 || chan >= MAX_CHANS) { + if (chan < 0 || chan >= MAX_TOTAL_CHANS) { text_color_set(DW_COLOR_ERROR); - dw_printf ("Line %d: Invalid channel %d for KISSPORT command. Must be in range 0 thru %d.\n", line, chan, MAX_CHANS-1); + dw_printf ("Line %d: Invalid channel %d for KISSPORT command. Must be in range 0 thru %d.\n", line, chan, MAX_TOTAL_CHANS-1); continue; } } @@ -5510,25 +5568,25 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ int i, j, k, b; - for (i=0; ienabled[i][j]) { - if ( strcmp(p_audio_config->achan[i].mycall, "") == 0 || - strcmp(p_audio_config->achan[i].mycall, "NOCALL") == 0 || - strcmp(p_audio_config->achan[i].mycall, "N0CALL") == 0) { + if ( strcmp(p_audio_config->mycall[i], "") == 0 || + strcmp(p_audio_config->mycall[i], "NOCALL") == 0 || + strcmp(p_audio_config->mycall[i], "N0CALL") == 0) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: MYCALL must be set for receive channel %d before digipeating is allowed.\n", i); p_digi_config->enabled[i][j] = 0; } - if ( strcmp(p_audio_config->achan[j].mycall, "") == 0 || - strcmp(p_audio_config->achan[j].mycall, "NOCALL") == 0 || - strcmp(p_audio_config->achan[j].mycall, "N0CALL") == 0) { + if ( strcmp(p_audio_config->mycall[j], "") == 0 || + strcmp(p_audio_config->mycall[j], "NOCALL") == 0 || + strcmp(p_audio_config->mycall[j], "N0CALL") == 0) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: MYCALL must be set for transmit channel %d before digipeating is allowed.\n", i); @@ -5550,20 +5608,20 @@ void config_init (char *fname, struct audio_s *p_audio_config, /* Connected mode digipeating. */ - if (p_cdigi_config->enabled[i][j]) { + if (i < MAX_RADIO_CHANS && j < MAX_RADIO_CHANS && p_cdigi_config->enabled[i][j]) { - if ( strcmp(p_audio_config->achan[i].mycall, "") == 0 || - strcmp(p_audio_config->achan[i].mycall, "NOCALL") == 0 || - strcmp(p_audio_config->achan[i].mycall, "N0CALL") == 0) { + if ( strcmp(p_audio_config->mycall[i], "") == 0 || + strcmp(p_audio_config->mycall[i], "NOCALL") == 0 || + strcmp(p_audio_config->mycall[i], "N0CALL") == 0) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: MYCALL must be set for receive channel %d before digipeating is allowed.\n", i); p_cdigi_config->enabled[i][j] = 0; } - if ( strcmp(p_audio_config->achan[j].mycall, "") == 0 || - strcmp(p_audio_config->achan[j].mycall, "NOCALL") == 0 || - strcmp(p_audio_config->achan[j].mycall, "N0CALL") == 0) { + if ( strcmp(p_audio_config->mycall[j], "") == 0 || + strcmp(p_audio_config->mycall[j], "NOCALL") == 0 || + strcmp(p_audio_config->mycall[j], "N0CALL") == 0) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: MYCALL must be set for transmit channel %d before digipeating is allowed.\n", i); @@ -5587,7 +5645,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, if (strlen(p_igate_config->t2_login) > 0 && (p_audio_config->chan_medium[i] == MEDIUM_RADIO || p_audio_config->chan_medium[i] == MEDIUM_NETTNC)) { - if (strcmp(p_audio_config->achan[i].mycall, "NOCALL") == 0 || strcmp(p_audio_config->achan[i].mycall, "N0CALL") == 0) { + if (strcmp(p_audio_config->mycall[i], "NOCALL") == 0 || strcmp(p_audio_config->mycall[i], "N0CALL") == 0) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: MYCALL must be set for receive channel %d before Rx IGate is allowed.\n", i); strlcpy (p_igate_config->t2_login, "", sizeof(p_igate_config->t2_login)); @@ -5595,9 +5653,9 @@ void config_init (char *fname, struct audio_s *p_audio_config, // Currently we can have only one transmit channel. // This might be generalized someday to allow more. if (p_igate_config->tx_chan >= 0 && - ( strcmp(p_audio_config->achan[p_igate_config->tx_chan].mycall, "") == 0 || - strcmp(p_audio_config->achan[p_igate_config->tx_chan].mycall, "NOCALL") == 0 || - strcmp(p_audio_config->achan[p_igate_config->tx_chan].mycall, "N0CALL") == 0)) { + ( strcmp(p_audio_config->mycall[p_igate_config->tx_chan], "") == 0 || + strcmp(p_audio_config->mycall[p_igate_config->tx_chan], "NOCALL") == 0 || + strcmp(p_audio_config->mycall[p_igate_config->tx_chan], "N0CALL") == 0)) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: MYCALL must be set for transmit channel %d before Tx IGate is allowed.\n", i); @@ -5610,10 +5668,10 @@ void config_init (char *fname, struct audio_s *p_audio_config, // This will handle eventual case of multiple transmit channels. if (strlen(p_igate_config->t2_login) > 0) { - 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/180"); + if (p_digi_config->filter_str[MAX_TOTAL_CHANS][j] == NULL) { + p_digi_config->filter_str[MAX_TOTAL_CHANS][j] = strdup("i/180"); } } } @@ -5746,7 +5804,7 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_ } else if (value[0] == 'r' || value[0] == 'R') { int n = atoi(value+1); - if (( n < 0 || n >= MAX_CHANS || p_audio_config->chan_medium[n] == MEDIUM_NONE) + if (( n < 0 || n >= MAX_TOTAL_CHANS || p_audio_config->chan_medium[n] == MEDIUM_NONE) && p_audio_config->chan_medium[n] != MEDIUM_IGATE) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file, line %d: Simulated receive on channel %d is not valid.\n", line, n); @@ -5757,7 +5815,7 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_ } else if (value[0] == 't' || value[0] == 'T' || value[0] == 'x' || value[0] == 'X') { int n = atoi(value+1); - if (( n < 0 || n >= MAX_CHANS || p_audio_config->chan_medium[n] == MEDIUM_NONE) + if (( n < 0 || n >= MAX_TOTAL_CHANS || p_audio_config->chan_medium[n] == MEDIUM_NONE) && p_audio_config->chan_medium[n] != MEDIUM_IGATE) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file, line %d: Send to channel %d is not valid.\n", line, n); @@ -5769,7 +5827,7 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_ } else { int n = atoi(value); - if (( n < 0 || n >= MAX_CHANS || p_audio_config->chan_medium[n] == MEDIUM_NONE) + if (( n < 0 || n >= MAX_TOTAL_CHANS || p_audio_config->chan_medium[n] == MEDIUM_NONE) && p_audio_config->chan_medium[n] != MEDIUM_IGATE) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file, line %d: Send to channel %d is not valid.\n", line, n); @@ -6020,7 +6078,7 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_ if (b->sendto_type == SENDTO_XMIT) { - if (( b->sendto_chan < 0 || b->sendto_chan >= MAX_CHANS || p_audio_config->chan_medium[b->sendto_chan] == MEDIUM_NONE) + if (( b->sendto_chan < 0 || b->sendto_chan >= MAX_TOTAL_CHANS || p_audio_config->chan_medium[b->sendto_chan] == MEDIUM_NONE) && p_audio_config->chan_medium[b->sendto_chan] != MEDIUM_IGATE) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file, line %d: Send to channel %d is not valid.\n", line, b->sendto_chan); @@ -6029,18 +6087,18 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_ if (p_audio_config->chan_medium[b->sendto_chan] == MEDIUM_IGATE) { // Prevent subscript out of bounds. // Will be using call from chan 0 later. - if ( strcmp(p_audio_config->achan[0].mycall, "") == 0 || - strcmp(p_audio_config->achan[0].mycall, "NOCALL") == 0 || - strcmp(p_audio_config->achan[0].mycall, "N0CALL") == 0 ) { + if ( strcmp(p_audio_config->mycall[0], "") == 0 || + strcmp(p_audio_config->mycall[0], "NOCALL") == 0 || + strcmp(p_audio_config->mycall[0], "N0CALL") == 0 ) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: MYCALL must be set for channel %d before beaconing is allowed.\n", 0); return (0); } } else { - if ( strcmp(p_audio_config->achan[b->sendto_chan].mycall, "") == 0 || - strcmp(p_audio_config->achan[b->sendto_chan].mycall, "NOCALL") == 0 || - strcmp(p_audio_config->achan[b->sendto_chan].mycall, "N0CALL") == 0 ) { + if ( strcmp(p_audio_config->mycall[b->sendto_chan], "") == 0 || + strcmp(p_audio_config->mycall[b->sendto_chan], "NOCALL") == 0 || + strcmp(p_audio_config->mycall[b->sendto_chan], "N0CALL") == 0 ) { text_color_set(DW_COLOR_ERROR); dw_printf ("Config file: MYCALL must be set for channel %d before beaconing is allowed.\n", b->sendto_chan); diff --git a/src/config.h b/src/config.h index 360ac492..e4675238 100644 --- a/src/config.h +++ b/src/config.h @@ -30,7 +30,7 @@ enum sendto_type_e { SENDTO_XMIT, SENDTO_IGATE, SENDTO_RECV }; #define MAX_BEACONS 30 -#define MAX_KISS_TCP_PORTS (MAX_CHANS+1) +#define MAX_KISS_TCP_PORTS (MAX_RADIO_CHANS+1) struct misc_config_s { diff --git a/src/decode_aprs.c b/src/decode_aprs.c index 71eb9469..ce658eb6 100644 --- a/src/decode_aprs.c +++ b/src/decode_aprs.c @@ -1652,7 +1652,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int // It is essential to keep trailing spaces. e.g. VX-8 suffix is "_ " char mcomment[256]; - strlcpy (mcomment, info + sizeof(struct aprs_mic_e_s), sizeof(mcomment)); + strlcpy (mcomment, ((char*)info) + sizeof(struct aprs_mic_e_s), sizeof(mcomment)); if (mcomment[strlen(mcomment)-1] == '\r') { mcomment[strlen(mcomment)-1] = '\0'; } diff --git a/src/demod.c b/src/demod.c index cc522271..efcfde71 100644 --- a/src/demod.c +++ b/src/demod.c @@ -63,11 +63,11 @@ static struct audio_s *save_audio_config_p; // Current state of all the decoders. -static struct demodulator_state_s demodulator_state[MAX_CHANS][MAX_SUBCHANS]; +static struct demodulator_state_s demodulator_state[MAX_RADIO_CHANS][MAX_SUBCHANS]; -static int sample_sum[MAX_CHANS][MAX_SUBCHANS]; -static int sample_count[MAX_CHANS][MAX_SUBCHANS]; +static int sample_sum[MAX_RADIO_CHANS][MAX_SUBCHANS]; +static int sample_count[MAX_RADIO_CHANS][MAX_SUBCHANS]; /*------------------------------------------------------------------ @@ -100,7 +100,7 @@ int demod_init (struct audio_s *pa) save_audio_config_p = pa; - for (chan = 0; chan < MAX_CHANS; chan++) { + for (chan = 0; chan < MAX_RADIO_CHANS; chan++) { if (save_audio_config_p->chan_medium[chan] == MEDIUM_RADIO) { @@ -812,7 +812,7 @@ int demod_init (struct audio_s *pa) // Now the virtual channels. FIXME: could be single loop. - for (chan = MAX_CHANS; chan < MAX_TOTAL_CHANS; chan++) { + for (chan = MAX_RADIO_CHANS; chan < MAX_TOTAL_CHANS; chan++) { // FIXME dw_printf ("-------- virtual channel loop %d \n", chan); @@ -927,7 +927,7 @@ int demod_get_sample (int a) * *--------------------------------------------------------------------*/ -static volatile int mute_input[MAX_CHANS]; +static volatile int mute_input[MAX_RADIO_CHANS]; // New in 1.7. // A few people have a really bad audio cross talk situation where they receive their own transmissions. @@ -939,7 +939,7 @@ static volatile int mute_input[MAX_CHANS]; void demod_mute_input (int chan, int mute_during_xmit) { - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); mute_input[chan] = mute_during_xmit; } @@ -952,7 +952,7 @@ void demod_process_sample (int chan, int subchan, int sam) struct demodulator_state_s *D; - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); assert (subchan >= 0 && subchan < MAX_SUBCHANS); if (mute_input[chan]) { @@ -1066,7 +1066,7 @@ alevel_t demod_get_audio_level (int chan, int subchan) struct demodulator_state_s *D; alevel_t alevel; - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); assert (subchan >= 0 && subchan < MAX_SUBCHANS); /* We have to consider two different cases here. */ diff --git a/src/demod_9600.c b/src/demod_9600.c index 705d1fa7..99432bfe 100644 --- a/src/demod_9600.c +++ b/src/demod_9600.c @@ -395,7 +395,7 @@ void demod_9600_process_sample (int chan, int sam, int upsample, struct demodula int subchan = 0; - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); assert (subchan >= 0 && subchan < MAX_SUBCHANS); /* Scale to nice number for convenience. */ @@ -611,7 +611,10 @@ inline static void nudge_pll (int chan, int subchan, int slice, float demod_out_ /* Overflow. Was large positive, wrapped around, now large negative. */ - hdlc_rec_bit (chan, subchan, slice, demod_out_f > 0, D->modem_type == MODEM_SCRAMBLE, D->slicer[slice].lfsr); + hdlc_rec_bit_new (chan, subchan, slice, demod_out_f > 0, D->modem_type == MODEM_SCRAMBLE, D->slicer[slice].lfsr, + &(D->slicer[slice].pll_nudge_total), &(D->slicer[slice].pll_symbol_count)); + D->slicer[slice].pll_symbol_count++; + pll_dcd_each_symbol2 (D, chan, subchan, slice); } @@ -627,12 +630,14 @@ inline static void nudge_pll (int chan, int subchan, int slice, float demod_out_ float target = D->pll_step_per_sample * demod_out_f / (demod_out_f - D->slicer[slice].prev_demod_out_f); + 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 + target * (1.0f - D->pll_locked_inertia) ); } else { D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_searching_inertia + target * (1.0f - D->pll_searching_inertia) ); } + D->slicer[slice].pll_nudge_total += (int64_t)((signed int)(D->slicer[slice].data_clock_pll)) - (int64_t)before; } diff --git a/src/demod_afsk.c b/src/demod_afsk.c index b4d6c295..3e5d03ec 100644 --- a/src/demod_afsk.c +++ b/src/demod_afsk.c @@ -609,7 +609,7 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat static int seq = 0; /* for log file name */ #endif - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); assert (subchan >= 0 && subchan < MAX_SUBCHANS); /* diff --git a/src/demod_psk.c b/src/demod_psk.c index bc058185..3d06c915 100644 --- a/src/demod_psk.c +++ b/src/demod_psk.c @@ -72,7 +72,10 @@ * V.26 has two variations, A and B. Initially I implemented the A alternative. * It later turned out that the MFJ-2400 used the B alternative. In version 1.6 you have a * choice between compatibility with MFJ (and probably the others) or the original implementation. - * + * The B alternative works a little more reliably, perhaps because there is never a + * zero phase difference between adjacent symbols. + * Eventually the A alternative might disappear to reduce confusion. + * *---------------------------------------------------------------*/ #include "direwolf.h" @@ -94,7 +97,7 @@ #include "fsk_demod_state.h" // Values above override defaults. #include "audio.h" -#include "tune.h" +//#include "tune.h" // obsolete. eventually remove all references. #include "fsk_gen_filter.h" #include "hdlc_rec.h" #include "textcolor.h" @@ -102,7 +105,13 @@ #include "dsp.h" - +#define TUNE(envvar,param,name,fmt) { \ + char *e = getenv(envvar); \ + if (e != NULL) { \ + param = atof(e); \ + text_color_set (DW_COLOR_ERROR); \ + dw_printf ("TUNE: " name " = " fmt "\n", param); \ + } } static const int phase_to_gray_v26[4] = {0, 1, 3, 2}; @@ -202,9 +211,10 @@ void demod_psk_init (enum modem_t modem_type, enum v26_e v26_alt, int samples_pe D->num_slicers = 1; // Haven't thought about this yet. Is it even applicable? -#ifdef TUNE_PROFILE - profile = TUNE_PROFILE; -#endif +//#ifdef TUNE_PROFILE +// profile = TUNE_PROFILE; +//#endif + TUNE("TUNE_PROFILE", profile, "profile", "%c") if (modem_type == MODEM_QPSK) { @@ -290,9 +300,16 @@ void demod_psk_init (enum modem_t modem_type, enum v26_e v26_alt, int samples_pe D->u.psk.delay_line_width_sym = 1.25; // Delay line > 13/12 * symbol period +// JWL experiment 11-7. Should delay be based on audio freq rather than baud? +#if 0 // experiment made things much worse. 55 went down to 21. + D->u.psk.coffs = (int) round( (11.f / 12.f) * (float)samples_per_sec / (float)carrier_freq ); + D->u.psk.boffs = (int) round( (float)samples_per_sec / (float)carrier_freq ); + D->u.psk.soffs = (int) round( (13.f / 12.f) * (float)samples_per_sec / (float)carrier_freq ); +#else D->u.psk.coffs = (int) round( (11.f / 12.f) * (float)samples_per_sec / (float)correct_baud ); D->u.psk.boffs = (int) round( (float)samples_per_sec / (float)correct_baud ); D->u.psk.soffs = (int) round( (13.f / 12.f) * (float)samples_per_sec / (float)correct_baud ); +#endif } else { @@ -393,26 +410,40 @@ void demod_psk_init (enum modem_t modem_type, enum v26_e v26_alt, int samples_pe } } -#ifdef TUNE_PRE_BAUD - D->u.psk.prefilter_baud = TUNE_PRE_BAUD; -#endif -#ifdef TUNE_PRE_WINDOW - D->u.psk.pre_window = TUNE_PRE_WINDOW; -#endif +//#ifdef TUNE_PRE_BAUD +// D->u.psk.prefilter_baud = TUNE_PRE_BAUD; +//#endif + TUNE("TUNE_PRE_BAUD", D->u.psk.prefilter_baud, "prefilter_baud", "%.3f") -#ifdef TUNE_LPF_BAUD - D->u.psk.lpf_baud = TUNE_LPF_BAUD; -#endif -#ifdef TUNE_LP_WINDOW - D->u.psk.lp_window = TUNE_LP_WINDOW; -#endif +//#ifdef TUNE_PRE_WINDOW +// D->u.psk.pre_window = TUNE_PRE_WINDOW; +//#endif + TUNE("TUNE_PRE_WINDOW", D->u.psk.pre_window, "pre_window", "%d") + +//#ifdef TUNE_LPF_BAUD +// D->u.psk.lpf_baud = TUNE_LPF_BAUD; +//#endif +//#ifdef TUNE_LP_WINDOW +// D->u.psk.lp_window = TUNE_LP_WINDOW; +//#endif + TUNE("TUNE_LPF_BAUD", D->u.psk.lpf_baud, "lpf_baud", "%.3f") + TUNE("TUNE_LP_WINDOW", D->u.psk.lp_window, "lp_window", "%d") -#if defined(TUNE_PLL_SEARCHING) - D->pll_searching_inertia = TUNE_PLL_SEARCHING; -#endif -#if defined(TUNE_PLL_LOCKED) - D->pll_locked_inertia = TUNE_PLL_LOCKED; -#endif + + TUNE("TUNE_LP_FILTER_WIDTH_SYM", D->u.psk.lp_filter_width_sym, "lp_filter_width_sym", "%.3f") + + + + + +//#if defined(TUNE_PLL_SEARCHING) +// D->pll_searching_inertia = TUNE_PLL_SEARCHING; +//#endif +//#if defined(TUNE_PLL_LOCKED) +// D->pll_locked_inertia = TUNE_PLL_LOCKED; +//#endif + TUNE("TUNE_PLL_LOCKED", D->pll_locked_inertia, "pll_locked_inertia", "%.2f") + TUNE("TUNE_PLL_SEARCHING", D->pll_searching_inertia, "pll_searching_inertia", "%.2f") /* @@ -427,17 +458,24 @@ void demod_psk_init (enum modem_t modem_type, enum v26_e v26_alt, int samples_pe */ D->u.psk.pre_filter_taps = (int) round( D->u.psk.pre_filter_width_sym * (float)samples_per_sec / (float)correct_baud ); + +// JWL experiment 11/7 - Should delay line be based on audio frequency? + D->u.psk.delay_line_taps = (int) round( D->u.psk.delay_line_width_sym * (float)samples_per_sec / (float)correct_baud ); D->u.psk.delay_line_taps = (int) round( D->u.psk.delay_line_width_sym * (float)samples_per_sec / (float)correct_baud ); + + D->u.psk.lp_filter_taps = (int) round( D->u.psk.lp_filter_width_sym * (float)samples_per_sec / (float)correct_baud ); -#ifdef TUNE_PRE_FILTER_TAPS - D->u.psk.pre_filter_taps = TUNE_PRE_FILTER_TAPS; -#endif +//#ifdef TUNE_PRE_FILTER_TAPS +// D->u.psk.pre_filter_taps = TUNE_PRE_FILTER_TAPS; +//#endif + TUNE("TUNE_PRE_FILTER_TAPS", D->u.psk.pre_filter_taps, "pre_filter_taps", "%d") -#ifdef TUNE_lp_filter_taps - D->u.psk.lp_filter_taps = TUNE_lp_filter_taps; -#endif +//#ifdef TUNE_lp_filter_taps +// D->u.psk.lp_filter_taps = TUNE_lp_filter_taps; +//#endif + TUNE("TUNE_LP_FILTER_TAPS", D->u.psk.lp_filter_taps, "lp_filter_taps (FIR)", "%d") if (D->u.psk.pre_filter_taps > MAX_FILTER_SIZE) { @@ -665,7 +703,7 @@ void demod_psk_process_sample (int chan, int subchan, int sam, struct demodulato { int slice = 0; // Would it make sense to have more than one? - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); assert (subchan >= 0 && subchan < MAX_SUBCHANS); /* Scale to nice number for plotting during debug. */ @@ -800,16 +838,22 @@ static void nudge_pll (int chan, int subchan, int slice, int demod_bits, struct int gray = demod_bits; - hdlc_rec_bit (chan, subchan, slice, (gray >> 1) & 1, 0, bit_quality[1]); - hdlc_rec_bit (chan, subchan, slice, gray & 1, 0, bit_quality[0]); + hdlc_rec_bit_new (chan, subchan, slice, (gray >> 1) & 1, 0, bit_quality[1], + &(D->slicer[slice].pll_nudge_total), &(D->slicer[slice].pll_symbol_count)); + hdlc_rec_bit_new (chan, subchan, slice, gray & 1, 0, bit_quality[0], + &(D->slicer[slice].pll_nudge_total), &(D->slicer[slice].pll_symbol_count)); } else { int gray = demod_bits; - hdlc_rec_bit (chan, subchan, slice, (gray >> 2) & 1, 0, bit_quality[2]); - hdlc_rec_bit (chan, subchan, slice, (gray >> 1) & 1, 0, bit_quality[1]); - hdlc_rec_bit (chan, subchan, slice, gray & 1, 0, bit_quality[0]); + hdlc_rec_bit_new (chan, subchan, slice, (gray >> 2) & 1, 0, bit_quality[2], + &(D->slicer[slice].pll_nudge_total), &(D->slicer[slice].pll_symbol_count)); + hdlc_rec_bit_new (chan, subchan, slice, (gray >> 1) & 1, 0, bit_quality[1], + &(D->slicer[slice].pll_nudge_total), &(D->slicer[slice].pll_symbol_count)); + hdlc_rec_bit_new (chan, subchan, slice, gray & 1, 0, bit_quality[0], + &(D->slicer[slice].pll_nudge_total), &(D->slicer[slice].pll_symbol_count)); } + D->slicer[slice].pll_symbol_count++; pll_dcd_each_symbol2 (D, chan, subchan, slice); } @@ -826,12 +870,14 @@ static void nudge_pll (int chan, int subchan, int slice, int demod_bits, struct pll_dcd_signal_transition2 (D, slice, D->slicer[slice].data_clock_pll); + 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)floorf((float)(D->slicer[slice].data_clock_pll) * D->pll_locked_inertia); } else { D->slicer[slice].data_clock_pll = (int)floorf((float)(D->slicer[slice].data_clock_pll) * D->pll_searching_inertia); } + D->slicer[slice].pll_nudge_total += (int64_t)((signed int)(D->slicer[slice].data_clock_pll)) - (int64_t)before; } /* diff --git a/src/digipeater.c b/src/digipeater.c index fbe89370..fcf59568 100644 --- a/src/digipeater.c +++ b/src/digipeater.c @@ -91,7 +91,7 @@ static struct digi_config_s *save_digi_config_p; * Maintain count of packets digipeated for each combination of from/to channel. */ -static int digi_count[MAX_CHANS][MAX_CHANS]; +static int digi_count[MAX_TOTAL_CHANS][MAX_TOTAL_CHANS]; int digipeater_get_count (int from_chan, int to_chan) { return (digi_count[from_chan][to_chan]); @@ -154,7 +154,7 @@ void digipeater (int from_chan, packet_t pp) // Network TNC is OK for UI frames where we don't care about timing. - if ( from_chan < 0 || from_chan >= MAX_CHANS || + if ( from_chan < 0 || from_chan >= MAX_TOTAL_CHANS || (save_audio_config_p->chan_medium[from_chan] != MEDIUM_RADIO && save_audio_config_p->chan_medium[from_chan] != MEDIUM_NETTNC)) { text_color_set(DW_COLOR_ERROR); @@ -195,14 +195,14 @@ void digipeater (int from_chan, packet_t pp) * */ - for (to_chan=0; to_chanenabled[from_chan][to_chan]) { if (to_chan == from_chan) { packet_t result; - result = digipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall, - save_audio_config_p->achan[to_chan].mycall, - &save_digi_config_p->alias[from_chan][to_chan], &save_digi_config_p->wide[from_chan][to_chan], + result = digipeat_match (from_chan, pp, save_audio_config_p->mycall[from_chan], + save_audio_config_p->mycall[to_chan], + &save_digi_config_p->alias[from_chan][to_chan], &save_digi_config_p->wide[from_chan][to_chan], to_chan, save_digi_config_p->preempt[from_chan][to_chan], save_digi_config_p->atgp[from_chan][to_chan], save_digi_config_p->filter_str[from_chan][to_chan]); @@ -222,14 +222,14 @@ void digipeater (int from_chan, packet_t pp) * These are lower priority */ - for (to_chan=0; to_chanenabled[from_chan][to_chan]) { if (to_chan != from_chan) { packet_t result; - result = digipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall, - save_audio_config_p->achan[to_chan].mycall, - &save_digi_config_p->alias[from_chan][to_chan], &save_digi_config_p->wide[from_chan][to_chan], + result = digipeat_match (from_chan, pp, save_audio_config_p->mycall[from_chan], + save_audio_config_p->mycall[to_chan], + &save_digi_config_p->alias[from_chan][to_chan], &save_digi_config_p->wide[from_chan][to_chan], to_chan, save_digi_config_p->preempt[from_chan][to_chan], save_digi_config_p->atgp[from_chan][to_chan], save_digi_config_p->filter_str[from_chan][to_chan]); @@ -641,9 +641,9 @@ void digi_regen (int from_chan, packet_t pp) // dw_printf ("digi_regen()\n"); - assert (from_chan >= 0 && from_chan < MAX_CHANS); + assert (from_chan >= 0 && from_chan < MAX_TOTAL_CHANS); - for (to_chan=0; to_chanregen[from_chan][to_chan]) { result = ax25_dup (pp); if (result != NULL) { diff --git a/src/digipeater.h b/src/digipeater.h index 5c849769..46d955da 100644 --- a/src/digipeater.h +++ b/src/digipeater.h @@ -4,7 +4,7 @@ #include "regex.h" -#include "direwolf.h" /* for MAX_CHANS */ +#include "direwolf.h" /* for MAX_TOTAL_CHANS */ #include "ax25_pad.h" /* for packet_t */ #include "audio.h" /* for radio channel properties */ @@ -29,25 +29,25 @@ struct digi_config_s { * Rules for each of the [from_chan][to_chan] combinations. */ - regex_t alias[MAX_CHANS][MAX_CHANS]; + regex_t alias[MAX_TOTAL_CHANS][MAX_TOTAL_CHANS]; - regex_t wide[MAX_CHANS][MAX_CHANS]; + regex_t wide[MAX_TOTAL_CHANS][MAX_TOTAL_CHANS]; - int enabled[MAX_CHANS][MAX_CHANS]; + int enabled[MAX_TOTAL_CHANS][MAX_TOTAL_CHANS]; - enum preempt_e { PREEMPT_OFF, PREEMPT_DROP, PREEMPT_MARK, PREEMPT_TRACE } preempt[MAX_CHANS][MAX_CHANS]; + enum preempt_e { PREEMPT_OFF, PREEMPT_DROP, PREEMPT_MARK, PREEMPT_TRACE } preempt[MAX_TOTAL_CHANS][MAX_TOTAL_CHANS]; // ATGP is an ugly hack for the specific need of ATGP which needs more that 8 digipeaters. // DO NOT put this in the User Guide. On a need to know basis. - char atgp[MAX_CHANS][MAX_CHANS][AX25_MAX_ADDR_LEN]; + char atgp[MAX_TOTAL_CHANS][MAX_TOTAL_CHANS][AX25_MAX_ADDR_LEN]; - char *filter_str[MAX_CHANS+1][MAX_CHANS+1]; + char *filter_str[MAX_TOTAL_CHANS+1][MAX_TOTAL_CHANS+1]; // NULL or optional Packet Filter strings such as "t/m". // Notice the size of arrays is one larger than normal. // That extra position is for the IGate. - int regen[MAX_CHANS][MAX_CHANS]; // Regenerate packet. + int regen[MAX_TOTAL_CHANS][MAX_TOTAL_CHANS]; // Regenerate packet. // Sort of like digipeating but passed along unchanged. }; diff --git a/src/direwolf.c b/src/direwolf.c index 2dfa58d3..2bffcc21 100644 --- a/src/direwolf.c +++ b/src/direwolf.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019, 2020, 2021, 2023 John Langner, WB2OSZ +// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019, 2020, 2021, 2023, 2024 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 @@ -130,6 +130,7 @@ #include "dns_sd_dw.h" #include "dlq.h" // for fec_type_t definition. #include "deviceid.h" +#include "nettnc.h" //static int idx_decoded = 0; @@ -228,6 +229,7 @@ int main (int argc, char *argv[]) #endif int d_x_opt = 1; /* "-d x" option for FX.25. Default minimal. Repeat for more detail. -qx to silence. */ int d_2_opt = 0; /* "-d 2" option for IL2P. Default minimal. Repeat for more detail. */ + int d_c_opt = 0; /* "-d c" option for connected mode data link state machine. */ int aprstt_debug = 0; /* "-d d" option for APRStt (think Dtmf) debug. */ @@ -303,7 +305,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 7\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); - dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "A", __DATE__); + dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "D", __DATE__); //dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION); @@ -390,6 +392,7 @@ int main (int argc, char *argv[]) text_color_set(DW_COLOR_ERROR); for (int n=0; n<15; n++) { dw_printf ("\n"); + dw_printf ("Why are you running this as root user?.\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); @@ -558,7 +561,7 @@ int main (int argc, char *argv[]) break; } } - if (x_opt_chan < 0 || x_opt_chan >= MAX_CHANS) { + if (x_opt_chan < 0 || x_opt_chan >= MAX_RADIO_CHANS) { text_color_set(DW_COLOR_ERROR); dw_printf ("Invalid channel %d for -x. \n", x_opt_chan); text_color_set(DW_COLOR_INFO); @@ -637,6 +640,7 @@ int main (int argc, char *argv[]) #if USE_HAMLIB case 'h': d_h_opt++; break; // Hamlib verbose level. #endif + case 'c': d_c_opt++; break; // Connected mode data link state machine case 'x': d_x_opt++; break; // FX.25 case '2': d_2_opt++; break; // IL2P case 'd': aprstt_debug++; break; // APRStt (mnemonic Dtmf) @@ -1004,6 +1008,13 @@ int main (int argc, char *argv[]) fx25_init (d_x_opt); il2p_init (d_2_opt); +/* + * New in 1.8 - Allow a channel to be mapped to a network TNC rather than + * an internal modem and radio. + * I put it here so channel properties would come out in right order. + */ + nettnc_init (&audio_config); + /* * Initialize the touch tone decoder & APRStt gateway. */ @@ -1108,7 +1119,7 @@ int main (int argc, char *argv[]) igate_init (&audio_config, &igate_config, &digi_config, d_i_opt); cdigipeater_init (&audio_config, &cdigi_config); pfilter_init (&igate_config, d_f_opt); - ax25_link_init (&misc_config); + ax25_link_init (&misc_config, d_c_opt); /* * Provide the AGW & KISS socket interfaces for use by a client application. @@ -1167,7 +1178,10 @@ int main (int argc, char *argv[]) * * Inputs: chan - Audio channel number, 0 or 1. * subchan - Which modem caught it. - * Special case -1 for DTMF decoder. + * Special cases: + * -1 for DTMF decoder. + * -2 for channel mapped to APRS-IS. + * -3 for channel mapped to network TNC. * slice - Slicer which caught it. * pp - Packet handle. * alevel - Audio level, range of 0 - 100. @@ -1198,7 +1212,7 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev // Can indicate FX.25/IL2P or fix_bits. assert (chan >= 0 && chan < MAX_TOTAL_CHANS); // TOTAL for virtual channels - assert (subchan >= -2 && subchan < MAX_SUBCHANS); + assert (subchan >= -3 && subchan < MAX_SUBCHANS); assert (slice >= 0 && slice < MAX_SLICERS); assert (pp != NULL); // 1.1J+ @@ -1279,7 +1293,13 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev ax25_get_addr_with_ssid(pp, h-1, probably_really); - dw_printf ("%s (probably %s) audio level = %s %s %s\n", heard, probably_really, alevel_text, display_retries, spectrum); + // audio level applies only for internal modem channels. + if (subchan >=0) { + dw_printf ("%s (probably %s) audio level = %s %s %s\n", heard, probably_really, alevel_text, display_retries, spectrum); + } + else { + dw_printf ("%s (probably %s)\n", heard, probably_really); + } } else if (strcmp(heard, "DTMF") == 0) { @@ -1288,7 +1308,13 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev } else { - dw_printf ("%s audio level = %s %s %s\n", heard, alevel_text, display_retries, spectrum); + // audio level applies only for internal modem channels. + if (subchan >= 0) { + dw_printf ("%s audio level = %s %s %s\n", heard, alevel_text, display_retries, spectrum); + } + else { + dw_printf ("%s\n", heard); + } } } } @@ -1305,7 +1331,7 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev dw_printf ("Audio input level is too high. Reduce so most stations are around 50.\n"); } // FIXME: rather than checking for ichannel, how about checking medium==radio - else if (alevel.rec < 5 && chan != audio_config.igate_vchannel) { + else if (alevel.rec < 5 && chan != audio_config.igate_vchannel && subchan != -3) { text_color_set(DW_COLOR_ERROR); dw_printf ("Audio input level is too low. Increase so most stations are around 50.\n"); @@ -1330,14 +1356,18 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev strlcpy (ts, "", sizeof(ts)); } - if (subchan == -1) { + if (subchan == -1) { // dtmf text_color_set(DW_COLOR_REC); dw_printf ("[%d.dtmf%s] ", chan, ts); } - else if (subchan == -2) { + else if (subchan == -2) { // APRS-IS text_color_set(DW_COLOR_REC); dw_printf ("[%d.is%s] ", chan, ts); } + else if (subchan == -3) { // nettnc + text_color_set(DW_COLOR_REC); + dw_printf ("[%d%s] ", chan, ts); + } else { if (ax25_is_aprs(pp)) { text_color_set(DW_COLOR_REC); @@ -1498,7 +1528,7 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev 0, 0, 0, A.g_comment, // freq, tone, offset ais_obj_info, sizeof(ais_obj_info)); - snprintf (ais_obj_packet, sizeof(ais_obj_packet), "%s>%s%1d%1d:%s", A.g_src, APP_TOCALL, MAJOR_VERSION, MINOR_VERSION, ais_obj_info); + snprintf (ais_obj_packet, sizeof(ais_obj_packet), "%s>%s%1d%1d,NOGATE:%s", A.g_src, APP_TOCALL, MAJOR_VERSION, MINOR_VERSION, ais_obj_info); dw_printf ("[%d.AIS] %s\n", chan, ais_obj_packet); @@ -1614,9 +1644,10 @@ 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 || fec_type == fec_type_fx25 || fec_type == fec_type_il2p) { - - cdigipeater (chan, pp); + if (chan < MAX_RADIO_CHANS) { + if (retries == RETRY_NONE || fec_type == fec_type_fx25 || fec_type == fec_type_il2p) { + cdigipeater (chan, pp); + } } } @@ -1708,6 +1739,7 @@ static void usage (char **argv) #if USE_HAMLIB dw_printf (" h h = hamlib increase verbose level.\n"); #endif + dw_printf (" c c = Connected mode data link state machine.\n"); dw_printf (" x x = FX.25 increase verbose level.\n"); dw_printf (" 2 2 = IL2P.\n"); dw_printf (" d d = APRStt (DTMF to APRS object translation).\n"); diff --git a/src/direwolf.h b/src/direwolf.h index 69b09529..a6db3221 100644 --- a/src/direwolf.h +++ b/src/direwolf.h @@ -56,15 +56,10 @@ * * ADevice 0: channel 0 * ADevice 1: left = 2, right = 3 - * - * TODO1.2: Look for any places that have - * for (ch=0; ch= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); /* Allocate a new queue item. */ @@ -556,7 +556,7 @@ void dlq_disconnect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int dw_printf ("dlq_disconnect_request (...)\n"); #endif - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); /* Allocate a new queue item. */ @@ -619,7 +619,7 @@ void dlq_outstanding_frames_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LE dw_printf ("dlq_outstanding_frames_request (...)\n"); #endif - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); /* Allocate a new queue item. */ @@ -691,7 +691,7 @@ void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int n dw_printf ("dlq_xmit_data_request (...)\n"); #endif - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); /* Allocate a new queue item. */ @@ -758,7 +758,7 @@ void dlq_register_callsign (char *addr, int chan, int client) dw_printf ("dlq_register_callsign (%s, chan=%d, client=%d)\n", addr, chan, client); #endif - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); /* Allocate a new queue item. */ @@ -793,7 +793,7 @@ void dlq_unregister_callsign (char *addr, int chan, int client) dw_printf ("dlq_unregister_callsign (%s, chan=%d, client=%d)\n", addr, chan, client); #endif - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); /* Allocate a new queue item. */ diff --git a/src/dtmf.c b/src/dtmf.c index 953b0f70..447366f9 100644 --- a/src/dtmf.c +++ b/src/dtmf.c @@ -80,7 +80,7 @@ static struct dd_s { /* Separate for each audio channel. */ char prev_debounced; int timeout; -} dd[MAX_CHANS]; +} dd[MAX_RADIO_CHANS]; static int s_amplitude = 100; // range of 0 .. 100 @@ -129,7 +129,7 @@ void dtmf_init (struct audio_s *p_audio_config, int amp) * Larger = narrower bandwidth, slower response. */ - for (c=0; cn = 0; for (j=0; j= MAX_RADIO_CHANS) { + return ('$'); + } + D = &(dd[c]); for (i=0; i 999999) alt_ft = 999999; - snprintf (salt, sizeof(salt), "/A=%06d", alt_ft); + snprintf (salt, sizeof(salt), "/A=%06d", alt_ft); // /A=123456 ot /A=-12345 strlcat (presult, salt, result_size); result_len += strlen(salt); } diff --git a/src/fsk_demod_state.h b/src/fsk_demod_state.h index c9b26c23..e094bb41 100644 --- a/src/fsk_demod_state.h +++ b/src/fsk_demod_state.h @@ -469,7 +469,7 @@ struct demodulator_state_s * * Inputs: D Pointer to demodulator state. * - * chan Radio channel: 0 to MAX_CHANS - 1 + * chan Radio channel: 0 to MAX_RADIO_CHANS - 1 * * subchan Which of multiple demodulators: 0 to MAX_SUBCHANS - 1 * diff --git a/src/fx25_rec.c b/src/fx25_rec.c index 9cb5c4d9..8e6d4222 100644 --- a/src/fx25_rec.c +++ b/src/fx25_rec.c @@ -59,7 +59,7 @@ struct fx_context_s { unsigned char block[FX25_BLOCK_SIZE+1]; }; -static struct fx_context_s *fx_context[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS]; +static struct fx_context_s *fx_context[MAX_RADIO_CHANS][MAX_SUBCHANS][MAX_SLICERS]; static void process_rs_block (int chan, int subchan, int slice, struct fx_context_s *F); @@ -157,7 +157,7 @@ void fx25_rec_bit (int chan, int subchan, int slice, int dbit) struct fx_context_s *F = fx_context[chan][subchan][slice]; if (F == NULL) { - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); assert (subchan >= 0 && subchan < MAX_SUBCHANS); assert (slice >= 0 && slice < MAX_SLICERS); F = fx_context[chan][subchan][slice] = (struct fx_context_s *)malloc(sizeof (struct fx_context_s)); @@ -256,7 +256,7 @@ void fx25_rec_bit (int chan, int subchan, int slice, int dbit) int fx25_rec_busy (int chan) { - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); // This could be a little faster if we knew number of // subchannels and slicers but it is probably insignificant. diff --git a/src/fx25_send.c b/src/fx25_send.c index 7435be9f..0841a3fd 100644 --- a/src/fx25_send.c +++ b/src/fx25_send.c @@ -41,7 +41,7 @@ static void send_bit (int chan, int b); static int stuff_it (unsigned char *in, int ilen, unsigned char *out, int osize); -static int number_of_bits_sent[MAX_CHANS]; // Count number of bits sent by "fx25_send_frame" or "???" +static int number_of_bits_sent[MAX_RADIO_CHANS]; // Count number of bits sent by "fx25_send_frame" or "???" #if FXTEST @@ -249,7 +249,7 @@ static void send_bytes (int chan, unsigned char *b, int count) */ static void send_bit (int chan, int b) { - static int output[MAX_CHANS]; + static int output[MAX_RADIO_CHANS]; if (b == 0) { output[chan] = ! output[chan]; diff --git a/src/gen_packets.c b/src/gen_packets.c index 57b2741c..e98e774f 100644 --- a/src/gen_packets.c +++ b/src/gen_packets.c @@ -242,7 +242,7 @@ int main(int argc, char **argv) modem.adev[0].samples_per_sec = DEFAULT_SAMPLES_PER_SEC; /* -r option */ modem.adev[0].bits_per_sample = DEFAULT_BITS_PER_SAMPLE; /* -8 for 8 instead of 16 bits */ - for (chan = 0; chan < MAX_CHANS; chan++) { + for (chan = 0; chan < MAX_RADIO_CHANS; chan++) { modem.achan[chan].modem_type = MODEM_AFSK; /* change with -g */ modem.achan[chan].mark_freq = DEFAULT_MARK_FREQ; /* -m option */ modem.achan[chan].space_freq = DEFAULT_SPACE_FREQ; /* -s option */ diff --git a/src/gen_tone.c b/src/gen_tone.c index 6a816556..400c2920 100644 --- a/src/gen_tone.c +++ b/src/gen_tone.c @@ -63,14 +63,14 @@ static struct audio_s *save_audio_config_p = NULL; #define TICKS_PER_CYCLE ( 256.0 * 256.0 * 256.0 * 256.0 ) -static int ticks_per_sample[MAX_CHANS]; /* Same for both channels of same soundcard */ +static int ticks_per_sample[MAX_RADIO_CHANS]; /* Same for both channels of same soundcard */ /* because they have same sample rate */ /* but less confusing to have for each channel. */ -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 int ticks_per_bit[MAX_RADIO_CHANS]; +static int f1_change_per_sample[MAX_RADIO_CHANS]; +static int f2_change_per_sample[MAX_RADIO_CHANS]; +static float samples_per_symbol[MAX_RADIO_CHANS]; static short sine_table[256]; @@ -78,7 +78,7 @@ static short sine_table[256]; /* Accumulators. */ -static unsigned int tone_phase[MAX_CHANS]; // Phase accumulator for tone generation. +static unsigned int tone_phase[MAX_RADIO_CHANS]; // Phase accumulator for tone generation. // Upper bits are used as index into sine table. #define PHASE_SHIFT_180 ( 128u << 24 ) @@ -86,11 +86,11 @@ static unsigned int tone_phase[MAX_CHANS]; // Phase accumulator for tone generat #define PHASE_SHIFT_45 ( 32u << 24 ) -static int bit_len_acc[MAX_CHANS]; // To accumulate fractional samples per bit. +static int bit_len_acc[MAX_RADIO_CHANS]; // To accumulate fractional samples per bit. -static int lfsr[MAX_CHANS]; // Shift register for scrambler. +static int lfsr[MAX_RADIO_CHANS]; // Shift register for scrambler. -static int bit_count[MAX_CHANS]; // Counter incremented for each bit transmitted +static int bit_count[MAX_RADIO_CHANS]; // Counter incremented for each bit transmitted // on the channel. This is only used for QPSK. // The LSB determines if we save the bit until // next time, or send this one with the previously saved. @@ -101,10 +101,10 @@ static int bit_count[MAX_CHANS]; // Counter incremented for each bit transmitted // For 8PSK, it has a different meaning. It is the // number of bits in 'save_bit' so we can accumulate // three for each symbol. -static int save_bit[MAX_CHANS]; +static int save_bit[MAX_RADIO_CHANS]; -static int prev_dat[MAX_CHANS]; // Previous data bit. Used for G3RUH style. +static int prev_dat[MAX_RADIO_CHANS]; // Previous data bit. Used for G3RUH style. @@ -163,7 +163,7 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets) amp16bit = (int)((32767 * amp) / 100); - for (chan = 0; chan < MAX_CHANS; chan++) { + for (chan = 0; chan < MAX_RADIO_CHANS; chan++) { if (audio_config_p->chan_medium[chan] == MEDIUM_RADIO) { @@ -352,8 +352,8 @@ 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. +static int xmit_octant[MAX_RADIO_CHANS]; // absolute phase in 45 degree units. +static int xmit_prev_octant[MAX_RADIO_CHANS]; // from previous symbol. // For PSK, we generate the final signal by combining fixed frequency cosine and // sine by the following weights. diff --git a/src/hdlc_rec.c b/src/hdlc_rec.c index d87a1b50..cfae77a6 100644 --- a/src/hdlc_rec.c +++ b/src/hdlc_rec.c @@ -114,11 +114,11 @@ struct hdlc_state_s { int eas_fields_after_plus; /* Number of "-" characters after the "+". */ }; -static struct hdlc_state_s hdlc_state[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS]; +static struct hdlc_state_s hdlc_state[MAX_RADIO_CHANS][MAX_SUBCHANS][MAX_SLICERS]; -static int num_subchan[MAX_CHANS]; //TODO1.2 use ptr rather than copy. +static int num_subchan[MAX_RADIO_CHANS]; //TODO1.2 use ptr rather than copy. -static int composite_dcd[MAX_CHANS][MAX_SUBCHANS+1]; +static int composite_dcd[MAX_RADIO_CHANS][MAX_SUBCHANS+1]; /*********************************************************************************** @@ -149,7 +149,7 @@ void hdlc_rec_init (struct audio_s *pa) memset (composite_dcd, 0, sizeof(composite_dcd)); - for (ch = 0; ch < MAX_CHANS; ch++) + for (ch = 0; ch < MAX_RADIO_CHANS; ch++) { if (pa->chan_medium[ch] == MEDIUM_RADIO) { @@ -429,17 +429,24 @@ a good modem here and providing a result when it is received. ***********************************************************************************/ void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled, int not_used_remove) +{ + static int64_t dummyll = 0; + static int dummy = 0; + hdlc_rec_bit_new (chan, subchan, slice, raw, is_scrambled, not_used_remove, + &dummyll, &dummy); +} + +void hdlc_rec_bit_new (int chan, int subchan, int slice, int raw, int is_scrambled, int not_used_remove, + int64_t *pll_nudge_total, int *pll_symbol_count) { int dbit; /* Data bit after undoing NRZI. */ /* Should be only 0 or 1. */ - struct hdlc_state_s *H; assert (was_init == 1); - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); assert (subchan >= 0 && subchan < MAX_SUBCHANS); - assert (slice >= 0 && slice < MAX_SLICERS); // -e option can be used to artificially introduce the desired @@ -467,7 +474,7 @@ void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled, /* * Different state information for each channel / subchannel / slice. */ - H = &hdlc_state[chan][subchan][slice]; + struct hdlc_state_s *H = &hdlc_state[chan][subchan][slice]; /* @@ -589,16 +596,44 @@ void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled, dw_printf ("\nfound flag, channel %d.%d, %d bits in frame\n", chan, subchan, rrbb_get_len(H->rrbb) - 1); #endif if (rrbb_get_len(H->rrbb) >= MIN_FRAME_LEN * 8) { - + +//JWL - end of frame + + float speed_error; // in percentage. + if (*pll_symbol_count > 0) { // avoid divde by 0. + + // TODO: + // Fudged to get +-2.0 with gen_packets -b 1224 & 1176. + // Also initialized the symbol counter to -1. + + speed_error = (float)((double)(*pll_nudge_total) * 100. / (256. * 256. * 256. * 256.) / (double)(*pll_symbol_count) + 0.02); + + text_color_set(DW_COLOR_DEBUG); + +// std dw_printf ("DEBUG: total %lld, count %d\n", *pll_nudge_total, *pll_symbol_count); +// mingw +// dw_printf ("DEBUG: total %I64d, count %d\n", *pll_nudge_total, *pll_symbol_count); +// dw_printf ("DEBUG: speed error %+0.2f%% -> %+0.1f%% \n", speed_error, speed_error); + } + else { + speed_error = 0; + } + rrbb_set_speed_error (H->rrbb, speed_error); + alevel_t alevel = demod_get_audio_level (chan, subchan); rrbb_set_audio_level (H->rrbb, alevel); hdlc_rec2_block (H->rrbb); /* Now owned by someone else who will free it. */ + H->rrbb = NULL; H->rrbb = rrbb_new (chan, subchan, slice, is_scrambled, H->lfsr, H->prev_descram); /* Allocate a new one. */ } else { + +//JWL - start of frame + *pll_nudge_total = 0; + *pll_symbol_count = -1; // comes out better than using 0. rrbb_clear (H->rrbb, is_scrambled, H->lfsr, H->prev_descram); } @@ -730,7 +765,7 @@ void dcd_change (int chan, int subchan, int slice, int state) { int old, new; - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); assert (subchan >= 0 && subchan <= MAX_SUBCHANS); assert (slice >= 0 && slice < MAX_SLICERS); assert (state == 0 || state == 1); @@ -791,7 +826,7 @@ int hdlc_rec_data_detect_any (int chan) { int sc; - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); for (sc = 0; sc < num_subchan[chan]; sc++) { if (composite_dcd[chan][sc] != 0) diff --git a/src/hdlc_rec.h b/src/hdlc_rec.h index 69b60a92..21cbf6c8 100644 --- a/src/hdlc_rec.h +++ b/src/hdlc_rec.h @@ -1,12 +1,22 @@ +/* hdlc_rec.h */ + + + + +#include // int64_t #include "audio.h" void hdlc_rec_init (struct audio_s *pa); +// TODO: change all to _new. void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled, int descram_state); +void hdlc_rec_bit_new (int chan, int subchan, int slice, int raw, int is_scrambled, int descram_state, + int64_t *pll_nudge_total, int *pll_nudge_count); + /* Provided elsewhere to process a complete frame. */ //void process_rec_frame (int chan, unsigned char *fbuf, int flen, int level); diff --git a/src/hdlc_rec2.c b/src/hdlc_rec2.c index b817018f..ebaac6c0 100644 --- a/src/hdlc_rec2.c +++ b/src/hdlc_rec2.c @@ -216,6 +216,8 @@ void hdlc_rec2_init (struct audio_s *p_audio_config) * Purpose: Extract HDLC frame from a stream of bits. * * Inputs: block - Handle for bit array. + * This will be deallocated so the caller + * must not hold on to the address. * * Description: The other (original) hdlc decoder took one bit at a time * right out of the demodulator. @@ -287,12 +289,10 @@ void hdlc_rec2_block (rrbb_t block) /* Let thru even with bad CRC. Of course, it still */ /* needs to be a minimum number of whole octets. */ ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 1); - rrbb_delete (block); - } - else { - rrbb_delete (block); } + rrbb_delete (block); + } /* end hdlc_rec2_block */ @@ -438,7 +438,7 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int slice, retry_cfg.u_bits.sep.bit_idx_c = -1; #ifdef DEBUG_LATER - tstart = dtime_now(); + tstart = dtime_monotonic(); dw_printf ("*** Try flipping TWO SEPARATED BITS %d bits\n", len); #endif len = rrbb_get_len(block); diff --git a/src/hdlc_send.c b/src/hdlc_send.c index 8a1cdc6d..5b2008d3 100644 --- a/src/hdlc_send.c +++ b/src/hdlc_send.c @@ -39,7 +39,7 @@ static void send_bit_nrzi (int, int); -static int number_of_bits_sent[MAX_CHANS]; // Count number of bits sent by "hdlc_send_frame" or "hdlc_send_flags" +static int number_of_bits_sent[MAX_RADIO_CHANS]; // Count number of bits sent by "hdlc_send_frame" or "hdlc_send_flags" @@ -240,7 +240,7 @@ static void send_byte_msb_first (int chan, int x, int polarity) // Data (non flags) use bit stuffing. -static int stuff[MAX_CHANS]; // Count number of "1" bits to keep track of when we +static int stuff[MAX_RADIO_CHANS]; // Count number of "1" bits to keep track of when we // need to break up a long run by "bit stuffing." // Needs to be array because we could be transmitting // on multiple channels at the same time. @@ -284,7 +284,7 @@ static void send_data_nrzi (int chan, int x) static void send_bit_nrzi (int chan, int b) { - static int output[MAX_CHANS]; + static int output[MAX_RADIO_CHANS]; if (b == 0) { output[chan] = ! output[chan]; diff --git a/src/igate.c b/src/igate.c index 7f84228a..1e5d56ed 100644 --- a/src/igate.c +++ b/src/igate.c @@ -216,8 +216,8 @@ int main (int argc, char *argv[]) memset (&audio_config, 0, sizeof(audio_config)); audio_config.adev[0].num_channels = 2; - strlcpy (audio_config.achan[0].mycall, "WB2OSZ-1", sizeof(audio_config.achan[0].mycall)); - strlcpy (audio_config.achan[1].mycall, "WB2OSZ-2", sizeof(audio_config.achan[0].mycall)); + strlcpy (audio_config.mycall[0], "WB2OSZ-1", sizeof(audio_config.achan[0].mycall)); + strlcpy (audio_config.mycall[1], "WB2OSZ-2", sizeof(audio_config.achan[0].mycall)); memset (&igate_config, 0, sizeof(igate_config)); @@ -909,10 +909,10 @@ void igate_send_rec_packet (int chan, packet_t recv_pp) // Beacon will be channel -1. // Client app to ICHANNEL is outside of radio channel range. - if (chan >= 0 && chan < MAX_CHANS && // in radio channel range - save_digi_config_p->filter_str[chan][MAX_CHANS] != NULL) { + if (chan >= 0 && chan < MAX_TOTAL_CHANS && // in radio channel range + save_digi_config_p->filter_str[chan][MAX_TOTAL_CHANS] != NULL) { - if (pfilter(chan, MAX_CHANS, save_digi_config_p->filter_str[chan][MAX_CHANS], recv_pp, 1) != 1) { + if (pfilter(chan, MAX_TOTAL_CHANS, save_digi_config_p->filter_str[chan][MAX_TOTAL_CHANS], recv_pp, 1) != 1) { // Is this useful troubleshooting information or just distracting noise? // Originally this was always printed but there was a request to add a "quiet" option to suppress this. @@ -920,7 +920,7 @@ void igate_send_rec_packet (int chan, packet_t recv_pp) if (s_debug >= 1) { text_color_set(DW_COLOR_INFO); - dw_printf ("Packet from channel %d to IGate was rejected by filter: %s\n", chan, save_digi_config_p->filter_str[chan][MAX_CHANS]); + dw_printf ("Packet from channel %d to IGate was rejected by filter: %s\n", chan, save_digi_config_p->filter_str[chan][MAX_TOTAL_CHANS]); } return; } @@ -1141,7 +1141,7 @@ static void send_packet_to_server (packet_t pp, int chan) strlcat (msg, ",qAO,", sizeof(msg)); // new for version 1.4. } - strlcat (msg, save_audio_config_p->achan[chan].mycall, sizeof(msg)); + strlcat (msg, save_audio_config_p->mycall[chan], sizeof(msg)); strlcat (msg, ":", sizeof(msg)); @@ -1781,7 +1781,7 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan) { int n; - assert (to_chan >= 0 && to_chan < MAX_CHANS); + assert (to_chan >= 0 && to_chan < MAX_TOTAL_CHANS); /* * Try to parse it into a packet object; we need this for the packet filtering. @@ -1856,7 +1856,7 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan) * filtering by stations along the way or the q construct. */ - assert (to_chan >= 0 && to_chan < MAX_CHANS); + assert (to_chan >= 0 && to_chan < MAX_TOTAL_CHANS); /* @@ -1906,9 +1906,9 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan) if ( ! msp_special_case) { - if (save_digi_config_p->filter_str[MAX_CHANS][to_chan] != NULL) { + if (save_digi_config_p->filter_str[MAX_TOTAL_CHANS][to_chan] != NULL) { - if (pfilter(MAX_CHANS, to_chan, save_digi_config_p->filter_str[MAX_CHANS][to_chan], pp3, 1) != 1) { + if (pfilter(MAX_TOTAL_CHANS, to_chan, save_digi_config_p->filter_str[MAX_TOTAL_CHANS][to_chan], pp3, 1) != 1) { // 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. @@ -1965,7 +1965,7 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan) 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); + src, dest, save_audio_config_p->mycall[to_chan], pinfo); #if DEBUGx @@ -1991,7 +1991,7 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan) if (ig_to_tx_allow (pp3, to_chan)) { char radio [2400]; snprintf (radio, sizeof(radio), "%s>%s%d%d%s:}%s", - save_audio_config_p->achan[to_chan].mycall, + save_audio_config_p->mycall[to_chan], APP_TOCALL, MAJOR_VERSION, MINOR_VERSION, save_igate_config_p->tx_via, payload); diff --git a/src/il2p_rec.c b/src/il2p_rec.c index 5ad457a0..a1e27263 100644 --- a/src/il2p_rec.c +++ b/src/il2p_rec.c @@ -69,7 +69,7 @@ struct il2p_context_s { int corrected; // Number of symbols corrected by RS FEC. }; -static struct il2p_context_s *il2p_context[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS]; +static struct il2p_context_s *il2p_context[MAX_RADIO_CHANS][MAX_SUBCHANS][MAX_SLICERS]; @@ -101,7 +101,7 @@ void il2p_rec_bit (int chan, int subchan, int slice, int dbit) struct il2p_context_s *F = il2p_context[chan][subchan][slice]; if (F == NULL) { - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); assert (subchan >= 0 && subchan < MAX_SUBCHANS); assert (slice >= 0 && slice < MAX_SLICERS); F = il2p_context[chan][subchan][slice] = (struct il2p_context_s *)malloc(sizeof (struct il2p_context_s)); @@ -251,12 +251,11 @@ void il2p_rec_bit (int chan, int subchan, int slice, int dbit) if (pp != NULL) { alevel_t alevel = demod_get_audio_level (chan, subchan); retry_t retries = F->corrected; - int is_fx25 = 1; // FIXME: distinguish fx.25 and IL2P. - // Currently this just means that a FEC mode was used. + fec_type_t fec_type = fec_type_il2p; // TODO: Could we put last 3 arguments in packet object rather than passing around separately? - 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); } } // end block for local variables. diff --git a/src/il2p_send.c b/src/il2p_send.c index 3c4554e0..28948766 100644 --- a/src/il2p_send.c +++ b/src/il2p_send.c @@ -30,7 +30,7 @@ #include "gen_tone.h" -static int number_of_bits_sent[MAX_CHANS]; // Count number of bits sent by "il2p_send_frame" +static int number_of_bits_sent[MAX_RADIO_CHANS]; // Count number of bits sent by "il2p_send_frame" static void send_bytes (int chan, unsigned char *b, int count, int polarity); static void send_bit (int chan, int b, int polarity); diff --git a/src/kiss_frame.c b/src/kiss_frame.c index aa581dd2..65a09422 100644 --- a/src/kiss_frame.c +++ b/src/kiss_frame.c @@ -612,7 +612,7 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, struct /* Verify that the radio channel number is valid. */ /* Any sort of medium should be OK here. */ - if ((chan < 0 || chan >= MAX_CHANS || save_audio_config_p->chan_medium[chan] == MEDIUM_NONE) + if ((chan < 0 || chan >= MAX_TOTAL_CHANS || save_audio_config_p->chan_medium[chan] == MEDIUM_NONE) && save_audio_config_p->chan_medium[chan] != MEDIUM_IGATE) { text_color_set(DW_COLOR_ERROR); dw_printf ("Invalid transmit channel %d from KISS client app.\n", chan); @@ -663,10 +663,11 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, struct } text_color_set(DW_COLOR_INFO); dw_printf ("KISS protocol set TXDELAY = %d (*10mS units = %d mS), chan %d\n", kiss_msg[1], kiss_msg[1] * 10, chan); - if (kiss_msg[1] < 4 || kiss_msg[1] > 100) { + if (kiss_msg[1] < 10 || kiss_msg[1] >= 100) { text_color_set(DW_COLOR_ERROR); dw_printf ("Are you sure you want such an extreme value for TXDELAY?\n"); - dw_printf ("See \"Radio Channel - Transmit Timing\" section of User Guide for explanation.\n"); + dw_printf ("Read the Dire Wolf User Guide, \"Radio Channel - Transmit Timing\"\n"); + dw_printf ("section, to understand what this means.\n"); } xmit_set_txdelay (chan, kiss_msg[1]); break; @@ -683,7 +684,8 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, struct if (kiss_msg[1] < 5 || kiss_msg[1] > 250) { text_color_set(DW_COLOR_ERROR); dw_printf ("Are you sure you want such an extreme value for PERSIST?\n"); - dw_printf ("See \"Radio Channel - Transmit Timing\" section of User Guide for explanation.\n"); + dw_printf ("Read the Dire Wolf User Guide, \"Radio Channel - Transmit Timing\"\n"); + dw_printf ("section, to understand what this means.\n"); } xmit_set_persist (chan, kiss_msg[1]); break; @@ -700,7 +702,8 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, struct if (kiss_msg[1] < 2 || kiss_msg[1] > 50) { text_color_set(DW_COLOR_ERROR); dw_printf ("Are you sure you want such an extreme value for SLOTTIME?\n"); - dw_printf ("See \"Radio Channel - Transmit Timing\" section of User Guide for explanation.\n"); + dw_printf ("Read the Dire Wolf User Guide, \"Radio Channel - Transmit Timing\"\n"); + dw_printf ("section, to understand what this means.\n"); } xmit_set_slottime (chan, kiss_msg[1]); break; @@ -714,10 +717,11 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, struct } text_color_set(DW_COLOR_INFO); dw_printf ("KISS protocol set TXtail = %d (*10mS units = %d mS), chan %d\n", kiss_msg[1], kiss_msg[1] * 10, chan); - if (kiss_msg[1] < 2) { + if (kiss_msg[1] < 5) { text_color_set(DW_COLOR_ERROR); dw_printf ("Setting TXTAIL so low is asking for trouble. You probably don't want to do this.\n"); - dw_printf ("See \"Radio Channel - Transmit Timing\" section of User Guide for explanation.\n"); + dw_printf ("Read the Dire Wolf User Guide, \"Radio Channel - Transmit Timing\"\n"); + dw_printf ("section, to understand what this means.\n"); } xmit_set_txtail (chan, kiss_msg[1]); break; diff --git a/src/multi_modem.c b/src/multi_modem.c index d2382f1a..7770a19a 100644 --- a/src/multi_modem.c +++ b/src/multi_modem.c @@ -126,7 +126,7 @@ static struct { int age; unsigned int crc; int score; -} candidate[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS]; +} candidate[MAX_RADIO_CHANS][MAX_SUBCHANS][MAX_SLICERS]; @@ -135,7 +135,7 @@ static struct { #define PROCESS_AFTER_BITS 3 -static int process_age[MAX_CHANS]; +static int process_age[MAX_RADIO_CHANS]; static void pick_best_candidate (int chan); @@ -172,7 +172,7 @@ void multi_modem_init (struct audio_s *pa) demod_init (save_audio_config_p); hdlc_rec_init (save_audio_config_p); - for (chan=0; chanchan_medium[chan] == MEDIUM_RADIO) { if (save_audio_config_p->achan[chan].baud <= 0) { text_color_set(DW_COLOR_ERROR); @@ -222,7 +222,7 @@ void multi_modem_init (struct audio_s *pa) * *------------------------------------------------------------------------------*/ -static float dc_average[MAX_CHANS]; +static float dc_average[MAX_RADIO_CHANS]; int multi_modem_get_dc_average (int chan) { @@ -319,7 +319,7 @@ void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned c packet_t pp; - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); assert (subchan >= 0 && subchan < MAX_SUBCHANS); assert (slice >= 0 && slice < MAX_SUBCHANS); @@ -329,8 +329,15 @@ void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned c char nmea[256]; ais_to_nmea (fbuf, flen, nmea, sizeof(nmea)); + // The intention is for the AIS sentences to go only to attached applications. + // e.g. SARTrack knows how to parse the AIS sentences. + + // Put NOGATE in path so RF>IS IGates will block this. + // TODO: Use station callsign, rather than "AIS," so we know where it is coming from, + // if it happens to get onto RF somehow. + char monfmt[276]; - snprintf (monfmt, sizeof(monfmt), "AIS>%s%1d%1d:{%c%c%s", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION, USER_DEF_USER_ID, USER_DEF_TYPE_AIS, nmea); + snprintf (monfmt, sizeof(monfmt), "AIS>%s%1d%1d,NOGATE:{%c%c%s", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION, USER_DEF_USER_ID, USER_DEF_TYPE_AIS, nmea); pp = ax25_from_text (monfmt, 1); // alevel gets in there somehow making me question why it is passed thru here. @@ -338,7 +345,7 @@ void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned c else if (save_audio_config_p->achan[chan].modem_type == MODEM_EAS) { char monfmt[300]; // EAS SAME message max length is 268 - snprintf (monfmt, sizeof(monfmt), "EAS>%s%1d%1d:{%c%c%s", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION, USER_DEF_USER_ID, USER_DEF_TYPE_EAS, fbuf); + snprintf (monfmt, sizeof(monfmt), "EAS>%s%1d%1d,NOGATE:{%c%c%s", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION, USER_DEF_USER_ID, USER_DEF_TYPE_EAS, fbuf); pp = ax25_from_text (monfmt, 1); // alevel gets in there somehow making me question why it is passed thru here. diff --git a/src/pfilter.c b/src/pfilter.c index 35767a67..cc51519e 100644 --- a/src/pfilter.c +++ b/src/pfilter.c @@ -99,7 +99,7 @@ typedef enum token_type_e { TOKEN_AND, TOKEN_OR, TOKEN_NOT, TOKEN_LPAREN, TOKEN_ typedef struct pfstate_s { - int from_chan; /* From and to channels. MAX_CHANS is used for IGate. */ + int from_chan; /* From and to channels. MAX_TOTAL_CHANS is used for IGate. */ int to_chan; /* Used only for debug and error messages. */ /* @@ -175,7 +175,7 @@ static char *bool2text (int val) * * Inputs: from_chan - Channel packet is coming from. * to_chan - Channel packet is going to. - * Both are 0 .. MAX_CHANS-1 or MAX_CHANS for IGate. + * Both are 0 .. MAX_TOTAL_CHANS-1 or MAX_TOTAL_CHANS for IGate. * For debug/error messages only. * * filter - String of filter specs and logical operators to combine them. @@ -201,8 +201,8 @@ int pfilter (int from_chan, int to_chan, char *filter, packet_t pp, int is_aprs) char *p; int result; - assert (from_chan >= 0 && from_chan <= MAX_CHANS); - assert (to_chan >= 0 && to_chan <= MAX_CHANS); + assert (from_chan >= 0 && from_chan <= MAX_TOTAL_CHANS); + assert (to_chan >= 0 && to_chan <= MAX_TOTAL_CHANS); memset (&pfstate, 0, sizeof(pfstate)); @@ -258,10 +258,10 @@ int pfilter (int from_chan, int to_chan, char *filter, packet_t pp, int is_aprs) if (s_debug >= 1) { text_color_set(DW_COLOR_DEBUG); - if (from_chan == MAX_CHANS) { + if (from_chan == MAX_TOTAL_CHANS) { dw_printf (" Packet filter from IGate to radio channel %d returns %s\n", to_chan, bool2text(result)); } - else if (to_chan == MAX_CHANS) { + else if (to_chan == MAX_TOTAL_CHANS) { dw_printf (" Packet filter from radio channel %d to IGate returns %s\n", from_chan, bool2text(result)); } else if (is_aprs) { @@ -1478,9 +1478,9 @@ static void print_error (pfstate_t *pf, char *msg) { char intro[50]; - if (pf->from_chan == MAX_CHANS) { + if (pf->from_chan == MAX_TOTAL_CHANS) { - if (pf->to_chan == MAX_CHANS) { + if (pf->to_chan == MAX_TOTAL_CHANS) { snprintf (intro, sizeof(intro), "filter[IG,IG]: "); } else { @@ -1489,7 +1489,7 @@ static void print_error (pfstate_t *pf, char *msg) } else { - if (pf->to_chan == MAX_CHANS) { + if (pf->to_chan == MAX_TOTAL_CHANS) { snprintf (intro, sizeof(intro), "filter[%d,IG]: ", pf->from_chan); } else { diff --git a/src/ptt.c b/src/ptt.c index af746626..ec093878 100644 --- a/src/ptt.c +++ b/src/ptt.c @@ -730,12 +730,12 @@ int gpiod_probe(const char *chip_name, int line_number) -static HANDLE ptt_fd[MAX_CHANS][NUM_OCTYPES]; +static HANDLE ptt_fd[MAX_RADIO_CHANS][NUM_OCTYPES]; /* Serial port handle or fd. */ /* Could be the same for two channels */ /* if using both RTS and DTR. */ #if USE_HAMLIB -static RIG *rig[MAX_CHANS][NUM_OCTYPES]; +static RIG *rig[MAX_RADIO_CHANS][NUM_OCTYPES]; #endif static char otnames[NUM_OCTYPES][8]; @@ -761,7 +761,7 @@ void ptt_init (struct audio_s *audio_config_p) strlcpy (otnames[OCTYPE_CON], "CON", sizeof(otnames[OCTYPE_CON])); - for (ch = 0; ch < MAX_CHANS; ch++) { + for (ch = 0; ch < MAX_RADIO_CHANS; ch++) { int ot; for (ot = 0; ot < NUM_OCTYPES; ot++) { @@ -791,7 +791,7 @@ void ptt_init (struct audio_s *audio_config_p) * Set up serial ports. */ - for (ch = 0; ch < MAX_CHANS; ch++) { + for (ch = 0; ch < MAX_RADIO_CHANS; ch++) { if (audio_config_p->chan_medium[ch] == MEDIUM_RADIO) { int ot; @@ -906,7 +906,7 @@ void ptt_init (struct audio_s *audio_config_p) */ using_gpio = 0; - for (ch=0; chchan_medium[ch] == MEDIUM_RADIO) { int ot; for (ot = 0; ot < NUM_OCTYPES; ot++) { @@ -927,7 +927,7 @@ void ptt_init (struct audio_s *audio_config_p) } #if defined(USE_GPIOD) // GPIOD - for (ch = 0; ch < MAX_CHANS; ch++) { + for (ch = 0; ch < MAX_RADIO_CHANS; ch++) { if (save_audio_config_p->chan_medium[ch] == MEDIUM_RADIO) { for (int ot = 0; ot < NUM_OCTYPES; ot++) { if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_GPIOD) { @@ -952,7 +952,7 @@ void ptt_init (struct audio_s *audio_config_p) * the pins we want to use. */ - for (ch = 0; ch < MAX_CHANS; ch++) { + for (ch = 0; ch < MAX_RADIO_CHANS; ch++) { if (save_audio_config_p->chan_medium[ch] == MEDIUM_RADIO) { int ot; // output control type, PTT, DCD, CON, ... @@ -984,7 +984,7 @@ void ptt_init (struct audio_s *audio_config_p) #if ( defined(__i386__) || defined(__x86_64__) ) && ( defined(__linux__) || defined(__unix__) ) - for (ch = 0; ch < MAX_CHANS; ch++) { + for (ch = 0; ch < MAX_RADIO_CHANS; ch++) { if (save_audio_config_p->chan_medium[ch] == MEDIUM_RADIO) { int ot; for (ot = 0; ot < NUM_OCTYPES; ot++) { @@ -1051,7 +1051,7 @@ void ptt_init (struct audio_s *audio_config_p) #endif /* x86 Linux */ #ifdef USE_HAMLIB - for (ch = 0; ch < MAX_CHANS; ch++) { + for (ch = 0; ch < MAX_RADIO_CHANS; ch++) { if (save_audio_config_p->chan_medium[ch] == MEDIUM_RADIO) { int ot; for (ot = 0; ot < NUM_OCTYPES; ot++) { @@ -1163,7 +1163,7 @@ void ptt_init (struct audio_s *audio_config_p) #if USE_CM108 - for (ch = 0; ch < MAX_CHANS; ch++) { + for (ch = 0; ch < MAX_RADIO_CHANS; ch++) { if (audio_config_p->chan_medium[ch] == MEDIUM_RADIO) { int ot; @@ -1185,7 +1185,7 @@ void ptt_init (struct audio_s *audio_config_p) /* Why doesn't it transmit? Probably forgot to specify PTT option. */ - for (ch=0; chchan_medium[ch] == MEDIUM_RADIO) { if(audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_method == PTT_METHOD_NONE) { text_color_set(DW_COLOR_INFO); @@ -1251,14 +1251,14 @@ void ptt_set (int ot, int chan, int ptt_signal) int ptt2 = ptt_signal; assert (ot >= 0 && ot < NUM_OCTYPES); - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); if (ptt_debug_level >= 1) { text_color_set(DW_COLOR_DEBUG); dw_printf ("%s %d = %d\n", otnames[ot], chan, ptt_signal); } - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); if ( save_audio_config_p->chan_medium[chan] != MEDIUM_RADIO) { text_color_set(DW_COLOR_ERROR); @@ -1494,7 +1494,7 @@ void ptt_set (int ot, int chan, int ptt_signal) int get_input (int it, int chan) { assert (it >= 0 && it < NUM_ICTYPES); - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); if ( save_audio_config_p->chan_medium[chan] != MEDIUM_RADIO) { text_color_set(DW_COLOR_ERROR); @@ -1559,7 +1559,7 @@ void ptt_term (void) { int n; - for (n = 0; n < MAX_CHANS; n++) { + for (n = 0; n < MAX_RADIO_CHANS; n++) { if (save_audio_config_p->chan_medium[n] == MEDIUM_RADIO) { int ot; for (ot = 0; ot < NUM_OCTYPES; ot++) { @@ -1568,7 +1568,7 @@ void ptt_term (void) } } - for (n = 0; n < MAX_CHANS; n++) { + for (n = 0; n < MAX_RADIO_CHANS; n++) { if (save_audio_config_p->chan_medium[n] == MEDIUM_RADIO) { int ot; for (ot = 0; ot < NUM_OCTYPES; ot++) { @@ -1586,7 +1586,7 @@ void ptt_term (void) #ifdef USE_HAMLIB - for (n = 0; n < MAX_CHANS; n++) { + for (n = 0; n < MAX_RADIO_CHANS; n++) { if (save_audio_config_p->chan_medium[n] == MEDIUM_RADIO) { int ot; for (ot = 0; ot < NUM_OCTYPES; ot++) { diff --git a/src/recv.c b/src/recv.c index 49040e55..51f2f016 100644 --- a/src/recv.c +++ b/src/recv.c @@ -108,6 +108,7 @@ #include "dtmf.h" #include "aprs_tt.h" #include "ax25_link.h" +#include "ring.h" #if __WIN32__ @@ -278,7 +279,7 @@ static void * recv_adev_thread (void *arg) // Try to re-init the audio device a couple times before giving up? text_color_set(DW_COLOR_ERROR); - dw_printf ("Terminating after audio input failure.\n"); + dw_printf ("Terminating after audio device %d input failure.\n", a); exit (1); } diff --git a/src/rrbb.c b/src/rrbb.c index e787dae5..0666d42b 100644 --- a/src/rrbb.c +++ b/src/rrbb.c @@ -83,7 +83,7 @@ rrbb_t rrbb_new (int chan, int subchan, int slice, int is_scrambled, int descram { rrbb_t result; - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); assert (subchan >= 0 && subchan < MAX_SUBCHANS); assert (slice >= 0 && slice < MAX_SLICERS); @@ -333,7 +333,7 @@ int rrbb_get_chan (rrbb_t b) assert (b->magic1 == MAGIC1); assert (b->magic2 == MAGIC2); - assert (b->chan >= 0 && b->chan < MAX_CHANS); + assert (b->chan >= 0 && b->chan < MAX_RADIO_CHANS); return (b->chan); } diff --git a/src/server.c b/src/server.c index 2cc108b2..7814d9c6 100644 --- a/src/server.c +++ b/src/server.c @@ -1413,7 +1413,7 @@ static THREAD_F cmd_listen_thread (void *arg) /* * Take some precautions to guard against bad data which could cause problems later. */ - if (cmd.hdr.portx < 0 || cmd.hdr.portx >= MAX_CHANS) { + if (cmd.hdr.portx < 0 || cmd.hdr.portx >= MAX_TOTAL_CHANS) { text_color_set(DW_COLOR_ERROR); dw_printf ("\nInvalid port number, %d, in command '%c', from AGW client application %d.\n", cmd.hdr.portx, cmd.hdr.datakind, client); @@ -1544,7 +1544,7 @@ static THREAD_F cmd_listen_thread (void *arg) // No other place cares about total number. count = 0; - for (j=0; jchan_medium[j] == MEDIUM_RADIO || save_audio_config_p->chan_medium[j] == MEDIUM_IGATE || save_audio_config_p->chan_medium[j] == MEDIUM_NETTNC) { @@ -1553,7 +1553,7 @@ static THREAD_F cmd_listen_thread (void *arg) } snprintf (reply.info, sizeof(reply.info), "%d;", count); - for (j=0; jchan_medium[j]) { @@ -1850,7 +1850,7 @@ static THREAD_F cmd_listen_thread (void *arg) // Connected mode can only be used with internal modems. - if (chan >= 0 && chan < MAX_CHANS && save_audio_config_p->chan_medium[chan] == MEDIUM_RADIO) { + if (chan >= 0 && chan < MAX_RADIO_CHANS && save_audio_config_p->chan_medium[chan] == MEDIUM_RADIO) { ok = 1; dlq_register_callsign (cmd.hdr.call_from, chan, client); } @@ -1879,7 +1879,7 @@ static THREAD_F cmd_listen_thread (void *arg) // Connected mode can only be used with internal modems. - if (chan >= 0 && chan < MAX_CHANS && save_audio_config_p->chan_medium[chan] == MEDIUM_RADIO) { + if (chan >= 0 && chan < MAX_RADIO_CHANS && save_audio_config_p->chan_medium[chan] == MEDIUM_RADIO) { dlq_unregister_callsign (cmd.hdr.call_from, chan, client); } else { @@ -2066,7 +2066,7 @@ static THREAD_F cmd_listen_thread (void *arg) reply.hdr.data_len_NETLE = host2netle(4); int n = 0; - if (cmd.hdr.portx >= 0 && cmd.hdr.portx < MAX_CHANS) { + if (cmd.hdr.portx >= 0 && cmd.hdr.portx < MAX_RADIO_CHANS) { // Count both normal and expedited in transmit queue for given channel. n = tq_count (cmd.hdr.portx, -1, "", "", 0); } diff --git a/src/telemetry.c b/src/telemetry.c index 2a6c690c..d796cf14 100644 --- a/src/telemetry.c +++ b/src/telemetry.c @@ -71,7 +71,7 @@ #define T_NUM_ANALOG 5 /* Number of analog channels. */ #define T_NUM_DIGITAL 8 /* Number of digital channels. */ -#define T_STR_LEN 16 /* Max len for labels and units. */ +#define T_STR_LEN 32 /* Max len for labels and units. */ #define MAGIC1 0x5a1111a5 /* For checking storage allocation problems. */ diff --git a/src/tnctest.c b/src/tnctest.c index 0d4c26b4..f5fb8616 100644 --- a/src/tnctest.c +++ b/src/tnctest.c @@ -285,7 +285,7 @@ int main (int argc, char *argv[]) setlinebuf (stdout); #endif - start_dtime = dtime_now(); + start_dtime = dtime_monotonic(); /* * Extract command line args. @@ -615,7 +615,7 @@ void process_rec_data (int my_index, char *data) * and sent to a common function to check that they * all arrived in order. * - * Global Out: is_connected - Updated when connected/disconnected notifications are received. + * Global Out: is_connected - Updated when connected/disconnected notfications are received. * * Description: Perform any necessary configuration for the TNC then wait * for responses and process them. @@ -859,7 +859,7 @@ static void * tnc_thread_net (void *arg) * What did we get? */ - dnow = dtime_now(); + dnow = dtime_monotonic(); switch (mon_cmd.datakind) { @@ -943,7 +943,7 @@ static void * tnc_thread_net (void *arg) * and sent to a common function to check that they * all arrived in order. * - * Global Out: is_connected - Updated when connected/disconnected notifications are received. + * Global Out: is_connected - Updated when connected/disconnected notfications are received. * * Description: Perform any necessary configuration for the TNC then wait * for responses and process them. @@ -1038,12 +1038,12 @@ static void * tnc_thread_serial (void *arg) done = 1; } else if (ch == XOFF) { - double dnow = dtime_now(); + double dnow = dtime_monotonic(); printf("%*s[R %.3f] \n", my_index*column_width, "", dnow-start_dtime); busy[my_index] = 1; } else if (ch == XON) { - double dnow = dtime_now(); + double dnow = dtime_monotonic(); printf("%*s[R %.3f] \n", my_index*column_width, "", dnow-start_dtime); busy[my_index] = 0; } @@ -1070,7 +1070,7 @@ static void * tnc_thread_serial (void *arg) if (len > 0) { - double dnow = dtime_now(); + double dnow = dtime_monotonic(); printf("%*s[R %.3f] %s\n", my_index*column_width, "", dnow-start_dtime, result); @@ -1109,7 +1109,7 @@ static void * tnc_thread_serial (void *arg) static void tnc_connect (int from, int to) { - double dnow = dtime_now(); + double dnow = dtime_monotonic(); printf("%*s[T %.3f] *** Send connect request ***\n", from*column_width, "", dnow-start_dtime); @@ -1160,7 +1160,7 @@ static void tnc_connect (int from, int to) static void tnc_disconnect (int from, int to) { - double dnow = dtime_now(); + double dnow = dtime_monotonic(); printf("%*s[T %.3f] *** Send disconnect request ***\n", from*column_width, "", dnow-start_dtime); @@ -1201,7 +1201,7 @@ static void tnc_disconnect (int from, int to) static void tnc_reset (int from, int to) { - double dnow = dtime_now(); + double dnow = dtime_monotonic(); printf("%*s[T %.3f] *** Send reset ***\n", from*column_width, "", dnow-start_dtime); @@ -1232,7 +1232,7 @@ static void tnc_reset (int from, int to) static void tnc_send_data (int from, int to, char * data) { - double dnow = dtime_now(); + double dnow = dtime_monotonic(); printf("%*s[T %.3f] %s\n", from*column_width, "", dnow-start_dtime, data); @@ -1257,7 +1257,7 @@ static void tnc_send_data (int from, int to, char * data) else { // The assumption is that we are in CONVERS mode. - // The data should be terminated by carriage return. + // The data sould be terminated by carriage return. int timeout = 600; // 60 sec. I've seen it take more than 20. while (timeout > 0 && busy[from]) { diff --git a/src/tq.c b/src/tq.c index c656af54..0738eca1 100644 --- a/src/tq.c +++ b/src/tq.c @@ -52,10 +52,10 @@ #include "dedupe.h" #include "igate.h" #include "dtime_now.h" +#include "nettnc.h" - -static packet_t queue_head[MAX_CHANS][TQ_NUM_PRIO]; /* Head of linked list for each queue. */ +static packet_t queue_head[MAX_RADIO_CHANS][TQ_NUM_PRIO]; /* Head of linked list for each queue. */ static dw_mutex_t tq_mutex; /* Critical section for updating queues. */ @@ -63,15 +63,15 @@ static dw_mutex_t tq_mutex; /* Critical section for updating queues. */ #if __WIN32__ -static HANDLE wake_up_event[MAX_CHANS]; /* Notify transmit thread when queue not empty. */ +static HANDLE wake_up_event[MAX_RADIO_CHANS]; /* Notify transmit thread when queue not empty. */ #else -static pthread_cond_t wake_up_cond[MAX_CHANS]; /* Notify transmit thread when queue not empty. */ +static pthread_cond_t wake_up_cond[MAX_RADIO_CHANS]; /* Notify transmit thread when queue not empty. */ -static pthread_mutex_t wake_up_mutex[MAX_CHANS]; /* Required by cond_wait. */ +static pthread_mutex_t wake_up_mutex[MAX_RADIO_CHANS]; /* Required by cond_wait. */ -static int xmit_thread_is_waiting[MAX_CHANS]; +static int xmit_thread_is_waiting[MAX_RADIO_CHANS]; #endif @@ -128,7 +128,7 @@ void tq_init (struct audio_s *audio_config_p) save_audio_config_p = audio_config_p; - for (c=0; cchan_medium[c] == MEDIUM_RADIO) { @@ -164,7 +164,7 @@ void tq_init (struct audio_s *audio_config_p) #else int err; - for (c = 0; c < MAX_CHANS; c++) { + for (c = 0; c < MAX_RADIO_CHANS; c++) { xmit_thread_is_waiting[c] = 0; @@ -199,6 +199,9 @@ void tq_init (struct audio_s *audio_config_p) * New in 1.7: * Channel can be assigned to IGate rather than a radio. * + * New in 1.8: + * Channel can be assigned to a network TNC. + * * prio - Priority, use TQ_PRIO_0_HI for digipeated or * TQ_PRIO_1_LO for normal. * @@ -252,10 +255,13 @@ void tq_append (int chan, int prio, packet_t pp) #endif // New in 1.7 - A channel can be assigned to the IGate rather than a radio. +// New in 1.8: Assign a channel to external network TNC. +// Send somewhere else, rather than the transmit queue. #ifndef DIGITEST // avoid dtest link error - if (save_audio_config_p->chan_medium[chan] == MEDIUM_IGATE) { + if (save_audio_config_p->chan_medium[chan] == MEDIUM_IGATE || + save_audio_config_p->chan_medium[chan] == MEDIUM_NETTNC) { char ts[100]; // optional time stamp. @@ -274,21 +280,39 @@ void tq_append (int chan, int prio, packet_t pp) unsigned char *pinfo; int info_len = ax25_get_info (pp, &pinfo); text_color_set(DW_COLOR_XMIT); - dw_printf ("[%d>is%s] ", chan, ts); - dw_printf ("%s", stemp); /* stations followed by : */ - ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp)); - dw_printf ("\n"); - igate_send_rec_packet (chan, pp); + if (save_audio_config_p->chan_medium[chan] == MEDIUM_IGATE) { + + dw_printf ("[%d>is%s] ", chan, ts); + dw_printf ("%s", stemp); /* stations followed by : */ + ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp)); + dw_printf ("\n"); + + igate_send_rec_packet (chan, pp); + } + else { // network TNC + dw_printf ("[%d>nt%s] ", chan, ts); + dw_printf ("%s", stemp); /* stations followed by : */ + ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp)); + dw_printf ("\n"); + + nettnc_send_packet (chan, pp); + + } + ax25_delete(pp); return; } #endif + + + + // Normal case - put in queue for radio transmission. // Error if trying to transmit to a radio channel which was not configured. - if (chan < 0 || chan >= MAX_CHANS || save_audio_config_p->chan_medium[chan] == MEDIUM_NONE) { + if (chan < 0 || chan >= MAX_RADIO_CHANS || save_audio_config_p->chan_medium[chan] == MEDIUM_NONE) { text_color_set(DW_COLOR_ERROR); dw_printf ("ERROR - Request to transmit on invalid radio channel %d.\n", chan); dw_printf ("This is probably a client application error, not a problem with direwolf.\n"); @@ -490,7 +514,7 @@ void lm_data_request (int chan, int prio, packet_t pp) } #endif - if (chan < 0 || chan >= MAX_CHANS || save_audio_config_p->chan_medium[chan] != MEDIUM_RADIO) { + if (chan < 0 || chan >= MAX_RADIO_CHANS || save_audio_config_p->chan_medium[chan] != MEDIUM_RADIO) { // Connected mode is allowed only with internal modems. text_color_set(DW_COLOR_ERROR); dw_printf ("ERROR - Request to transmit on invalid radio channel %d.\n", chan); @@ -648,7 +672,7 @@ void lm_seize_request (int chan) #endif - if (chan < 0 || chan >= MAX_CHANS || save_audio_config_p->chan_medium[chan] != MEDIUM_RADIO) { + if (chan < 0 || chan >= MAX_RADIO_CHANS || save_audio_config_p->chan_medium[chan] != MEDIUM_RADIO) { // Connected mode is allowed only with internal modems. text_color_set(DW_COLOR_ERROR); dw_printf ("ERROR - Request to transmit on invalid radio channel %d.\n", chan); @@ -748,7 +772,7 @@ void tq_wait_while_empty (int chan) text_color_set(DW_COLOR_DEBUG); dw_printf ("tq_wait_while_empty (%d) : enter critical section\n", chan); #endif - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); dw_mutex_lock (&tq_mutex); @@ -944,7 +968,7 @@ static int tq_is_empty (int chan) { int p; - assert (chan >= 0 && chan < MAX_CHANS); + assert (chan >= 0 && chan < MAX_RADIO_CHANS); for (p=0; p= MAX_CHANS || prio < 0 || prio >= TQ_NUM_PRIO) { + if (chan < 0 || chan >= MAX_RADIO_CHANS || prio < 0 || prio >= TQ_NUM_PRIO) { text_color_set(DW_COLOR_DEBUG); dw_printf ("INTERNAL ERROR - tq_count(%d, %d, \"%s\", \"%s\", %d)\n", chan, prio, source, dest, bytes); return (0); diff --git a/src/tt_user.c b/src/tt_user.c index a73d6a46..63043b2d 100644 --- a/src/tt_user.c +++ b/src/tt_user.c @@ -819,10 +819,10 @@ static void xmit_object_report (int i, int first_time) */ if (save_tt_config_p->obj_xmit_chan >= 0) { - strlcpy (stemp, save_audio_config_p->achan[save_tt_config_p->obj_xmit_chan].mycall, sizeof(stemp)); + strlcpy (stemp, save_audio_config_p->mycall[save_tt_config_p->obj_xmit_chan], sizeof(stemp)); } else { - strlcpy (stemp, save_audio_config_p->achan[save_tt_config_p->obj_recv_chan].mycall, sizeof(stemp)); + strlcpy (stemp, save_audio_config_p->mycall[save_tt_config_p->obj_recv_chan], sizeof(stemp)); } strlcat (stemp, ">", sizeof(stemp)); strlcat (stemp, APP_TOCALL, sizeof(stemp)); @@ -1134,7 +1134,7 @@ int main (int argc, char *argv[]) memset (&my_audio_config, 0, sizeof(my_audio_config)); - strlcpy (my_audio_config.achan[0].mycall, "WB2OSZ-15", sizeof(my_audio_config.achan[0].mycall)); + strlcpy (my_audio_config.mycall[0], "WB2OSZ-15", sizeof(my_audio_config.mycall[0])); /* Fake TT gateway config. */ diff --git a/src/xmit.c b/src/xmit.c index 13bbaecb..0d938efa 100644 --- a/src/xmit.c +++ b/src/xmit.c @@ -88,24 +88,24 @@ */ -static int xmit_slottime[MAX_CHANS]; /* Slot time in 10 mS units for persistence algorithm. */ +static int xmit_slottime[MAX_RADIO_CHANS]; /* Slot time in 10 mS units for persistence algorithm. */ -static int xmit_persist[MAX_CHANS]; /* Sets probability for transmitting after each */ +static int xmit_persist[MAX_RADIO_CHANS]; /* Sets probability for transmitting after each */ /* slot time delay. Transmit if a random number */ /* in range of 0 - 255 <= persist value. */ /* Otherwise wait another slot time and try again. */ -static int xmit_txdelay[MAX_CHANS]; /* After turning on the transmitter, */ +static int xmit_txdelay[MAX_RADIO_CHANS]; /* After turning on the transmitter, */ /* send "flags" for txdelay * 10 mS. */ -static int xmit_txtail[MAX_CHANS]; /* Amount of time to keep transmitting after we */ +static int xmit_txtail[MAX_RADIO_CHANS]; /* Amount of time to keep transmitting after we */ /* are done sending the data. This is to avoid */ /* dropping PTT too soon and chopping off the end */ /* of the frame. Again 10 mS units. */ -static int xmit_fulldup[MAX_CHANS]; /* Full duplex if non-zero. */ +static int xmit_fulldup[MAX_RADIO_CHANS]; /* Full duplex if non-zero. */ -static int xmit_bits_per_sec[MAX_CHANS]; /* Data transmission rate. */ +static int xmit_bits_per_sec[MAX_RADIO_CHANS]; /* Data transmission rate. */ /* Often called baud rate which is equivalent for */ /* 1200 & 9600 cases but could be different with other */ /* modulation techniques. */ @@ -211,11 +211,11 @@ void xmit_init (struct audio_s *p_modem, int debug_xmit_packet) int ad; #if __WIN32__ - HANDLE xmit_th[MAX_CHANS]; + HANDLE xmit_th[MAX_RADIO_CHANS]; #else //pthread_attr_t attr; //struct sched_param sp; - pthread_t xmit_tid[MAX_CHANS]; + pthread_t xmit_tid[MAX_RADIO_CHANS]; #endif //int e; @@ -247,7 +247,7 @@ void xmit_init (struct audio_s *p_modem, int debug_xmit_packet) * TODO1.2: Any reason to use global config rather than making a copy? */ - for (j=0; jachan[j].baud; xmit_slottime[j] = p_modem->achan[j].slottime; xmit_persist[j] = p_modem->achan[j].persist; @@ -276,7 +276,7 @@ void xmit_init (struct audio_s *p_modem, int debug_xmit_packet) // underrun on the audio output device. - for (j=0; jchan_medium[j] == MEDIUM_RADIO) { #if __WIN32__ @@ -365,35 +365,35 @@ void xmit_init (struct audio_s *p_modem, int debug_xmit_packet) void xmit_set_txdelay (int channel, int value) { - if (channel >= 0 && channel < MAX_CHANS) { + if (channel >= 0 && channel < MAX_RADIO_CHANS) { xmit_txdelay[channel] = value; } } void xmit_set_persist (int channel, int value) { - if (channel >= 0 && channel < MAX_CHANS) { + if (channel >= 0 && channel < MAX_RADIO_CHANS) { xmit_persist[channel] = value; } } void xmit_set_slottime (int channel, int value) { - if (channel >= 0 && channel < MAX_CHANS) { + if (channel >= 0 && channel < MAX_RADIO_CHANS) { xmit_slottime[channel] = value; } } void xmit_set_txtail (int channel, int value) { - if (channel >= 0 && channel < MAX_CHANS) { + if (channel >= 0 && channel < MAX_RADIO_CHANS) { xmit_txtail[channel] = value; } } void xmit_set_fulldup (int channel, int value) { - if (channel >= 0 && channel < MAX_CHANS) { + if (channel >= 0 && channel < MAX_RADIO_CHANS) { xmit_fulldup[channel] = value; } } From b9f654d3c939073b300deb40e802384899c325cf Mon Sep 17 00:00:00 2001 From: wb2osz Date: Fri, 19 Jul 2024 00:41:12 +0100 Subject: [PATCH 34/57] New NCHANNEL feature. --- src/nettnc.c | 490 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/nettnc.h | 7 + 2 files changed, 497 insertions(+) create mode 100644 src/nettnc.c create mode 100644 src/nettnc.h diff --git a/src/nettnc.c b/src/nettnc.c new file mode 100644 index 00000000..9b95ab14 --- /dev/null +++ b/src/nettnc.c @@ -0,0 +1,490 @@ + +// +// This file is part of Dire Wolf, an amateur radio packet TNC. +// +// Copyright (C) 2024 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 +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + + + +/*------------------------------------------------------------------ + * + * Module: nettnc.c + * + * Purpose: Attach to Network KISS TNC(s) for NCHANNEL config file item(s). + * + * Description: Called once at application start up. + * + *---------------------------------------------------------------*/ + + +#include "direwolf.h" // Sets _WIN32_WINNT for XP API level needed by ws2tcpip.h + +#if __WIN32__ +#include +#include // _WIN32_WINNT must be set to 0x0501 before including this +#else +#include +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "textcolor.h" +#include "audio.h" // configuration. +#include "kiss.h" +#include "dwsock.h" // socket helper functions. +#include "ax25_pad.h" // for AX25_MAX_PACKET_LEN +#include "dlq.h" // received packet queue + +#include "nettnc.h" + + + +void hex_dump (unsigned char *p, int len); + + +// TODO: define macros in common locaation to hide platform specifics. + +#if __WIN32__ +#define THREAD_F unsigned __stdcall +#else +#define THREAD_F void * +#endif + +#if __WIN32__ +static HANDLE nettnc_listen_th[MAX_TOTAL_CHANS]; +static THREAD_F nettnc_listen_thread (void *arg); +#else +static pthread_t nettnc_listen_tid[MAX_TOTAL_CHANS]; +static THREAD_F nettnc_listen_thread (void *arg); +#endif + +static void my_kiss_rec_byte (kiss_frame_t *kf, unsigned char b, int debug, int channel_override); + +int s_kiss_debug = 0; + + +/*------------------------------------------------------------------- + * + * Name: nettnc_init + * + * Purpose: Attach to Network KISS TNC(s) for NCHANNEL config file item(s). + * + * Inputs: pa - Address of structure of type audio_s. + * + * debug ? TBD + * + * + * Returns: 0 for success, -1 for failure. + * + * Description: Called once at direwolf application start up time. + * Calls nettnc_attach for each NCHANNEL configuration item. + * + *--------------------------------------------------------------------*/ + +void nettnc_init (struct audio_s *pa) +{ + for (int i = 0; i < MAX_TOTAL_CHANS; i++) { + + if (pa->chan_medium[i] == MEDIUM_NETTNC) { + text_color_set(DW_COLOR_DEBUG); + dw_printf ("Channel %d: Network TNC %s %d\n", i, pa->nettnc_addr[i], pa->nettnc_port[i]); + int e = nettnc_attach (i, pa->nettnc_addr[i], pa->nettnc_port[i]); + if (e < 0) { + exit (1); + } + } + } + +} // end nettnc_init + + + +/*------------------------------------------------------------------- + * + * Name: nettnc_attach + * + * Purpose: Attach to one Network KISS TNC. + * + * Inputs: chan - channel number from NCHANNEL configuration. + * + * host - Host name or IP address. Often "localhost". + * + * port - TCP port number. Typically 8001. + * + * init_func - Call this function after establishing communication // + * with the TNC. We put it here, so that it can be done// + * again automatically if the TNC disappears and we// + * reattach to it.// + * It must return 0 for success.// + * Can be NULL if not needed.// + * + * Returns: 0 for success, -1 for failure. + * + * Description: This starts up a thread, for each socket, which listens to the socket and + * dispatches the messages to the corresponding callback functions. + * It will also attempt to re-establish communication with the + * TNC if it goes away. + * + *--------------------------------------------------------------------*/ + +static char s_tnc_host[MAX_TOTAL_CHANS][80]; +static char s_tnc_port[MAX_TOTAL_CHANS][20]; +static volatile int s_tnc_sock[MAX_TOTAL_CHANS]; // Socket handle or file descriptor. -1 for invalid. + + +int nettnc_attach (int chan, char *host, int port) +{ + assert (chan >= 0 && chan < MAX_TOTAL_CHANS); + + char tncaddr[DWSOCK_IPADDR_LEN]; + + char sport[20]; // need port as text string later. + snprintf (sport, sizeof(sport), "%d", port); + + strlcpy (s_tnc_host[chan], host, sizeof(s_tnc_host[chan])); + strlcpy (s_tnc_port[chan], sport, sizeof(s_tnc_port[chan])); + s_tnc_sock[chan] = -1; + + dwsock_init(); + + s_tnc_sock[chan] = dwsock_connect (s_tnc_host[chan], s_tnc_port[chan], "Network TNC", 0, 0, tncaddr); + + if (s_tnc_sock[chan] == -1) { + return (-1); + } + + +/* + * Read frames from the network TNC. + * If the TNC disappears, try to reestablish communication. + */ + + +#if __WIN32__ + nettnc_listen_th[chan] = (HANDLE)_beginthreadex (NULL, 0, nettnc_listen_thread, (void *)(ptrdiff_t)chan, 0, NULL); + if (nettnc_listen_th[chan] == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Internal error: Could not create remore TNC listening thread\n"); + return (-1); + } +#else + int e = pthread_create (&nettnc_listen_tid[chan], NULL, nettnc_listen_thread, (void *)(ptrdiff_t)chan); + if (e != 0) { + text_color_set(DW_COLOR_ERROR); + perror("Internal error: Could not create network TNC listening thread"); + return (-1); + } +#endif + +// TNC initialization if specified. + +// if (s_tnc_init_func != NULL) { +// e = (*s_tnc_init_func)(); +// return (e); +// } + + return (0); + +} // end nettnc_attach + + + +/*------------------------------------------------------------------- + * + * Name: nettnc_listen_thread + * + * Purpose: Listen for anything from TNC and process it. + * Reconnect if something goes wrong and we got disconnected. + * + * Inputs: arg - Channel number. + * s_tnc_host[chan] - Host & port for re-connection. + * s_tnc_port[chan] + * + * Outputs: s_tnc_sock[chan] - File descriptor for communicating with TNC. + * Will be -1 if not connected. + * + *--------------------------------------------------------------------*/ + +#if __WIN32__ +static unsigned __stdcall nettnc_listen_thread (void *arg) +#else +static void * nettnc_listen_thread (void *arg) +#endif +{ + int chan = (int)(ptrdiff_t)arg; + assert (chan >= 0 && chan < MAX_TOTAL_CHANS); + + kiss_frame_t kstate; // State machine to gather a KISS frame. + memset (&kstate, 0, sizeof(kstate)); + + char tncaddr[DWSOCK_IPADDR_LEN]; // IP address used by dwsock_connect. + // Useful when rotate addresses used. + +// Set up buffer for collecting a KISS frame.$CC exttnc.c + + while (1) { +/* + * Re-attach to TNC if not currently attached. + */ + if (s_tnc_sock[chan] == -1) { + + text_color_set(DW_COLOR_ERROR); + // I'm using the term "attach" here, in an attempt to + // avoid confusion with the AX.25 connect. + dw_printf ("Attempting to reattach to network TNC...\n"); + + s_tnc_sock[chan] = dwsock_connect (s_tnc_host[chan], s_tnc_port[chan], "Network TNC", 0, 0, tncaddr); + + if (s_tnc_sock[chan] != -1) { + dw_printf ("Successfully reattached to network TNC.\n"); + } + } + else { +#define NETTNCBUFSIZ 2048 + unsigned char buf[NETTNCBUFSIZ]; + int n = SOCK_RECV (s_tnc_sock[chan], (char *)buf, sizeof(buf)); + + if (n == -1) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Lost communication with network TNC. Will try to reattach.\n"); + dwsock_close (s_tnc_sock[chan]); + s_tnc_sock[chan] = -1; + SLEEP_SEC(5); + continue; + } + +#if 0 + text_color_set(DW_COLOR_DEBUG); + dw_printf ("TEMP DEBUG: %d bytes received from channel %d network TNC.\n", n, chan); +#endif + for (int j = 0; j < n; j++) { + // Separate the byte stream into KISS frame(s) and make it + // look like this came from a radio channel. + my_kiss_rec_byte (&kstate, buf[j], s_kiss_debug, chan); + } + } // s_tnc_sock != -1 + } // while (1) + + return (0); // unreachable but shutup warning. + +} // end nettnc_listen_thread + + + +/*------------------------------------------------------------------- + * + * Name: my_kiss_rec_byte + * + * Purpose: Process one byte from a KISS network TNC. + * + * Inputs: kf - Current state of building a frame. + * b - A byte from the input stream. + * debug - Activates debug output. + * channel_overide - Set incoming channel number to the NCHANNEL + * number rather than the channel in the KISS frame. + * + * Outputs: kf - Current state is updated. + * + * Returns: none. + * + * Description: This is a simplified version of kiss_rec_byte used + * for talking to KISS client applications. It already has + * too many special cases and I don't want to make it worse. + * This also needs to make the packet look like it came from + * a radio channel, not from a client app. + * + *-----------------------------------------------------------------*/ + +static void my_kiss_rec_byte (kiss_frame_t *kf, unsigned char b, int debug, int channel_override) +{ + + //dw_printf ("my_kiss_rec_byte ( %c %02x ) \n", b, b); + + switch (kf->state) { + + case KS_SEARCHING: /* Searching for starting FEND. */ + default: + + if (b == FEND) { + + /* Start of frame. */ + + kf->kiss_len = 0; + kf->kiss_msg[kf->kiss_len++] = b; + kf->state = KS_COLLECTING; + return; + } + return; + break; + + case KS_COLLECTING: /* Frame collection in progress. */ + + + if (b == FEND) { + + unsigned char unwrapped[AX25_MAX_PACKET_LEN]; + int ulen; + + /* End of frame. */ + + if (kf->kiss_len == 0) { + /* Empty frame. Starting a new one. */ + kf->kiss_msg[kf->kiss_len++] = b; + return; + } + if (kf->kiss_len == 1 && kf->kiss_msg[0] == FEND) { + /* Empty frame. Just go on collecting. */ + return; + } + + kf->kiss_msg[kf->kiss_len++] = b; + if (debug) { + /* As received over the wire from network TNC. */ + // May include escapted characters. What about FEND? +// FIXME: make it say Network TNC. + kiss_debug_print (FROM_CLIENT, NULL, kf->kiss_msg, kf->kiss_len); + } + + ulen = kiss_unwrap (kf->kiss_msg, kf->kiss_len, unwrapped); + + if (debug >= 2) { + /* Append CRC to this and it goes out over the radio. */ + text_color_set(DW_COLOR_DEBUG); + dw_printf ("\n"); + dw_printf ("Frame content after removing KISS framing and any escapes:\n"); + /* Don't include the "type" indicator. */ + /* It contains the radio channel and type should always be 0 here. */ + hex_dump (unwrapped+1, ulen-1); + } + + // Convert to packet object and send to received packet queue. + // Note that we use channel associated with the network TNC, not channel in KISS frame. + + int subchan = -3; + int slice = 0; + alevel_t alevel; + memset(&alevel, 0, sizeof(alevel)); + packet_t pp = ax25_from_frame (unwrapped+1, ulen-1, alevel); + if (pp != NULL) { + fec_type_t fec_type = fec_type_none; + retry_t retries; + memset (&retries, 0, sizeof(retries)); + char spectrum[] = "Network TNC"; + dlq_rec_frame (channel_override, subchan, slice, pp, alevel, fec_type, retries, spectrum); + } + else { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Failed to create packet object for KISS frame from channel %d network TNC.\n", channel_override); + } + + kf->state = KS_SEARCHING; + return; + } + + if (kf->kiss_len < MAX_KISS_LEN) { + kf->kiss_msg[kf->kiss_len++] = b; + } + else { + text_color_set(DW_COLOR_ERROR); + dw_printf ("KISS frame from network TNC exceeded maximum length.\n"); + } + return; + break; + } + + return; /* unreachable but suppress compiler warning. */ + +} /* end my_kiss_rec_byte */ + + + + + + + + + +/*------------------------------------------------------------------- + * + * Name: nettnc_send_packet + * + * Purpose: Send packet to a KISS network TNC. + * + * Inputs: chan - Channel number from NCHANNEL configuration. + * pp - Packet object. + * b - A byte from the input stream. + * + * Outputs: Packet is converted to KISS and send to network TNC. + * + * Returns: none. + * + * Description: This does not free the packet object; caller is responsible. + * + *-----------------------------------------------------------------*/ + +void nettnc_send_packet (int chan, packet_t pp) +{ + +// First, get the on-air frame format from packet object. +// Prepend 0 byte for KISS command and channel. + + unsigned char frame_buff[AX25_MAX_PACKET_LEN + 2]; // One byte for channel/command, + // followed by the AX.25 on-air format frame. + frame_buff[0] = 0; // For now, set channel to 0. + + unsigned char *fbuf = ax25_get_frame_data_ptr (pp); + int flen = ax25_get_frame_len (pp); + + memcpy (frame_buff+1, fbuf, flen); + +// Next, encapsulate into KISS frame with surrounding FENDs and any escapes. + + unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN]; + int kiss_len = kiss_encapsulate (frame_buff, flen+1, kiss_buff); + +#if __WIN32__ + int err = SOCK_SEND(s_tnc_sock[chan], (char*)kiss_buff, kiss_len); + if (err == SOCKET_ERROR) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("\nError %d sending packet to KISS Network TNC for channel %d. Closing connection.\n\n", WSAGetLastError(), chan); + closesocket (s_tnc_sock[chan]); + s_tnc_sock[chan] = -1; + } +#else + int err = SOCK_SEND (kps->client_sock[chan], kiss_buff, kiss_len); + if (err <= 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("\nError %d sending packet to KISS Network TNC for channel %d. Closing connection.\n\n", err, chan); + close (s_tnc_sock[chan]); + s_tnc_sock[chan] = -1; + } +#endif + + // Do not free packet object; caller will take care of it. + +} /* end nettnc_send_packet */ + diff --git a/src/nettnc.h b/src/nettnc.h new file mode 100644 index 00000000..d8a10f43 --- /dev/null +++ b/src/nettnc.h @@ -0,0 +1,7 @@ + + +void nettnc_init (struct audio_s *pa); + +int nettnc_attach (int chan, char *host, int port); + +void nettnc_send_packet (int chan, packet_t pp); \ No newline at end of file From dd04883707ccdb55889a14d5b038335d848cf5a3 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Fri, 19 Jul 2024 00:44:20 +0100 Subject: [PATCH 35/57] New NCHANNEL feature. --- src/nettnc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/nettnc.c b/src/nettnc.c index 9b95ab14..e16dd8d0 100644 --- a/src/nettnc.c +++ b/src/nettnc.c @@ -51,6 +51,7 @@ #include #include #include +#include #include "textcolor.h" #include "audio.h" // configuration. From 1033f8a7bfa7d8164f734f5adc541193fba6a199 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Fri, 19 Jul 2024 00:48:08 +0100 Subject: [PATCH 36/57] New NCHANNEL feature. --- src/nettnc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nettnc.c b/src/nettnc.c index e16dd8d0..72d18cc5 100644 --- a/src/nettnc.c +++ b/src/nettnc.c @@ -476,7 +476,7 @@ void nettnc_send_packet (int chan, packet_t pp) s_tnc_sock[chan] = -1; } #else - int err = SOCK_SEND (kps->client_sock[chan], kiss_buff, kiss_len); + int err = SOCK_SEND (s_tnc_sock[chan], kiss_buff, kiss_len); if (err <= 0) { text_color_set(DW_COLOR_ERROR); dw_printf ("\nError %d sending packet to KISS Network TNC for channel %d. Closing connection.\n\n", err, chan); From 312d5589235cd49b722dd750b24332e7a20ed062 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Fri, 19 Jul 2024 01:15:30 +0100 Subject: [PATCH 37/57] New NCHANNEL feature. --- src/recv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/recv.c b/src/recv.c index 51f2f016..d6281567 100644 --- a/src/recv.c +++ b/src/recv.c @@ -108,7 +108,7 @@ #include "dtmf.h" #include "aprs_tt.h" #include "ax25_link.h" -#include "ring.h" +//#include "ring.h" #if __WIN32__ From 48f524d5fced5f920d9429ace7a7641c301eab6e Mon Sep 17 00:00:00 2001 From: wb2osz Date: Fri, 13 Sep 2024 16:13:21 +0100 Subject: [PATCH 38/57] issue 530 - Put * after all used addresses. --- src/server.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/server.c b/src/server.c index 7814d9c6..34e8ab24 100644 --- a/src/server.c +++ b/src/server.c @@ -976,6 +976,9 @@ void server_send_monitored (int chan, packet_t pp, int own_xmit) // Format addresses in AGWPR monitoring format such as: // 1:Fm ZL4FOX-8 To Q7P2U2 Via WIDE3-3 +// Issue 530 pointed out that in this situation it is customary to put * after each used address, +// not just the last used as in the TNC-2 monitoring format. + static void mon_addrs (int chan, packet_t pp, char *result, int result_size) { char src[AX25_MAX_ADDR_LEN]; @@ -986,16 +989,20 @@ static void mon_addrs (int chan, packet_t pp, char *result, int result_size) int num_digi = ax25_get_num_repeaters(pp); if (num_digi > 0) { + char via[AX25_MAX_REPEATERS*(AX25_MAX_ADDR_LEN+1)]; // complete via path + strlcpy (via, "", sizeof(via)); - char via[AX25_MAX_REPEATERS*(AX25_MAX_ADDR_LEN+1)]; - char stemp[AX25_MAX_ADDR_LEN+1]; - int j; + for (int j = 0; j < num_digi; j++) { + char digiaddr[AX25_MAX_ADDR_LEN]; - ax25_get_addr_with_ssid (pp, AX25_REPEATER_1, via); - for (j = 1; j < num_digi; j++) { - ax25_get_addr_with_ssid (pp, AX25_REPEATER_1 + j, stemp); - strlcat (via, ",", sizeof(via)); - strlcat (via, stemp, sizeof(via)); + if (j != 0) { + strlcat (via, ",", sizeof(via)); // comma if not first address + } + ax25_get_addr_with_ssid (pp, AX25_REPEATER_1 + j, digiaddr); + strlcat (via, digiaddr, sizeof(via)); + if (ax25_get_h(pp, AX25_REPEATER_1 + j)) { + strlcat (via, "*", sizeof(via)); + } } snprintf (result, result_size, " %d:Fm %s To %s Via %s ", chan+1, src, dst, via); From c69252fc7c6a701905789a84ccea408fab96b3b8 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Fri, 13 Sep 2024 17:51:01 +0100 Subject: [PATCH 39/57] Fix typo. --- test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index da732ac8..37c2b2c8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -524,7 +524,7 @@ if(OPTIONAL_TEST) ${CUSTOM_SRC_DIR}/pfilter.c ${CUSTOM_SRC_DIR}/telemetry.c ${CUSTOM_SRC_DIR}/decode_aprs.c - ${CUSTOM_SRC_DIR}/deviceid.c.c + ${CUSTOM_SRC_DIR}/deviceid.c ${CUSTOM_SRC_DIR}/dwgpsnmea.c ${CUSTOM_SRC_DIR}/dwgps.c ${CUSTOM_SRC_DIR}/dwgpsd.c From 5d7b10abd9759d3287da83fd5a4eefc0369b3a3b Mon Sep 17 00:00:00 2001 From: wb2osz Date: Fri, 13 Sep 2024 18:15:18 +0100 Subject: [PATCH 40/57] Mark only heard digi, not all used. --- src/server.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/server.c b/src/server.c index 34e8ab24..023e43f7 100644 --- a/src/server.c +++ b/src/server.c @@ -976,8 +976,13 @@ void server_send_monitored (int chan, packet_t pp, int own_xmit) // Format addresses in AGWPR monitoring format such as: // 1:Fm ZL4FOX-8 To Q7P2U2 Via WIDE3-3 -// Issue 530 pointed out that in this situation it is customary to put * after each used address, -// not just the last used as in the TNC-2 monitoring format. +// There is some disagreement, in the user community, about whether to: +// * follow the lead of UZ7HO SoundModem and mark all of the used addresses, or +// * follow the TNC-2 Monitoring format and mark only the last used, i.e. the station heard. + +// I think my opinion (which could change) is that we should try to be consistent with TNC-2 format +// rather than continuing to propagate historical inconsistencies. + static void mon_addrs (int chan, packet_t pp, char *result, int result_size) { @@ -1000,9 +1005,14 @@ static void mon_addrs (int chan, packet_t pp, char *result, int result_size) } ax25_get_addr_with_ssid (pp, AX25_REPEATER_1 + j, digiaddr); strlcat (via, digiaddr, sizeof(via)); +#if 0 // Mark each used with * as seen in UZ7HO SoundModem. if (ax25_get_h(pp, AX25_REPEATER_1 + j)) { +#else // Mark only last used (i.e. the heard station) with * as in TNC-2 Monitoring format. + if (AX25_REPEATER_1 + j == ax25_get_heard(pp)) { +#endif strlcat (via, "*", sizeof(via)); } + } snprintf (result, result_size, " %d:Fm %s To %s Via %s ", chan+1, src, dst, via); From 0734e4613439413f65c7053a59b5d1d15ab3ca73 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sat, 21 Sep 2024 19:12:03 +0100 Subject: [PATCH 41/57] Issue 545 - Saved station location overwritten by Object report from that station. --- src/mheard.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/mheard.c b/src/mheard.c index f11c68f0..0e88b833 100644 --- a/src/mheard.c +++ b/src/mheard.c @@ -406,6 +406,7 @@ void mheard_save_rf (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, r mptr->chan = chan; mptr->num_digi_hops = hops; mptr->last_heard_rf = now; + // Why did I do this instead of saving the location for a position report? mptr->dlat = G_UNKNOWN; mptr->dlon = G_UNKNOWN; @@ -446,9 +447,16 @@ void mheard_save_rf (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, r } } - if (A->g_lat != G_UNKNOWN && A->g_lon != G_UNKNOWN) { - mptr->dlat = A->g_lat; - mptr->dlon = A->g_lon; + // Issue 545. This was not thought out well. + // There was a case where a station sent a position report and the location was stored. + // Later, the same station sent an object report and the stations's location was overwritten + // by the object location. Solution: Save location only if position report. + + if (A->g_packet_type == packet_type_position) { + if (A->g_lat != G_UNKNOWN && A->g_lon != G_UNKNOWN) { + mptr->dlat = A->g_lat; + mptr->dlon = A->g_lon; + } } if (mheard_debug >= 2) { From b26c5a4d7d4b2fd19160a1ff20cece13b2c18b13 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sat, 28 Sep 2024 01:58:16 +0100 Subject: [PATCH 42/57] Improve error message. --- src/deviceid.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/deviceid.c b/src/deviceid.c index de910e50..d57a6f2f 100644 --- a/src/deviceid.c +++ b/src/deviceid.c @@ -528,7 +528,7 @@ static int mice_cmp (const void *px, const void *py) void deviceid_decode_dest (char *dest, char *device, size_t device_size) { - *device = '\0'; + strlcpy (device, "UNKNOWN vendor/model", device_size); if (ptocalls == NULL) { text_color_set(DW_COLOR_ERROR); @@ -554,6 +554,7 @@ void deviceid_decode_dest (char *dest, char *device, size_t device_size) } } +// Not found in table. strlcpy (device, "UNKNOWN vendor/model", device_size); } // end deviceid_decode_dest @@ -610,7 +611,7 @@ static inline int strncmp_z (char *a, char *b, size_t len) void deviceid_decode_mice (char *comment, char *trimmed, size_t trimmed_size, char *device, size_t device_size) { - *device = '\0'; + strlcpy (device, "UNKNOWN vendor/model", device_size); if (ptocalls == NULL) { text_color_set(DW_COLOR_ERROR); From a83a1ca5f5d8ec960db6921dc8162bbd7de30dd2 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Fri, 18 Oct 2024 17:41:55 +0100 Subject: [PATCH 43/57] Issue 550: Remove extra trailing nul for Send Unproto Via. --- src/server.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/server.c b/src/server.c index 023e43f7..a54dd6b4 100644 --- a/src/server.c +++ b/src/server.c @@ -1769,7 +1769,11 @@ static THREAD_F cmd_listen_thread (void *arg) break; } - ax25_set_info (pp, (unsigned char*)p, data_len - ndigi * 10); + // Issue 550: Info part was one byte too long resulting in an extra nul character. + // Original calculation was data_len-ndigi*10 but we need to subtract one + // for first byte which is number of digipeaters. + ax25_set_info (pp, (unsigned char*)p, data_len - ndigi * 10 - 1); + // Issue 527: NET/ROM routing broadcasts use PID 0xCF which was not preserved here. ax25_set_pid (pp, pid); From debe703a474675ade40dee9a981b09a17b3fda60 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Fri, 18 Oct 2024 23:45:13 +0100 Subject: [PATCH 44/57] MAX_ADEVS==4 enabled debug output options. --- src/config.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/src/config.c b/src/config.c index 69fa80e2..863edcdf 100644 --- a/src/config.c +++ b/src/config.c @@ -19,8 +19,8 @@ #define CONFIG_C 1 // influences behavior of aprs_tt.h - -// #define DEBUG 1 +// FIXME: +#define DEBUG 1 /*------------------------------------------------------------------ * @@ -716,6 +716,7 @@ static void rtfm() 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"); + dw_printf (" general APRS info: https://how.aprs.works\n"); } void config_init (char *fname, struct audio_s *p_audio_config, @@ -763,12 +764,17 @@ void config_init (char *fname, struct audio_s *p_audio_config, p_audio_config->adev[0].defined = 2; // 2 means it was done by default and not the user's config file. +// MAX_TOTAL_CHANS for (channel=0; channelchan_medium[channel] = MEDIUM_NONE; /* One or both channels will be */ /* set to radio when corresponding */ /* audio device is defined. */ + } + +// MAX_RADIO_CHANS for achan[] + for (channel=0; channelachan[channel].modem_type = MODEM_AFSK; p_audio_config->achan[channel].v26_alternative = V26_UNSPECIFIED; p_audio_config->achan[channel].mark_freq = DEFAULT_MARK_FREQ; /* -m option */ @@ -980,8 +986,13 @@ void config_init (char *fname, struct audio_s *p_audio_config, if (fp == NULL) { // TODO: not exactly right for all situations. text_color_set(DW_COLOR_ERROR); - dw_printf ("ERROR - Could not open config file %s\n", filepath); + dw_printf ("ERROR - Could not open configuration file %s\n", filepath); dw_printf ("Try using -c command line option for alternate location.\n"); +#ifndef __WIN32__ + dw_printf ("A sample direwolf.conf file should be found in one of:\n"); + dw_printf (" /usr/local/share/doc/direwolf/conf/\n"); + dw_printf (" /usr/share/doc/direwolf/conf/\n"); +#endif rtfm(); exit(EXIT_FAILURE); } @@ -1423,6 +1434,12 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ else if (strcasecmp(t, "MODEM") == 0) { + if (channel < 0 || channel >= MAX_RADIO_CHANS) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: MODEM can only be used with radio channel 0 - %d.\n", line, MAX_RADIO_CHANS-1); + continue; + } + int n; t = split(NULL,0); if (t == NULL) { @@ -1758,6 +1775,11 @@ void config_init (char *fname, struct audio_s *p_audio_config, else if (strcasecmp(t, "DTMF") == 0) { + if (channel < 0 || channel >= MAX_RADIO_CHANS) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: DTMF can only be used with radio channel 0 - %d.\n", line, MAX_RADIO_CHANS-1); + continue; + } p_audio_config->achan[channel].dtmf_decode = DTMF_DECODE_ON; @@ -1773,6 +1795,11 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ else if (strcasecmp(t, "FIX_BITS") == 0) { + if (channel < 0 || channel >= MAX_RADIO_CHANS) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: FIX_BITS can only be used with radio channel 0 - %d.\n", line, MAX_RADIO_CHANS-1); + continue; + } int n; t = split(NULL,0); if (t == NULL) { @@ -1851,6 +1878,11 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ else if (strcasecmp(t, "PTT") == 0 || strcasecmp(t, "DCD") == 0 || strcasecmp(t, "CON") == 0) { + if (channel < 0 || channel >= MAX_RADIO_CHANS) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: PTT can only be used with radio channel 0 - %d.\n", line, MAX_RADIO_CHANS-1); + continue; + } int ot; char otname[8]; @@ -2222,6 +2254,11 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ else if (strcasecmp(t, "TXINH") == 0) { + if (channel < 0 || channel >= MAX_RADIO_CHANS) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: TXINH can only be used with radio channel 0 - %d.\n", line, MAX_RADIO_CHANS-1); + continue; + } char itname[8]; strlcpy (itname, "TXINH", sizeof(itname)); @@ -2268,6 +2305,11 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ else if (strcasecmp(t, "DWAIT") == 0) { + if (channel < 0 || channel >= MAX_RADIO_CHANS) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: DWAIT can only be used with radio channel 0 - %d.\n", line, MAX_RADIO_CHANS-1); + continue; + } int n; t = split(NULL,0); if (t == NULL) { @@ -2292,6 +2334,11 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ else if (strcasecmp(t, "SLOTTIME") == 0) { + if (channel < 0 || channel >= MAX_RADIO_CHANS) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: SLOTTIME can only be used with radio channel 0 - %d.\n", line, MAX_RADIO_CHANS-1); + continue; + } int n; t = split(NULL,0); if (t == NULL) { @@ -2322,6 +2369,11 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ else if (strcasecmp(t, "PERSIST") == 0) { + if (channel < 0 || channel >= MAX_RADIO_CHANS) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: PERSIST can only be used with radio channel 0 - %d.\n", line, MAX_RADIO_CHANS-1); + continue; + } int n; t = split(NULL,0); if (t == NULL) { @@ -2349,6 +2401,11 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ else if (strcasecmp(t, "TXDELAY") == 0) { + if (channel < 0 || channel >= MAX_RADIO_CHANS) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: TXDELAY can only be used with radio channel 0 - %d.\n", line, MAX_RADIO_CHANS-1); + continue; + } int n; t = split(NULL,0); if (t == NULL) { @@ -2390,6 +2447,11 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ else if (strcasecmp(t, "TXTAIL") == 0) { + if (channel < 0 || channel >= MAX_RADIO_CHANS) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: TXTAIL can only be used with radio channel 0 - %d.\n", line, MAX_RADIO_CHANS-1); + continue; + } int n; t = split(NULL,0); if (t == NULL) { @@ -2430,6 +2492,11 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ else if (strcasecmp(t, "FULLDUP") == 0) { + if (channel < 0 || channel >= MAX_RADIO_CHANS) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: FULLDUP can only be used with radio channel 0 - %d.\n", line, MAX_RADIO_CHANS-1); + continue; + } t = split(NULL,0); if (t == NULL) { text_color_set(DW_COLOR_ERROR); @@ -2457,6 +2524,11 @@ void config_init (char *fname, struct audio_s *p_audio_config, else if (strcasecmp(t, "SPEECH") == 0) { + if (channel < 0 || channel >= MAX_RADIO_CHANS) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: SPEECH can only be used with radio channel 0 - %d.\n", line, MAX_RADIO_CHANS-1); + continue; + } t = split(NULL,0); if (t == NULL) { text_color_set(DW_COLOR_ERROR); @@ -2488,6 +2560,11 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ else if (strcasecmp(t, "FX25TX") == 0) { + if (channel < 0 || channel >= MAX_RADIO_CHANS) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: FX25TX can only be used with radio channel 0 - %d.\n", line, MAX_RADIO_CHANS-1); + continue; + } int n; t = split(NULL,0); if (t == NULL) { @@ -2510,7 +2587,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, } /* - * FX25AUTO n - Enable Automatic use of FX.25 for connected mode. + * FX25AUTO n - Enable Automatic use of FX.25 for connected mode. *** Not Implemented *** * Automatically enable, for that session only, when an identical * frame is sent more than this number of times. * Default 5 based on half of default RETRY. @@ -2519,6 +2596,11 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ else if (strcasecmp(t, "FX25AUTO") == 0) { + if (channel < 0 || channel >= MAX_RADIO_CHANS) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: FX25AUTO can only be used with radio channel 0 - %d.\n", line, MAX_RADIO_CHANS-1); + continue; + } int n; t = split(NULL,0); if (t == NULL) { @@ -2550,6 +2632,11 @@ void config_init (char *fname, struct audio_s *p_audio_config, else if (strcasecmp(t, "IL2PTX") == 0) { + if (channel < 0 || channel >= MAX_RADIO_CHANS) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: IL2PTX can only be used with radio channel 0 - %d.\n", line, MAX_RADIO_CHANS-1); + continue; + } p_audio_config->achan[channel].layer2_xmit = LAYER2_IL2P; p_audio_config->achan[channel].il2p_max_fec = 1; p_audio_config->achan[channel].il2p_invert_polarity = 0; From e1e5be36e02d0497637e45b81c7ee0137ca6c09c Mon Sep 17 00:00:00 2001 From: wb2osz Date: Fri, 18 Oct 2024 23:46:42 +0100 Subject: [PATCH 45/57] MAX_ADEVS==4 enabled debug output options. --- src/config.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/config.c b/src/config.c index 863edcdf..097c908f 100644 --- a/src/config.c +++ b/src/config.c @@ -19,8 +19,8 @@ #define CONFIG_C 1 // influences behavior of aprs_tt.h -// FIXME: -#define DEBUG 1 + +//#define DEBUG 1 /*------------------------------------------------------------------ * @@ -772,6 +772,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, } // MAX_RADIO_CHANS for achan[] +// Maybe achan should be renamed to radiochan to make it clearer. for (channel=0; channel= MAX_RADIO_CHANS) { text_color_set(DW_COLOR_ERROR); dw_printf ("Line %d: MODEM can only be used with radio channel 0 - %d.\n", line, MAX_RADIO_CHANS-1); continue; } - int n; t = split(NULL,0); if (t == NULL) { From a627abc4849714ea3ee47085ab935dadaf8a867b Mon Sep 17 00:00:00 2001 From: wb2osz Date: Fri, 18 Oct 2024 23:52:24 +0100 Subject: [PATCH 46/57] Fix compile warning. --- external/hidapi/hid.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/external/hidapi/hid.c b/external/hidapi/hid.c index e483cd4f..f5c9858a 100644 --- a/external/hidapi/hid.c +++ b/external/hidapi/hid.c @@ -20,6 +20,8 @@ https://github.com/libusb/hidapi . ********************************************************/ +#include "../../src/direwolf.h" // for strlcpy + #include #ifndef _NTDEF_ @@ -465,7 +467,8 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor if (str) { len = strlen(str); cur_dev->path = (char*) calloc(len+1, sizeof(char)); - strncpy(cur_dev->path, str, len+1); + //strncpy(cur_dev->path, str, len+1); // produces warning + strlcpy(cur_dev->path, str, len+1); cur_dev->path[len] = '\0'; } else From 88cf0ba55d5deda66371648a1fa32fba5e5351f0 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sat, 19 Oct 2024 00:16:26 +0100 Subject: [PATCH 47/57] Add comments. --- src/ax25_pad.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ax25_pad.c b/src/ax25_pad.c index 57fd79d2..2fce2df9 100644 --- a/src/ax25_pad.c +++ b/src/ax25_pad.c @@ -174,6 +174,7 @@ #include "regex.h" #if __WIN32__ +// TODO: Why is this here, rather than in direwolf.h? char *strtok_r(char *str, const char *delim, char **saveptr); #endif @@ -194,6 +195,7 @@ static volatile int last_seq_num = 0; #if AX25MEMDEBUG +// TODO: Make static and use function for any extern references. int ax25memdebug = 0; From 3ba464dfff1e815cef27b27fc3355b05064075f1 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sat, 19 Oct 2024 00:25:36 +0100 Subject: [PATCH 48/57] Add info about layer 2 transmit per channel. --- src/audio.h | 11 ++++------- src/demod.c | 7 ++++++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/audio.h b/src/audio.h index 92fd944e..f69fc1d7 100644 --- a/src/audio.h +++ b/src/audio.h @@ -115,12 +115,6 @@ struct audio_s { float recv_ber; /* Receive Bit Error Rate (BER). */ /* Probability of inverting a bit coming out of the modem. */ - //int fx25_xmit_enable; /* Enable transmission of FX.25. */ - /* See fx25_init.c for explanation of values. */ - /* Initially this applies to all channels. */ - /* This should probably be per channel. One step at a time. */ - /* v1.7 - replaced by layer2_xmit==LAYER2_FX25 */ - int fx25_auto_enable; /* Turn on FX.25 for current connected mode session */ /* under poor conditions. */ /* Set to 0 to disable feature. */ @@ -198,7 +192,7 @@ struct audio_s { /* Might try MFJ-2400 / CCITT v.26 / Bell 201 someday. */ /* No modem. Might want this for DTMF only channel. */ - enum layer2_t { LAYER2_AX25 = 0, LAYER2_FX25, LAYER2_IL2P } layer2_xmit; + enum layer2_t { LAYER2_AX25 = 0, LAYER2_FX25, LAYER2_IL2P } layer2_xmit; // Must keep in sync with layer2_tx, below. // IL2P - New for version 1.7. // New layer 2 with FEC. Much less overhead than FX.25 but no longer backward compatible. @@ -405,6 +399,9 @@ struct audio_s { }; +#if DEMOD_C + const static char *layer2_tx[3] = {"AX.25", "FX.25", "IL2P"}; // Must keep in sync with enum layer2_t above. +#endif #if __WIN32__ #define DEFAULT_ADEVICE "" /* Windows: Empty string = default audio device. */ diff --git a/src/demod.c b/src/demod.c index efcfde71..ebbcbed4 100644 --- a/src/demod.c +++ b/src/demod.c @@ -31,6 +31,8 @@ * *---------------------------------------------------------------*/ +#define DEMOD_C 1 + #include "direwolf.h" #include @@ -306,6 +308,7 @@ int demod_init (struct audio_s *pa) save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec); if (save_audio_config_p->achan[chan].decimate != 1) dw_printf (" / %d", save_audio_config_p->achan[chan].decimate); + dw_printf (", Tx %s", layer2_tx[(int)(save_audio_config_p->achan[chan].layer2_xmit)]); if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF) dw_printf (", DTMF decoder enabled"); dw_printf (".\n"); @@ -540,7 +543,7 @@ int demod_init (struct audio_s *pa) save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec); if (save_audio_config_p->achan[chan].decimate != 1) dw_printf (" / %d", save_audio_config_p->achan[chan].decimate); - + dw_printf (", Tx %s", layer2_tx[(int)(save_audio_config_p->achan[chan].layer2_xmit)]); if (save_audio_config_p->achan[chan].v26_alternative == V26_B) dw_printf (", compatible with MFJ-2400"); else @@ -601,6 +604,7 @@ int demod_init (struct audio_s *pa) save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec); if (save_audio_config_p->achan[chan].decimate != 1) dw_printf (" / %d", save_audio_config_p->achan[chan].decimate); + dw_printf (", Tx %s", layer2_tx[(int)(save_audio_config_p->achan[chan].layer2_xmit)]); if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF) dw_printf (", DTMF decoder enabled"); dw_printf (".\n"); @@ -736,6 +740,7 @@ int demod_init (struct audio_s *pa) save_audio_config_p->achan[chan].profiles, save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec, save_audio_config_p->achan[chan].upsample); + dw_printf (", Tx %s", layer2_tx[(int)(save_audio_config_p->achan[chan].layer2_xmit)]); if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF) dw_printf (", DTMF decoder enabled"); dw_printf (".\n"); From f61f33fa51eb22559fb7604693e8983785ef4938 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sat, 19 Oct 2024 00:41:40 +0100 Subject: [PATCH 49/57] Issue 549 - direwolf man page. --- man/direwolf.1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/man/direwolf.1 b/man/direwolf.1 index 93f786dc..c6c8fa8d 100644 --- a/man/direwolf.1 +++ b/man/direwolf.1 @@ -132,6 +132,8 @@ f = Packet filtering. x = FX.25 increase verbose level. .P d = APRStt (DTMF to APRS object conversion). +.P +2 = IL2P. .RE .RE .PD From 07708110472286acbbf8913dd05af4d340394272 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sat, 19 Oct 2024 02:17:35 +0100 Subject: [PATCH 50/57] patch from https://sources.debian.org/src/direwolf/1.7+dfsg-2/debian/patches/desktop-main-category/ --- cmake/cpack/direwolf.desktop.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/cpack/direwolf.desktop.in b/cmake/cpack/direwolf.desktop.in index 79c63aa6..6546ad7f 100644 --- a/cmake/cpack/direwolf.desktop.in +++ b/cmake/cpack/direwolf.desktop.in @@ -6,5 +6,5 @@ Icon=@CMAKE_PROJECT_NAME@_icon.png StartupNotify=true Terminal=false Type=Application -Categories=HamRadio -Keywords=Ham Radio;APRS;Soundcard TNC;KISS;AGWPE;AX.25 \ No newline at end of file +Categories=Network;HamRadio +Keywords=Ham Radio;APRS;Soundcard TNC;KISS;AGWPE;AX.25 From 33beb24fb384c8997aa2dfcdfe1e7f821e8f02b5 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sat, 19 Oct 2024 02:32:47 +0100 Subject: [PATCH 51/57] patch from https://sources.debian.org/src/direwolf/1.7+dfsg-2/debian/patches/lib-udev-rules/ --- conf/CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/conf/CMakeLists.txt b/conf/CMakeLists.txt index d4a229d7..ffc809b3 100644 --- a/conf/CMakeLists.txt +++ b/conf/CMakeLists.txt @@ -25,8 +25,13 @@ string(REGEX REPLACE "^%C%([^\n]*)" "\\1" file_content "${file_content}") file(WRITE "${CMAKE_BINARY_DIR}/direwolf.conf" "${file_content}") # install udev rules for CM108 +# There are two locations. The one in /etc/udev/rules.d is meant for local customization and +# takes precedence for the same name. +# https://sources.debian.org/src/direwolf/1.7+dfsg-2/debian/patches/lib-udev-rules/ +# says that we should use the /usr/lib/udev/rules.d location. if(LINUX) - install(FILES "${CUSTOM_CONF_DIR}/99-direwolf-cmedia.rules" DESTINATION /etc/udev/rules.d/) + #install(FILES "${CUSTOM_CONF_DIR}/99-direwolf-cmedia.rules" DESTINATION /etc/udev/rules.d/) + install(FILES "${CUSTOM_CONF_DIR}/99-direwolf-cmedia.rules" DESTINATION /usr/lib/udev/rules.d/) endif() install(FILES "${CMAKE_BINARY_DIR}/direwolf.conf" DESTINATION ${INSTALL_CONF_DIR}) From 73943ed67d46d084cb46aceead8e0638300659ec Mon Sep 17 00:00:00 2001 From: wb2osz Date: Tue, 22 Oct 2024 12:29:39 +0100 Subject: [PATCH 52/57] More error checking. --- src/decode_aprs.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/decode_aprs.c b/src/decode_aprs.c index ce658eb6..a402473f 100644 --- a/src/decode_aprs.c +++ b/src/decode_aprs.c @@ -1421,6 +1421,15 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int strlcpy (A->g_data_type_desc, "MIC-E", sizeof(A->g_data_type_desc)); + if (ilen < sizeof(struct aprs_mic_e_s)) { + if ( ! A->g_quiet) { + text_color_set(DW_COLOR_ERROR); + dw_printf("MIC-E format must have at least %d characters in the information part.\n", sizeof(struct aprs_mic_e_s)); + } + return; + } + info[ilen] = '\0';\ + p = (struct aprs_mic_e_s *)info; /* Destination is really latitude of form ddmmhh. */ @@ -3875,7 +3884,7 @@ double get_longitude_9 (char *p, int quiet) * * Inputs: p - Pointer to first byte. * - * Returns: time_t data type. (UTC) + * Returns: time_t data type. (UTC) Zero if error. * * Description: * @@ -3945,6 +3954,13 @@ time_t get_timestamp (decode_aprs_t *A, char *p) /* h = UTC. */ } *phms; + if ( ! (isdigit(p[0]) && isdigit(p[1]) && isdigit(p[2]) && isdigit(p[3]) && isdigit(p[4]) && isdigit(p[5]) && + (p[6] == 'z' || p[6] == '/' || p[6] == 'h'))) { + text_color_set(DW_COLOR_ERROR); + dw_printf("Timestamp must be 6 digits followed by z, h, or /.\n"); + return ((time_t)0); + } + struct tm *ptm; time_t ts; From 89029db6106028414fc2e17b960b55186f9cfda8 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Tue, 22 Oct 2024 13:58:55 +0100 Subject: [PATCH 53/57] More error checking. --- src/decode_aprs.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/decode_aprs.c b/src/decode_aprs.c index a402473f..81ae9bf2 100644 --- a/src/decode_aprs.c +++ b/src/decode_aprs.c @@ -1428,7 +1428,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int } return; } - info[ilen] = '\0';\ + info[ilen] = '\0'; p = (struct aprs_mic_e_s *)info; @@ -1658,12 +1658,26 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int // The rest is a comment which can have other information cryptically embedded. // Remove any trailing CR, which I would argue, violates the protocol spec. -// It is essential to keep trailing spaces. e.g. VX-8 suffix is "_ " +// It is essential to keep trailing spaces. e.g. VX-8 device id suffix is "_ " + + if (ilen <= sizeof(struct aprs_mic_e_s)) { + // Too short for a comment. We are finished. + strlcpy (A->g_mfr, "UNKNOWN vendor/model", sizeof(A->g_mfr)); + return; + } char mcomment[256]; strlcpy (mcomment, ((char*)info) + sizeof(struct aprs_mic_e_s), sizeof(mcomment)); + + assert (strlen(mcomment) > 0); + if (mcomment[strlen(mcomment)-1] == '\r') { mcomment[strlen(mcomment)-1] = '\0'; + if (strlen(mcomment) == 0) { + // Nothing left after removing trailing CR. + strlcpy (A->g_mfr, "UNKNOWN vendor/model", sizeof(A->g_mfr)); + return; + } } /* Now try to pick out manufacturer and other optional items. */ @@ -1678,7 +1692,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int // Three base 91 characters followed by } - if (strlen(trimmed) >=4 && + if (strlen(trimmed) >= 4 && isdigit91(trimmed[0]) && isdigit91(trimmed[1]) && isdigit91(trimmed[2]) && From 0d7e296d6d4d666b7af0b15a8771770c6d075e0c Mon Sep 17 00:00:00 2001 From: wb2osz Date: Tue, 22 Oct 2024 15:43:41 +0100 Subject: [PATCH 54/57] Fix MIC-E comment when device id is missing. --- src/deviceid.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/deviceid.c b/src/deviceid.c index d57a6f2f..3a4562d3 100644 --- a/src/deviceid.c +++ b/src/deviceid.c @@ -568,16 +568,18 @@ void deviceid_decode_dest (char *dest, char *device, size_t device_size) * * Inputs: comment - MIC-E comment that might have vendor/model encoded as * a prefix and/or suffix. + * Any trailing CR has already been removed. * * trimmed_size - Amount of space available for result to avoid buffer overflow. * * device_size - Amount of space available for result to avoid buffer overflow. * * Outputs: trimmed - Final comment with device vendor/model removed. + * This would include any altitude. * * device - Vendor and model. * - * Description: This has a tortured history. + * Description: MIC-E device identification has a tortured history. * * The Kenwood TH-D7A put ">" at the beginning of the comment. * The Kenwood TM-D700 put "]" at the beginning of the comment. @@ -593,7 +595,9 @@ void deviceid_decode_dest (char *dest, char *device, size_t device_size) * * References: http://www.aprs.org/aprs12/mic-e-types.txt * http://www.aprs.org/aprs12/mic-e-examples.txt - * + * https://github.com/wb2osz/aprsspec containing: + * APRS Protocol Specification 1.2 + * Understanding APRS Packets *------------------------------------------------------------------*/ // The strncmp documentation doesn't mention behavior if length is zero. @@ -612,6 +616,10 @@ static inline int strncmp_z (char *a, char *b, size_t len) void deviceid_decode_mice (char *comment, char *trimmed, size_t trimmed_size, char *device, size_t device_size) { strlcpy (device, "UNKNOWN vendor/model", device_size); + strlcpy (trimmed, comment, sizeof(trimmed)); + if (strlen(comment) < 1) { + return; + } if (ptocalls == NULL) { text_color_set(DW_COLOR_ERROR); @@ -663,6 +671,7 @@ void deviceid_decode_mice (char *comment, char *trimmed, size_t trimmed_size, ch // Not found. strlcpy (device, "UNKNOWN vendor/model", device_size); + strlcpy (trimmed, comment, sizeof(trimmed)); } // end deviceid_decode_mice From aede01d6ac33077378eb3260c484dacae6d54fd1 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Tue, 22 Oct 2024 16:41:15 +0100 Subject: [PATCH 55/57] Add another test case. --- src/deviceid.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/deviceid.c b/src/deviceid.c index 3a4562d3..036c7b0c 100644 --- a/src/deviceid.c +++ b/src/deviceid.c @@ -135,6 +135,10 @@ int main (int argc, char *argv[]) assert (strcmp(comment_out, "Comment") == 0); assert (strcmp(device, "UNKNOWN vendor/model") == 0); + deviceid_decode_mice ("", comment_out, sizeof(comment_out), device, sizeof(device)); + dw_printf ("%s %s\n", comment_out, device); + assert (strcmp(comment_out, "") == 0); + assert (strcmp(device, "UNKNOWN vendor/model") == 0); // Tocall From 44f576cb733b6394f1500058631bc5d25754bc90 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Tue, 22 Oct 2024 23:38:38 +0100 Subject: [PATCH 56/57] MIC-E improvements --- src/decode_aprs.c | 2 +- src/deviceid.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/decode_aprs.c b/src/decode_aprs.c index 81ae9bf2..acfed6b8 100644 --- a/src/decode_aprs.c +++ b/src/decode_aprs.c @@ -1424,7 +1424,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int if (ilen < sizeof(struct aprs_mic_e_s)) { if ( ! A->g_quiet) { text_color_set(DW_COLOR_ERROR); - dw_printf("MIC-E format must have at least %d characters in the information part.\n", sizeof(struct aprs_mic_e_s)); + dw_printf("MIC-E format must have at least %d characters in the information part.\n", (int)(sizeof(struct aprs_mic_e_s))); } return; } diff --git a/src/deviceid.c b/src/deviceid.c index 036c7b0c..49b9b346 100644 --- a/src/deviceid.c +++ b/src/deviceid.c @@ -620,7 +620,7 @@ static inline int strncmp_z (char *a, char *b, size_t len) void deviceid_decode_mice (char *comment, char *trimmed, size_t trimmed_size, char *device, size_t device_size) { strlcpy (device, "UNKNOWN vendor/model", device_size); - strlcpy (trimmed, comment, sizeof(trimmed)); + strlcpy (trimmed, comment, trimmed_size); if (strlen(comment) < 1) { return; } @@ -675,7 +675,7 @@ void deviceid_decode_mice (char *comment, char *trimmed, size_t trimmed_size, ch // Not found. strlcpy (device, "UNKNOWN vendor/model", device_size); - strlcpy (trimmed, comment, sizeof(trimmed)); + strlcpy (trimmed, comment, trimmed_size); } // end deviceid_decode_mice From 5736b0f601e55b2fdafe32858679b68db00f5995 Mon Sep 17 00:00:00 2001 From: wb2osz Date: Tue, 29 Oct 2024 19:41:35 +0100 Subject: [PATCH 57/57] Check index. --- src/igate.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/igate.c b/src/igate.c index 1e5d56ed..b11f7a31 100644 --- a/src/igate.c +++ b/src/igate.c @@ -1071,6 +1071,8 @@ void igate_send_rec_packet (int chan, packet_t recv_pp) * Inputs: pp - Packet object. * * chan - Radio channel where it was received. + * This will be -1 if from a beacon with sendto=ig + * so be careful if using as subscript. * * Description: Duplicate detection is handled here. * Suppress if same was sent recently. @@ -1141,7 +1143,7 @@ static void send_packet_to_server (packet_t pp, int chan) strlcat (msg, ",qAO,", sizeof(msg)); // new for version 1.4. } - strlcat (msg, save_audio_config_p->mycall[chan], sizeof(msg)); + strlcat (msg, save_audio_config_p->mycall[chan>=0 ? chan : 0], sizeof(msg)); strlcat (msg, ":", sizeof(msg));