diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..82c129b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,170 @@ +name: 'build direwolf' + +on: + # permit to manually trigger the CI + workflow_dispatch: + inputs: + cmake_flags: + description: 'Custom CMAKE flags' + required: false + push: + paths-ignore: + - '.github/**' + pull_request: + paths-ignore: + - '.github/**' + +jobs: + build: + name: ${{ matrix.config.name }} + runs-on: ${{ matrix.config.os }} + strategy: + fail-fast: false + matrix: + config: + - { + name: 'Windows Latest MinGW 64bit', + os: windows-latest, + cc: 'x86_64-w64-mingw32-gcc', + cxx: 'x86_64-w64-mingw32-g++', + ar: 'x86_64-w64-mingw32-ar', + windres: 'x86_64-w64-mingw32-windres', + arch: 'x86_64', + build_type: 'Release', + cmake_extra_flags: '-G "MinGW Makefiles"' + } + - { + name: 'Windows 2019 MinGW 32bit', + os: windows-2019, + cc: 'i686-w64-mingw32-gcc', + cxx: 'i686-w64-mingw32-g++', + ar: 'i686-w64-mingw32-ar', + windres: 'i686-w64-mingw32-windres', + arch: 'i686', + build_type: 'Release', + cmake_extra_flags: '-G "MinGW Makefiles"' + } + - { + name: 'macOS latest', + os: macos-latest, + cc: 'clang', + cxx: 'clang++', + arch: 'x86_64', + build_type: 'Release', + cmake_extra_flags: '' + } + - { + name: 'Ubuntu latest Debug', + os: ubuntu-latest, + cc: 'gcc', + cxx: 'g++', + arch: 'x86_64', + build_type: 'Debug', + cmake_extra_flags: '' + } + - { + name: 'Ubuntu 22.04', + os: ubuntu-22.04, + cc: 'gcc', + cxx: 'g++', + arch: 'x86_64', + build_type: 'Release', + cmake_extra_flags: '' + } + - { + name: 'Ubuntu 20.04', + os: ubuntu-20.04, + cc: 'gcc', + cxx: 'g++', + arch: 'x86_64', + build_type: 'Release', + cmake_extra_flags: '' + } + - { + name: 'Ubuntu 18.04', + os: ubuntu-18.04, + cc: 'gcc', + cxx: 'g++', + arch: 'x86_64', + build_type: 'Release', + cmake_extra_flags: '' + } + steps: + - name: checkout + uses: actions/checkout@v2 + with: + fetch-depth: 8 + - name: dependency + shell: bash + run: | + # this is not perfect but enought for now + if [ "$RUNNER_OS" == "Linux" ]; then + sudo apt-get update + sudo apt-get install libasound2-dev libudev-dev libhamlib-dev gpsd + elif [ "$RUNNER_OS" == "macOS" ]; then + # just to simplify I use homebrew but + # we can use macports (latest direwolf is already available as port) + brew install portaudio hamlib gpsd + elif [ "$RUNNER_OS" == "Windows" ]; then + # add the folder to PATH + echo "C:\msys64\mingw32\bin" >> $GITHUB_PATH + fi + - name: create build environment + run: | + cmake -E make_directory ${{github.workspace}}/build + - name: configure + shell: bash + working-directory: ${{github.workspace}}/build + run: | + if [ "$RUNNER_OS" == "Windows" ]; then + export CC=${{ matrix.config.cc }} + export CXX=${{ matrix.config.cxx }} + export AR=${{ matrix.config.ar }} + export WINDRES=${{ matrix.config.windres }} + fi + cmake $GITHUB_WORKSPACE \ + -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \ + -DCMAKE_C_COMPILER=${{ matrix.config.cc }} \ + -DCMAKE_CXX_COMPILER=${{ matrix.config.cxx }} \ + -DCMAKE_CXX_FLAGS="-Werror" -DUNITTEST=1 \ + ${{ matrix.config.cmake_extra_flags }} \ + ${{ github.event.inputs.cmake_flags }} + - name: build + shell: bash + working-directory: ${{github.workspace}}/build + run: | + if [ "$RUNNER_OS" == "Windows" ]; then + export CC=${{ matrix.config.cc }} + export CXX=${{ matrix.config.cxx }} + export AR=${{ matrix.config.ar }} + export WINDRES=${{ matrix.config.windres }} + fi + cmake --build . --config ${{ matrix.config.build_type }} \ + ${{ github.event.inputs.cmake_flags }} + - name: test + continue-on-error: true + shell: bash + working-directory: ${{github.workspace}}/build + run: | + ctest -C ${{ matrix.config.build_type }} \ + --parallel 2 --output-on-failure \ + ${{ github.event.inputs.cmake_flags }} + - name: package + shell: bash + working-directory: ${{github.workspace}}/build + run: | + if [ "$RUNNER_OS" == "Windows" ] || [ "$RUNNER_OS" == "macOS" ]; then + make package + fi + - name: archive binary + uses: actions/upload-artifact@v2 + with: + name: direwolf_${{ matrix.config.os }}_${{ matrix.config.arch }}_${{ github.sha }} + path: | + ${{github.workspace}}/build/direwolf-*.zip + ${{github.workspace}}/build/direwolf.conf + ${{github.workspace}}/build/src/* + ${{github.workspace}}/build/CMakeCache.txt + !${{github.workspace}}/build/src/cmake_install.cmake + !${{github.workspace}}/build/src/CMakeFiles + !${{github.workspace}}/build/src/Makefile diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..7134f21 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,73 @@ +# 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' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp', '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@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # 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@v1 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + - run: | + mkdir build + cd build + cmake -DUNITTEST=1 .. + make + make test + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/CHANGES.md b/CHANGES.md index f979b54..ba28d1d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,9 @@ ### New Features: ### +- Additional documentation location to slow down growth of main repository. [https://github.com/wb2osz/direwolf-doc](https://github.com/wb2osz/direwolf-doc) + +- New ICHANNEL configuration option to map a KISS client application channel to APRS-IS. Packets from APRS-IS will be presented to client applications as the specified channel. Packets sent, by client applications, to that channel will go to APRS-IS rather than a radio channel. Details in ***Internal-Packet-Routing.pdf***. - New variable speed option for gen_packets. For example, "-v 5,0.1" would generate packets from 5% too slow to 5% too fast with increments of 0.1. Some implementations might have imprecise timing. Use this to test how well TNCs tolerate sloppy timing. diff --git a/CMakeLists.txt b/CMakeLists.txt index 61ffc4b..e44f99b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -167,15 +167,16 @@ elseif(APPLE) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_MACOS_DNSSD") elseif (WIN32) - if(NOT VS2015 AND NOT VS2017) - message(FATAL_ERROR "You must use Microsoft Visual Studio 2015 or 2017 as compiler") + if(C_MSVC) + if (NOT VS2015 AND NOT VS2017 AND NOT VS2019) + message(FATAL_ERROR "You must use Microsoft Visual Studio 2015, 2017 or 2019 as compiler") + else() + # compile with full multicore + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + set(CUSTOM_SHELL_BIN "") + endif() endif() - - # compile with full multicore - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") - - set(CUSTOM_SHELL_BIN "") endif() if (C_CLANG OR C_GCC) diff --git a/cmake/modules/FindCompiler.cmake b/cmake/modules/FindCompiler.cmake index f339a73..fe036e4 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_EQUAL 1920 AND MSVC_VERSION LESS_EQUAL 1929) + set(VS2019 ON) + elseif(MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS_EQUAL 1919) set(VS2017 ON) elseif(MSVC_VERSION GREATER 1899 AND MSVC_VERSION LESS 1910) set(VS2015 ON) diff --git a/src/agwlib.c b/src/agwlib.c index 33d490c..2c03ada 100644 --- a/src/agwlib.c +++ b/src/agwlib.c @@ -365,7 +365,7 @@ static void * tnc_listen_thread (void *arg) } /* - * Call to/from fields are 10 bytes but contents must not exceeed 9 characters. + * Call to/from fields are 10 bytes but contents must not exceed 9 characters. * It's not guaranteed that unused bytes will contain 0 so we * don't issue error message in this case. */ diff --git a/src/atest.c b/src/atest.c index 61dbcd3..aec626f 100644 --- a/src/atest.c +++ b/src/atest.c @@ -68,6 +68,7 @@ #include #include #include +#include #define ATEST_C 1 diff --git a/src/audio.c b/src/audio.c index b8cf6b1..5335db5 100644 --- a/src/audio.c +++ b/src/audio.c @@ -1532,7 +1532,7 @@ int audio_flush (int a) * (3) Call this function, which might or might not wait long enough. * (4) Add (1) and (2) resulting in when PTT should be turned off. * (5) Take difference between current time and desired PPT off time - * and wait for additoinal time if required. + * and wait for additional time if required. * *----------------------------------------------------------------*/ diff --git a/src/audio.h b/src/audio.h index 78327a7..87d6c9c 100644 --- a/src/audio.h +++ b/src/audio.h @@ -72,7 +72,7 @@ struct audio_s { struct adev_param_s { - /* Properites of the sound device. */ + /* Properties of the sound device. */ int defined; /* Was device defined? */ /* First one defaults to yes. */ @@ -102,7 +102,7 @@ struct audio_s { /* This is the probability, in per cent, of randomly corrupting it. */ /* Normally this is 0. 25 would mean corrupt it 25% of the time. */ - int recv_error_rate; /* Similar but the % probablity of dropping a received frame. */ + int recv_error_rate; /* Similar but the % probability of dropping a received frame. */ float recv_ber; /* Receive Bit Error Rate (BER). */ /* Probability of inverting a bit coming out of the modem. */ diff --git a/src/audio_portaudio.c b/src/audio_portaudio.c index 836390b..cb6ccf1 100644 --- a/src/audio_portaudio.c +++ b/src/audio_portaudio.c @@ -1260,7 +1260,7 @@ int audio_flush (int a) * (3) Call this function, which might or might not wait long enough. * (4) Add (1) and (2) resulting in when PTT should be turned off. * (5) Take difference between current time and desired PPT off time - * and wait for additoinal time if required. + * and wait for additional time if required. * *----------------------------------------------------------------*/ diff --git a/src/audio_win.c b/src/audio_win.c index 1ba64bb..85a1548 100644 --- a/src/audio_win.c +++ b/src/audio_win.c @@ -84,7 +84,7 @@ static struct audio_s *save_audio_config_p; */ /* - * Originally, we had an abitrary buf time of 40 mS. + * Originally, we had an arbitrary buf time of 40 mS. * * For mono, the buffer size was rounded up from 3528 to 4k so * it was really about 50 mS per buffer or about 20 per second. @@ -1074,7 +1074,7 @@ int audio_flush (int a) * (3) Call this function, which might or might not wait long enough. * (4) Add (1) and (2) resulting in when PTT should be turned off. * (5) Take difference between current time and desired PPT off time - * and wait for additoinal time if required. + * and wait for additional time if required. * *----------------------------------------------------------------*/ diff --git a/src/ax25_link.c b/src/ax25_link.c index 09e7135..ab2875d 100644 --- a/src/ax25_link.c +++ b/src/ax25_link.c @@ -347,7 +347,7 @@ typedef struct ax25_dlsm_s { // Sometimes the flow chart has SAT instead of SRT. // I think that is a typographical error. - float t1v; // How long to wait for an acknowlegement before resending. + float t1v; // How long to wait for an acknowledgement before resending. // Value used when starting timer T1, in seconds. // "FRACK" parameter in some implementations. // Typically it might be 3 seconds after frame has been @@ -6049,7 +6049,7 @@ static void check_need_for_response (ax25_dlsm_t *S, ax25_frame_type_t frame_typ * * Outputs: S->srt New smoothed roundtrip time. * - * S->t1v How long to wait for an acknowlegement before resending. + * S->t1v How long to wait for an acknowledgement before resending. * Value used when starting timer T1, in seconds. * Here it is dynamically adjusted. * diff --git a/src/ax25_pad.c b/src/ax25_pad.c index b5d4763..0f07580 100644 --- a/src/ax25_pad.c +++ b/src/ax25_pad.c @@ -1866,7 +1866,7 @@ packet_t ax25_get_nextp (packet_t this_p) * * Inputs: this_p - Current packet object. * - * release_time - Time as returned by dtime_now(). + * release_time - Time as returned by dtime_monotonic(). * *------------------------------------------------------------------------------*/ @@ -2923,7 +2923,9 @@ int ax25_alevel_to_text (alevel_t alevel, char text[AX25_ALEVEL_TO_TEXT_SIZE]) snprintf (text, AX25_ALEVEL_TO_TEXT_SIZE, "%d(%+d/%+d)", alevel.rec, alevel.mark, alevel.space); } - else if (alevel.mark == -1 && alevel.space == -1) { /* PSK - single number. */ + else if ((alevel.mark == -1 && alevel.space == -1) || /* PSK */ + (alevel.mark == -99 && alevel.space == -99)) { /* v. 1.7 "B" FM demodulator. */ + // ?? Where does -99 come from? snprintf (text, AX25_ALEVEL_TO_TEXT_SIZE, "%d", alevel.rec); } diff --git a/src/beacon.c b/src/beacon.c index 4c78198..69a7270 100644 --- a/src/beacon.c +++ b/src/beacon.c @@ -162,6 +162,7 @@ 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 (g_modem_config_p->chan_medium[chan] == MEDIUM_RADIO || g_modem_config_p->chan_medium[chan] == MEDIUM_NETTNC) { @@ -621,6 +622,7 @@ static void * beacon_thread (void *arg) // On reboot, the time is in the past. // After time gets set from GPS, all beacons from that interval are sent. // FIXME: This will surely break time slotted scheduling. + // TODO: The correct fix will be using monotonic, rather than clock, time. /* craigerl: if next beacon is scheduled in the past, then set next beacon relative to now (happens when NTP pushes clock AHEAD) */ /* fixme: if NTP sets clock BACK an hour, this thread will sleep for that hour */ @@ -805,11 +807,17 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo) assert (bp->sendto_chan >= 0); - strlcpy (mycall, g_modem_config_p->achan[bp->sendto_chan].mycall, sizeof(mycall)); + 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)); + } + else { + strlcpy (mycall, g_modem_config_p->achan[bp->sendto_chan].mycall, sizeof(mycall)); + } if (strlen(mycall) == 0 || strcmp(mycall, "NOCALL") == 0) { text_color_set(DW_COLOR_ERROR); - dw_printf ("MYCALL not set for beacon in config file line %d.\n", bp->lineno); + dw_printf ("MYCALL not set for beacon to chan %d in config file line %d.\n", bp->sendto_chan, bp->lineno); return; } @@ -1046,7 +1054,7 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo) text_color_set(DW_COLOR_XMIT); dw_printf ("[ig] %s\n", beacon_text); - igate_send_rec_packet (0, pp); + igate_send_rec_packet (-1, pp); // Channel -1 to avoid RF>IS filtering. ax25_delete (pp); break; diff --git a/src/config.c b/src/config.c index 5a8eead..4f65293 100644 --- a/src/config.c +++ b/src/config.c @@ -5594,7 +5594,8 @@ 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_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); continue; @@ -5604,7 +5605,8 @@ 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_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); continue; @@ -5615,7 +5617,8 @@ 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_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); continue; @@ -5829,7 +5832,7 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_ /* * Process symbol now that we have any later overlay. * - * FIXME: Someone who used this was surprized to end up with Solar Powser (S-). + * FIXME: Someone who used this was surprised to end up with Solar Powser (S-). * overlay=S symbol="/-" * We should complain if overlay used with symtab other than \. */ @@ -5864,19 +5867,32 @@ 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_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); return (0); } - 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 (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 ) { - 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); - return (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 ) { + + 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); + return (0); + } } } diff --git a/src/decode_aprs.c b/src/decode_aprs.c index e2c2524..d7cbd72 100644 --- a/src/decode_aprs.c +++ b/src/decode_aprs.c @@ -858,6 +858,32 @@ static void aprs_ll_pos (decode_aprs_t *A, unsigned char *info, int ilen) strlcpy (A->g_data_type_desc, "Weather Report", sizeof(A->g_data_type_desc)); weather_data (A, p->comment, TRUE); +/* +Here is an interesting case. +The protocol spec states that a position report with symbol _ is a special case +and the information part must contain wxnow.txt format weather data. +But, here we see it being generated like a normal position report. + +N8VIM>BEACON,AB1OC-10*,WIDE2-1:!4240.85N/07133.99W_PHG72604/ Pepperell, MA. WX. 442.9+ PL100<0x0d> +Didn't find wind direction in form c999. +Didn't find wind speed in form s999. +Didn't find wind gust in form g999. +Didn't find temperature in form t999. +Weather Report, WEATHER Station (blue) +N 42 40.8500, W 071 33.9900 +, "PHG72604/ Pepperell, MA. WX. 442.9+ PL100" + +It seems, to me, that this is a violation of the protocol spec. +Then, immediately following, we have a positionless weather report in Ultimeter format. + +N8VIM>APN391,AB1OC-10*,WIDE2-1:$ULTW006F00CA01421C52275800008A00000102FA000F04A6000B002A<0x0d><0x0a> +Ultimeter, Kantronics KPC-3 rom versions +wind 6.9 mph, direction 284, temperature 32.2, barometer 29.75, humidity 76 + +aprs.fi merges these two together. Is that anywhere in the protocol spec or +just a heuristic added after noticing a pair of packets like this? +*/ + } else { /* Regular position report. */ @@ -1375,7 +1401,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int } } -/* 6th character of destintation indicates east / west. */ +/* 6th character of destination indicates east / west. */ /* * Example of apparently invalid encoding. 6th character missing. @@ -1579,7 +1605,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int * Purpose: Decode "Message Format." * The word message is used loosely all over the place, but it has a very specific meaning here. * - * Inputs: info - Pointer to Information field. Be carefull not to modify it here! + * Inputs: info - Pointer to Information field. Be careful not to modify it here! * ilen - Information field length. * quiet - suppress error messages. * @@ -2372,6 +2398,20 @@ static void aprs_status_report (decode_aprs_t *A, char *info, int ilen) * *------------------------------------------------------------------*/ +/* +https://groups.io/g/direwolf/topic/95961245#7357 + +What APRS queries should DireWolf respond to? Well, it should be configurable whether it responds to queries at all, in case some other application is using DireWolf as a dumb TNC (KISS or AGWPE style) and wants to handle the queries itself. + +Assuming query responding is enabled, the following broadcast queries should be supported (if the corresponding data is configured in DireWolf): + +?APRS (I am an APRS station) +?IGATE (I am operating as a I-gate) +?WX (I am providing local weather data in my beacon) + +*/ + + static void aprs_general_query (decode_aprs_t *A, char *info, int ilen, int quiet) { char *q2; @@ -2524,6 +2564,28 @@ static void aprs_general_query (decode_aprs_t *A, char *info, int ilen, int quie * *------------------------------------------------------------------*/ +/* +https://groups.io/g/direwolf/topic/95961245#7357 + +The following directed queries (sent as bodies of APRS text messages) would also be useful (if corresponding data configured): + +?APRSP (force my current beacon) +?APRST and ?PING (trace my path to requestor) +?APRSD (all stations directly heard [no digipeat hops] by local station) +?APRSO (any Objects/Items originated by this station) +?APRSH (how often or how many times the specified 3rd station was heard by the queried station) +?APRSS (immediately send the Status message if configured) (can DireWolf do Status messages?) + +Lynn KJ4ERJ and I have implemented a non-standard query which might be useful: + +?VER (send the human-readable software version of the queried station) + +Hope this is useful. It's just my $.02. + +Andrew, KA2DDO +author of YAAC +*/ + static void aprs_directed_station_query (decode_aprs_t *A, char *addressee, char *query, int quiet) { //char query_type[20]; /* Does the query type always need to be exactly 5 characters? */ diff --git a/src/demod.c b/src/demod.c index 482c107..9f94dd8 100644 --- a/src/demod.c +++ b/src/demod.c @@ -832,7 +832,7 @@ int demod_init (struct audio_s *pa) * * Name: demod_get_sample * - * Purpose: Get one audio sample fromt the specified sound input source. + * Purpose: Get one audio sample from the specified sound input source. * * Inputs: a - Index for audio device. 0 = first. * diff --git a/src/demod_afsk.c b/src/demod_afsk.c index f32137c..b4d6c29 100644 --- a/src/demod_afsk.c +++ b/src/demod_afsk.c @@ -309,10 +309,6 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq, D->lp_window = BP_WINDOW_TRUNCATED; } - D->agc_fast_attack = 0.820; - D->agc_slow_decay = 0.000214; - D->agc_fast_attack = 0.45; - D->agc_slow_decay = 0.000195; D->agc_fast_attack = 0.70; D->agc_slow_decay = 0.000090; @@ -372,10 +368,16 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq, // For scaling phase shift into normallized -1 to +1 range for mark and space. D->u.afsk.normalize_rpsam = 1.0 / (0.5 * abs(mark_freq - space_freq) * 2 * M_PI / samples_per_sec); + // New "B" demodulator does not use AGC but demod.c needs this to derive "quick" and + // "sluggish" values for overall signal amplitude. That probably should be independent + // of these values. + D->agc_fast_attack = 0.70; + D->agc_slow_decay = 0.000090; + D->pll_locked_inertia = 0.74; D->pll_searching_inertia = 0.50; - D->alevel_mark_peak = -1; // FIXME: disable display + D->alevel_mark_peak = -1; // Disable received signal (m/s) display. D->alevel_space_peak = -1; break; @@ -868,6 +870,7 @@ static void nudge_pll (int chan, int subchan, int slice, float demod_out, struct { D->slicer[slice].prev_d_c_pll = D->slicer[slice].data_clock_pll; + // Perform the add as unsigned to avoid signed overflow error. D->slicer[slice].data_clock_pll = (signed)((unsigned)(D->slicer[slice].data_clock_pll) + (unsigned)(D->pll_step_per_sample)); @@ -901,7 +904,15 @@ static void nudge_pll (int chan, int subchan, int slice, float demod_out, struct #endif + +#if 1 hdlc_rec_bit (chan, subchan, slice, demod_out > 0, 0, quality); +#else // TODO: new feature to measure data speed error. +// Maybe hdlc_rec_bit could provide indication when frame starts. + hdlc_rec_bit_new (chan, subchan, slice, demod_out > 0, 0, quality, + &(D->slicer[slice].pll_nudge_total), &(D->slicer[slice].pll_symbol_count)); + D->slicer[slice].pll_symbol_count++; +#endif pll_dcd_each_symbol2 (D, chan, subchan, slice); } @@ -912,12 +923,14 @@ static void nudge_pll (int chan, int subchan, int slice, float demod_out, struct pll_dcd_signal_transition2 (D, slice, D->slicer[slice].data_clock_pll); +// TODO: signed int before = (signed int)(D->slicer[slice].data_clock_pll); // Treat as signed. if (D->slicer[slice].data_detect) { D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_locked_inertia); } else { D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_searching_inertia); } +// TODO: D->slicer[slice].pll_nudge_total += (int64_t)((signed int)(D->slicer[slice].data_clock_pll)) - (int64_t)before; } /* diff --git a/src/direwolf.c b/src/direwolf.c index 3ad404e..f2aeca1 100644 --- a/src/direwolf.c +++ b/src/direwolf.c @@ -1053,7 +1053,7 @@ int main (int argc, char *argv[]) audio_config.achan[x_opt_chan].mark_freq, x_opt_chan); while (n-- > 0) { - tone_gen_put_bit(x_opt_chan, 0); + tone_gen_put_bit(x_opt_chan, 1); } break; case 's': // "Space" tone: -x s @@ -1061,7 +1061,7 @@ int main (int argc, char *argv[]) audio_config.achan[x_opt_chan].space_freq, x_opt_chan); while (n-- > 0) { - tone_gen_put_bit(x_opt_chan, 1); + tone_gen_put_bit(x_opt_chan, 0); } break; case 'p': // Silence - set PTT only: -x p diff --git a/src/dtime_now.c b/src/dtime_now.c index af7b98a..e5b40c6 100644 --- a/src/dtime_now.c +++ b/src/dtime_now.c @@ -25,9 +25,9 @@ /*------------------------------------------------------------------ * - * Name: dtime_now + * Name: dtime_realtime * - * Purpose: Return current time as double precision. + * Purpose: Return current wall clock time as double precision. * * Input: none * @@ -40,11 +40,24 @@ * part of a second, and having extra calculations everywhere, * simply use double precision floating point to make usage * easier. - * + * + * NOTE: This is not a good way to calculate elapsed time because + * it can jump forward or backware via NTP or other manual setting. + * + * Use the monotonic version for measuring elapsed time. + * + * History: Originally I called this dtime_now. We ran into issues where + * we really cared about elapsed time, rather than wall clock time. + * The wall clock time could be wrong at start up time if there + * is no realtime clock or Internet access. It can then jump + * when GPS time or Internet access becomes available. + * All instances of dtime_now should be replaced by dtime_realtime + * if we want wall clock time, or dtime_monotonic if it is to be + * used for measuring elapsed time, such as between becons. + * *---------------------------------------------------------------*/ - -double dtime_now (void) +double dtime_realtime (void) { double result; @@ -55,7 +68,7 @@ double dtime_now (void) GetSystemTimeAsFileTime (&ft); - result = ((( (double)ft.dwHighDateTime * (256. * 256. * 256. * 256.) + + result = ((( (double)ft.dwHighDateTime * (256. * 256. * 256. * 256.) + (double)ft.dwLowDateTime ) / 10000000.) - 11644473600.); #else /* tv_sec is seconds from Jan 1, 1970. */ @@ -63,6 +76,10 @@ double dtime_now (void) struct timespec ts; #ifdef __APPLE__ + +// Why didn't I use clock_gettime? +// Not available before Max OSX 10.12? https://github.com/gambit/gambit/issues/293 + struct timeval tp; gettimeofday(&tp, NULL); ts.tv_nsec = tp.tv_usec * 1000; @@ -75,6 +92,83 @@ double dtime_now (void) #endif +#if DEBUG + text_color_set(DW_COLOR_DEBUG); + dw_printf ("dtime_realtime() returns %.3f\n", result ); +#endif + + return (result); +} + + +/*------------------------------------------------------------------ + * + * Name: dtime_monotonic + * + * Purpose: Return montonically increasing time, which is not influenced + * by the wall clock changing. e.g. leap seconds, NTP adjustments. + * + * Input: none + * + * Returns: Time as double precision, so we can get resolution + * finer than one second. + * + * Description: Use this when calculating elapsed time. + * + *---------------------------------------------------------------*/ + +double dtime_monotonic (void) +{ + double result; + +#if __WIN32__ + +// FIXME: +// This is still returning wall clock time. +// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-gettickcount64 +// GetTickCount64 would be ideal but it requires Vista or Server 2008. +// As far as I know, the current version of direwolf still works on XP. +// +// As a work-around, GetTickCount could be used if we add extra code to deal +// with the wrap around after about 49.7 days. +// Resolution is only about 10 or 16 milliseconds. Is that good enough? + + /* 64 bit integer is number of 100 nanosecond intervals from Jan 1, 1601. */ + + FILETIME ft; + + GetSystemTimeAsFileTime (&ft); + + result = ((( (double)ft.dwHighDateTime * (256. * 256. * 256. * 256.) + + (double)ft.dwLowDateTime ) / 10000000.) - 11644473600.); +#else + /* tv_sec is seconds from Jan 1, 1970. */ + + struct timespec ts; + +#ifdef __APPLE__ + +// FIXME: Does MacOS have a monotonically increasing time? +// https://stackoverflow.com/questions/41509505/clock-gettime-on-macos + + struct timeval tp; + gettimeofday(&tp, NULL); + ts.tv_nsec = tp.tv_usec * 1000; + ts.tv_sec = tp.tv_sec; +#else + +// This is the only case handled properly. +// Probably the only one that matters. +// It is common to have a Raspberry Pi, without Internet, +// starting up direwolf before GPS/NTP adjusts the time. + + clock_gettime (CLOCK_MONOTONIC, &ts); +#endif + + result = ((double)(ts.tv_sec) + (double)(ts.tv_nsec) * 0.000000001); + +#endif + #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("dtime_now() returns %.3f\n", result ); @@ -84,6 +178,7 @@ double dtime_now (void) } + /*------------------------------------------------------------------ * * Name: timestamp_now @@ -104,7 +199,7 @@ double dtime_now (void) void timestamp_now (char *result, int result_size, int show_ms) { - double now = dtime_now(); + double now = dtime_realtime(); time_t t = (int)now; struct tm tm; @@ -150,7 +245,7 @@ void timestamp_now (char *result, int result_size, int show_ms) void timestamp_user_format (char *result, int result_size, char *user_format) { - double now = dtime_now(); + double now = dtime_realtime(); time_t t = (int)now; struct tm tm; @@ -191,7 +286,7 @@ void timestamp_user_format (char *result, int result_size, char *user_format) void timestamp_filename (char *result, int result_size) { - double now = dtime_now(); + double now = dtime_realtime(); time_t t = (int)now; struct tm tm; diff --git a/src/dtime_now.h b/src/dtime_now.h index ac22f7d..411534b 100644 --- a/src/dtime_now.h +++ b/src/dtime_now.h @@ -1,9 +1,18 @@ -extern double dtime_now (void); +extern double dtime_realtime (void); + +extern double dtime_monotonic (void); + void timestamp_now (char *result, int result_size, int show_ms); void timestamp_user_format (char *result, int result_size, char *user_format); -void timestamp_filename (char *result, int result_size); \ No newline at end of file +void timestamp_filename (char *result, int result_size); + + +// FIXME: remove temp workaround. +// Needs many scattered updates. + +#define dtime_now dtime_realtime diff --git a/src/dwgpsd.c b/src/dwgpsd.c index f8357ea..1bc9d47 100644 --- a/src/dwgpsd.c +++ b/src/dwgpsd.c @@ -57,18 +57,36 @@ -// An incompatibility was introduced with version 7 -// and again with 9 and again with 10. +// An API incompatibility was introduced with API version 7. +// and again with 9. +// and again with 10. +// We deal with it by using a bunch of conditional code such as: +// #if GPSD_API_MAJOR_VERSION >= 9 -// release lib version API Raspberry Pi OS -// 3.22 28 11 bullseye -// 3.23 29 12 -// 3.24 14 Not tested yet. -#if GPSD_API_MAJOR_VERSION < 5 || GPSD_API_MAJOR_VERSION > 12 -#error libgps API version might be incompatible. +// release lib version API Raspberry Pi OS Testing status +// 3.22 28 11 bullseye OK. +// 3.23 29 12 OK. +// 3.25 30 14 OK, Jan. 2023 + + +// Previously the compilation would fail if the API version was later +// than the last one tested. Now it is just a warning because it changes so +// often but more recent versions have not broken backward compatibility. + +#define MAX_TESTED_VERSION 14 + +#if (GPSD_API_MAJOR_VERSION < 5) || (GPSD_API_MAJOR_VERSION > MAX_TESTED_VERSION) +#pragma message "Your version of gpsd might be incompatible with this application." +#pragma message "The libgps application program interface (API) often" +#pragma message "changes to be incompatible with earlier versions." +// I could not figure out how to do value substitution here. +#pragma message "You have libgpsd API version GPSD_API_MAJOR_VERSION." +#pragma message "The last that has been tested is MAX_TESTED_VERSION." +#pragma message "Even if this builds successfully, it might not run properly." #endif + /* * Information for interface to gpsd daemon. */ @@ -168,6 +186,22 @@ static void * read_gpsd_thread (void *arg); * can't find it there. Solution is to define environment variable: * * export LD_LIBRARY_PATH=/use/local/lib + * + * January 2023: Now using 64 bit Raspberry Pi OS, bullseye. + * See https://gitlab.com/gpsd/gpsd/-/blob/master/build.adoc + * Try to install in proper library place so we don't have to mess with LD_LIBRARY_PATH. + * + * (Remove any existing gpsd first so we are not mixing mismatched pieces.) + * + * sudo apt-get install libncurses5-dev + * sudo apt-get install gtk+-3.0 + * + * git clone https://gitlab.com/gpsd/gpsd.git gpsd-gitlab + * cd gpsd-gitlab + * scons prefix=/usr libdir=lib/aarch64-linux-gnu + * [ scons check ] + * sudo scons udev-install + * */ diff --git a/src/fsk_demod_state.h b/src/fsk_demod_state.h index efb9d2c..c9b26c2 100644 --- a/src/fsk_demod_state.h +++ b/src/fsk_demod_state.h @@ -367,7 +367,7 @@ struct demodulator_state_s // Add a sample to the total when putting it in our array of recent samples. // Subtract it from the total when it gets pushed off the end. // We can also eliminate the need to shift them all down by using a circular buffer. - // This only works with integers because float would have cummulated round off errors. + // This only works with integers because float would have cumulated round off errors. cic_t cic_center1; cic_t cic_above; diff --git a/src/igate.c b/src/igate.c index 719bef4..8bdf109 100644 --- a/src/igate.c +++ b/src/igate.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2013, 2014, 2015, 2016 John Langner, WB2OSZ +// Copyright (C) 2013, 2014, 2015, 2016, 2023 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -328,7 +328,7 @@ static int stats_uplink_packets; /* Number of packets passed along to the IGate /* server after filtering. */ static int stats_uplink_bytes; /* Total number of bytes sent to IGate server */ - /* including login, packets, and hearbeats. */ + /* including login, packets, and heartbeats. */ static int stats_downlink_bytes; /* Total number of bytes from IGate server including */ /* packets, heartbeats, other messages. */ @@ -855,6 +855,9 @@ static void * connnect_thread (void *arg) * Purpose: Send a packet to the IGate server * * Inputs: chan - Radio channel it was received on. + * This is required for the RF>IS filtering. + * Beaconing (sendto=ig, chan=-1) and a client app sending + * to ICHANNEL should bypass the filtering. * * recv_pp - Pointer to packet object. * *** CALLER IS RESPONSIBLE FOR DELETING IT! ** @@ -902,7 +905,12 @@ void igate_send_rec_packet (int chan, packet_t recv_pp) * In that case, the payload will have TCPIP in the path and it will be dropped. */ - if (save_digi_config_p->filter_str[chan][MAX_CHANS] != NULL) { +// Apply RF>IS filtering only if it same from a radio channel. +// 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 (pfilter(chan, MAX_CHANS, save_digi_config_p->filter_str[chan][MAX_CHANS], recv_pp, 1) != 1) { @@ -1221,7 +1229,7 @@ static void send_packet_to_server (packet_t pp, int chan) * Name: send_msg_to_server * * Purpose: Send something to the IGate server. - * This one function should be used for login, hearbeats, + * This one function should be used for login, heartbeats, * and packets. * * Inputs: imsg - Message. We will add CR/LF here. @@ -1517,32 +1525,36 @@ static void * igate_recv_thread (void *arg) int ichan = save_audio_config_p->igate_vchannel; - // Try to parse it into a packet object. - // This will contain "q constructs" and we might see an address - // with two alphnumeric characters in the SSID so we must use - // the non-strict parsing. + // My original poorly thoughtout idea was to parse it into a packet object, + // using the non-strict option, and send to the client app. + // + // A lot of things can go wrong with that approach. - // Possible problem: Up to 8 digipeaters are allowed in radio format. - // There is a potential of finding a larger number here. + // (1) Up to 8 digipeaters are allowed in radio format. + // There is a potential of finding a larger number here. + // + // (2) The via path can have names that are not valid in the radio format. + // e.g. qAC, T2HAKATA, N5JXS-F1. + // Non-strict parsing would force uppercase, truncate names too long, + // and drop unacceptable SSIDs. + // + // (3) The source address could be invalid for the RF address format. + // e.g. WHO-IS>APJIW4,TCPIP*,qAC,AE5PL-JF::ZL1JSH-9 :Charles Beadfield/New Zealand{583 + // That is essential information that we absolutely need to preserve. + // + // I think the only correct solution is to apply a third party header + // wrapper so the original contents are preserved. This will be a little + // more work for the application developer. Search for ":}" and use only + // the part after that. At this point, I don't see any value in encoding + // information in the source/destination so I will just use "X>X:}" as a prefix - packet_t pp3 = ax25_from_text((char*)message, 0); // 0 means not strict + char stemp[AX25_MAX_INFO_LEN]; + strlcpy (stemp, "X>X:}", sizeof(stemp)); + strlcat (stemp, (char*)message, sizeof(stemp)); + + packet_t pp3 = ax25_from_text(stemp, 0); // 0 means not strict if (pp3 != NULL) { - // Should we remove the VIA path? - - // For example, we might get something like this from the server. - // Lower case 'q' and non-numeric SSID are not valid for AX.25 over the air. - // K1USN-1>APWW10,TCPIP*,qAC,N5JXS-F1:T#479,100,048,002,500,000,10000000 - - // Should we try to retain all information and pass that along, to the best of our ability, - // to the client app, or should we remove the via path so it looks like this? - // K1USN-1>APWW10:T#479,100,048,002,500,000,10000000 - - // For now, keep it intact and see if it causes problems. Easy to remove like this: - // while (ax25_get_num_repeaters(pp3) > 0) { - // ax25_remove_addr (pp3, AX25_REPEATER_1); - // } - alevel_t alevel; memset (&alevel, 0, sizeof(alevel)); alevel.mark = -2; // FIXME: Do we want some other special case? @@ -1831,8 +1843,19 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan) * If we recently transmitted a 'message' from some station, * send the position of the message sender when it comes along later. * + * Some refer to this as a courtesy posit report but I don't + * think that is an official term. + * * If we have a position report, look up the sender and see if we should * bypass the normal filtering. + * + * Reference: https://www.aprs-is.net/IGating.aspx + * + * "Passing all message packets also includes passing the sending station's position + * along with the message. When APRS-IS was small, we did this using historical position + * packets. This has become problematic as it introduces historical data on to RF. + * The IGate should note the station(s) it has gated messages to RF for and pass + * the next position packet seen for that station(s) to RF." */ // TODO: Not quite this simple. Should have a function to check for position. diff --git a/src/il2p_header.c b/src/il2p_header.c index 9a1e9ea..0ab34a0 100644 --- a/src/il2p_header.c +++ b/src/il2p_header.c @@ -437,7 +437,7 @@ packet_t il2p_decode_header_type_1 (unsigned char *hdr, int num_sym_changed) // However, I have seen cases, where the error rate is very high, where the RS decoder // thinks it found a valid code block by changing one symbol but it was the wrong one. // The result is trash. This shows up as address fields like 'R&G4"A' and 'TEW\ !'. -// I added a sanity check here to catch characters other than uppper case letters and digits. +// I added a sanity check here to catch characters other than upper case letters and digits. // The frame should be rejected in this case. The question is whether to discard it // silently or print a message so the user can see that something strange is happening? // My current thinking is that it should be silently ignored if the header has been diff --git a/src/il2p_payload.c b/src/il2p_payload.c index 67c79a9..d5fb488 100644 --- a/src/il2p_payload.c +++ b/src/il2p_payload.c @@ -194,7 +194,7 @@ int il2p_encode_payload (unsigned char *payload, int payload_size, int max_fec, * Purpose: Extract original data from encoded payload. * * Inputs: received Array of bytes. Size is unknown but in practice it - * must not exceeed IL2P_MAX_ENCODED_SIZE. + * must not exceed IL2P_MAX_ENCODED_SIZE. * payload_size 0 to 1023. (IL2P_MAX_PAYLOAD_SIZE) * Expected result size based on header. * max_fec true for 16 parity symbols, false for automatic. diff --git a/src/kiss_frame.c b/src/kiss_frame.c index 862fb71..e304a83 100644 --- a/src/kiss_frame.c +++ b/src/kiss_frame.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2013, 2014, 2017 John Langner, WB2OSZ +// Copyright (C) 2013, 2014, 2017, 2023 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -611,7 +611,8 @@ 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_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); dw_printf ("\n"); diff --git a/src/server.c b/src/server.c index 8103ea3..da20d0d 100644 --- a/src/server.c +++ b/src/server.c @@ -1421,7 +1421,7 @@ static THREAD_F cmd_listen_thread (void *arg) } /* - * Call to/from fields are 10 bytes but contents must not exceeed 9 characters. + * Call to/from fields are 10 bytes but contents must not exceed 9 characters. * It's not guaranteed that unused bytes will contain 0 so we * don't issue error message in this case. */ diff --git a/src/symbols.c b/src/symbols.c index 35dba80..c9f07e6 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -681,7 +681,7 @@ void symbols_from_dest_or_src (char dti, char *src, char *dest, char *symtab, ch // The position and object formats all contain a proper symbol and table. // There doesn't seem to be much reason to have a symbol for something without -// a postion because it would not show up on a map. +// a position because it would not show up on a map. // This just seems to be a remnant of something used long ago and no longer needed. // The protocol spec mentions a "MIM tracker" but I can't find any references to it. diff --git a/src/tq.c b/src/tq.c index 3d1b056..c656af5 100644 --- a/src/tq.c +++ b/src/tq.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2012, 2014, 2015, 2016 John Langner, WB2OSZ +// Copyright (C) 2011, 2012, 2014, 2015, 2016, 2023 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -50,7 +50,8 @@ #include "audio.h" #include "tq.h" #include "dedupe.h" - +#include "igate.h" +#include "dtime_now.h" @@ -195,6 +196,9 @@ void tq_init (struct audio_s *audio_config_p) * * Inputs: chan - Channel, 0 is first. * + * New in 1.7: + * Channel can be assigned to IGate rather than a radio. + * * prio - Priority, use TQ_PRIO_0_HI for digipeated or * TQ_PRIO_1_LO for normal. * @@ -247,6 +251,43 @@ 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. + +#ifndef DIGITEST // avoid dtest link error + + if (save_audio_config_p->chan_medium[chan] == MEDIUM_IGATE) { + + char ts[100]; // optional time stamp. + + if (strlen(save_audio_config_p->timestamp_format) > 0) { + char tstmp[100]; + timestamp_user_format (tstmp, sizeof(tstmp), save_audio_config_p->timestamp_format); + strlcpy (ts, " ", sizeof(ts)); // space after channel. + strlcat (ts, tstmp, sizeof(ts)); + } + else { + strlcpy (ts, "", sizeof(ts)); + } + + char stemp[256]; // Formated addresses. + ax25_format_addrs (pp, stemp); + 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); + 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) { text_color_set(DW_COLOR_ERROR); dw_printf ("ERROR - Request to transmit on invalid radio channel %d.\n", chan); @@ -281,8 +322,6 @@ void tq_append (int chan, int prio, packet_t pp) * The check would allow an unlimited number of other types. * * Limit was 20. Changed to 100 in version 1.2 as a workaround. - * - * Implementing the 6PACK protocol is probably the proper solution. */ if (ax25_is_aprs(pp) && tq_count(chan,prio,"","",0) > 100) { diff --git a/src/tt_user.c b/src/tt_user.c index 46e4445..a73d6a4 100644 --- a/src/tt_user.c +++ b/src/tt_user.c @@ -882,7 +882,7 @@ static void xmit_object_report (int i, int first_time) * IGate. * * When transmitting over the radio, it gets sent multiple times, to help - * probablity of being heard, with increasing delays between. + * probability of being heard, with increasing delays between. * * The other methods are reliable so we only want to send it once. */ diff --git a/src/waypoint.c b/src/waypoint.c index 70ea320..20c1cdb 100644 --- a/src/waypoint.c +++ b/src/waypoint.c @@ -298,7 +298,7 @@ void waypoint_send_sentence (char *name_in, double dlat, double dlong, char symt dw_printf ("waypoint_send_sentence (\"%s\", \"%c%c\")\n", name_in, symtab, symbol); #endif -// Don't waste time if no destintations specified. +// Don't waste time if no destinations specified. if (s_waypoint_serial_port_fd == MYFDERROR && s_waypoint_udp_sock_fd == -1) { diff --git a/src/xmit.c b/src/xmit.c index 9494dac..13bbaec 100644 --- a/src/xmit.c +++ b/src/xmit.c @@ -600,7 +600,7 @@ static void * xmit_thread (void *arg) // I don't know if this in some official specification // somewhere, but it is generally agreed that APRS digipeaters // should send only one frame at a time rather than - // bunding multiple frames into a single transmission. + // bundling multiple frames into a single transmission. // Discussion here: http://lists.tapr.org/pipermail/aprssig_lists.tapr.org/2021-September/049034.html break;