diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc9ba53..3eecf0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,7 +83,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 8 - name: dependency @@ -149,7 +149,7 @@ jobs: make package fi - name: archive binary - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: direwolf_${{ matrix.config.os }}_${{ matrix.config.arch }}_${{ github.sha }} path: | diff --git a/CHANGES.md b/CHANGES.md index 0903e9e..69a1a85 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/CMakeLists.txt b/CMakeLists.txt index 966fbaf..7d0e1fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,6 +210,7 @@ if (C_CLANG OR C_GCC) # TODO: # Try error checking -fsanitize=bounds-strict -fsanitize=leak # Requires libubsan and liblsan, respectively. + # Maybe -fstack-protector-all, -fstack-check ###set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wvla -ffast-math -ftree-vectorize -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE ${EXTRA_FLAGS}") if(FREEBSD) diff --git a/cmake/cpack/direwolf.desktop.in b/cmake/cpack/direwolf.desktop.in index 79c63aa..6546ad7 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 diff --git a/conf/CMakeLists.txt b/conf/CMakeLists.txt index d4a229d..9f45e15 100644 --- a/conf/CMakeLists.txt +++ b/conf/CMakeLists.txt @@ -25,8 +25,18 @@ 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 when building a package. +# TODO: I think the proper solution is to select the location based on whether +# the application installation location is /usr/local or /usr. if(LINUX) - install(FILES "${CUSTOM_CONF_DIR}/99-direwolf-cmedia.rules" DESTINATION /etc/udev/rules.d/) + if (CMAKE_INSTALL_PREFIX STREQUAL "/usr/local") + install(FILES "${CUSTOM_CONF_DIR}/99-direwolf-cmedia.rules" DESTINATION /etc/udev/rules.d/) + else() + install(FILES "${CUSTOM_CONF_DIR}/99-direwolf-cmedia.rules" DESTINATION /usr/lib/udev/rules.d/) + endif() endif() install(FILES "${CMAKE_BINARY_DIR}/direwolf.conf" DESTINATION ${INSTALL_CONF_DIR}) diff --git a/conf/generic.conf b/conf/generic.conf index 41d50fe..58c4ea1 100644 --- a/conf/generic.conf +++ b/conf/generic.conf @@ -1,6 +1,6 @@ %C%############################################################# %C%# # -%C%# Configuration file for Dire Wolf # +%C%# Sample configuration file for Dire Wolf # %C%# # %L%# Linux version # %W%# Windows version # @@ -14,7 +14,7 @@ %R% It would be a maintenance burden to keep most of %R% two different versions in sync. %R% This common source is now used to generate the -%R% two different variations while having only a single +%R% three different variations while having only a single %R% copy of the common parts. %R% %R% The first column contains one of the following: @@ -38,6 +38,10 @@ %M%# /usr/local/share/doc/direwolf/ or /usr/share/doc/direwolf/ %M%# Concise "man" pages are also available for Mac OSX. %C%# +%C%# Recommended Reading for everyone: +%C%# "Understanding APRS Packets" in https://github.com/wb2osz/aprsspec +%C%# +%C%# %C%# Questions??? Join the discussion forum: https://groups.io/g/direwolf %C%# %C%# @@ -90,7 +94,7 @@ %C%############################################################# %C%# # %C%# FIRST AUDIO DEVICE PROPERTIES # -%C%# (Channel 0 + 1 if in stereo) # +%C%# (Channel 0 or 0 + 1 if in stereo) # %C%# # %C%############################################################# %C% @@ -121,11 +125,12 @@ %W%# * 4: Speakers (Realtek High Definiti (channels 0 & 1) %W%# 5: Realtek Digital Output (Realtek %W%# -%W%# Example: To use the microphone and speaker connections on the -%W%# system board, either of these forms can be used: +%W%# It is recommended that you use a unique substring of the device description. +%W%# For example, use "High" or "Realtek High Def" for the built in sound system. +%W%# Use "USB", or a longer string to distinguish amount multiple devices for a USB audio. +%W%# You can also use numbers but you are asking for trouble. Device numbers can change. %W% -%W%#ADEVICE High -%W%#ADEVICE 3 4 +%W%#ADEVICE USB %W% %W% %W%# Example: To use the USB Audio, use a command like this with @@ -158,16 +163,6 @@ %L% %L%# ADEVICE plughw:1,0 %L% -%L%# You can also use "-" or "stdin" to pipe stdout from -%L%# some other application such as a software defined radio. -%L%# "stdin" is not an audio device. Don't use this unless you -%L%# understand what this means. Read the User Guide. -%L%# You can also specify "UDP:" and an optional port for input. -%L%# Something different must be specified for output. -%L% -%L%# ADEVICE stdin plughw:1,0 -%L%# ADEVICE UDP:7355 default -%L% %R% ---------- Mac ---------- %R% %M%# Macintosh Operating System uses portaudio driver for audio @@ -182,44 +177,9 @@ %M% %M%# ADEVICE "USB Audio Codec:6" "USB Audio Codec:5" %M%# -%M%# -%M%# You can also use "-" or "stdin" to pipe stdout from -%M%# some other application such as a software defined radio. -%M%# "stdin" is not an audio device. Don't use this unless you -%M%# understand what this means. Read the User Guide. -%M%# You can also specify "UDP:" and an optional port for input. -%M%# Something different must be specified for output. %M% -%M%# ADEVICE UDP:7355 default -%M%# -%C% -%C%# -%C%# Number of audio channels for this souncard: 1 (mono) or 2 (stereo). -%C%# 1 is the default so there is no need to specify it. -%C%# -%C% -%C%#ACHANNELS 2 -%C% -%C% -%C%############################################################# -%C%# # -%C%# SECOND AUDIO DEVICE PROPERTIES # -%C%# (Channel 2 + 3 if in stereo) # -%C%# # -%C%############################################################# -%C% -%C%#ADEVICE1 ... -%C% -%C% -%C%############################################################# -%C%# # -%C%# THIRD AUDIO DEVICE PROPERTIES # -%C%# (Channel 4 + 5 if in stereo) # -%C%# # -%C%############################################################# -%C% -%C%#ADEVICE2 ... -%C% +%C%# Many more details and examples can be found in: +%C%# https://github.com/wb2osz/direwolf-doc/blob/main/Radio-Interface-Guide.pdf %C% %C%############################################################# %C%# # @@ -230,11 +190,6 @@ %C%CHANNEL 0 %C% %C%# -%C%# The following MYCALL, MODEM, PTT, etc. configuration items -%C%# apply to the most recent CHANNEL. -%C%# -%C% -%C%# %C%# Station identifier for this channel. %C%# Multiple channels can have the same or different names. %C%# @@ -259,7 +214,7 @@ %C%# In most cases you can just specify the speed. Examples: %C%# %C% -%C%MODEM 1200 +%C%#MODEM 300 %C%#MODEM 9600 %C% %C%# @@ -267,14 +222,8 @@ %C%# See User Guide for details. %C%# %C% -%C%# -%C%# Uncomment line below to enable the DTMF decoder for this channel. -%C%# -%C% -%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% %C%# If using a C-Media CM108/CM119 or similar USB Audio Adapter, @@ -284,60 +233,10 @@ %C% %C%#PTT CM108 %C% -%C%# -%C%# The transmitter Push to Talk (PTT) control can be wired to a serial port -%C%# with a suitable interface circuit. DON'T connect it directly! -%C%# -%C%# For the PTT command, specify the device and either RTS or DTR. -%C%# RTS or DTR may be preceded by "-" to invert the signal. -%C%# Both can be used for interfaces that want them driven with opposite polarity. -%C%# -%L%# COM1 can be used instead of /dev/ttyS0, COM2 for /dev/ttyS1, and so on. -%L%# %C% -%C%#PTT COM1 RTS -%C%#PTT COM1 RTS -DTR -%L%#PTT /dev/ttyUSB0 RTS -%L%#PTT /dev/ttyUSB0 RTS -DTR -%C% -%L%# -%L%# On Linux, you can also use general purpose I/O pins if -%L%# your system is configured for user access to them. -%L%# This would apply mostly to microprocessor boards, not a regular PC. -%L%# See separate Raspberry Pi document for more details. -%L%# The number may be preceded by "-" to invert the signal. -%L%# -%L% -%L%#PTT GPIO 25 -%L% -%C%# The Data Carrier Detect (DCD) signal can be sent to most of the same places -%C%# as the PTT signal. This could be used to light up an LED like a normal TNC. -%C% -%C%#DCD COM1 -DTR -%L%#DCD GPIO 24 -%C% -%C% -%C%############################################################# -%C%# # -%C%# CHANNEL 1 PROPERTIES # -%C%# # -%C%############################################################# -%C% -%C%#CHANNEL 1 -%C% -%C%# -%C%# Specify MYCALL, MODEM, PTT, etc. configuration items for -%C%# CHANNEL 1. Repeat for any other channels. -%C% -%C% -%C%############################################################# -%C%# # -%C%# TEXT TO SPEECH COMMAND FILE # -%C%# # -%C%############################################################# -%C% -%W%#SPEECH dwespeak.bat -%L%#SPEECH dwespeak.sh +%C%# There are other possibilities such as serial port RTS, Raspberry Pi GPIO pins, +%C%# and hamlib for CAT control. For more details see: +%C%# https://github.com/wb2osz/direwolf-doc/blob/main/Radio-Interface-Guide.pdf %C% %C% %C%############################################################# @@ -355,38 +254,6 @@ %W%# - KISS TNC via serial port %L%# - KISS TNC via pseudo terminal (-p command line option) %C%# -%C% -%C%AGWPORT 8000 -%C%KISSPORT 8001 -%C% -%W%# -%W%# Some applications are designed to operate with only a physical -%W%# TNC attached to a serial port. For these, we provide a virtual serial -%W%# port that appears to be connected to a TNC. -%W%# -%W%# Take a look at the User Guide for instructions to set up -%W%# two virtual serial ports named COM3 and COM4 connected by -%W%# a null modem. -%W%# -%W%# Using the configuration described, Dire Wolf will connect to -%W%# COM3 and the client application will use COM4. -%W%# -%W%# Uncomment following line to use this feature. -%W% -%W%#NULLMODEM COM3 -%W% -%W% -%C%# -%C%# It is sometimes possible to recover frames with a bad FCS. -%C%# This is not a global setting. -%C%# It applies only the the most recent CHANNEL specified. -%C%# -%C%# 0 - Don't try to repair. (default) -%C%# 1 - Attempt to fix single bit error. -%C%# -%C% -%C%#FIX_BITS 0 -%C% %C%# %C%############################################################# %C%# # @@ -404,16 +271,10 @@ %C%# Each has a series of keywords and values for options. %C%# See User Guide for details. %C%# -%C%# Example: -%C%# -%C%# This results in a broadcast once every 10 minutes. -%C%# Every half hour, it can travel via one digipeater hop. -%C%# The others are kept local. +%C%# Example: PLEASE change the latitude and longitude. %C%# %C% -%C%#PBEACON delay=1 every=30 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W power=50 height=20 gain=4 comment="Chelmsford MA" via=WIDE1-1 -%C%#PBEACON delay=11 every=30 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W power=50 height=20 gain=4 comment="Chelmsford MA" -%C%#PBEACON delay=21 every=30 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W power=50 height=20 gain=4 comment="Chelmsford MA" +%C%#PBEACON overlay=S symbol="digi" lat=42^37.14N long=071^20.83W power=50 height=20 gain=4 comment="Chelmsford MA" %C% %C%# %C%# Did you know that APRS comments and messages can contain UTF-8 characters, not only plain ASCII? @@ -422,29 +283,6 @@ %C%#PBEACON delay=11 every=30 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W comment=" Did you know that APRS comments and messages can contain UTF-8 characters? \xce\xa1\xce\xb1\xce\xb4\xce\xb9\xce\xbf\xce\xb5\xcf\x81\xce\xb1\xcf\x83\xce\xb9\xcf\x84\xce\xb5\xcf\x87\xce\xbd\xce\xb9\xcf\x83\xce\xbc\xcf\x8c\xcf\x82" %C%#PBEACON delay=21 every=30 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W comment=" Did you know that APRS comments and messages can contain UTF-8 characters? \xe3\x82\xa2\xe3\x83\x9e\xe3\x83\x81\xe3\x83\xa5\xe3\x82\xa2\xe7\x84\xa1\xe7\xb7\x9a" %C%# -%C%# With UTM coordinates instead of latitude and longitude. -%C% -%C%#PBEACON delay=1 every=10 overlay=S symbol="digi" zone=19T easting=307477 northing=4720178 -%C% -%C% -%C%# -%C%# When the destination field is set to "SPEECH" the information part is -%C%# converted to speech rather than transmitted as a data frame. -%C%# -%C% -%C%#CBEACON dest="SPEECH" info="Club meeting tonight at 7 pm." -%C% -%C%# Similar for Morse code. If SSID is specified, it is multiplied -%C%# by 2 to get speed in words per minute (WPM). -%C% -%C%#CBEACON dest="MORSE-6" info="de MYCALL" -%C% -%C% -%C%# -%C%# Modify for your particular situation before removing -%C%# the # comment character from the beginning of appropriate lines above. -%C%# -%C% %C% %C%############################################################# %C%# # @@ -491,29 +329,12 @@ %C%# That's all you need for a receive only IGate which relays %C%# messages from the local radio channel to the global servers. %C% -%C%# Some might want to send an IGate client position directly to a server -%C%# without sending it over the air and relying on someone else to -%C%# forward it to an IGate server. This is done by using sendto=IG rather -%C%# than a radio channel number. Overlay R for receive only, T for two way. -%C%# There is no need to send it as often as you would over the radio. -%C% -%C%#PBEACON sendto=IG delay=0:30 every=60:00 symbol="igate" overlay=R lat=42^37.14N long=071^20.83W -%C%#PBEACON sendto=IG delay=0:30 every=60:00 symbol="igate" overlay=T lat=42^37.14N long=071^20.83W -%C% -%C% -%C%# To relay messages from the Internet to radio, you need to add +%C%# To relay APRS "messages" from the Internet to radio, you need to add %C%# one more option with the transmit channel number and a VIA path. %C% %C%#IGTXVIA 0 WIDE1-1,WIDE2-1 %C% -%C% -%C%# Finally, we don't want to flood the radio channel. -%C%# The IGate function will limit the number of packets transmitted -%C%# during 1 minute and 5 minute intervals. If a limit would -%C%# be exceeded, the packet is dropped and message is displayed in red. -%C%# This might be low for APRS Thursday when there is abnormally high activity. -%C% -%C%IGTXLIMIT 6 10 +%C%# For more information see Successful-IGate-Operation.pdf. %C% %C% %C%############################################################# diff --git a/external/hidapi/hid.c b/external/hidapi/hid.c index e483cd4..f5c9858 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 diff --git a/man/direwolf.1 b/man/direwolf.1 index 93f786d..c6c8fa8 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 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d159e3f..44d1782 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -85,6 +85,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 2c03ada..cee4f99 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 b0ef7d8..bc2e281 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 7b12575..a2d35ec 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 c5f4ec5..a24ed72 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; @@ -1099,7 +1099,12 @@ int audio_get (int a) dw_printf ("Audio input device %d error code %d: %s\n", a, n, snd_strerror(n)); if (n == (-EPIPE)) { - dw_printf ("This is most likely caused by the CPU being too slow to keep up with the audio stream.\n"); + dw_printf ("If receiving is fine and strange things happen when transmitting, it is probably RF energy\n"); + dw_printf ("getting into your audio or digital wiring. This can cause USB to lock up or PTT to get stuck on.\n"); + dw_printf ("Move the radio, and especially the antenna, farther away from the computer.\n"); + dw_printf ("Use shieled cable and put ferrite beads on the cables to reduce RF going where it is not wanted.\n"); + dw_printf ("\n"); + dw_printf ("A less likely cause is the CPU being too slow to keep up with the audio stream.\n"); dw_printf ("Use the \"top\" command, in another command window, to look at CPU usage.\n"); dw_printf ("This might be a temporary condition so we will attempt to recover a few times before giving up.\n"); dw_printf ("If using a very slow CPU, try reducing the CPU load by using -P- command\n"); diff --git a/src/audio.h b/src/audio.h index 4fc0570..f69fc1d 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; @@ -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. */ @@ -139,10 +133,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 +157,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 +182,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; @@ -183,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. @@ -381,7 +390,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 */ @@ -390,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/audio_portaudio.c b/src/audio_portaudio.c index cb6ccf1..92ba2cb 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 85a1548..21c09ce 100644 --- a/src/audio_win.c +++ b/src/audio_win.c @@ -1,4 +1,4 @@ - +// FIXME: Add longer input timeout and more retries // // This file is part of Dire Wolf, an amateur radio packet TNC. // @@ -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; @@ -786,7 +792,8 @@ int audio_get (int a) * Wait if nothing available. * Could use an event to wake up but this is adequate. */ - int timeout = 25; + // Issue 544: change from 25 to 200. That's 2 seconds total with current buff time. + int timeout = 200; while (A->in_headp == NULL) { //SLEEP_MS (ONE_BUF_TIME / 5); @@ -888,7 +895,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 +910,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 98d6c45..50495cd 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 40fa401..52cacee 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 4a52638..2fce2df 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 @@ -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; @@ -355,12 +357,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 69a7270..b868f22 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 06128b2..844af47 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 69a4b8c..89b0302 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/cm108.c b/src/cm108.c index 27e5887..7bff379 100644 --- a/src/cm108.c +++ b/src/cm108.c @@ -262,8 +262,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. @@ -1062,8 +1063,12 @@ static int cm108_write (char *name, int iomask, int iodata) dw_printf (" crw-rw---- 1 root audio 247, 0 Oct 6 19:24 %s\n", name); dw_printf ("rather than root-only access like this:\n"); dw_printf (" crw------- 1 root root 247, 0 Sep 24 09:40 %s\n", name); + dw_printf ("This permission should be set by one of:\n"); + dw_printf ("/etc/udev/rules.d/99-direwolf-cmedia.rules\n"); + dw_printf ("/usr/lib/udev/rules.d/99-direwolf-cmedia.rules\n"); + dw_printf ("which should be created by the installation process.\n"); + dw_printf ("Your account must be in the 'audio' group.\n"); } - close (fd); return (-1); } diff --git a/src/config.c b/src/config.c index de8d74d..097c908 100644 --- a/src/config.c +++ b/src/config.c @@ -20,7 +20,7 @@ #define CONFIG_C 1 // influences behavior of aprs_tt.h -// #define DEBUG 1 +//#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,18 @@ 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 */ /* set to radio when corresponding */ /* audio device is defined. */ + } + +// MAX_RADIO_CHANS for achan[] +// Maybe achan should be renamed to radiochan to make it clearer. + 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 +987,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); } @@ -1221,9 +1233,12 @@ 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. */ +// TODO: allow full range so mycall can be set for network channels. +// Watch out for achan[] out of bounds. + else if (strcasecmp(t, "CHANNEL") == 0) { int n; t = split(NULL,0); @@ -1233,7 +1248,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 +1268,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 +1289,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 +1301,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 +1403,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])); } } } @@ -1365,6 +1438,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) { @@ -1700,6 +1779,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; @@ -1715,6 +1799,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) { @@ -1793,6 +1882,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]; @@ -2164,6 +2258,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)); @@ -2210,6 +2309,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) { @@ -2234,6 +2338,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) { @@ -2264,6 +2373,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) { @@ -2291,6 +2405,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) { @@ -2332,6 +2451,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) { @@ -2372,6 +2496,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); @@ -2399,6 +2528,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); @@ -2430,6 +2564,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) { @@ -2452,7 +2591,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. @@ -2461,6 +2600,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) { @@ -2492,6 +2636,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; @@ -2552,10 +2701,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 +2731,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 +2862,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 +2891,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 +2936,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 +2969,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 +3055,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 +3065,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 +3094,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 +3104,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 +3169,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 +3194,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 +4354,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 +4385,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 +4677,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 +4946,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 +5659,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 +5699,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 +5736,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 +5744,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 +5759,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 +5895,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 +5906,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 +5918,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 +6169,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 +6178,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 360ac49..e467523 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 08534f7..acfed6b 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 @@ -1394,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", (int)(sizeof(struct aprs_mic_e_s))); + } + return; + } + info[ilen] = '\0'; + p = (struct aprs_mic_e_s *)info; /* Destination is really latitude of form ddmmhh. */ @@ -1622,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, info + sizeof(struct aprs_mic_e_s), sizeof(mcomment)); + 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. */ @@ -1642,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]) && @@ -3848,7 +3898,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: * @@ -3918,6 +3968,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; diff --git a/src/demod.c b/src/demod.c index cc52227..ebbcbed 100644 --- a/src/demod.c +++ b/src/demod.c @@ -31,6 +31,8 @@ * *---------------------------------------------------------------*/ +#define DEMOD_C 1 + #include "direwolf.h" #include @@ -63,11 +65,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 +102,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) { @@ -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"); @@ -812,7 +817,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 +932,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 +944,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 +957,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 +1071,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 705d1fa..99432bf 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 b4d6c29..3e5d03e 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 bc05818..3d06c91 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") -#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 +//#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") + + + 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/deviceid.c b/src/deviceid.c index de910e5..49b9b34 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 @@ -528,7 +532,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 +558,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 @@ -567,16 +572,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. @@ -592,7 +599,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. @@ -610,7 +619,11 @@ 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); + strlcpy (trimmed, comment, trimmed_size); + if (strlen(comment) < 1) { + return; + } if (ptocalls == NULL) { text_color_set(DW_COLOR_ERROR); @@ -662,6 +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, trimmed_size); } // end deviceid_decode_mice diff --git a/src/digipeater.c b/src/digipeater.c index fbe8937..fcf5956 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 5c84976..46d955d 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 2dfa58d..d98cf04 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); @@ -337,6 +339,13 @@ int main (int argc, char *argv[]) #endif +// TODO: Display hardware and OS version to help with troubleshooting. +// cat /proc/cpuinfo | grep ^Model +// BSD, Deb?: /etc/os-release +// /etc/issue + + + /* * Starting with version 0.9, the prebuilt Windows version * requires a minimum of a Pentium 3 or equivalent so we can @@ -390,6 +399,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 +568,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 +647,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 +1015,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 +1126,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 +1185,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 +1219,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 +1300,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 +1315,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); + } } } } @@ -1302,10 +1335,12 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev if (alevel.rec > 110) { text_color_set(DW_COLOR_ERROR); - dw_printf ("Audio input level is too high. Reduce so most stations are around 50.\n"); + dw_printf ("Audio input level is too high. This may cause distortion and reduced decode performance.\n"); + dw_printf ("Solution is to decrease the audio input level.\n"); + dw_printf ("Setting audio input level so most stations are around 50 will provide good dyanmic range.\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 +1365,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 +1537,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 +1653,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 +1748,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 69b0952..a6db322 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 953b0f7..447366f 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 c9b26c2..e094bb4 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 9cb5c4d..8e6d422 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 7435be9..0841a3f 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 57b2741..e98e774 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 6a81655..400c292 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 d87a1b5..cfae77a 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 69b60a9..21cbf6c 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 b817018..ebaac6c 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 8a1cdc6..5b2008d 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 7f84228..4809e57 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; } @@ -998,6 +998,7 @@ void igate_send_rec_packet (int chan, packet_t recv_pp) /* * Do not relay generic query. + * TODO: Should probably block in other direction too, in case rf>is gateway did not drop. */ if (ax25_get_dti(pp) == '?') { if (s_debug >= 1) { @@ -1071,6 +1072,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 +1144,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>=0 ? chan : 0], sizeof(msg)); strlcat (msg, ":", sizeof(msg)); @@ -1781,7 +1784,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 +1859,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 +1909,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 +1968,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 +1994,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 5ad457a..a1e2726 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 3c4554e..2894876 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 aa581dd..65a0942 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/mheard.c b/src/mheard.c index f11c68f..0e88b83 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) { diff --git a/src/multi_modem.c b/src/multi_modem.c index d2382f1..7770a19 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/nettnc.c b/src/nettnc.c new file mode 100644 index 0000000..72d18cc --- /dev/null +++ b/src/nettnc.c @@ -0,0 +1,491 @@ + +// +// 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 + +#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 (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); + 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 0000000..d8a10f4 --- /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 diff --git a/src/pfilter.c b/src/pfilter.c index 35767a6..cc51519 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 af74662..ec09387 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 49040e5..d628156 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 e787dae..0666d42 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 2cc108b..a54dd6b 100644 --- a/src/server.c +++ b/src/server.c @@ -976,6 +976,14 @@ 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 +// 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) { char src[AX25_MAX_ADDR_LEN]; @@ -986,16 +994,25 @@ 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]; + + 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 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)); + } - 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)); } snprintf (result, result_size, " %d:Fm %s To %s Via %s ", chan+1, src, dst, via); @@ -1413,7 +1430,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 +1561,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 +1570,7 @@ static THREAD_F cmd_listen_thread (void *arg) } snprintf (reply.info, sizeof(reply.info), "%d;", count); - for (j=0; jchan_medium[j]) { @@ -1752,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); @@ -1850,7 +1871,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 +1900,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 +2087,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 2a6c690..d796cf1 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 0d4c26b..f5fb861 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 c656af5..0738eca 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 a73d6a4..63043b2 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 13bbaec..0d938ef 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; } } diff --git a/systemd/direwolf.service b/systemd/direwolf.service index c3380fa..8d0709f 100644 --- a/systemd/direwolf.service +++ b/systemd/direwolf.service @@ -25,3 +25,4 @@ WantedBy=multi-user.target DefaultInstance=1 # alternate version: https://www.f4fxl.org/start-direwolf-at-boot-the-systemd-way/ +# or: https://groups.io/g/direwolf/message/9883 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index da732ac..37c2b2c 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