Merge branch 'dev' into dev

This commit is contained in:
wb2osz 2025-04-27 16:58:24 -04:00 committed by GitHub
commit 997894c6ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
70 changed files with 1511 additions and 643 deletions

View File

@ -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: |

View File

@ -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 ##

View File

@ -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)

View File

@ -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
Categories=Network;HamRadio
Keywords=Ham Radio;APRS;Soundcard TNC;KISS;AGWPE;AX.25

View File

@ -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})

View File

@ -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%#############################################################

View File

@ -20,6 +20,8 @@
https://github.com/libusb/hidapi .
********************************************************/
#include "../../src/direwolf.h" // for strlcpy
#include <windows.h>
#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

View File

@ -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

View File

@ -85,6 +85,7 @@ list(APPEND direwolf_SOURCES
morse.c
multi_modem.c
waypoint.c
nettnc.c
serial_port.c
pfilter.c
ptt.c

View File

@ -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);

View File

@ -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))) {

View File

@ -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<MAX_CHANS; c++) {
for (c=0; c<MAX_RADIO_CHANS; c++) {
msg_len[c] = 0;
msg_str[c][0] = '\0';
}
@ -226,7 +226,7 @@ void aprs_tt_button (int chan, char button)
{
static int poll_period = 0;
assert (chan >= 0 && chan < MAX_CHANS);
assert (chan >= 0 && chan < MAX_RADIO_CHANS);
//if (button != '.') {

View File

@ -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; channel<MAX_CHANS; channel++) {
for (channel=0; channel<MAX_RADIO_CHANS; channel++) {
my_audio_config.achan[channel].modem_type = MODEM_AFSK;
@ -628,9 +628,10 @@ int main (int argc, char *argv[])
dw_printf ("%d samples per second. %d bits per sample. %d audio channels.\n",
my_audio_config.adev[0].samples_per_sec,
my_audio_config.adev[0].bits_per_sample,
my_audio_config.adev[0].num_channels);
(int)(my_audio_config.adev[0].num_channels));
// nnum_channels is known to be 1 or 2.
one_filetime = (double) wav_data.datasize /
((my_audio_config.adev[0].bits_per_sample / 8) * my_audio_config.adev[0].num_channels * my_audio_config.adev[0].samples_per_sec);
((my_audio_config.adev[0].bits_per_sample / 8) * (int)(my_audio_config.adev[0].num_channels) * my_audio_config.adev[0].samples_per_sec);
total_filetime += one_filetime;
dw_printf ("%d audio bytes in file. Duration = %.1f seconds.\n",
@ -654,7 +655,7 @@ int main (int argc, char *argv[])
int audio_sample;
int c;
for (c=0; c<my_audio_config.adev[0].num_channels; c++)
for (c=0; c<(int)(my_audio_config.adev[0].num_channels); c++)
{
/* This reads either 1 or 2 bytes depending on */
@ -921,7 +922,7 @@ void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alev
void ptt_set (int ot, int chan, int ptt_signal)
{
// Should only get here for DCD output control.
static double dcd_start_time[MAX_CHANS];
static double dcd_start_time[MAX_RADIO_CHANS];
if (d_o_opt) {
double t = (double)sample_number / my_audio_config.adev[0].samples_per_sec;

View File

@ -257,7 +257,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;
@ -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");

View File

@ -16,7 +16,7 @@
#include <hamlib/rig.h>
#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. */

View File

@ -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;

View File

@ -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<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;
@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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 */

View File

@ -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_chan<MAX_CHANS; to_chan++) {
for (to_chan=0; to_chan<MAX_RADIO_CHANS; to_chan++) {
if (save_cdigi_config_p->enabled[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_chan<MAX_CHANS; to_chan++) {
for (to_chan=0; to_chan<MAX_RADIO_CHANS; to_chan++) {
if (save_cdigi_config_p->enabled[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]);

View File

@ -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".
};

View File

@ -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);
}

View File

@ -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; channel<MAX_CHANS; channel++) {
int ot, it;
// MAX_TOTAL_CHANS
for (channel=0; channel<MAX_TOTAL_CHANS; channel++) {
p_audio_config->chan_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; channel<MAX_RADIO_CHANS; channel++) {
int ot, it;
p_audio_config->achan[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; i<MAX_CHANS; i++) {
for (j=0; j<MAX_CHANS; j++) {
for (i=0; i<MAX_TOTAL_CHANS; i++) {
for (j=0; j<MAX_TOTAL_CHANS; j++) {
/* APRS digipeating. */
if (p_digi_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_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; j<MAX_CHANS; j++) {
for (j=0; j<MAX_TOTAL_CHANS; j++) {
if (p_audio_config->chan_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);

View File

@ -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 {

View File

@ -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;

View File

@ -31,6 +31,8 @@
*
*---------------------------------------------------------------*/
#define DEMOD_C 1
#include "direwolf.h"
#include <stdlib.h>
@ -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. */

View File

@ -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;
}

View File

@ -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);
/*

View File

@ -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;
}
/*

View File

@ -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

View File

@ -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_chan<MAX_CHANS; to_chan++) {
for (to_chan=0; to_chan<MAX_TOTAL_CHANS; to_chan++) {
if (save_digi_config_p->enabled[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_chan<MAX_CHANS; to_chan++) {
for (to_chan=0; to_chan<MAX_TOTAL_CHANS; to_chan++) {
if (save_digi_config_p->enabled[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_chan<MAX_CHANS; to_chan++) {
for (to_chan=0; to_chan<MAX_TOTAL_CHANS; to_chan++) {
if (save_digi_config_p->regen[from_chan][to_chan]) {
result = ax25_dup (pp);
if (result != NULL) {

View File

@ -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.
};

View File

@ -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");

View File

@ -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<MAX_CHANS; ch++) ...
* and make sure they handle undefined channels correctly.
*/
#define MAX_RADIO_CHANS ((MAX_ADEVS) * 2)
#define MAX_CHANS MAX_RADIO_CHANS // TODO: Replace all former with latter to avoid confusion with following.
#define MAX_TOTAL_CHANS 16 // v1.7 allows additional virtual channels which are connected
// to something other than radio modems.
@ -77,7 +72,7 @@
*/
#ifdef USE_HAMLIB
#define MAX_RIGS MAX_CHANS
#define MAX_RIGS MAX_RADIO_CHANS
#endif
/*

View File

@ -498,7 +498,7 @@ void dlq_connect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num
dw_printf ("dlq_connect_request (...)\n");
#endif
assert (chan >= 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. */

View File

@ -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; c<MAX_CHANS; c++) {
for (c=0; c<MAX_RADIO_CHANS; c++) {
struct dd_s *D = &(dd[c]);
int a = ACHAN2ADEV(c);
@ -167,7 +167,7 @@ void dtmf_init (struct audio_s *p_audio_config, int amp)
}
}
for (c=0; c<MAX_CHANS; c++) {
for (c=0; c<MAX_RADIO_CHANS; c++) {
struct dd_s *D = &(dd[c]);
D->n = 0;
for (j=0; j<NUM_TONES; j++) {
@ -214,6 +214,11 @@ char dtmf_sample (int c, float input)
'7', '8', '9', 'C',
'*', '0', '#', 'D' };
// Only applies to radio channels. Should not be here.
if (c >= MAX_RADIO_CHANS) {
return ('$');
}
D = &(dd[c]);
for (i=0; i<NUM_TONES; i++) {

View File

@ -68,7 +68,7 @@
// 3.22 28 11 bullseye OK.
// 3.23 29 12 OK.
// 3.25 30 14 OK, Jan. 2023
// 3.25.1 30 14 bookworm TBD, Feb. 2025
// Previously the compilation would fail if the API version was later
// than the last one tested. Now it is just a warning because it changes so
@ -201,7 +201,14 @@ static void * read_gpsd_thread (void *arg);
* scons prefix=/usr libdir=lib/aarch64-linux-gnu
* [ scons check ]
* sudo scons udev-install
*
*
* Start and test
*
* sudo killall gpsd
* cat /dev/ttyACM0
*
* sudo gpsd /dev/ttyACM0 -F /var/run/gpsd.sock
* cgps
*/
@ -536,7 +543,8 @@ int main (int argc, char *argv[])
while (1) {
dwfix_t fix;
fix = dwgps_read (&info) ;
fix = dwgps_read (&info)
;
text_color_set (DW_COLOR_INFO);
switch (fix) {
case DWFIX_2D:

View File

@ -125,7 +125,7 @@ int dwsock_init(void)
/*-------------------------------------------------------------------
*
* Name: sock_connect
* Name: dwsock_connect
*
* Purpose: Connect to given host / port.
*

View File

@ -596,14 +596,22 @@ int encode_position (int messaging, int compressed, double lat, double lon, int
presult[result_len] = '\0';
/* Altitude. Can be anywhere in comment. */
// Officially, altitude must be six digits.
// What about all the places on the earth's surface that are below sea level?
// https://en.wikipedia.org/wiki/List_of_places_on_land_with_elevations_below_sea_level
// The MIC-E format allows negative altitudes; not allowing it for /A=123456 seems to be an oversight.
// Most modern applications recognize the form /A=-12345 with minus and five digits.
// This maintains the same total field width and the range is more than adequate.
if (alt_ft != G_UNKNOWN) {
char salt[12];
/* Not clear if altitude can be negative. */
/* Be sure it will be converted to 6 digits. */
if (alt_ft < 0) alt_ft = 0;
// if (alt_ft < 0) alt_ft = 0;
if (alt_ft < -99999) alt_ft = -99999;
if (alt_ft > 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);
}

View File

@ -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
*

View File

@ -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.

View File

@ -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];

View File

@ -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 */

View File

@ -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.

View File

@ -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)

View File

@ -1,12 +1,22 @@
/* hdlc_rec.h */
#include <stdint.h> // 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);

View File

@ -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);

View File

@ -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];

View File

@ -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);

View File

@ -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.

View File

@ -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);

View File

@ -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;

View File

@ -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) {

View File

@ -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; chan<MAX_CHANS; chan++) {
for (chan=0; chan<MAX_RADIO_CHANS; chan++) {
if (save_audio_config_p->chan_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.

491
src/nettnc.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
//
/*------------------------------------------------------------------
*
* 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 <winsock2.h>
#include <ws2tcpip.h> // _WIN32_WINNT must be set to 0x0501 before including this
#else
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#endif
#include <unistd.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <stddef.h>
#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 */

7
src/nettnc.h Normal file
View File

@ -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);

View File

@ -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 {

View File

@ -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; 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++) {
@ -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; ch<MAX_CHANS; ch++) {
for (ch=0; ch<MAX_RADIO_CHANS; ch++) {
if (audio_config_p->chan_medium[ch] == MEDIUM_RADIO) {
if(audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_method == PTT_METHOD_NONE) {
text_color_set(DW_COLOR_INFO);
@ -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++) {

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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; j<MAX_CHANS; j++) {
for (j=0; j<MAX_TOTAL_CHANS; j++) {
if (save_audio_config_p->chan_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; j<MAX_CHANS; j++) {
for (j=0; j<MAX_TOTAL_CHANS; j++) {
switch (save_audio_config_p->chan_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);
}

View File

@ -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. */

View File

@ -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] <XOFF>\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] <XON>\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]) {

View File

@ -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; c<MAX_CHANS; c++) {
for (c=0; c<MAX_RADIO_CHANS; c++) {
for (p=0; p<TQ_NUM_PRIO; p++) {
queue_head[c][p] = NULL;
}
@ -147,7 +147,7 @@ void tq_init (struct audio_s *audio_config_p)
#if __WIN32__
for (c = 0; c < MAX_CHANS; c++) {
for (c = 0; c < MAX_RADIO_CHANS; c++) {
if (audio_config_p->chan_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<TQ_NUM_PRIO; p++) {
@ -1001,7 +1025,7 @@ int tq_count (int chan, int prio, char *source, char *dest, int bytes)
// Array bounds check. FIXME: TODO: should have internal error instead of dying.
if (chan < 0 || chan >= 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);

View File

@ -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. */

View File

@ -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; j<MAX_CHANS; j++) {
for (j=0; j<MAX_RADIO_CHANS; j++) {
xmit_bits_per_sec[j] = p_modem->achan[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; j<MAX_CHANS; j++) {
for (j=0; j<MAX_RADIO_CHANS; j++) {
if (p_modem->chan_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;
}
}

View File

@ -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

View File

@ -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