mirror of https://github.com/wb2osz/direwolf.git
				
				
				
			Merge branch 'dev' into dev
This commit is contained in:
		
						commit
						997894c6ab
					
				| 
						 | 
					@ -83,7 +83,7 @@ jobs:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: checkout
 | 
					      - name: checkout
 | 
				
			||||||
        uses: actions/checkout@v2
 | 
					        uses: actions/checkout@v3
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          fetch-depth: 8
 | 
					          fetch-depth: 8
 | 
				
			||||||
      - name: dependency
 | 
					      - name: dependency
 | 
				
			||||||
| 
						 | 
					@ -149,7 +149,7 @@ jobs:
 | 
				
			||||||
            make package
 | 
					            make package
 | 
				
			||||||
          fi
 | 
					          fi
 | 
				
			||||||
      - name: archive binary
 | 
					      - name: archive binary
 | 
				
			||||||
        uses: actions/upload-artifact@v2
 | 
					        uses: actions/upload-artifact@v3
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          name: direwolf_${{ matrix.config.os }}_${{ matrix.config.arch }}_${{ github.sha }}
 | 
					          name: direwolf_${{ matrix.config.os }}_${{ matrix.config.arch }}_${{ github.sha }}
 | 
				
			||||||
          path: |
 | 
					          path: |
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,9 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### New Features: ###
 | 
					### 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.
 | 
					- [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 ##
 | 
					## Version 1.7  --  October 2023 ##
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -210,6 +210,7 @@ if (C_CLANG OR C_GCC)
 | 
				
			||||||
  # TODO:
 | 
					  # TODO:
 | 
				
			||||||
  # Try error checking -fsanitize=bounds-strict -fsanitize=leak
 | 
					  # Try error checking -fsanitize=bounds-strict -fsanitize=leak
 | 
				
			||||||
  # Requires libubsan and liblsan, respectively.
 | 
					  # 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}")
 | 
					  ###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)
 | 
					  if(FREEBSD)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,5 +6,5 @@ Icon=@CMAKE_PROJECT_NAME@_icon.png
 | 
				
			||||||
StartupNotify=true
 | 
					StartupNotify=true
 | 
				
			||||||
Terminal=false
 | 
					Terminal=false
 | 
				
			||||||
Type=Application
 | 
					Type=Application
 | 
				
			||||||
Categories=HamRadio
 | 
					Categories=Network;HamRadio
 | 
				
			||||||
Keywords=Ham Radio;APRS;Soundcard TNC;KISS;AGWPE;AX.25
 | 
					Keywords=Ham Radio;APRS;Soundcard TNC;KISS;AGWPE;AX.25
 | 
				
			||||||
| 
						 | 
					@ -25,8 +25,18 @@ string(REGEX REPLACE "^%C%([^\n]*)" "\\1" file_content "${file_content}")
 | 
				
			||||||
file(WRITE "${CMAKE_BINARY_DIR}/direwolf.conf" "${file_content}")
 | 
					file(WRITE "${CMAKE_BINARY_DIR}/direwolf.conf" "${file_content}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# install udev rules for CM108
 | 
					# 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)
 | 
					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()
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
install(FILES "${CMAKE_BINARY_DIR}/direwolf.conf" DESTINATION ${INSTALL_CONF_DIR})
 | 
					install(FILES "${CMAKE_BINARY_DIR}/direwolf.conf" DESTINATION ${INSTALL_CONF_DIR})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
%C%#############################################################
 | 
					%C%#############################################################
 | 
				
			||||||
%C%#                                                           #
 | 
					%C%#                                                           #
 | 
				
			||||||
%C%#               Configuration file for Dire Wolf            #
 | 
					%C%#        Sample configuration file for Dire Wolf            #
 | 
				
			||||||
%C%#                                                           #
 | 
					%C%#                                                           #
 | 
				
			||||||
%L%#                   Linux version                           #
 | 
					%L%#                   Linux version                           #
 | 
				
			||||||
%W%#                   Windows version                         #
 | 
					%W%#                   Windows version                         #
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@
 | 
				
			||||||
%R%	It would be a maintenance burden to keep most of
 | 
					%R%	It would be a maintenance burden to keep most of
 | 
				
			||||||
%R%	two different versions in sync.
 | 
					%R%	two different versions in sync.
 | 
				
			||||||
%R%	This common source is now used to generate the
 | 
					%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%	copy of the common parts.
 | 
				
			||||||
%R%
 | 
					%R%
 | 
				
			||||||
%R%	The first column contains one of the following:
 | 
					%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%# /usr/local/share/doc/direwolf/ or /usr/share/doc/direwolf/
 | 
				
			||||||
%M%# Concise "man" pages are also available for Mac OSX.
 | 
					%M%# Concise "man" pages are also available for Mac OSX.
 | 
				
			||||||
%C%#
 | 
					%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%# Questions???  Join the discussion forum:  https://groups.io/g/direwolf
 | 
				
			||||||
%C%#
 | 
					%C%#
 | 
				
			||||||
%C%#
 | 
					%C%#
 | 
				
			||||||
| 
						 | 
					@ -90,7 +94,7 @@
 | 
				
			||||||
%C%#############################################################
 | 
					%C%#############################################################
 | 
				
			||||||
%C%#                                                           #
 | 
					%C%#                                                           #
 | 
				
			||||||
%C%#               FIRST AUDIO DEVICE PROPERTIES               #
 | 
					%C%#               FIRST AUDIO DEVICE PROPERTIES               #
 | 
				
			||||||
%C%#               (Channel 0 + 1 if in stereo)                #
 | 
					%C%#               (Channel 0 or 0 + 1 if in stereo)           #
 | 
				
			||||||
%C%#                                                           #
 | 
					%C%#                                                           #
 | 
				
			||||||
%C%#############################################################
 | 
					%C%#############################################################
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
| 
						 | 
					@ -121,11 +125,12 @@
 | 
				
			||||||
%W%#	 *    4: Speakers (Realtek High Definiti   (channels 0 & 1)
 | 
					%W%#	 *    4: Speakers (Realtek High Definiti   (channels 0 & 1)
 | 
				
			||||||
%W%#	      5: Realtek Digital Output (Realtek
 | 
					%W%#	      5: Realtek Digital Output (Realtek
 | 
				
			||||||
%W%#
 | 
					%W%#
 | 
				
			||||||
%W%# Example: To use the microphone and speaker connections on the
 | 
					%W%# It is recommended that you use a unique substring of the device description.
 | 
				
			||||||
%W%# system board, either of these forms can be used:
 | 
					%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%
 | 
				
			||||||
%W%#ADEVICE High
 | 
					%W%#ADEVICE USB
 | 
				
			||||||
%W%#ADEVICE  3 4
 | 
					 | 
				
			||||||
%W%
 | 
					%W%
 | 
				
			||||||
%W%
 | 
					%W%
 | 
				
			||||||
%W%# Example: To use the USB Audio, use a command like this with
 | 
					%W%# Example: To use the USB Audio, use a command like this with
 | 
				
			||||||
| 
						 | 
					@ -158,16 +163,6 @@
 | 
				
			||||||
%L%
 | 
					%L%
 | 
				
			||||||
%L%# ADEVICE  plughw:1,0
 | 
					%L%# ADEVICE  plughw:1,0
 | 
				
			||||||
%L%
 | 
					%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% ---------- Mac ----------
 | 
				
			||||||
%R%
 | 
					%R%
 | 
				
			||||||
%M%# Macintosh Operating System uses portaudio driver for audio
 | 
					%M%# Macintosh Operating System uses portaudio driver for audio
 | 
				
			||||||
| 
						 | 
					@ -182,44 +177,9 @@
 | 
				
			||||||
%M%
 | 
					%M%
 | 
				
			||||||
%M%# ADEVICE  "USB Audio Codec:6"  "USB Audio Codec:5"
 | 
					%M%# ADEVICE  "USB Audio Codec:6"  "USB Audio Codec:5"
 | 
				
			||||||
%M%#
 | 
					%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%
 | 
				
			||||||
%M%# ADEVICE UDP:7355 default
 | 
					%C%# Many more details and examples can be found in: 
 | 
				
			||||||
%M%#
 | 
					%C%# https://github.com/wb2osz/direwolf-doc/blob/main/Radio-Interface-Guide.pdf
 | 
				
			||||||
%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%
 | 
					%C%
 | 
				
			||||||
%C%#############################################################
 | 
					%C%#############################################################
 | 
				
			||||||
%C%#                                                           #
 | 
					%C%#                                                           #
 | 
				
			||||||
| 
						 | 
					@ -230,11 +190,6 @@
 | 
				
			||||||
%C%CHANNEL 0
 | 
					%C%CHANNEL 0
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
%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%# Station identifier for this channel.
 | 
				
			||||||
%C%# Multiple channels can have the same or different names.
 | 
					%C%# Multiple channels can have the same or different names.
 | 
				
			||||||
%C%#
 | 
					%C%#
 | 
				
			||||||
| 
						 | 
					@ -259,7 +214,7 @@
 | 
				
			||||||
%C%# In most cases you can just specify the speed.  Examples:
 | 
					%C%# In most cases you can just specify the speed.  Examples:
 | 
				
			||||||
%C%#
 | 
					%C%#
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
%C%MODEM 1200
 | 
					%C%#MODEM 300
 | 
				
			||||||
%C%#MODEM 9600
 | 
					%C%#MODEM 9600
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
%C%#
 | 
					%C%#
 | 
				
			||||||
| 
						 | 
					@ -267,14 +222,8 @@
 | 
				
			||||||
%C%# See User Guide for details.
 | 
					%C%# See User Guide for details.
 | 
				
			||||||
%C%#
 | 
					%C%#
 | 
				
			||||||
%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%# 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%# goes into detail about the various options.
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
%C%# If using a C-Media CM108/CM119 or similar USB Audio Adapter,
 | 
					%C%# If using a C-Media CM108/CM119 or similar USB Audio Adapter,
 | 
				
			||||||
| 
						 | 
					@ -284,60 +233,10 @@
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
%C%#PTT CM108
 | 
					%C%#PTT CM108
 | 
				
			||||||
%C%
 | 
					%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%
 | 
				
			||||||
%C%#PTT COM1 RTS
 | 
					%C%# There are other possibilities such as serial port RTS, Raspberry Pi GPIO pins,
 | 
				
			||||||
%C%#PTT COM1 RTS -DTR
 | 
					%C%# and hamlib for CAT control.   For more details see:
 | 
				
			||||||
%L%#PTT /dev/ttyUSB0 RTS
 | 
					%C%# https://github.com/wb2osz/direwolf-doc/blob/main/Radio-Interface-Guide.pdf
 | 
				
			||||||
%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%
 | 
					%C%
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
%C%#############################################################
 | 
					%C%#############################################################
 | 
				
			||||||
| 
						 | 
					@ -355,38 +254,6 @@
 | 
				
			||||||
%W%#	- KISS TNC via serial port
 | 
					%W%#	- KISS TNC via serial port
 | 
				
			||||||
%L%#	- KISS TNC via pseudo terminal   (-p command line option)
 | 
					%L%#	- KISS TNC via pseudo terminal   (-p command line option)
 | 
				
			||||||
%C%#
 | 
					%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%#############################################################
 | 
					%C%#############################################################
 | 
				
			||||||
%C%#                                                           #
 | 
					%C%#                                                           #
 | 
				
			||||||
| 
						 | 
					@ -404,16 +271,10 @@
 | 
				
			||||||
%C%# Each has a series of keywords and values for options.
 | 
					%C%# Each has a series of keywords and values for options.
 | 
				
			||||||
%C%# See User Guide for details.
 | 
					%C%# See User Guide for details.
 | 
				
			||||||
%C%#
 | 
					%C%#
 | 
				
			||||||
%C%# Example:
 | 
					%C%# Example:  PLEASE change the latitude and longitude.  
 | 
				
			||||||
%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%#
 | 
					%C%#
 | 
				
			||||||
%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 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W power=50 height=20 gain=4 comment="Chelmsford MA"
 | 
				
			||||||
%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%
 | 
					%C%
 | 
				
			||||||
%C%#
 | 
					%C%#
 | 
				
			||||||
%C%# Did you know that APRS comments and messages can contain UTF-8 characters, not only plain ASCII?
 | 
					%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=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%#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%#
 | 
				
			||||||
%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%#############################################################
 | 
					%C%#############################################################
 | 
				
			||||||
%C%#                                                           #
 | 
					%C%#                                                           #
 | 
				
			||||||
| 
						 | 
					@ -491,29 +329,12 @@
 | 
				
			||||||
%C%# That's all you need for a receive only IGate which relays
 | 
					%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%# messages from the local radio channel to the global servers.
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
%C%# Some might want to send an IGate client position directly to a server
 | 
					%C%# To relay APRS "messages" from the Internet to radio, you need to add
 | 
				
			||||||
%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%# one more option with the transmit channel number and a VIA path.
 | 
					%C%# one more option with the transmit channel number and a VIA path.
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
%C%#IGTXVIA 0 WIDE1-1,WIDE2-1
 | 
					%C%#IGTXVIA 0 WIDE1-1,WIDE2-1
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
%C%
 | 
					%C%# For more information see Successful-IGate-Operation.pdf.
 | 
				
			||||||
%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%
 | 
					%C%
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
%C%#############################################################
 | 
					%C%#############################################################
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,8 @@
 | 
				
			||||||
        https://github.com/libusb/hidapi .
 | 
					        https://github.com/libusb/hidapi .
 | 
				
			||||||
********************************************************/
 | 
					********************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../../src/direwolf.h"	// for strlcpy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <windows.h>
 | 
					#include <windows.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef _NTDEF_
 | 
					#ifndef _NTDEF_
 | 
				
			||||||
| 
						 | 
					@ -465,7 +467,8 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor
 | 
				
			||||||
			if (str) {
 | 
								if (str) {
 | 
				
			||||||
				len = strlen(str);
 | 
									len = strlen(str);
 | 
				
			||||||
				cur_dev->path = (char*) calloc(len+1, sizeof(char));
 | 
									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';
 | 
									cur_dev->path[len] = '\0';
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -132,6 +132,8 @@ f = Packet filtering.
 | 
				
			||||||
x = FX.25 increase verbose level.
 | 
					x = FX.25 increase verbose level.
 | 
				
			||||||
.P
 | 
					.P
 | 
				
			||||||
d = APRStt (DTMF to APRS object conversion).
 | 
					d = APRStt (DTMF to APRS object conversion).
 | 
				
			||||||
 | 
					.P
 | 
				
			||||||
 | 
					2 = IL2P.
 | 
				
			||||||
.RE
 | 
					.RE
 | 
				
			||||||
.RE
 | 
					.RE
 | 
				
			||||||
.PD
 | 
					.PD
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -85,6 +85,7 @@ list(APPEND direwolf_SOURCES
 | 
				
			||||||
  morse.c
 | 
					  morse.c
 | 
				
			||||||
  multi_modem.c
 | 
					  multi_modem.c
 | 
				
			||||||
  waypoint.c
 | 
					  waypoint.c
 | 
				
			||||||
 | 
					  nettnc.c
 | 
				
			||||||
  serial_port.c
 | 
					  serial_port.c
 | 
				
			||||||
  pfilter.c
 | 
					  pfilter.c
 | 
				
			||||||
  ptt.c
 | 
					  ptt.c
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -357,7 +357,7 @@ static void * tnc_listen_thread (void *arg)
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Take some precautions to guard against bad data which could cause problems later.
 | 
					 * 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);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf ("Invalid channel number, %d, in command '%c', from network TNC.\n",
 | 
						      dw_printf ("Invalid channel number, %d, in command '%c', from network TNC.\n",
 | 
				
			||||||
			cmd.hdr.portx, cmd.hdr.datakind);
 | 
								cmd.hdr.portx, cmd.hdr.datakind);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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])) {
 | 
						    if (strncasecmp(p, "Port", 4) == 0 && isdigit(p[4])) {
 | 
				
			||||||
	    
 | 
						    
 | 
				
			||||||
	      int chan = atoi(p+4) - 1;	// "Port1" is our channel 0.
 | 
						      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;
 | 
						        char *desc = p + 4;
 | 
				
			||||||
	        while (*desc != '\0' && (*desc == ' ' || isdigit(*desc))) {
 | 
						        while (*desc != '\0' && (*desc == ' ' || isdigit(*desc))) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -95,8 +95,8 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MAX_MSG_LEN 100
 | 
					#define MAX_MSG_LEN 100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static char msg_str[MAX_CHANS][MAX_MSG_LEN+1];
 | 
					static char msg_str[MAX_RADIO_CHANS][MAX_MSG_LEN+1];
 | 
				
			||||||
static int msg_len[MAX_CHANS];
 | 
					static int msg_len[MAX_RADIO_CHANS];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int parse_fields (char *msg);
 | 
					static int parse_fields (char *msg);
 | 
				
			||||||
static int parse_callsign (char *e);
 | 
					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.
 | 
						// TODO: Keep ptr instead of making a copy.
 | 
				
			||||||
	memcpy (&tt_config, p, sizeof(struct tt_config_s));
 | 
						memcpy (&tt_config, p, sizeof(struct tt_config_s));
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	for (c=0; c<MAX_CHANS; c++) {	
 | 
						for (c=0; c<MAX_RADIO_CHANS; c++) {
 | 
				
			||||||
	  msg_len[c] = 0;
 | 
						  msg_len[c] = 0;
 | 
				
			||||||
	  msg_str[c][0] = '\0';
 | 
						  msg_str[c][0] = '\0';
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -226,7 +226,7 @@ void aprs_tt_button (int chan, char button)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	static int poll_period = 0;
 | 
						static int poll_period = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//if (button != '.') {
 | 
						//if (button != '.') {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/atest.c
								
								
								
								
							
							
						
						
									
										11
									
								
								src/atest.c
								
								
								
								
							| 
						 | 
					@ -231,7 +231,7 @@ int main (int argc, char *argv[])
 | 
				
			||||||
	my_audio_config.adev[0].bits_per_sample = DEFAULT_BITS_PER_SAMPLE;	
 | 
						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;
 | 
						  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",
 | 
						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].samples_per_sec,
 | 
				
			||||||
		my_audio_config.adev[0].bits_per_sample,
 | 
							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 /
 | 
						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;
 | 
						total_filetime += one_filetime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dw_printf ("%d audio bytes in file.  Duration = %.1f seconds.\n",
 | 
						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 audio_sample;
 | 
				
			||||||
          int c;
 | 
					          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 */
 | 
					            /* 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)
 | 
					void ptt_set (int ot, int chan, int ptt_signal)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// Should only get here for DCD output control.
 | 
						// 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) {
 | 
						if (d_o_opt) {
 | 
				
			||||||
	  double t = (double)sample_number / my_audio_config.adev[0].samples_per_sec;
 | 
						  double t = (double)sample_number / my_audio_config.adev[0].samples_per_sec;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -257,7 +257,7 @@ int audio_open (struct audio_s *pa)
 | 
				
			||||||
	  if (pa->adev[a].bits_per_sample == 0)
 | 
						  if (pa->adev[a].bits_per_sample == 0)
 | 
				
			||||||
	    pa->adev[a].bits_per_sample = DEFAULT_BITS_PER_SAMPLE;
 | 
						    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)
 | 
						    if (pa->achan[chan].mark_freq == 0)
 | 
				
			||||||
	      pa->achan[chan].mark_freq = DEFAULT_MARK_FREQ;
 | 
						      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));
 | 
						        dw_printf ("Audio input device %d error code %d: %s\n", a, n, snd_strerror(n));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	        if (n == (-EPIPE)) {
 | 
						        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 ("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 ("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");
 | 
						          dw_printf ("If using a very slow CPU, try reducing the CPU load by using -P- command\n");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										38
									
								
								src/audio.h
								
								
								
								
							
							
						
						
									
										38
									
								
								src/audio.h
								
								
								
								
							| 
						 | 
					@ -16,7 +16,7 @@
 | 
				
			||||||
#include <hamlib/rig.h>
 | 
					#include <hamlib/rig.h>
 | 
				
			||||||
#endif
 | 
					#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 "ax25_pad.h"		/* for AX25_MAX_ADDR_LEN */
 | 
				
			||||||
#include "version.h"
 | 
					#include "version.h"
 | 
				
			||||||
				
 | 
									
 | 
				
			||||||
| 
						 | 
					@ -59,7 +59,7 @@ typedef enum retry_e {
 | 
				
			||||||
enum medium_e { MEDIUM_NONE = 0,	// Channel is not valid for use.
 | 
					enum medium_e { MEDIUM_NONE = 0,	// Channel is not valid for use.
 | 
				
			||||||
		MEDIUM_RADIO,		// Internal modem for radio.
 | 
							MEDIUM_RADIO,		// Internal modem for radio.
 | 
				
			||||||
		MEDIUM_IGATE,		// Access IGate as ordinary channel.
 | 
							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;
 | 
					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). */
 | 
						float recv_ber;			/* Receive Bit Error Rate (BER). */
 | 
				
			||||||
					/* Probability of inverting a bit coming out of the modem. */
 | 
										/* 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 */
 | 
						int fx25_auto_enable;		/* Turn on FX.25 for current connected mode session */
 | 
				
			||||||
					/* under poor conditions. */
 | 
										/* under poor conditions. */
 | 
				
			||||||
					/* Set to 0 to disable feature. */
 | 
										/* Set to 0 to disable feature. */
 | 
				
			||||||
| 
						 | 
					@ -139,10 +133,19 @@ struct audio_s {
 | 
				
			||||||
	/* originally a "channel" was always connected to an internal modem. */
 | 
						/* 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) */
 | 
						/* 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 */
 | 
						/* 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.
 | 
						// 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];
 | 
						enum medium_e chan_medium[MAX_TOTAL_CHANS];
 | 
				
			||||||
					// MEDIUM_NONE for invalid.
 | 
										// MEDIUM_NONE for invalid.
 | 
				
			||||||
					// MEDIUM_RADIO for internal modem.  (only possibility earlier)
 | 
										// MEDIUM_RADIO for internal modem.  (only possibility earlier)
 | 
				
			||||||
| 
						 | 
					@ -154,6 +157,14 @@ struct audio_s {
 | 
				
			||||||
					/* Redundant but it makes things quicker and simpler */
 | 
										/* Redundant but it makes things quicker and simpler */
 | 
				
			||||||
					/* than always searching thru above. */
 | 
										/* 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. */
 | 
						/* Properties for each radio channel, common to receive and transmit. */
 | 
				
			||||||
	/* Can be different for each radio channel. */
 | 
						/* 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]
 | 
						    // 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.  ???
 | 
						    // 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;
 | 
						    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. */
 | 
										/* Might try MFJ-2400 / CCITT v.26 / Bell 201 someday. */
 | 
				
			||||||
					/* No modem.  Might want this for DTMF only channel. */
 | 
										/* 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.
 | 
										// IL2P - New for version 1.7.
 | 
				
			||||||
					// New layer 2 with FEC.  Much less overhead than FX.25 but no longer backward compatible.
 | 
										// 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. */
 | 
						    int fulldup;		/* Full Duplex. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	} achan[MAX_CHANS];
 | 
						} achan[MAX_RADIO_CHANS];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef USE_HAMLIB
 | 
					#ifdef USE_HAMLIB
 | 
				
			||||||
    int rigs;               /* Total number of configured rigs */
 | 
					    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__
 | 
					#if __WIN32__
 | 
				
			||||||
#define DEFAULT_ADEVICE	""		/* Windows: Empty string = default audio device. */
 | 
					#define DEFAULT_ADEVICE	""		/* Windows: Empty string = default audio device. */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -578,7 +578,7 @@ int audio_open (struct audio_s *pa)
 | 
				
			||||||
		if (pa->adev[a].bits_per_sample == 0)
 | 
							if (pa->adev[a].bits_per_sample == 0)
 | 
				
			||||||
			pa->adev[a].bits_per_sample = DEFAULT_BITS_PER_SAMPLE;
 | 
								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)
 | 
								if (pa->achan[chan].mark_freq == 0)
 | 
				
			||||||
				pa->achan[chan].mark_freq = DEFAULT_MARK_FREQ;
 | 
									pa->achan[chan].mark_freq = DEFAULT_MARK_FREQ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
 | 
					// FIXME:  Add longer input timeout and more retries
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//    This file is part of Dire Wolf, an amateur radio packet TNC.
 | 
					//    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;
 | 
						    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)
 | 
						      if (pa -> achan[chan].mark_freq == 0)
 | 
				
			||||||
	        pa -> achan[chan].mark_freq = DEFAULT_MARK_FREQ;
 | 
						        pa -> achan[chan].mark_freq = DEFAULT_MARK_FREQ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -660,7 +660,13 @@ int audio_open (struct audio_s *pa)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
   	       case AUDIO_IN_TYPE_STDIN:
 | 
					   	       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_next= 0;
 | 
				
			||||||
	         A->stream_len = 0;
 | 
						         A->stream_len = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -786,7 +792,8 @@ int audio_get (int a)
 | 
				
			||||||
	       * Wait if nothing available.
 | 
						       * Wait if nothing available.
 | 
				
			||||||
	       * Could use an event to wake up but this is adequate.
 | 
						       * 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) {
 | 
						      while (A->in_headp == NULL) {
 | 
				
			||||||
	        //SLEEP_MS (ONE_BUF_TIME / 5);
 | 
						        //SLEEP_MS (ONE_BUF_TIME / 5);
 | 
				
			||||||
| 
						 | 
					@ -888,7 +895,7 @@ int audio_get (int a)
 | 
				
			||||||
	    while (A->stream_next >= A->stream_len) {
 | 
						    while (A->stream_next >= A->stream_len) {
 | 
				
			||||||
	      int res;
 | 
						      int res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      res = read(STDIN_FILENO, A->stream_data, 1024);
 | 
						      res = read(STDIN_FILENO, A->stream_data, sizeof(A->stream_data));
 | 
				
			||||||
	      if (res <= 0) {
 | 
						      if (res <= 0) {
 | 
				
			||||||
	        text_color_set(DW_COLOR_INFO);
 | 
						        text_color_set(DW_COLOR_INFO);
 | 
				
			||||||
	        dw_printf ("\nEnd of file on stdin.  Exiting.\n");
 | 
						        dw_printf ("\nEnd of file on stdin.  Exiting.\n");
 | 
				
			||||||
| 
						 | 
					@ -903,9 +910,13 @@ int audio_get (int a)
 | 
				
			||||||
	      A->stream_len = res;
 | 
						      A->stream_len = res;
 | 
				
			||||||
	      A->stream_next = 0;
 | 
						      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;
 | 
						    break;
 | 
				
			||||||
  	}
 | 
					
 | 
				
			||||||
 | 
						}  // end switch audio in type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return (-1);
 | 
						return (-1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//    This file is part of Dire Wolf, an amateur radio packet TNC.
 | 
					//    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
 | 
					//    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
 | 
					//    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.
 | 
					 * Inputs:	pconfig		- misc. configuration from config file or command line.
 | 
				
			||||||
 *				  Beacon stuff ended up here.
 | 
					 *				  Beacon stuff ended up here.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
					 *		debug 		- debug level.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 * Outputs:	Remember required information for future use.  That's all.
 | 
					 * 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;
 | 
						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 */
 | 
					} /* 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 dcd_status[MAX_RADIO_CHANS];
 | 
				
			||||||
static int ptt_status[MAX_CHANS];
 | 
					static int ptt_status[MAX_RADIO_CHANS];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void lm_channel_busy (dlq_item_t *E)
 | 
					void lm_channel_busy (dlq_item_t *E)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int busy;
 | 
						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->activity == OCTYPE_PTT || E->activity == OCTYPE_DCD);
 | 
				
			||||||
	assert (E->status == 1 || E->status == 0);
 | 
						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)
 | 
					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;
 | 
						ax25_dlsm_t *S;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,7 +43,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Call once at startup time.
 | 
					// 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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//    This file is part of Dire Wolf, an amateur radio packet TNC.
 | 
					//    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
 | 
					//    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
 | 
					//    it under the terms of the GNU General Public License as published by
 | 
				
			||||||
| 
						 | 
					@ -174,6 +174,7 @@
 | 
				
			||||||
#include "regex.h"
 | 
					#include "regex.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if __WIN32__
 | 
					#if __WIN32__
 | 
				
			||||||
 | 
					// TODO:  Why is this here, rather than in direwolf.h?
 | 
				
			||||||
char *strtok_r(char *str, const char *delim, char **saveptr);
 | 
					char *strtok_r(char *str, const char *delim, char **saveptr);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -194,6 +195,7 @@ static volatile int last_seq_num = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if AX25MEMDEBUG
 | 
					#if AX25MEMDEBUG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO:  Make static and use function for any extern references.
 | 
				
			||||||
int ax25memdebug = 0;
 | 
					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.
 | 
					 *			  The SSID can be 2 alphanumeric characters, not just 1 to 15.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *			  We can just truncate the name because we will only
 | 
					 *			  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.
 | 
					 * Returns:	Pointer to new packet object in the current implementation.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Outputs:	Use the "get" functions to retrieve information in different ways.
 | 
					 * 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
 | 
					#if AX25MEMDEBUG
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										14
									
								
								src/beacon.c
								
								
								
								
							
							
						
						
									
										14
									
								
								src/beacon.c
								
								
								
								
							| 
						 | 
					@ -162,14 +162,14 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct
 | 
				
			||||||
	  int chan = g_misc_config_p->beacon[j].sendto_chan;
 | 
						  int chan = g_misc_config_p->beacon[j].sendto_chan;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  if (chan < 0) chan = 0;	/* For IGate, use channel 0 call. */
 | 
						  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 ||
 | 
						  if (g_modem_config_p->chan_medium[chan] == MEDIUM_RADIO ||
 | 
				
			||||||
	      g_modem_config_p->chan_medium[chan] == MEDIUM_NETTNC) {
 | 
						      g_modem_config_p->chan_medium[chan] == MEDIUM_NETTNC) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    if (strlen(g_modem_config_p->achan[chan].mycall) > 0 &&
 | 
						    if (strlen(g_modem_config_p->mycall[chan]) > 0 &&
 | 
				
			||||||
			 strcasecmp(g_modem_config_p->achan[chan].mycall, "N0CALL") != 0 &&
 | 
								 strcasecmp(g_modem_config_p->mycall[chan], "N0CALL") != 0 &&
 | 
				
			||||||
			 strcasecmp(g_modem_config_p->achan[chan].mycall, "NOCALL") != 0) {
 | 
								 strcasecmp(g_modem_config_p->mycall[chan], "NOCALL") != 0) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              switch (g_misc_config_p->beacon[j].btype) {
 | 
					              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.
 | 
						      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.
 | 
														// 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 {
 | 
						      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) {
 | 
						      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:
 | 
							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->symtab, bp->symbol,
 | 
				
			||||||
			bp->power, bp->height, bp->gain, bp->dir,
 | 
								bp->power, bp->height, bp->gain, bp->dir,
 | 
				
			||||||
			G_UNKNOWN, G_UNKNOWN, /* course, speed */
 | 
								G_UNKNOWN, G_UNKNOWN, /* course, speed */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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.
 | 
					 * 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) {
 | 
					int cdigipeater_get_count (int from_chan, int to_chan) {
 | 
				
			||||||
	return (cdigi_count[from_chan][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.
 | 
						// 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.
 | 
						// 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);
 | 
						  text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	  dw_printf ("cdigipeater: Did not expect to receive on invalid channel %d.\n", from_chan);
 | 
						  dw_printf ("cdigipeater: Did not expect to receive on invalid channel %d.\n", from_chan);
 | 
				
			||||||
	  return;
 | 
						  return;
 | 
				
			||||||
| 
						 | 
					@ -145,13 +147,13 @@ void cdigipeater (int from_chan, packet_t pp)
 | 
				
			||||||
 * Might not have a benefit here.
 | 
					 * 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 (save_cdigi_config_p->enabled[from_chan][to_chan]) {
 | 
				
			||||||
	    if (to_chan == from_chan) {
 | 
						    if (to_chan == from_chan) {
 | 
				
			||||||
	      packet_t result;
 | 
						      packet_t result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      result = cdigipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall, 
 | 
						      result = cdigipeat_match (from_chan, pp, save_audio_config_p->mycall[from_chan],
 | 
				
			||||||
					   save_audio_config_p->achan[to_chan].mycall,
 | 
										   save_audio_config_p->mycall[to_chan],
 | 
				
			||||||
			save_cdigi_config_p->has_alias[from_chan][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->alias[from_chan][to_chan]), to_chan,
 | 
				
			||||||
				save_cdigi_config_p->cfilter_str[from_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.
 | 
					 * 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 (save_cdigi_config_p->enabled[from_chan][to_chan]) {
 | 
				
			||||||
	    if (to_chan != from_chan) {
 | 
						    if (to_chan != from_chan) {
 | 
				
			||||||
	      packet_t result;
 | 
						      packet_t result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      result = cdigipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall, 
 | 
						      result = cdigipeat_match (from_chan, pp, save_audio_config_p->mycall[from_chan],
 | 
				
			||||||
					   save_audio_config_p->achan[to_chan].mycall,
 | 
										   save_audio_config_p->mycall[to_chan],
 | 
				
			||||||
	                save_cdigi_config_p->has_alias[from_chan][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->alias[from_chan][to_chan]), to_chan,
 | 
				
			||||||
				save_cdigi_config_p->cfilter_str[from_chan][to_chan]);
 | 
									save_cdigi_config_p->cfilter_str[from_chan][to_chan]);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "regex.h"
 | 
					#include "regex.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "direwolf.h"		/* for MAX_CHANS */
 | 
					#include "direwolf.h"		/* for MAX_RADIO_CHANS */
 | 
				
			||||||
#include "ax25_pad.h"		/* for packet_t */
 | 
					#include "ax25_pad.h"		/* for packet_t */
 | 
				
			||||||
#include "audio.h"		/* for radio channel properties */
 | 
					#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.
 | 
					 * 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
 | 
											// the structure below will not be set up
 | 
				
			||||||
						// properly and an attempt to use it could
 | 
											// properly and an attempt to use it could
 | 
				
			||||||
						// result in a crash.  (fixed v1.5)
 | 
											// result in a crash.  (fixed v1.5)
 | 
				
			||||||
						// Not needed for [APRS] DIGIPEAT because
 | 
											// Not needed for [APRS] DIGIPEAT because
 | 
				
			||||||
						// the alias is mandatory there.
 | 
											// 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".
 | 
											// NULL or optional Packet Filter strings such as "t/m".
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -262,8 +262,9 @@ static void substr_se (char *dest, const char *src, int start, int endp1)
 | 
				
			||||||
// Maximum length of name for PTT HID.
 | 
					// Maximum length of name for PTT HID.
 | 
				
			||||||
// For Linux, this was originally 17 to handle names like /dev/hidraw3.
 | 
					// 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.
 | 
					// 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.
 | 
					 * 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 ("    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 ("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 ("    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);
 | 
						  close (fd);
 | 
				
			||||||
	  return (-1);
 | 
						  return (-1);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										307
									
								
								src/config.c
								
								
								
								
							
							
						
						
									
										307
									
								
								src/config.c
								
								
								
								
							| 
						 | 
					@ -20,7 +20,7 @@
 | 
				
			||||||
#define CONFIG_C 1		// influences behavior of aprs_tt.h
 | 
					#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 ("    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 ("    development version:    https://github.com/wb2osz/direwolf/tree/dev/doc\n");
 | 
				
			||||||
	dw_printf ("    additional topics:    https://github.com/wb2osz/direwolf-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, 
 | 
					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.
 | 
						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++) {
 | 
					// MAX_TOTAL_CHANS
 | 
				
			||||||
	  int ot, it;
 | 
						for (channel=0; channel<MAX_TOTAL_CHANS; channel++) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
	  p_audio_config->chan_medium[channel] = MEDIUM_NONE;	/* One or both channels will be */
 | 
						  p_audio_config->chan_medium[channel] = MEDIUM_NONE;	/* One or both channels will be */
 | 
				
			||||||
								/* set to radio when corresponding */
 | 
													/* set to radio when corresponding */
 | 
				
			||||||
								/* audio device is defined. */
 | 
													/* 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].modem_type = MODEM_AFSK;			
 | 
				
			||||||
	  p_audio_config->achan[channel].v26_alternative = V26_UNSPECIFIED;
 | 
						  p_audio_config->achan[channel].v26_alternative = V26_UNSPECIFIED;
 | 
				
			||||||
	  p_audio_config->achan[channel].mark_freq = DEFAULT_MARK_FREQ;		/* -m option */
 | 
						  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)	{
 | 
						if (fp == NULL)	{
 | 
				
			||||||
	  // TODO: not exactly right for all situations.
 | 
						  // TODO: not exactly right for all situations.
 | 
				
			||||||
	  text_color_set(DW_COLOR_ERROR);
 | 
						  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");
 | 
						  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();
 | 
						  rtfm();
 | 
				
			||||||
	  exit(EXIT_FAILURE);
 | 
						  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) {
 | 
						  else if (strcasecmp(t, "CHANNEL") == 0) {
 | 
				
			||||||
	    int n;
 | 
						    int n;
 | 
				
			||||||
	    t = split(NULL,0);
 | 
						    t = split(NULL,0);
 | 
				
			||||||
| 
						 | 
					@ -1233,7 +1248,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
	      continue;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    n = atoi(t);
 | 
						    n = atoi(t);
 | 
				
			||||||
            if (n >= 0 && n < MAX_CHANS) {
 | 
					            if (n >= 0 && n < MAX_RADIO_CHANS) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      channel = n;
 | 
						      channel = n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1253,7 +1268,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    else {
 | 
						    else {
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      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;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    int ichan = atoi(t);
 | 
						    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) {
 | 
						      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 {
 | 
						      else {
 | 
				
			||||||
	        text_color_set(DW_COLOR_ERROR);
 | 
						        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 {
 | 
						    else {
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      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
 | 
					 * MYCALL station
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -1330,14 +1403,14 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      int c;
 | 
						      int c;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      for (c = 0; c < MAX_CHANS; c++) {
 | 
						      for (c = 0; c < MAX_TOTAL_CHANS; c++) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	        if (c == channel || 
 | 
						        if (c == channel || 
 | 
				
			||||||
			strlen(p_audio_config->achan[c].mycall) == 0 || 
 | 
								strlen(p_audio_config->mycall[c]) == 0 || 
 | 
				
			||||||
			strcasecmp(p_audio_config->achan[c].mycall, "NOCALL") == 0 ||
 | 
								strcasecmp(p_audio_config->mycall[c], "NOCALL") == 0 ||
 | 
				
			||||||
			strcasecmp(p_audio_config->achan[c].mycall, "N0CALL") == 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) {
 | 
						  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;
 | 
						    int n;
 | 
				
			||||||
	    t = split(NULL,0);
 | 
						    t = split(NULL,0);
 | 
				
			||||||
	    if (t == NULL) {
 | 
						    if (t == NULL) {
 | 
				
			||||||
| 
						 | 
					@ -1700,6 +1779,11 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  else if (strcasecmp(t, "DTMF") == 0) {
 | 
						  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;
 | 
						    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) {
 | 
						  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;
 | 
						    int n;
 | 
				
			||||||
	    t = split(NULL,0);
 | 
						    t = split(NULL,0);
 | 
				
			||||||
	    if (t == NULL) {
 | 
						    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) {
 | 
						  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;
 | 
						    int ot;
 | 
				
			||||||
	    char otname[8];
 | 
						    char otname[8];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2164,6 +2258,11 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  else if (strcasecmp(t, "TXINH") == 0) {
 | 
						  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];
 | 
						    char itname[8];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    strlcpy (itname, "TXINH", sizeof(itname));
 | 
						    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) {
 | 
						  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;
 | 
						    int n;
 | 
				
			||||||
	    t = split(NULL,0);
 | 
						    t = split(NULL,0);
 | 
				
			||||||
	    if (t == NULL) {
 | 
						    if (t == NULL) {
 | 
				
			||||||
| 
						 | 
					@ -2234,6 +2338,11 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  else if (strcasecmp(t, "SLOTTIME") == 0) {
 | 
						  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;
 | 
						    int n;
 | 
				
			||||||
	    t = split(NULL,0);
 | 
						    t = split(NULL,0);
 | 
				
			||||||
	    if (t == NULL) {
 | 
						    if (t == NULL) {
 | 
				
			||||||
| 
						 | 
					@ -2264,6 +2373,11 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  else if (strcasecmp(t, "PERSIST") == 0) {
 | 
						  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;
 | 
						    int n;
 | 
				
			||||||
	    t = split(NULL,0);
 | 
						    t = split(NULL,0);
 | 
				
			||||||
	    if (t == NULL) {
 | 
						    if (t == NULL) {
 | 
				
			||||||
| 
						 | 
					@ -2291,6 +2405,11 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  else if (strcasecmp(t, "TXDELAY") == 0) {
 | 
						  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;
 | 
						    int n;
 | 
				
			||||||
	    t = split(NULL,0);
 | 
						    t = split(NULL,0);
 | 
				
			||||||
	    if (t == NULL) {
 | 
						    if (t == NULL) {
 | 
				
			||||||
| 
						 | 
					@ -2332,6 +2451,11 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  else if (strcasecmp(t, "TXTAIL") == 0) {
 | 
						  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;
 | 
						    int n;
 | 
				
			||||||
	    t = split(NULL,0);
 | 
						    t = split(NULL,0);
 | 
				
			||||||
	    if (t == NULL) {
 | 
						    if (t == NULL) {
 | 
				
			||||||
| 
						 | 
					@ -2372,6 +2496,11 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
	  else if (strcasecmp(t, "FULLDUP") == 0) {
 | 
						  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);
 | 
						    t = split(NULL,0);
 | 
				
			||||||
	    if (t == NULL) {
 | 
						    if (t == NULL) {
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      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) {
 | 
						  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);
 | 
						    t = split(NULL,0);
 | 
				
			||||||
	    if (t == NULL) {
 | 
						    if (t == NULL) {
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      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) {
 | 
						  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;
 | 
						    int n;
 | 
				
			||||||
	    t = split(NULL,0);
 | 
						    t = split(NULL,0);
 | 
				
			||||||
	    if (t == NULL) {
 | 
						    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
 | 
					 *				Automatically enable, for that session only, when an identical
 | 
				
			||||||
 *				frame is sent more than this number of times.
 | 
					 *				frame is sent more than this number of times.
 | 
				
			||||||
 *				Default 5 based on half of default RETRY.
 | 
					 *				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) {
 | 
						  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;
 | 
						    int n;
 | 
				
			||||||
	    t = split(NULL,0);
 | 
						    t = split(NULL,0);
 | 
				
			||||||
	    if (t == NULL) {
 | 
						    if (t == NULL) {
 | 
				
			||||||
| 
						 | 
					@ -2492,6 +2636,11 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  else if (strcasecmp(t, "IL2PTX") == 0) {
 | 
						  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].layer2_xmit = LAYER2_IL2P;
 | 
				
			||||||
	    p_audio_config->achan[channel].il2p_max_fec = 1;
 | 
						    p_audio_config->achan[channel].il2p_max_fec = 1;
 | 
				
			||||||
	    p_audio_config->achan[channel].il2p_invert_polarity = 0;
 | 
						    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;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    from_chan = atoi(t);
 | 
						    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);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf ("Config file: FROM-channel must be in range of 0 to %d on line %d.\n", 
 | 
						      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;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2582,10 +2731,10 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
	      continue;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    to_chan = atoi(t);
 | 
						    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);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf ("Config file: TO-channel must be in range of 0 to %d on line %d.\n", 
 | 
						      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;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2713,10 +2862,10 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
	      continue;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    from_chan = atoi(t);
 | 
						    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);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf ("Config file: FROM-channel must be in range of 0 to %d on line %d.\n", 
 | 
						      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;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2742,10 +2891,10 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
	      continue;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    to_chan = atoi(t);
 | 
						    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);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf ("Config file: TO-channel must be in range of 0 to %d on line %d.\n", 
 | 
						      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;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    if (p_audio_config->chan_medium[to_chan] != MEDIUM_RADIO) {
 | 
						    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;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    from_chan = atoi(t);
 | 
						    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);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf ("Config file: FROM-channel must be in range of 0 to %d on line %d.\n",
 | 
						      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;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2820,10 +2969,10 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
	      continue;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    to_chan = atoi(t);
 | 
						    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);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf ("Config file: TO-channel must be in range of 0 to %d on line %d.\n",
 | 
						      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;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    if (p_audio_config->chan_medium[to_chan] != MEDIUM_RADIO) {
 | 
						    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;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    if (*t == 'i' || *t == 'I') {
 | 
						    if (*t == 'i' || *t == 'I') {
 | 
				
			||||||
	      from_chan = MAX_CHANS;
 | 
						      from_chan = MAX_TOTAL_CHANS;
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf ("Config file: FILTER IG ... on line %d.\n", line);
 | 
						      dw_printf ("Config file: FILTER IG ... on line %d.\n", line);
 | 
				
			||||||
	      dw_printf ("Warning! Don't mess with IS>RF filtering unless you are an expert and have an unusual situation.\n");
 | 
						      dw_printf ("Warning! 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 {
 | 
						    else {
 | 
				
			||||||
	      from_chan = isdigit(*t) ? atoi(t) : -999;
 | 
						      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);
 | 
						        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", 
 | 
						        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;
 | 
						        continue;
 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2945,7 +3094,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
	      continue;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    if (*t == 'i' || *t == 'I') {
 | 
						    if (*t == 'i' || *t == 'I') {
 | 
				
			||||||
	      to_chan = MAX_CHANS;
 | 
						      to_chan = MAX_TOTAL_CHANS;
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf ("Config file: FILTER ... IG ... on line %d.\n", line);
 | 
						      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");
 | 
						      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 {
 | 
						    else {
 | 
				
			||||||
	      to_chan = isdigit(*t) ? atoi(t) : -999;
 | 
						      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);
 | 
						        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", 
 | 
						        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;
 | 
						        continue;
 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
	      if (p_audio_config->chan_medium[to_chan] != MEDIUM_RADIO &&
 | 
						      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;
 | 
						    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);
 | 
						      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",
 | 
						      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;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3045,10 +3194,10 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    to_chan = isdigit(*t) ? atoi(t) : -999;
 | 
						    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);
 | 
						      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",
 | 
						      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;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
            if (p_audio_config->chan_medium[to_chan] != MEDIUM_RADIO) {
 | 
					            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);
 | 
						    r = atoi(t);
 | 
				
			||||||
	    if (r < 0 || r > MAX_CHANS-1) {
 | 
						    if (r < 0 || r > MAX_RADIO_CHANS-1) {
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      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", 
 | 
						      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;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4236,9 +4385,9 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      if (isdigit(*p)) {
 | 
						      if (isdigit(*p)) {
 | 
				
			||||||
	        x = *p - '0';
 | 
						        x = *p - '0';
 | 
				
			||||||
	        if (x < 0 || x > MAX_CHANS-1) {
 | 
						        if (x < 0 || x > MAX_TOTAL_CHANS-1) {
 | 
				
			||||||
	          text_color_set(DW_COLOR_ERROR);
 | 
						          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;
 | 
						          x = -1;
 | 
				
			||||||
	        }
 | 
						        }
 | 
				
			||||||
	        else if (p_audio_config->chan_medium[x] != MEDIUM_RADIO &&
 | 
						        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);
 | 
						    n = atoi(t);
 | 
				
			||||||
	    if (n < 0 || n > MAX_CHANS-1) {
 | 
						    if (n < 0 || n > MAX_TOTAL_CHANS-1) {
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf ("Config file: Transmit channel must be in range of 0 to %d on line %d.\n", 
 | 
						      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;
 | 
						      continue;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    p_igate_config->tx_chan = n;
 | 
						    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);
 | 
						    t = split(NULL,0);
 | 
				
			||||||
	    if (t != NULL) {
 | 
						    if (t != NULL) {
 | 
				
			||||||
	      chan = atoi(t);
 | 
						      chan = atoi(t);
 | 
				
			||||||
	      if (chan < 0 || chan >= MAX_CHANS) {
 | 
						      if (chan < 0 || chan >= MAX_TOTAL_CHANS) {
 | 
				
			||||||
	        text_color_set(DW_COLOR_ERROR);
 | 
						        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;
 | 
						        continue;
 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
| 
						 | 
					@ -5510,25 +5659,25 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
	int i, j, k, b;
 | 
						int i, j, k, b;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i=0; i<MAX_CHANS; i++) {
 | 
						for (i=0; i<MAX_TOTAL_CHANS; i++) {
 | 
				
			||||||
	  for (j=0; j<MAX_CHANS; j++) {
 | 
						  for (j=0; j<MAX_TOTAL_CHANS; j++) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* APRS digipeating. */
 | 
					/* APRS digipeating. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    if (p_digi_config->enabled[i][j]) {
 | 
						    if (p_digi_config->enabled[i][j]) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      if ( strcmp(p_audio_config->achan[i].mycall, "") == 0 || 
 | 
						      if ( strcmp(p_audio_config->mycall[i], "") == 0 ||
 | 
				
			||||||
		   strcmp(p_audio_config->achan[i].mycall, "NOCALL") == 0 || 
 | 
							   strcmp(p_audio_config->mycall[i], "NOCALL") == 0 ||
 | 
				
			||||||
		   strcmp(p_audio_config->achan[i].mycall, "N0CALL") == 0) {
 | 
							   strcmp(p_audio_config->mycall[i], "N0CALL") == 0) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	        text_color_set(DW_COLOR_ERROR);
 | 
						        text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	        dw_printf ("Config file: MYCALL must be set for receive channel %d before digipeating is allowed.\n", i);
 | 
						        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;
 | 
						        p_digi_config->enabled[i][j] = 0;
 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      if ( strcmp(p_audio_config->achan[j].mycall, "") == 0 || 
 | 
						      if ( strcmp(p_audio_config->mycall[j], "") == 0 ||
 | 
				
			||||||
	           strcmp(p_audio_config->achan[j].mycall, "NOCALL") == 0 ||
 | 
						           strcmp(p_audio_config->mycall[j], "NOCALL") == 0 ||
 | 
				
			||||||
		   strcmp(p_audio_config->achan[j].mycall, "N0CALL") == 0) {
 | 
							   strcmp(p_audio_config->mycall[j], "N0CALL") == 0) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	        text_color_set(DW_COLOR_ERROR);
 | 
						        text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	        dw_printf ("Config file: MYCALL must be set for transmit channel %d before digipeating is allowed.\n", i); 
 | 
						        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. */
 | 
					/* 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 ||
 | 
						      if ( strcmp(p_audio_config->mycall[i], "") == 0 ||
 | 
				
			||||||
		   strcmp(p_audio_config->achan[i].mycall, "NOCALL") == 0 ||
 | 
							   strcmp(p_audio_config->mycall[i], "NOCALL") == 0 ||
 | 
				
			||||||
		   strcmp(p_audio_config->achan[i].mycall, "N0CALL") == 0) {
 | 
							   strcmp(p_audio_config->mycall[i], "N0CALL") == 0) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	        text_color_set(DW_COLOR_ERROR);
 | 
						        text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	        dw_printf ("Config file: MYCALL must be set for receive channel %d before digipeating is allowed.\n", i);
 | 
						        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;
 | 
						        p_cdigi_config->enabled[i][j] = 0;
 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      if ( strcmp(p_audio_config->achan[j].mycall, "") == 0 ||
 | 
						      if ( strcmp(p_audio_config->mycall[j], "") == 0 ||
 | 
				
			||||||
	           strcmp(p_audio_config->achan[j].mycall, "NOCALL") == 0 ||
 | 
						           strcmp(p_audio_config->mycall[j], "NOCALL") == 0 ||
 | 
				
			||||||
		   strcmp(p_audio_config->achan[j].mycall, "N0CALL") == 0) {
 | 
							   strcmp(p_audio_config->mycall[j], "N0CALL") == 0) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	        text_color_set(DW_COLOR_ERROR);
 | 
						        text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	        dw_printf ("Config file: MYCALL must be set for transmit channel %d before digipeating is allowed.\n", i);
 | 
						        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 &&
 | 
						  if (strlen(p_igate_config->t2_login) > 0 &&
 | 
				
			||||||
	      (p_audio_config->chan_medium[i] == MEDIUM_RADIO || p_audio_config->chan_medium[i] == MEDIUM_NETTNC)) {
 | 
						      (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);
 | 
						      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);
 | 
						      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));
 | 
						      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.
 | 
						    // Currently we can have only one transmit channel.
 | 
				
			||||||
	    // This might be generalized someday to allow more.
 | 
						    // This might be generalized someday to allow more.
 | 
				
			||||||
	    if (p_igate_config->tx_chan >= 0 && 
 | 
						    if (p_igate_config->tx_chan >= 0 && 
 | 
				
			||||||
			( strcmp(p_audio_config->achan[p_igate_config->tx_chan].mycall, "") == 0 ||
 | 
								( strcmp(p_audio_config->mycall[p_igate_config->tx_chan], "") == 0 ||
 | 
				
			||||||
		          strcmp(p_audio_config->achan[p_igate_config->tx_chan].mycall, "NOCALL") == 0 ||
 | 
							          strcmp(p_audio_config->mycall[p_igate_config->tx_chan], "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], "N0CALL") == 0)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      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);
 | 
						      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.
 | 
					// This will handle eventual case of multiple transmit channels.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (strlen(p_igate_config->t2_login) > 0) {
 | 
						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_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) {
 | 
						      if (p_digi_config->filter_str[MAX_TOTAL_CHANS][j] == NULL) {
 | 
				
			||||||
	        p_digi_config->filter_str[MAX_CHANS][j] = strdup("i/180");
 | 
						        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') {
 | 
						    else if (value[0] == 'r' || value[0] == 'R') {
 | 
				
			||||||
	       int n = atoi(value+1);
 | 
						       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) {
 | 
								&& p_audio_config->chan_medium[n] != MEDIUM_IGATE) {
 | 
				
			||||||
	         text_color_set(DW_COLOR_ERROR);
 | 
						         text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	         dw_printf ("Config file, line %d: Simulated receive on channel %d is not valid.\n", line, n);
 | 
						         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') {
 | 
						    else if (value[0] == 't' || value[0] == 'T' || value[0] == 'x' || value[0] == 'X') {
 | 
				
			||||||
	      int n = atoi(value+1);
 | 
						      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) {
 | 
								&& p_audio_config->chan_medium[n] != MEDIUM_IGATE) {
 | 
				
			||||||
	        text_color_set(DW_COLOR_ERROR);
 | 
						        text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	        dw_printf ("Config file, line %d: Send to channel %d is not valid.\n", line, n);
 | 
						        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 {
 | 
						    else {
 | 
				
			||||||
	       int n = atoi(value);
 | 
						       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) {
 | 
								&& p_audio_config->chan_medium[n] != MEDIUM_IGATE) {
 | 
				
			||||||
	         text_color_set(DW_COLOR_ERROR);
 | 
						         text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	         dw_printf ("Config file, line %d: Send to channel %d is not valid.\n", line, n);
 | 
						         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_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) {
 | 
							&& p_audio_config->chan_medium[b->sendto_chan] != MEDIUM_IGATE) {
 | 
				
			||||||
	    text_color_set(DW_COLOR_ERROR);
 | 
						    text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	    dw_printf ("Config file, line %d: Send to channel %d is not valid.\n", line, b->sendto_chan);
 | 
						    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.
 | 
						  if (p_audio_config->chan_medium[b->sendto_chan] == MEDIUM_IGATE) {  // Prevent subscript out of bounds.
 | 
				
			||||||
									     // Will be using call from chan 0 later.
 | 
														     // Will be using call from chan 0 later.
 | 
				
			||||||
	    if ( strcmp(p_audio_config->achan[0].mycall, "") == 0 || 
 | 
						    if ( strcmp(p_audio_config->mycall[0], "") == 0 ||
 | 
				
			||||||
	         strcmp(p_audio_config->achan[0].mycall, "NOCALL") == 0 || 
 | 
						         strcmp(p_audio_config->mycall[0], "NOCALL") == 0 ||
 | 
				
			||||||
	         strcmp(p_audio_config->achan[0].mycall, "N0CALL") == 0 ) {
 | 
						         strcmp(p_audio_config->mycall[0], "N0CALL") == 0 ) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf ("Config file: MYCALL must be set for channel %d before beaconing is allowed.\n", 0); 
 | 
						      dw_printf ("Config file: MYCALL must be set for channel %d before beaconing is allowed.\n", 0); 
 | 
				
			||||||
	      return (0);
 | 
						      return (0);
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	  } else {
 | 
						  } else {
 | 
				
			||||||
	    if ( strcmp(p_audio_config->achan[b->sendto_chan].mycall, "") == 0 || 
 | 
						    if ( strcmp(p_audio_config->mycall[b->sendto_chan], "") == 0 ||
 | 
				
			||||||
	         strcmp(p_audio_config->achan[b->sendto_chan].mycall, "NOCALL") == 0 || 
 | 
						         strcmp(p_audio_config->mycall[b->sendto_chan], "NOCALL") == 0 ||
 | 
				
			||||||
	         strcmp(p_audio_config->achan[b->sendto_chan].mycall, "N0CALL") == 0 ) {
 | 
						         strcmp(p_audio_config->mycall[b->sendto_chan], "N0CALL") == 0 ) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      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); 
 | 
						      dw_printf ("Config file: MYCALL must be set for channel %d before beaconing is allowed.\n", b->sendto_chan); 
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,7 +30,7 @@ enum sendto_type_e { SENDTO_XMIT, SENDTO_IGATE, SENDTO_RECV };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MAX_BEACONS 30
 | 
					#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 {
 | 
					struct misc_config_s {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//    This file is part of Dire Wolf, an amateur radio packet TNC.
 | 
					//    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
 | 
					//    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
 | 
					//    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_lon = G_UNKNOWN;
 | 
				
			||||||
	A->g_footprint_radius = 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.
 | 
					// 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
 | 
					// 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));
 | 
						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;
 | 
						p = (struct aprs_mic_e_s *)info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Destination is really latitude of form ddmmhh. */
 | 
					/* 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.
 | 
					// The rest is a comment which can have other information cryptically embedded.
 | 
				
			||||||
// Remove any trailing CR, which I would argue, violates the protocol spec.
 | 
					// 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];
 | 
						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') {
 | 
						if (mcomment[strlen(mcomment)-1] == '\r') {
 | 
				
			||||||
	  mcomment[strlen(mcomment)-1] = '\0';
 | 
						  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. */
 | 
					/* 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 }
 | 
					// Three base 91 characters followed by }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (strlen(trimmed) >=4 &&
 | 
						if (strlen(trimmed) >= 4 &&
 | 
				
			||||||
			isdigit91(trimmed[0]) &&
 | 
								isdigit91(trimmed[0]) &&
 | 
				
			||||||
			isdigit91(trimmed[1]) &&
 | 
								isdigit91(trimmed[1]) &&
 | 
				
			||||||
			isdigit91(trimmed[2]) &&
 | 
								isdigit91(trimmed[2]) &&
 | 
				
			||||||
| 
						 | 
					@ -3848,7 +3898,7 @@ double get_longitude_9 (char *p, int quiet)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Inputs:	p 	- Pointer to first byte.
 | 
					 * Inputs:	p 	- Pointer to first byte.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Returns:	time_t data type. (UTC)
 | 
					 * Returns:	time_t data type. (UTC)  Zero if error.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Description:	
 | 
					 * Description:	
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -3918,6 +3968,13 @@ time_t get_timestamp (decode_aprs_t *A, char *p)
 | 
				
			||||||
				/* h = UTC. */
 | 
									/* h = UTC. */
 | 
				
			||||||
	} *phms;
 | 
						} *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;
 | 
						struct tm *ptm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	time_t ts;
 | 
						time_t ts;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										25
									
								
								src/demod.c
								
								
								
								
							
							
						
						
									
										25
									
								
								src/demod.c
								
								
								
								
							| 
						 | 
					@ -31,6 +31,8 @@
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *---------------------------------------------------------------*/
 | 
					 *---------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DEMOD_C 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "direwolf.h"
 | 
					#include "direwolf.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
| 
						 | 
					@ -63,11 +65,11 @@ static struct audio_s          *save_audio_config_p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Current state of all the decoders.
 | 
					// 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_sum[MAX_RADIO_CHANS][MAX_SUBCHANS];
 | 
				
			||||||
static int sample_count[MAX_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;
 | 
						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) {
 | 
						 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);
 | 
							    save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec);
 | 
				
			||||||
	      if (save_audio_config_p->achan[chan].decimate != 1) 
 | 
						      if (save_audio_config_p->achan[chan].decimate != 1) 
 | 
				
			||||||
	        dw_printf (" / %d", save_audio_config_p->achan[chan].decimate);
 | 
						        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) 
 | 
						      if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF) 
 | 
				
			||||||
	        dw_printf (", DTMF decoder enabled");
 | 
						        dw_printf (", DTMF decoder enabled");
 | 
				
			||||||
	      dw_printf (".\n");
 | 
						      dw_printf (".\n");
 | 
				
			||||||
| 
						 | 
					@ -540,7 +543,7 @@ int demod_init (struct audio_s *pa)
 | 
				
			||||||
		    save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec);
 | 
							    save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec);
 | 
				
			||||||
	      if (save_audio_config_p->achan[chan].decimate != 1)
 | 
						      if (save_audio_config_p->achan[chan].decimate != 1)
 | 
				
			||||||
	        dw_printf (" / %d", save_audio_config_p->achan[chan].decimate);
 | 
						        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)
 | 
						      if (save_audio_config_p->achan[chan].v26_alternative == V26_B)
 | 
				
			||||||
	        dw_printf (", compatible with MFJ-2400");
 | 
						        dw_printf (", compatible with MFJ-2400");
 | 
				
			||||||
	      else
 | 
						      else
 | 
				
			||||||
| 
						 | 
					@ -601,6 +604,7 @@ int demod_init (struct audio_s *pa)
 | 
				
			||||||
		    save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec);
 | 
							    save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec);
 | 
				
			||||||
	      if (save_audio_config_p->achan[chan].decimate != 1)
 | 
						      if (save_audio_config_p->achan[chan].decimate != 1)
 | 
				
			||||||
	        dw_printf (" / %d", save_audio_config_p->achan[chan].decimate);
 | 
						        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)
 | 
						      if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF)
 | 
				
			||||||
	        dw_printf (", DTMF decoder enabled");
 | 
						        dw_printf (", DTMF decoder enabled");
 | 
				
			||||||
	      dw_printf (".\n");
 | 
						      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->achan[chan].profiles,
 | 
				
			||||||
		    save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec,
 | 
							    save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec,
 | 
				
			||||||
	            save_audio_config_p->achan[chan].upsample);
 | 
						            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) 
 | 
						      if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF) 
 | 
				
			||||||
	        dw_printf (", DTMF decoder enabled");
 | 
						        dw_printf (", DTMF decoder enabled");
 | 
				
			||||||
	      dw_printf (".\n");
 | 
						      dw_printf (".\n");
 | 
				
			||||||
| 
						 | 
					@ -812,7 +817,7 @@ int demod_init (struct audio_s *pa)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Now the virtual channels.  FIXME:  could be single loop.
 | 
						// 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);
 | 
					// 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.
 | 
					// New in 1.7.
 | 
				
			||||||
// A few people have a really bad audio cross talk situation where they receive their own transmissions.
 | 
					// 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)
 | 
					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;
 | 
						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;
 | 
						struct demodulator_state_s *D;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
						assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (mute_input[chan]) {
 | 
						if (mute_input[chan]) {
 | 
				
			||||||
| 
						 | 
					@ -1066,7 +1071,7 @@ alevel_t demod_get_audio_level (int chan, int subchan)
 | 
				
			||||||
	struct demodulator_state_s *D;
 | 
						struct demodulator_state_s *D;
 | 
				
			||||||
	alevel_t alevel;
 | 
						alevel_t alevel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
						assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* We have to consider two different cases here. */
 | 
						/* We have to consider two different cases here. */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -395,7 +395,7 @@ void demod_9600_process_sample (int chan, int sam, int upsample, struct demodula
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int subchan = 0;
 | 
						int subchan = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
						assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Scale to nice number for convenience. */
 | 
						/* 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. */
 | 
						  /* 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);
 | 
						  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);
 | 
						  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) {
 | 
						  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) );
 | 
						    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 {
 | 
						  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].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;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 */
 | 
						static int seq = 0;			/* for log file name */
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
						assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* 
 | 
					/* 
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										116
									
								
								src/demod_psk.c
								
								
								
								
							
							
						
						
									
										116
									
								
								src/demod_psk.c
								
								
								
								
							| 
						 | 
					@ -72,6 +72,9 @@
 | 
				
			||||||
 *		V.26 has two variations, A and B.  Initially I implemented the A alternative.
 | 
					 *		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
 | 
					 *		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.
 | 
					 *		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.
 | 
				
			||||||
 *	
 | 
					 *	
 | 
				
			||||||
 *---------------------------------------------------------------*/
 | 
					 *---------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -94,7 +97,7 @@
 | 
				
			||||||
#include "fsk_demod_state.h"		// Values above override defaults.
 | 
					#include "fsk_demod_state.h"		// Values above override defaults.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "audio.h"
 | 
					#include "audio.h"
 | 
				
			||||||
#include "tune.h"
 | 
					//#include "tune.h"	// obsolete. eventually remove all references.
 | 
				
			||||||
#include "fsk_gen_filter.h"
 | 
					#include "fsk_gen_filter.h"
 | 
				
			||||||
#include "hdlc_rec.h"
 | 
					#include "hdlc_rec.h"
 | 
				
			||||||
#include "textcolor.h"
 | 
					#include "textcolor.h"
 | 
				
			||||||
| 
						 | 
					@ -102,7 +105,13 @@
 | 
				
			||||||
#include "dsp.h"
 | 
					#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};	
 | 
					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?
 | 
						D->num_slicers = 1;		// Haven't thought about this yet.  Is it even applicable?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef TUNE_PROFILE
 | 
					//#ifdef TUNE_PROFILE
 | 
				
			||||||
	profile = TUNE_PROFILE;
 | 
					//	profile = TUNE_PROFILE;
 | 
				
			||||||
#endif
 | 
					//#endif
 | 
				
			||||||
 | 
						TUNE("TUNE_PROFILE", profile, "profile", "%c")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (modem_type == MODEM_QPSK) {
 | 
						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		
 | 
						  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.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.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 );
 | 
						  D->u.psk.soffs = (int) round( (13.f / 12.f) * (float)samples_per_sec / (float)correct_baud );
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else {
 | 
						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
 | 
					//#ifdef TUNE_PRE_BAUD
 | 
				
			||||||
	D->u.psk.prefilter_baud = TUNE_PRE_BAUD;
 | 
					//	D->u.psk.prefilter_baud = TUNE_PRE_BAUD;
 | 
				
			||||||
#endif
 | 
					//#endif
 | 
				
			||||||
#ifdef TUNE_PRE_WINDOW
 | 
						TUNE("TUNE_PRE_BAUD", D->u.psk.prefilter_baud, "prefilter_baud", "%.3f")
 | 
				
			||||||
	D->u.psk.pre_window = TUNE_PRE_WINDOW;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef TUNE_LPF_BAUD
 | 
					//#ifdef TUNE_PRE_WINDOW
 | 
				
			||||||
	D->u.psk.lpf_baud = TUNE_LPF_BAUD;
 | 
					//	D->u.psk.pre_window = TUNE_PRE_WINDOW;
 | 
				
			||||||
#endif
 | 
					//#endif
 | 
				
			||||||
#ifdef TUNE_LP_WINDOW
 | 
						TUNE("TUNE_PRE_WINDOW", D->u.psk.pre_window, "pre_window", "%d")
 | 
				
			||||||
	D->u.psk.lp_window = TUNE_LP_WINDOW;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(TUNE_PLL_SEARCHING)
 | 
					//#ifdef TUNE_LPF_BAUD
 | 
				
			||||||
	D->pll_searching_inertia = TUNE_PLL_SEARCHING;
 | 
					//	D->u.psk.lpf_baud = TUNE_LPF_BAUD;
 | 
				
			||||||
#endif
 | 
					//#endif
 | 
				
			||||||
#if defined(TUNE_PLL_LOCKED)
 | 
					//#ifdef TUNE_LP_WINDOW
 | 
				
			||||||
	D->pll_locked_inertia = TUNE_PLL_LOCKED;
 | 
					//	D->u.psk.lp_window = TUNE_LP_WINDOW;
 | 
				
			||||||
#endif
 | 
					//#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 );
 | 
						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.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 );
 | 
						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
 | 
					//#ifdef TUNE_PRE_FILTER_TAPS
 | 
				
			||||||
	D->u.psk.pre_filter_taps = TUNE_PRE_FILTER_TAPS;
 | 
					//	D->u.psk.pre_filter_taps = TUNE_PRE_FILTER_TAPS;
 | 
				
			||||||
#endif
 | 
					//#endif
 | 
				
			||||||
 | 
						  TUNE("TUNE_PRE_FILTER_TAPS", D->u.psk.pre_filter_taps, "pre_filter_taps", "%d")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef TUNE_lp_filter_taps
 | 
					//#ifdef TUNE_lp_filter_taps
 | 
				
			||||||
	D->u.psk.lp_filter_taps = TUNE_lp_filter_taps;
 | 
					//	D->u.psk.lp_filter_taps = TUNE_lp_filter_taps;
 | 
				
			||||||
#endif
 | 
					//#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) {
 | 
						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?
 | 
						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);
 | 
						assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Scale to nice number for plotting during debug. */
 | 
						/* 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;
 | 
						    int gray = demod_bits;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    hdlc_rec_bit (chan, subchan, slice, (gray >> 1) & 1, 0, bit_quality[1]);
 | 
						    hdlc_rec_bit_new (chan, subchan, slice, (gray >> 1) & 1, 0, bit_quality[1],
 | 
				
			||||||
	    hdlc_rec_bit (chan, subchan, slice, gray & 1, 0, bit_quality[0]);
 | 
								&(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 {
 | 
						  else {
 | 
				
			||||||
	    int gray = demod_bits;
 | 
						    int gray = demod_bits;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    hdlc_rec_bit (chan, subchan, slice, (gray >> 2) & 1, 0, bit_quality[2]);
 | 
						    hdlc_rec_bit_new (chan, subchan, slice, (gray >> 2) & 1, 0, bit_quality[2],
 | 
				
			||||||
	    hdlc_rec_bit (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 (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));
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
 | 
						  D->slicer[slice].pll_symbol_count++;
 | 
				
			||||||
	  pll_dcd_each_symbol2 (D, chan, subchan, slice);
 | 
						  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);
 | 
						  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) {
 | 
						  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);
 | 
						    D->slicer[slice].data_clock_pll = (int)floorf((float)(D->slicer[slice].data_clock_pll) * D->pll_locked_inertia);
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	  else {
 | 
						  else {
 | 
				
			||||||
	    D->slicer[slice].data_clock_pll = (int)floorf((float)(D->slicer[slice].data_clock_pll) * D->pll_searching_inertia);
 | 
						    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;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -135,6 +135,10 @@ int main (int argc, char *argv[])
 | 
				
			||||||
	assert (strcmp(comment_out, "Comment") == 0);
 | 
						assert (strcmp(comment_out, "Comment") == 0);
 | 
				
			||||||
	assert (strcmp(device, "UNKNOWN vendor/model") == 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
 | 
					// 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)
 | 
					void deviceid_decode_dest (char *dest, char *device, size_t device_size)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	*device = '\0';
 | 
						strlcpy (device, "UNKNOWN vendor/model", device_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ptocalls == NULL) {
 | 
						if (ptocalls == NULL) {
 | 
				
			||||||
	  text_color_set(DW_COLOR_ERROR);
 | 
						  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);
 | 
						strlcpy (device, "UNKNOWN vendor/model", device_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // end deviceid_decode_dest
 | 
					} // 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
 | 
					 * Inputs:	comment - MIC-E comment that might have vendor/model encoded as
 | 
				
			||||||
 *			a prefix and/or suffix.
 | 
					 *			a prefix and/or suffix.
 | 
				
			||||||
 | 
					 *			Any trailing CR has already been removed.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		trimmed_size - Amount of space available for result to avoid buffer overflow.
 | 
					 *		trimmed_size - Amount of space available for result to avoid buffer overflow.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		device_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.
 | 
					 * Outputs:	trimmed - Final comment with device vendor/model removed.
 | 
				
			||||||
 | 
					 *				This would include any altitude.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		device	- Vendor and model.
 | 
					 *		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 TH-D7A  put ">" at the beginning of the comment.
 | 
				
			||||||
 *		The Kenwood TM-D700 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
 | 
					 * References:	http://www.aprs.org/aprs12/mic-e-types.txt
 | 
				
			||||||
 *		http://www.aprs.org/aprs12/mic-e-examples.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.
 | 
					// 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)
 | 
					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) {
 | 
						if (ptocalls == NULL) {
 | 
				
			||||||
	  text_color_set(DW_COLOR_ERROR);
 | 
						  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.
 | 
					// Not found.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	strlcpy (device, "UNKNOWN vendor/model", device_size);
 | 
						strlcpy (device, "UNKNOWN vendor/model", device_size);
 | 
				
			||||||
 | 
						strlcpy (trimmed, comment, trimmed_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // end deviceid_decode_mice
 | 
					} // end deviceid_decode_mice
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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.
 | 
					 * 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) {
 | 
					int digipeater_get_count (int from_chan, int to_chan) {
 | 
				
			||||||
	return (digi_count[from_chan][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.
 | 
						// 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_RADIO &&
 | 
				
			||||||
	      save_audio_config_p->chan_medium[from_chan] != MEDIUM_NETTNC)) {
 | 
						      save_audio_config_p->chan_medium[from_chan] != MEDIUM_NETTNC)) {
 | 
				
			||||||
	  text_color_set(DW_COLOR_ERROR);
 | 
						  text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
| 
						 | 
					@ -195,13 +195,13 @@ 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 (save_digi_config_p->enabled[from_chan][to_chan]) {
 | 
				
			||||||
	    if (to_chan == from_chan) {
 | 
						    if (to_chan == from_chan) {
 | 
				
			||||||
	      packet_t result;
 | 
						      packet_t result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      result = digipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall, 
 | 
						      result = digipeat_match (from_chan, pp, save_audio_config_p->mycall[from_chan],
 | 
				
			||||||
					   save_audio_config_p->achan[to_chan].mycall, 
 | 
										   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],
 | 
								&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],
 | 
								to_chan, save_digi_config_p->preempt[from_chan][to_chan],
 | 
				
			||||||
				save_digi_config_p->atgp[from_chan][to_chan],
 | 
									save_digi_config_p->atgp[from_chan][to_chan],
 | 
				
			||||||
| 
						 | 
					@ -222,13 +222,13 @@ void digipeater (int from_chan, packet_t pp)
 | 
				
			||||||
 * These are lower priority
 | 
					 * 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 (save_digi_config_p->enabled[from_chan][to_chan]) {
 | 
				
			||||||
	    if (to_chan != from_chan) {
 | 
						    if (to_chan != from_chan) {
 | 
				
			||||||
	      packet_t result;
 | 
						      packet_t result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      result = digipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall, 
 | 
						      result = digipeat_match (from_chan, pp, save_audio_config_p->mycall[from_chan],
 | 
				
			||||||
					   save_audio_config_p->achan[to_chan].mycall, 
 | 
										   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],
 | 
								&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],
 | 
								to_chan, save_digi_config_p->preempt[from_chan][to_chan],
 | 
				
			||||||
				save_digi_config_p->atgp[from_chan][to_chan],
 | 
									save_digi_config_p->atgp[from_chan][to_chan],
 | 
				
			||||||
| 
						 | 
					@ -641,9 +641,9 @@ void digi_regen (int from_chan, packet_t pp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// dw_printf ("digi_regen()\n");
 | 
						// 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]) {
 | 
						  if (save_digi_config_p->regen[from_chan][to_chan]) {
 | 
				
			||||||
	    result = ax25_dup (pp); 
 | 
						    result = ax25_dup (pp); 
 | 
				
			||||||
	    if (result != NULL) {
 | 
						    if (result != NULL) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "regex.h"
 | 
					#include "regex.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "direwolf.h"		/* for MAX_CHANS */
 | 
					#include "direwolf.h"		/* for MAX_TOTAL_CHANS */
 | 
				
			||||||
#include "ax25_pad.h"		/* for packet_t */
 | 
					#include "ax25_pad.h"		/* for packet_t */
 | 
				
			||||||
#include "audio.h"		/* for radio channel properties */
 | 
					#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.
 | 
					 * 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.
 | 
						// 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.
 | 
						// 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".
 | 
											// NULL or optional Packet Filter strings such as "t/m".
 | 
				
			||||||
						// Notice the size of arrays is one larger than normal.
 | 
											// Notice the size of arrays is one larger than normal.
 | 
				
			||||||
						// That extra position is for the IGate.
 | 
											// 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.
 | 
											// Sort of like digipeating but passed along unchanged.
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//    This file is part of Dire Wolf, an amateur radio packet TNC.
 | 
					//    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
 | 
					//    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
 | 
					//    it under the terms of the GNU General Public License as published by
 | 
				
			||||||
| 
						 | 
					@ -130,6 +130,7 @@
 | 
				
			||||||
#include "dns_sd_dw.h"
 | 
					#include "dns_sd_dw.h"
 | 
				
			||||||
#include "dlq.h"		// for fec_type_t definition.
 | 
					#include "dlq.h"		// for fec_type_t definition.
 | 
				
			||||||
#include "deviceid.h"
 | 
					#include "deviceid.h"
 | 
				
			||||||
 | 
					#include "nettnc.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//static int idx_decoded = 0;
 | 
					//static int idx_decoded = 0;
 | 
				
			||||||
| 
						 | 
					@ -228,6 +229,7 @@ int main (int argc, char *argv[])
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	int d_x_opt = 1;	/* "-d x" option for FX.25.  Default minimal. Repeat for more detail.  -qx to silence. */
 | 
						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_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. */
 | 
						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_init(t_opt);
 | 
				
			||||||
	text_color_set(DW_COLOR_INFO);
 | 
						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 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);
 | 
						//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -337,6 +339,13 @@ int main (int argc, char *argv[])
 | 
				
			||||||
#endif
 | 
					#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 
 | 
					 * Starting with version 0.9, the prebuilt Windows version 
 | 
				
			||||||
 * requires a minimum of a Pentium 3 or equivalent so we can
 | 
					 * 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);
 | 
						    text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	    for (int n=0; n<15; n++) {
 | 
						    for (int n=0; n<15; n++) {
 | 
				
			||||||
	      dw_printf ("\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 ("Dire Wolf requires only privileges available to ordinary users.\n");
 | 
				
			||||||
	      dw_printf ("Running this as root is an unnecessary security risk.\n");
 | 
						      dw_printf ("Running this as root is an unnecessary security risk.\n");
 | 
				
			||||||
	      //SLEEP_SEC(1);
 | 
						      //SLEEP_SEC(1);
 | 
				
			||||||
| 
						 | 
					@ -558,7 +568,7 @@ int main (int argc, char *argv[])
 | 
				
			||||||
      	    	break;
 | 
					      	    	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);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf ("Invalid channel %d for -x. \n", x_opt_chan);
 | 
						      dw_printf ("Invalid channel %d for -x. \n", x_opt_chan);
 | 
				
			||||||
	      text_color_set(DW_COLOR_INFO);
 | 
						      text_color_set(DW_COLOR_INFO);
 | 
				
			||||||
| 
						 | 
					@ -637,6 +647,7 @@ int main (int argc, char *argv[])
 | 
				
			||||||
#if USE_HAMLIB
 | 
					#if USE_HAMLIB
 | 
				
			||||||
	      case 'h':  d_h_opt++; break;			// Hamlib verbose level.
 | 
						      case 'h':  d_h_opt++; break;			// Hamlib verbose level.
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
						      case 'c':  d_c_opt++; break;			// Connected mode data link state machine
 | 
				
			||||||
	      case 'x':  d_x_opt++; break;			// FX.25
 | 
						      case 'x':  d_x_opt++; break;			// FX.25
 | 
				
			||||||
	      case '2':  d_2_opt++; break;			// IL2P
 | 
						      case '2':  d_2_opt++; break;			// IL2P
 | 
				
			||||||
	      case 'd':	 aprstt_debug++; break;			// APRStt (mnemonic Dtmf)
 | 
						      case 'd':	 aprstt_debug++; break;			// APRStt (mnemonic Dtmf)
 | 
				
			||||||
| 
						 | 
					@ -1004,6 +1015,13 @@ int main (int argc, char *argv[])
 | 
				
			||||||
	fx25_init (d_x_opt);
 | 
						fx25_init (d_x_opt);
 | 
				
			||||||
	il2p_init (d_2_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.
 | 
					 * 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);
 | 
						igate_init (&audio_config, &igate_config, &digi_config, d_i_opt);
 | 
				
			||||||
	cdigipeater_init (&audio_config, &cdigi_config);
 | 
						cdigipeater_init (&audio_config, &cdigi_config);
 | 
				
			||||||
	pfilter_init (&igate_config, d_f_opt);
 | 
						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.
 | 
					 * 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.
 | 
					 * Inputs:	chan	- Audio channel number, 0 or 1.
 | 
				
			||||||
 *		subchan	- Which modem caught it.  
 | 
					 *		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.
 | 
					 *		slice	- Slicer which caught it.
 | 
				
			||||||
 *		pp	- Packet handle.
 | 
					 *		pp	- Packet handle.
 | 
				
			||||||
 *		alevel	- Audio level, range of 0 - 100.
 | 
					 *		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.
 | 
													// Can indicate FX.25/IL2P or fix_bits.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_TOTAL_CHANS);		// TOTAL for virtual channels
 | 
						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 (slice >= 0 && slice < MAX_SLICERS);
 | 
				
			||||||
	assert (pp != NULL);	// 1.1J+
 | 
						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);
 | 
						    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) {
 | 
						  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 {
 | 
						  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) {
 | 
						if (alevel.rec > 110) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  text_color_set(DW_COLOR_ERROR);
 | 
						  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
 | 
					// 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);
 | 
						  text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	  dw_printf ("Audio input level is too low.  Increase so most stations are around 50.\n");
 | 
						  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));
 | 
						  strlcpy (ts, "", sizeof(ts));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (subchan == -1) {
 | 
						if (subchan == -1) {	// dtmf
 | 
				
			||||||
	  text_color_set(DW_COLOR_REC);
 | 
						  text_color_set(DW_COLOR_REC);
 | 
				
			||||||
	  dw_printf ("[%d.dtmf%s] ", chan, ts);
 | 
						  dw_printf ("[%d.dtmf%s] ", chan, ts);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else if (subchan == -2) {
 | 
						else if (subchan == -2) {	// APRS-IS
 | 
				
			||||||
	  text_color_set(DW_COLOR_REC);
 | 
						  text_color_set(DW_COLOR_REC);
 | 
				
			||||||
	  dw_printf ("[%d.is%s] ", chan, ts);
 | 
						  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 {
 | 
						else {
 | 
				
			||||||
	  if (ax25_is_aprs(pp)) {
 | 
						  if (ax25_is_aprs(pp)) {
 | 
				
			||||||
	    text_color_set(DW_COLOR_REC);
 | 
						    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
 | 
							0, 0, 0, A.g_comment,	// freq, tone, offset
 | 
				
			||||||
		ais_obj_info, sizeof(ais_obj_info));
 | 
							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);
 | 
						      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.)
 | 
					 * Use only those with correct CRC (or using FEC.)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  if (retries == RETRY_NONE || fec_type == fec_type_fx25 || fec_type == fec_type_il2p) {
 | 
						  if (chan < MAX_RADIO_CHANS) {
 | 
				
			||||||
 | 
						    if (retries == RETRY_NONE || fec_type == fec_type_fx25 || fec_type == fec_type_il2p) {
 | 
				
			||||||
	    cdigipeater (chan, pp);
 | 
						      cdigipeater (chan, pp);
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1708,6 +1748,7 @@ static void usage (char **argv)
 | 
				
			||||||
#if USE_HAMLIB
 | 
					#if USE_HAMLIB
 | 
				
			||||||
	dw_printf ("       h             h = hamlib increase verbose level.\n");
 | 
						dw_printf ("       h             h = hamlib increase verbose level.\n");
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
						dw_printf ("       c             c = Connected mode data link state machine.\n");
 | 
				
			||||||
	dw_printf ("       x             x = FX.25 increase verbose level.\n");
 | 
						dw_printf ("       x             x = FX.25 increase verbose level.\n");
 | 
				
			||||||
	dw_printf ("       2             2 = IL2P.\n");
 | 
						dw_printf ("       2             2 = IL2P.\n");
 | 
				
			||||||
	dw_printf ("       d             d = APRStt (DTMF to APRS object translation).\n");
 | 
						dw_printf ("       d             d = APRStt (DTMF to APRS object translation).\n");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,15 +56,10 @@
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *	ADevice 0:	channel 0
 | 
					 *	ADevice 0:	channel 0
 | 
				
			||||||
 *	ADevice 1:	left = 2, right = 3
 | 
					 *	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_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
 | 
					#define MAX_TOTAL_CHANS 16		// v1.7 allows additional virtual channels which are connected
 | 
				
			||||||
					// to something other than radio modems.
 | 
										// to something other than radio modems.
 | 
				
			||||||
| 
						 | 
					@ -77,7 +72,7 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef USE_HAMLIB
 | 
					#ifdef USE_HAMLIB
 | 
				
			||||||
#define MAX_RIGS MAX_CHANS
 | 
					#define MAX_RIGS MAX_RADIO_CHANS
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										12
									
								
								src/dlq.c
								
								
								
								
							
							
						
						
									
										12
									
								
								src/dlq.c
								
								
								
								
							| 
						 | 
					@ -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");
 | 
						dw_printf ("dlq_connect_request (...)\n");
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Allocate a new queue item. */
 | 
					/* 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");
 | 
						dw_printf ("dlq_disconnect_request (...)\n");
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Allocate a new queue item. */
 | 
					/* 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");
 | 
						dw_printf ("dlq_outstanding_frames_request (...)\n");
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Allocate a new queue item. */
 | 
					/* 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");
 | 
						dw_printf ("dlq_xmit_data_request (...)\n");
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Allocate a new queue item. */
 | 
					/* 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);
 | 
						dw_printf ("dlq_register_callsign (%s, chan=%d, client=%d)\n", addr, chan, client);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Allocate a new queue item. */
 | 
					/* 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);
 | 
						dw_printf ("dlq_unregister_callsign (%s, chan=%d, client=%d)\n", addr, chan, client);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Allocate a new queue item. */
 | 
					/* Allocate a new queue item. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/dtmf.c
								
								
								
								
							
							
						
						
									
										11
									
								
								src/dtmf.c
								
								
								
								
							| 
						 | 
					@ -80,7 +80,7 @@ static struct dd_s {	 /* Separate for each audio channel. */
 | 
				
			||||||
	char prev_debounced;
 | 
						char prev_debounced;
 | 
				
			||||||
	int timeout;
 | 
						int timeout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} dd[MAX_CHANS];		
 | 
					} dd[MAX_RADIO_CHANS];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int s_amplitude = 100;	// range of 0 .. 100
 | 
					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.
 | 
					 * 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]);
 | 
						  struct dd_s *D = &(dd[c]);
 | 
				
			||||||
	  int a = ACHAN2ADEV(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]); 
 | 
						  struct dd_s *D = &(dd[c]); 
 | 
				
			||||||
	  D->n = 0;
 | 
						  D->n = 0;
 | 
				
			||||||
	  for (j=0; j<NUM_TONES; j++) {
 | 
						  for (j=0; j<NUM_TONES; j++) {
 | 
				
			||||||
| 
						 | 
					@ -214,6 +214,11 @@ char dtmf_sample (int c, float input)
 | 
				
			||||||
						'7', '8', '9', 'C',
 | 
											'7', '8', '9', 'C',
 | 
				
			||||||
						'*', '0', '#', 'D' };
 | 
											'*', '0', '#', 'D' };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Only applies to radio channels.  Should not be here.
 | 
				
			||||||
 | 
						if (c >= MAX_RADIO_CHANS) {
 | 
				
			||||||
 | 
						  return ('$');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	D = &(dd[c]);
 | 
						D = &(dd[c]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i=0; i<NUM_TONES; i++) {
 | 
						for (i=0; i<NUM_TONES; i++) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										12
									
								
								src/dwgpsd.c
								
								
								
								
							
							
						
						
									
										12
									
								
								src/dwgpsd.c
								
								
								
								
							| 
						 | 
					@ -68,7 +68,7 @@
 | 
				
			||||||
// 3.22		28		11	bullseye		OK.
 | 
					// 3.22		28		11	bullseye		OK.
 | 
				
			||||||
// 3.23		29		12				OK.
 | 
					// 3.23		29		12				OK.
 | 
				
			||||||
// 3.25		30		14				OK, Jan. 2023
 | 
					// 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
 | 
					// 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
 | 
					// than the last one tested.  Now it is just a warning because it changes so
 | 
				
			||||||
| 
						 | 
					@ -202,6 +202,13 @@ static void * read_gpsd_thread (void *arg);
 | 
				
			||||||
 *	[ scons check ]
 | 
					 *	[ scons check ]
 | 
				
			||||||
 *	sudo scons udev-install
 | 
					 *	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) {
 | 
						while (1) {
 | 
				
			||||||
	  dwfix_t fix;
 | 
						  dwfix_t fix;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  fix = dwgps_read (&info)
;
 | 
						  fix = dwgps_read (&info)
 | 
				
			||||||
 | 
					;
 | 
				
			||||||
	  text_color_set (DW_COLOR_INFO);
 | 
						  text_color_set (DW_COLOR_INFO);
 | 
				
			||||||
	  switch (fix) {
 | 
						  switch (fix) {
 | 
				
			||||||
	    case DWFIX_2D:
 | 
						    case DWFIX_2D:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -125,7 +125,7 @@ int dwsock_init(void)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*-------------------------------------------------------------------
 | 
					/*-------------------------------------------------------------------
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Name:        sock_connect
 | 
					 * Name:        dwsock_connect
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Purpose:     Connect to given host / port.  
 | 
					 * Purpose:     Connect to given host / port.  
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -596,14 +596,22 @@ int encode_position (int messaging, int compressed, double lat, double lon, int
 | 
				
			||||||
	presult[result_len] = '\0';
 | 
						presult[result_len] = '\0';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Altitude.  Can be anywhere in comment. */
 | 
					/* 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) {
 | 
						if (alt_ft != G_UNKNOWN) {
 | 
				
			||||||
	  char salt[12];
 | 
						  char salt[12];
 | 
				
			||||||
	  /* Not clear if altitude can be negative. */
 | 
						  /* Not clear if altitude can be negative. */
 | 
				
			||||||
	  /* Be sure it will be converted to 6 digits. */
 | 
						  /* 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;
 | 
						  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);
 | 
						  strlcat (presult, salt, result_size);
 | 
				
			||||||
	  result_len += strlen(salt);
 | 
						  result_len += strlen(salt);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -469,7 +469,7 @@ struct demodulator_state_s
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Inputs:	D		Pointer to demodulator state.
 | 
					 * 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
 | 
					 *		subchan		Which of multiple demodulators: 0 to MAX_SUBCHANS - 1
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,7 +59,7 @@ struct fx_context_s {
 | 
				
			||||||
	unsigned char block[FX25_BLOCK_SIZE+1];
 | 
						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);
 | 
					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];
 | 
						struct fx_context_s *F = fx_context[chan][subchan][slice];
 | 
				
			||||||
	if (F == NULL) {
 | 
						if (F == NULL) {
 | 
				
			||||||
          assert (chan >= 0 && chan < MAX_CHANS);
 | 
					          assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
          assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
					          assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
				
			||||||
          assert (slice >= 0 && slice < MAX_SLICERS);
 | 
					          assert (slice >= 0 && slice < MAX_SLICERS);
 | 
				
			||||||
	  F = fx_context[chan][subchan][slice] = (struct fx_context_s *)malloc(sizeof (struct fx_context_s));
 | 
						  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)
 | 
					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
 | 
						// This could be a little faster if we knew number of
 | 
				
			||||||
	// subchannels and slicers but it is probably insignificant.
 | 
						// subchannels and slicers but it is probably insignificant.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 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
 | 
					#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 void send_bit (int chan, int b)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	static int output[MAX_CHANS];
 | 
						static int output[MAX_RADIO_CHANS];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (b == 0) {
 | 
						if (b == 0) {
 | 
				
			||||||
	  output[chan] = ! output[chan];
 | 
						  output[chan] = ! output[chan];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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].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 */
 | 
					        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].modem_type = MODEM_AFSK;			/* change with -g */
 | 
				
			||||||
	  modem.achan[chan].mark_freq = DEFAULT_MARK_FREQ;              /* -m option */
 | 
						  modem.achan[chan].mark_freq = DEFAULT_MARK_FREQ;              /* -m option */
 | 
				
			||||||
          modem.achan[chan].space_freq = DEFAULT_SPACE_FREQ;            /* -s option */
 | 
					          modem.achan[chan].space_freq = DEFAULT_SPACE_FREQ;            /* -s option */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 )
 | 
					#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 */
 | 
										/* because they have same sample rate */
 | 
				
			||||||
					/* but less confusing to have for each channel. */
 | 
										/* but less confusing to have for each channel. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int ticks_per_bit[MAX_CHANS];
 | 
					static int ticks_per_bit[MAX_RADIO_CHANS];
 | 
				
			||||||
static int f1_change_per_sample[MAX_CHANS];
 | 
					static int f1_change_per_sample[MAX_RADIO_CHANS];
 | 
				
			||||||
static int f2_change_per_sample[MAX_CHANS];
 | 
					static int f2_change_per_sample[MAX_RADIO_CHANS];
 | 
				
			||||||
static float samples_per_symbol[MAX_CHANS];
 | 
					static float samples_per_symbol[MAX_RADIO_CHANS];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static short sine_table[256];
 | 
					static short sine_table[256];
 | 
				
			||||||
| 
						 | 
					@ -78,7 +78,7 @@ static short sine_table[256];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Accumulators. */
 | 
					/* 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.
 | 
										   // Upper bits are used as index into sine table.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define PHASE_SHIFT_180 ( 128u << 24 )
 | 
					#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 )
 | 
					#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.
 | 
										// on the channel.   This is only used for QPSK.
 | 
				
			||||||
					// The LSB determines if we save the bit until
 | 
										// The LSB determines if we save the bit until
 | 
				
			||||||
					// next time, or send this one with the previously saved.
 | 
										// 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
 | 
										// For 8PSK, it has a different meaning.  It is the
 | 
				
			||||||
					// number of bits in 'save_bit' so we can accumulate
 | 
										// number of bits in 'save_bit' so we can accumulate
 | 
				
			||||||
					// three for each symbol.
 | 
										// 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);
 | 
						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) {
 | 
						  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.
 | 
					// #define PSKIQ 1  // not ready for prime time yet.
 | 
				
			||||||
#if PSKIQ
 | 
					#if PSKIQ
 | 
				
			||||||
static int xmit_octant[MAX_CHANS];	// absolute phase in 45 degree units.
 | 
					static int xmit_octant[MAX_RADIO_CHANS];	// absolute phase in 45 degree units.
 | 
				
			||||||
static int xmit_prev_octant[MAX_CHANS];	// from previous symbol.
 | 
					static int xmit_prev_octant[MAX_RADIO_CHANS];	// from previous symbol.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// For PSK, we generate the final signal by combining fixed frequency cosine and
 | 
					// For PSK, we generate the final signal by combining fixed frequency cosine and
 | 
				
			||||||
// sine by the following weights.
 | 
					// sine by the following weights.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -114,11 +114,11 @@ struct hdlc_state_s {
 | 
				
			||||||
	int eas_fields_after_plus;	/* Number of "-" characters after the "+". */
 | 
						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));
 | 
						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) {
 | 
						  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)
 | 
					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. */
 | 
						int dbit;			/* Data bit after undoing NRZI. */
 | 
				
			||||||
					/* Should be only 0 or 1. */
 | 
										/* Should be only 0 or 1. */
 | 
				
			||||||
	struct hdlc_state_s *H;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (was_init == 1);
 | 
						assert (was_init == 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
						assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	assert (slice >= 0 && slice < MAX_SLICERS);
 | 
						assert (slice >= 0 && slice < MAX_SLICERS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// -e option can be used to artificially introduce the desired
 | 
					// -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.
 | 
					 * Different state information for each channel / subchannel / slice.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
	H = &hdlc_state[chan][subchan][slice];
 | 
						struct hdlc_state_s *H = &hdlc_state[chan][subchan][slice];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -590,15 +597,43 @@ void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled,
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	  if (rrbb_get_len(H->rrbb) >= MIN_FRAME_LEN * 8) {
 | 
						  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);
 | 
						    alevel_t alevel = demod_get_audio_level (chan, subchan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    rrbb_set_audio_level (H->rrbb, alevel);
 | 
						    rrbb_set_audio_level (H->rrbb, alevel);
 | 
				
			||||||
	    hdlc_rec2_block (H->rrbb);
 | 
						    hdlc_rec2_block (H->rrbb);
 | 
				
			||||||
	    	/* Now owned by someone else who will free it. */
 | 
						    	/* 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. */
 | 
						    H->rrbb = rrbb_new (chan, subchan, slice, is_scrambled, H->lfsr, H->prev_descram); /* Allocate a new one. */
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	  else {
 | 
						  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); 
 | 
						    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;
 | 
						int old, new;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
	assert (subchan >= 0 && subchan <= MAX_SUBCHANS);
 | 
						assert (subchan >= 0 && subchan <= MAX_SUBCHANS);
 | 
				
			||||||
	assert (slice >= 0 && slice < MAX_SLICERS);
 | 
						assert (slice >= 0 && slice < MAX_SLICERS);
 | 
				
			||||||
	assert (state == 0 || state == 1);
 | 
						assert (state == 0 || state == 1);
 | 
				
			||||||
| 
						 | 
					@ -791,7 +826,7 @@ int hdlc_rec_data_detect_any (int chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int sc;
 | 
						int sc;
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (sc = 0; sc < num_subchan[chan]; sc++) {
 | 
						for (sc = 0; sc < num_subchan[chan]; sc++) {
 | 
				
			||||||
	  if (composite_dcd[chan][sc] != 0)
 | 
						  if (composite_dcd[chan][sc] != 0)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,22 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* hdlc_rec.h */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdint.h>          // int64_t
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "audio.h"
 | 
					#include "audio.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void hdlc_rec_init (struct audio_s *pa);
 | 
					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 (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. */
 | 
					/* Provided elsewhere to process a complete frame. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//void process_rec_frame (int chan, unsigned char *fbuf, int flen, int level);
 | 
					//void process_rec_frame (int chan, unsigned char *fbuf, int flen, int level);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -216,6 +216,8 @@ void hdlc_rec2_init (struct audio_s *p_audio_config)
 | 
				
			||||||
 * Purpose:	Extract HDLC frame from a stream of bits.
 | 
					 * Purpose:	Extract HDLC frame from a stream of bits.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Inputs:	block 		- Handle for bit array.
 | 
					 * 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
 | 
					 * Description:	The other (original) hdlc decoder took one bit at a time
 | 
				
			||||||
 *		right out of the demodulator.
 | 
					 *		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 */
 | 
						  /* Let thru even with bad CRC.  Of course, it still */
 | 
				
			||||||
	  /* needs to be a minimum number of whole octets. */
 | 
						  /* needs to be a minimum number of whole octets. */
 | 
				
			||||||
	  ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 1);
 | 
						  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 */
 | 
					} /* 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;
 | 
						retry_cfg.u_bits.sep.bit_idx_c = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef DEBUG_LATER
 | 
					#ifdef DEBUG_LATER
 | 
				
			||||||
	tstart = dtime_now();
 | 
						tstart = dtime_monotonic();
 | 
				
			||||||
	dw_printf ("*** Try flipping TWO SEPARATED BITS %d bits\n", len);
 | 
						dw_printf ("*** Try flipping TWO SEPARATED BITS %d bits\n", len);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	len = rrbb_get_len(block);
 | 
						len = rrbb_get_len(block);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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.
 | 
					// 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."
 | 
										// need to break up a long run by "bit stuffing."
 | 
				
			||||||
					// Needs to be array because we could be transmitting
 | 
										// Needs to be array because we could be transmitting
 | 
				
			||||||
					// on multiple channels at the same time.
 | 
										// 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 void send_bit_nrzi (int chan, int b)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	static int output[MAX_CHANS];
 | 
						static int output[MAX_RADIO_CHANS];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (b == 0) {
 | 
						if (b == 0) {
 | 
				
			||||||
	  output[chan] = ! output[chan];
 | 
						  output[chan] = ! output[chan];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										29
									
								
								src/igate.c
								
								
								
								
							
							
						
						
									
										29
									
								
								src/igate.c
								
								
								
								
							| 
						 | 
					@ -216,8 +216,8 @@ int main (int argc, char *argv[])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memset (&audio_config, 0, sizeof(audio_config));
 | 
						memset (&audio_config, 0, sizeof(audio_config));
 | 
				
			||||||
	audio_config.adev[0].num_channels = 2;
 | 
						audio_config.adev[0].num_channels = 2;
 | 
				
			||||||
	strlcpy (audio_config.achan[0].mycall, "WB2OSZ-1", sizeof(audio_config.achan[0].mycall));
 | 
						strlcpy (audio_config.mycall[0], "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[1], "WB2OSZ-2", sizeof(audio_config.achan[0].mycall));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memset (&igate_config, 0, sizeof(igate_config));
 | 
						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.
 | 
					// Beacon will be channel -1.
 | 
				
			||||||
// Client app to ICHANNEL is outside of radio channel range.
 | 
					// Client app to ICHANNEL is outside of radio channel range.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (chan >= 0 && chan < MAX_CHANS && 		// in radio channel range
 | 
						if (chan >= 0 && chan < MAX_TOTAL_CHANS && 		// in radio channel range
 | 
				
			||||||
		save_digi_config_p->filter_str[chan][MAX_CHANS] != NULL) {
 | 
							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?
 | 
						    // 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.
 | 
						    // 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) {
 | 
						    if (s_debug >= 1) {
 | 
				
			||||||
	      text_color_set(DW_COLOR_INFO);
 | 
						      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;
 | 
						    return;
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
| 
						 | 
					@ -998,6 +998,7 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Do not relay generic query.
 | 
					 * 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 (ax25_get_dti(pp) == '?') {
 | 
				
			||||||
	  if (s_debug >= 1) {
 | 
						  if (s_debug >= 1) {
 | 
				
			||||||
| 
						 | 
					@ -1071,6 +1072,8 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
 | 
				
			||||||
 * Inputs:	pp 	- Packet object.
 | 
					 * Inputs:	pp 	- Packet object.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		chan	- Radio channel where it was received.
 | 
					 *		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.
 | 
					 * Description:	Duplicate detection is handled here.
 | 
				
			||||||
 *		Suppress if same was sent recently.
 | 
					 *		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, ",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));
 | 
						strlcat (msg, ":", sizeof(msg));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1781,7 +1784,7 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int n;
 | 
						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.
 | 
					 * 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.
 | 
					 * 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 ( ! 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.
 | 
						      // 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.
 | 
						      // 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. */
 | 
						char dest[AX25_MAX_ADDR_LEN];		/* Destination field. */
 | 
				
			||||||
	ax25_get_addr_with_ssid (pp3, AX25_DESTINATION, dest);
 | 
						ax25_get_addr_with_ssid (pp3, AX25_DESTINATION, dest);
 | 
				
			||||||
	snprintf (payload, sizeof(payload), "%s>%s,TCPIP,%s*:%s",
 | 
						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
 | 
					#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)) {
 | 
						if (ig_to_tx_allow (pp3, to_chan)) {
 | 
				
			||||||
	  char radio [2400];
 | 
						  char radio [2400];
 | 
				
			||||||
	  snprintf (radio, sizeof(radio), "%s>%s%d%d%s:}%s",
 | 
						  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,
 | 
									APP_TOCALL, MAJOR_VERSION, MINOR_VERSION,
 | 
				
			||||||
				save_igate_config_p->tx_via,
 | 
									save_igate_config_p->tx_via,
 | 
				
			||||||
				payload);
 | 
									payload);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,7 +69,7 @@ struct il2p_context_s {
 | 
				
			||||||
	int corrected;		// Number of symbols corrected by RS FEC.
 | 
						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];
 | 
						struct il2p_context_s *F = il2p_context[chan][subchan][slice];
 | 
				
			||||||
	if (F == NULL) {
 | 
						if (F == NULL) {
 | 
				
			||||||
          assert (chan >= 0 && chan < MAX_CHANS);
 | 
					          assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
          assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
					          assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
				
			||||||
          assert (slice >= 0 && slice < MAX_SLICERS);
 | 
					          assert (slice >= 0 && slice < MAX_SLICERS);
 | 
				
			||||||
	  F = il2p_context[chan][subchan][slice] = (struct il2p_context_s *)malloc(sizeof (struct il2p_context_s));
 | 
						  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) {
 | 
						      if (pp != NULL) {
 | 
				
			||||||
	          alevel_t alevel = demod_get_audio_level (chan, subchan);
 | 
						          alevel_t alevel = demod_get_audio_level (chan, subchan);
 | 
				
			||||||
	          retry_t retries = F->corrected;
 | 
						          retry_t retries = F->corrected;
 | 
				
			||||||
	          int is_fx25 = 1;		// FIXME: distinguish fx.25 and IL2P.
 | 
						          fec_type_t fec_type = fec_type_il2p;
 | 
				
			||||||
						// Currently this just means that a FEC mode was used.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	          // TODO: Could we put last 3 arguments in packet object rather than passing around separately?
 | 
						          // 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.
 | 
						    }   // end block for local variables.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,7 +30,7 @@
 | 
				
			||||||
#include "gen_tone.h"
 | 
					#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_bytes (int chan, unsigned char *b, int count, int polarity);
 | 
				
			||||||
static void send_bit (int chan, int b, int polarity);
 | 
					static void send_bit (int chan, int b, int polarity);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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. */
 | 
						    /* Verify that the radio channel number is valid. */
 | 
				
			||||||
	    /* Any sort of medium should be OK here. */
 | 
						    /* 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) {
 | 
							&& save_audio_config_p->chan_medium[chan]  != MEDIUM_IGATE) {
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf ("Invalid transmit channel %d from KISS client app.\n", chan);
 | 
						      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);
 | 
					          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);
 | 
						  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);
 | 
					            text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	    dw_printf ("Are you sure you want such an extreme value for TXDELAY?\n");
 | 
						    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]);
 | 
						  xmit_set_txdelay (chan, kiss_msg[1]);
 | 
				
			||||||
	  break;
 | 
						  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) {
 | 
						  if (kiss_msg[1] < 5 || kiss_msg[1] > 250) {
 | 
				
			||||||
            text_color_set(DW_COLOR_ERROR);
 | 
					            text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	    dw_printf ("Are you sure you want such an extreme value for PERSIST?\n");
 | 
						    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]);
 | 
						  xmit_set_persist (chan, kiss_msg[1]);
 | 
				
			||||||
	  break;
 | 
						  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) {
 | 
						  if (kiss_msg[1] < 2 || kiss_msg[1] > 50) {
 | 
				
			||||||
            text_color_set(DW_COLOR_ERROR);
 | 
					            text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	    dw_printf ("Are you sure you want such an extreme value for SLOTTIME?\n");
 | 
						    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]);
 | 
						  xmit_set_slottime (chan, kiss_msg[1]);
 | 
				
			||||||
	  break;
 | 
						  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);
 | 
					          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);
 | 
						  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);
 | 
					            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 ("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]);
 | 
						  xmit_set_txtail (chan, kiss_msg[1]);
 | 
				
			||||||
	  break;
 | 
						  break;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										14
									
								
								src/mheard.c
								
								
								
								
							
							
						
						
									
										14
									
								
								src/mheard.c
								
								
								
								
							| 
						 | 
					@ -406,6 +406,7 @@ void mheard_save_rf (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, r
 | 
				
			||||||
	  mptr->chan = chan;
 | 
						  mptr->chan = chan;
 | 
				
			||||||
	  mptr->num_digi_hops = hops;
 | 
						  mptr->num_digi_hops = hops;
 | 
				
			||||||
	  mptr->last_heard_rf = now;
 | 
						  mptr->last_heard_rf = now;
 | 
				
			||||||
 | 
						  // Why did I do this instead of saving the location for a position report?
 | 
				
			||||||
	  mptr->dlat = G_UNKNOWN;
 | 
						  mptr->dlat = G_UNKNOWN;
 | 
				
			||||||
	  mptr->dlon = 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) {
 | 
						// Issue 545.  This was not thought out well.
 | 
				
			||||||
	  mptr->dlat = A->g_lat;
 | 
						// There was a case where a station sent a position report and the location was stored.
 | 
				
			||||||
	  mptr->dlon = A->g_lon;
 | 
						// 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) {
 | 
						if (mheard_debug >= 2) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -126,7 +126,7 @@ static struct {
 | 
				
			||||||
	int age;
 | 
						int age;
 | 
				
			||||||
	unsigned int crc;
 | 
						unsigned int crc;
 | 
				
			||||||
	int score;
 | 
						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
 | 
					#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);
 | 
					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);
 | 
						demod_init (save_audio_config_p);
 | 
				
			||||||
	hdlc_rec_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->chan_medium[chan] == MEDIUM_RADIO) {
 | 
				
			||||||
	    if (save_audio_config_p->achan[chan].baud <= 0) {
 | 
						    if (save_audio_config_p->achan[chan].baud <= 0) {
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      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)
 | 
					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;
 | 
						packet_t pp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
						assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
				
			||||||
	assert (slice >= 0 && slice < 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];
 | 
						  char nmea[256];
 | 
				
			||||||
	  ais_to_nmea (fbuf, flen, nmea, sizeof(nmea));
 | 
						  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];
 | 
						  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);
 | 
						  pp = ax25_from_text (monfmt, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  // alevel gets in there somehow making me question why it is passed thru here.
 | 
						  // 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) {
 | 
						else if (save_audio_config_p->achan[chan].modem_type == MODEM_EAS) {
 | 
				
			||||||
	  char monfmt[300];	// EAS SAME message max length is 268
 | 
						  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);
 | 
						  pp = ax25_from_text (monfmt, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  // alevel gets in there somehow making me question why it is passed thru here.
 | 
						  // alevel gets in there somehow making me question why it is passed thru here.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
| 
						 | 
					@ -99,7 +99,7 @@ typedef enum token_type_e { TOKEN_AND, TOKEN_OR, TOKEN_NOT, TOKEN_LPAREN, TOKEN_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct pfstate_s {
 | 
					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. */
 | 
						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.  
 | 
					 * Inputs:	from_chan - Channel packet is coming from.  
 | 
				
			||||||
 *		to_chan	  - Channel packet is going to.
 | 
					 *		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.
 | 
					 *			 	For debug/error messages only.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		filter	- String of filter specs and logical operators to combine them.
 | 
					 *		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;
 | 
						char *p;
 | 
				
			||||||
	int result;
 | 
						int result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (from_chan >= 0 && from_chan <= MAX_CHANS);
 | 
						assert (from_chan >= 0 && from_chan <= MAX_TOTAL_CHANS);
 | 
				
			||||||
	assert (to_chan >= 0 && to_chan <= MAX_CHANS);
 | 
						assert (to_chan >= 0 && to_chan <= MAX_TOTAL_CHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memset (&pfstate, 0, sizeof(pfstate));
 | 
						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) {
 | 
						if (s_debug >= 1) {
 | 
				
			||||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
						  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));
 | 
						    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));
 | 
						    dw_printf (" Packet filter from radio channel %d to IGate returns %s\n", from_chan, bool2text(result));
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	  else if (is_aprs) {
 | 
						  else if (is_aprs) {
 | 
				
			||||||
| 
						 | 
					@ -1478,9 +1478,9 @@ static void print_error (pfstate_t *pf, char *msg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char intro[50];
 | 
						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]: ");
 | 
						    snprintf (intro, sizeof(intro), "filter[IG,IG]: ");
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	  else {
 | 
						  else {
 | 
				
			||||||
| 
						 | 
					@ -1489,7 +1489,7 @@ static void print_error (pfstate_t *pf, char *msg)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else {
 | 
						else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  if (pf->to_chan == MAX_CHANS) {
 | 
						  if (pf->to_chan == MAX_TOTAL_CHANS) {
 | 
				
			||||||
	    snprintf (intro, sizeof(intro), "filter[%d,IG]: ", pf->from_chan);
 | 
						    snprintf (intro, sizeof(intro), "filter[%d,IG]: ", pf->from_chan);
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	  else {
 | 
						  else {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										34
									
								
								src/ptt.c
								
								
								
								
							
							
						
						
									
										34
									
								
								src/ptt.c
								
								
								
								
							| 
						 | 
					@ -730,12 +730,12 @@ int gpiod_probe(const char *chip_name, int line_number)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static HANDLE ptt_fd[MAX_CHANS][NUM_OCTYPES];	
 | 
					static HANDLE ptt_fd[MAX_RADIO_CHANS][NUM_OCTYPES];
 | 
				
			||||||
					/* Serial port handle or fd.  */
 | 
										/* Serial port handle or fd.  */
 | 
				
			||||||
					/* Could be the same for two channels */	
 | 
										/* Could be the same for two channels */	
 | 
				
			||||||
					/* if using both RTS and DTR. */
 | 
										/* if using both RTS and DTR. */
 | 
				
			||||||
#if USE_HAMLIB
 | 
					#if USE_HAMLIB
 | 
				
			||||||
static RIG *rig[MAX_CHANS][NUM_OCTYPES];
 | 
					static RIG *rig[MAX_RADIO_CHANS][NUM_OCTYPES];
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static char otnames[NUM_OCTYPES][8];
 | 
					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]));
 | 
						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;
 | 
						  int ot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  for (ot = 0; ot < NUM_OCTYPES; 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.
 | 
					 * 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) {
 | 
						  if (audio_config_p->chan_medium[ch] == MEDIUM_RADIO) {
 | 
				
			||||||
	    int ot;
 | 
						    int ot;
 | 
				
			||||||
| 
						 | 
					@ -906,7 +906,7 @@ void ptt_init (struct audio_s *audio_config_p)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	using_gpio = 0;
 | 
						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) {
 | 
						  if (save_audio_config_p->chan_medium[ch] == MEDIUM_RADIO) {
 | 
				
			||||||
	    int ot;
 | 
						    int ot;
 | 
				
			||||||
	    for (ot = 0; ot < NUM_OCTYPES; 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)
 | 
					#if defined(USE_GPIOD)
 | 
				
			||||||
    // 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) {
 | 
						  if (save_audio_config_p->chan_medium[ch] == MEDIUM_RADIO) {
 | 
				
			||||||
	    for (int ot = 0; ot < NUM_OCTYPES; ot++) {
 | 
						    for (int ot = 0; ot < NUM_OCTYPES; ot++) {
 | 
				
			||||||
	      if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_GPIOD) {
 | 
						      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.
 | 
					 * 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) {
 | 
						  if (save_audio_config_p->chan_medium[ch] == MEDIUM_RADIO) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    int ot;	// output control type, PTT, DCD, CON, ...
 | 
						    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__) )
 | 
					#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) {
 | 
						  if (save_audio_config_p->chan_medium[ch] == MEDIUM_RADIO) {
 | 
				
			||||||
	    int ot;
 | 
						    int ot;
 | 
				
			||||||
	    for (ot = 0; ot < NUM_OCTYPES; ot++) {
 | 
						    for (ot = 0; ot < NUM_OCTYPES; ot++) {
 | 
				
			||||||
| 
						 | 
					@ -1051,7 +1051,7 @@ void ptt_init (struct audio_s *audio_config_p)
 | 
				
			||||||
#endif /* x86 Linux */
 | 
					#endif /* x86 Linux */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef USE_HAMLIB
 | 
					#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) {
 | 
						  if (save_audio_config_p->chan_medium[ch] == MEDIUM_RADIO) {
 | 
				
			||||||
	    int ot;
 | 
						    int ot;
 | 
				
			||||||
	    for (ot = 0; ot < NUM_OCTYPES; ot++) {
 | 
						    for (ot = 0; ot < NUM_OCTYPES; ot++) {
 | 
				
			||||||
| 
						 | 
					@ -1163,7 +1163,7 @@ void ptt_init (struct audio_s *audio_config_p)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if USE_CM108
 | 
					#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) {
 | 
						  if (audio_config_p->chan_medium[ch] == MEDIUM_RADIO) {
 | 
				
			||||||
	    int ot;
 | 
						    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. */
 | 
					/* 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->chan_medium[ch] == MEDIUM_RADIO) {
 | 
				
			||||||
	    if(audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_method == PTT_METHOD_NONE) {
 | 
						    if(audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_method == PTT_METHOD_NONE) {
 | 
				
			||||||
	      text_color_set(DW_COLOR_INFO);
 | 
						      text_color_set(DW_COLOR_INFO);
 | 
				
			||||||
| 
						 | 
					@ -1251,14 +1251,14 @@ void ptt_set (int ot, int chan, int ptt_signal)
 | 
				
			||||||
	int ptt2 = ptt_signal;
 | 
						int ptt2 = ptt_signal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (ot >= 0 && ot < NUM_OCTYPES);
 | 
						assert (ot >= 0 && ot < NUM_OCTYPES);
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ptt_debug_level >= 1) {
 | 
						if (ptt_debug_level >= 1) {
 | 
				
			||||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
						  text_color_set(DW_COLOR_DEBUG);
 | 
				
			||||||
	  dw_printf ("%s %d = %d\n", otnames[ot], chan, ptt_signal);
 | 
						  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) {
 | 
						if (   save_audio_config_p->chan_medium[chan] != MEDIUM_RADIO) {
 | 
				
			||||||
	  text_color_set(DW_COLOR_ERROR);
 | 
						  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)
 | 
					int get_input (int it, int chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	assert (it >= 0 && it < NUM_ICTYPES);
 | 
						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) {
 | 
						if (   save_audio_config_p->chan_medium[chan] != MEDIUM_RADIO) {
 | 
				
			||||||
	  text_color_set(DW_COLOR_ERROR);
 | 
						  text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
| 
						 | 
					@ -1559,7 +1559,7 @@ void ptt_term (void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int n;
 | 
						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) {
 | 
						  if (save_audio_config_p->chan_medium[n] == MEDIUM_RADIO) {
 | 
				
			||||||
	    int ot;
 | 
						    int ot;
 | 
				
			||||||
	    for (ot = 0; ot < NUM_OCTYPES; 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) {
 | 
						  if (save_audio_config_p->chan_medium[n] == MEDIUM_RADIO) {
 | 
				
			||||||
	    int ot;
 | 
						    int ot;
 | 
				
			||||||
	    for (ot = 0; ot < NUM_OCTYPES; ot++) {
 | 
						    for (ot = 0; ot < NUM_OCTYPES; ot++) {
 | 
				
			||||||
| 
						 | 
					@ -1586,7 +1586,7 @@ void ptt_term (void)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef USE_HAMLIB
 | 
					#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) {
 | 
						  if (save_audio_config_p->chan_medium[n] == MEDIUM_RADIO) {
 | 
				
			||||||
	    int ot;
 | 
						    int ot;
 | 
				
			||||||
	    for (ot = 0; ot < NUM_OCTYPES; ot++) {
 | 
						    for (ot = 0; ot < NUM_OCTYPES; ot++) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -108,6 +108,7 @@
 | 
				
			||||||
#include "dtmf.h"
 | 
					#include "dtmf.h"
 | 
				
			||||||
#include "aprs_tt.h"
 | 
					#include "aprs_tt.h"
 | 
				
			||||||
#include "ax25_link.h"
 | 
					#include "ax25_link.h"
 | 
				
			||||||
 | 
					//#include "ring.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if __WIN32__
 | 
					#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?
 | 
					// Try to re-init the audio device a couple times before giving up?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	text_color_set(DW_COLOR_ERROR);
 | 
						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);
 | 
						exit (1);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -83,7 +83,7 @@ rrbb_t rrbb_new (int chan, int subchan, int slice, int is_scrambled, int descram
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	rrbb_t result;
 | 
						rrbb_t result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
						assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
				
			||||||
	assert (slice >= 0 && slice < MAX_SLICERS);
 | 
						assert (slice >= 0 && slice < MAX_SLICERS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -333,7 +333,7 @@ int rrbb_get_chan (rrbb_t b)
 | 
				
			||||||
	assert (b->magic1 == MAGIC1);
 | 
						assert (b->magic1 == MAGIC1);
 | 
				
			||||||
	assert (b->magic2 == MAGIC2);
 | 
						assert (b->magic2 == MAGIC2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (b->chan >= 0 && b->chan < MAX_CHANS);
 | 
						assert (b->chan >= 0 && b->chan < MAX_RADIO_CHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return (b->chan);
 | 
						return (b->chan);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										51
									
								
								src/server.c
								
								
								
								
							
							
						
						
									
										51
									
								
								src/server.c
								
								
								
								
							| 
						 | 
					@ -976,6 +976,14 @@ void server_send_monitored (int chan, packet_t pp, int own_xmit)
 | 
				
			||||||
// Format addresses in AGWPR monitoring format such as:
 | 
					// Format addresses in AGWPR monitoring format such as:
 | 
				
			||||||
//	 1:Fm ZL4FOX-8 To Q7P2U2 Via WIDE3-3
 | 
					//	 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)
 | 
					static void mon_addrs (int chan, packet_t pp, char *result, int result_size)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char src[AX25_MAX_ADDR_LEN];
 | 
						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);
 | 
						int num_digi = ax25_get_num_repeaters(pp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (num_digi > 0) {
 | 
						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)];
 | 
						  for (int j = 0; j < num_digi; j++) {
 | 
				
			||||||
	  char stemp[AX25_MAX_ADDR_LEN+1];
 | 
						    char digiaddr[AX25_MAX_ADDR_LEN];
 | 
				
			||||||
	  int j;
 | 
					
 | 
				
			||||||
 | 
						    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 ",
 | 
						  snprintf (result, result_size, " %d:Fm %s To %s Via %s ",
 | 
				
			||||||
		chan+1, src, dst, via);
 | 
							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.
 | 
					 * 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);
 | 
						  text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	  dw_printf ("\nInvalid port number, %d, in command '%c', from AGW client application %d.\n",
 | 
						  dw_printf ("\nInvalid port number, %d, in command '%c', from AGW client application %d.\n",
 | 
				
			||||||
			cmd.hdr.portx, cmd.hdr.datakind, client);
 | 
								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.
 | 
							// No other place cares about total number.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		count = 0;
 | 
							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 ||
 | 
						          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_IGATE ||
 | 
				
			||||||
	              save_audio_config_p->chan_medium[j] == MEDIUM_NETTNC) {
 | 
						              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);
 | 
							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]) {
 | 
						          switch (save_audio_config_p->chan_medium[j]) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1752,7 +1769,11 @@ static THREAD_F cmd_listen_thread (void *arg)
 | 
				
			||||||
	          break;
 | 
						          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.
 | 
						        // Issue 527: NET/ROM routing broadcasts use PID 0xCF which was not preserved here.
 | 
				
			||||||
	        ax25_set_pid (pp, pid);
 | 
						        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.
 | 
						        // 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;
 | 
							  ok = 1;
 | 
				
			||||||
	          dlq_register_callsign (cmd.hdr.call_from, chan, client);
 | 
						          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.
 | 
						        // 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);
 | 
						          dlq_unregister_callsign (cmd.hdr.call_from, chan, client);
 | 
				
			||||||
	        }
 | 
						        }
 | 
				
			||||||
		else {
 | 
							else {
 | 
				
			||||||
| 
						 | 
					@ -2066,7 +2087,7 @@ static THREAD_F cmd_listen_thread (void *arg)
 | 
				
			||||||
	        reply.hdr.data_len_NETLE = host2netle(4);
 | 
						        reply.hdr.data_len_NETLE = host2netle(4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	        int n = 0;
 | 
						        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.
 | 
						          // Count both normal and expedited in transmit queue for given channel.
 | 
				
			||||||
		  n = tq_count (cmd.hdr.portx, -1, "", "", 0);
 | 
							  n = tq_count (cmd.hdr.portx, -1, "", "", 0);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,7 +71,7 @@
 | 
				
			||||||
#define T_NUM_ANALOG 5				/* Number of analog channels. */
 | 
					#define T_NUM_ANALOG 5				/* Number of analog channels. */
 | 
				
			||||||
#define T_NUM_DIGITAL 8				/* Number of digital 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. */
 | 
					#define MAGIC1  0x5a1111a5			/* For checking storage allocation problems. */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -285,7 +285,7 @@ int main (int argc, char *argv[])
 | 
				
			||||||
 	setlinebuf (stdout);
 | 
					 	setlinebuf (stdout);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	start_dtime = dtime_now();
 | 
						start_dtime = dtime_monotonic();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Extract command line args.
 | 
					 * 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
 | 
					 *				  and sent to a common function to check that they
 | 
				
			||||||
 *				  all arrived in order.
 | 
					 *				  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
 | 
					 * Description:	Perform any necessary configuration for the TNC then wait
 | 
				
			||||||
 *		for responses and process them.
 | 
					 *		for responses and process them.
 | 
				
			||||||
| 
						 | 
					@ -859,7 +859,7 @@ static void * tnc_thread_net (void *arg)
 | 
				
			||||||
 * What did we get?
 | 
					 * What did we get?
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  dnow = dtime_now();
 | 
						  dnow = dtime_monotonic();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  switch (mon_cmd.datakind) {
 | 
						  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
 | 
					 *				  and sent to a common function to check that they
 | 
				
			||||||
 *				  all arrived in order.
 | 
					 *				  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
 | 
					 * Description:	Perform any necessary configuration for the TNC then wait
 | 
				
			||||||
 *		for responses and process them.
 | 
					 *		for responses and process them.
 | 
				
			||||||
| 
						 | 
					@ -1038,12 +1038,12 @@ static void * tnc_thread_serial (void *arg)
 | 
				
			||||||
	      done = 1;
 | 
						      done = 1;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    else if (ch == XOFF) {
 | 
						    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);
 | 
						      printf("%*s[R %.3f] <XOFF>\n", my_index*column_width, "", dnow-start_dtime);
 | 
				
			||||||
	      busy[my_index] = 1;
 | 
						      busy[my_index] = 1;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    else if (ch == XON) {
 | 
						    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);
 | 
						      printf("%*s[R %.3f] <XON>\n", my_index*column_width, "", dnow-start_dtime);
 | 
				
			||||||
	      busy[my_index] = 0;
 | 
						      busy[my_index] = 0;
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
| 
						 | 
					@ -1070,7 +1070,7 @@ static void * tnc_thread_serial (void *arg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  if (len > 0) {
 | 
						  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);
 | 
						    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)
 | 
					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);
 | 
					 	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)
 | 
					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);
 | 
					 	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)
 | 
					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);
 | 
					 	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)
 | 
					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);
 | 
					 	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 {
 | 
						else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  // The assumption is that we are in CONVERS mode.
 | 
						  // 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.
 | 
						  int timeout = 600;	// 60 sec.  I've seen it take more than 20.
 | 
				
			||||||
	  while (timeout > 0 && busy[from]) {
 | 
						  while (timeout > 0 && busy[from]) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										66
									
								
								src/tq.c
								
								
								
								
							
							
						
						
									
										66
									
								
								src/tq.c
								
								
								
								
							| 
						 | 
					@ -52,10 +52,10 @@
 | 
				
			||||||
#include "dedupe.h"
 | 
					#include "dedupe.h"
 | 
				
			||||||
#include "igate.h"
 | 
					#include "igate.h"
 | 
				
			||||||
#include "dtime_now.h"
 | 
					#include "dtime_now.h"
 | 
				
			||||||
 | 
					#include "nettnc.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static packet_t queue_head[MAX_RADIO_CHANS][TQ_NUM_PRIO];	/* Head of linked list for each queue. */
 | 
				
			||||||
static packet_t queue_head[MAX_CHANS][TQ_NUM_PRIO];	/* Head of linked list for each queue. */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static dw_mutex_t tq_mutex;				/* Critical section for updating queues. */
 | 
					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__
 | 
					#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
 | 
					#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
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -128,7 +128,7 @@ void tq_init (struct audio_s *audio_config_p)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	save_audio_config_p = 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++) {
 | 
						  for (p=0; p<TQ_NUM_PRIO; p++) {
 | 
				
			||||||
	    queue_head[c][p] = NULL;
 | 
						    queue_head[c][p] = NULL;
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
| 
						 | 
					@ -147,7 +147,7 @@ void tq_init (struct audio_s *audio_config_p)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if __WIN32__
 | 
					#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) {
 | 
						  if (audio_config_p->chan_medium[c] == MEDIUM_RADIO) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -164,7 +164,7 @@ void tq_init (struct audio_s *audio_config_p)
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (c = 0; c < MAX_CHANS; c++) {
 | 
						for (c = 0; c < MAX_RADIO_CHANS; c++) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  xmit_thread_is_waiting[c] = 0;
 | 
						  xmit_thread_is_waiting[c] = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -199,6 +199,9 @@ void tq_init (struct audio_s *audio_config_p)
 | 
				
			||||||
 *				New in 1.7:
 | 
					 *				New in 1.7:
 | 
				
			||||||
 *				Channel can be assigned to IGate rather than a radio.
 | 
					 *				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
 | 
					 *		prio	- Priority, use TQ_PRIO_0_HI for digipeated or
 | 
				
			||||||
 *				TQ_PRIO_1_LO for normal.
 | 
					 *				TQ_PRIO_1_LO for normal.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -252,10 +255,13 @@ void tq_append (int chan, int prio, packet_t pp)
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// New in 1.7 - A channel can be assigned to the IGate rather than a radio.
 | 
					// 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
 | 
					#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.
 | 
						  char ts[100];		// optional time stamp.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -274,21 +280,39 @@ void tq_append (int chan, int prio, packet_t pp)
 | 
				
			||||||
	  unsigned char *pinfo;
 | 
						  unsigned char *pinfo;
 | 
				
			||||||
	  int info_len = ax25_get_info (pp, &pinfo);
 | 
						  int info_len = ax25_get_info (pp, &pinfo);
 | 
				
			||||||
	  text_color_set(DW_COLOR_XMIT);
 | 
						  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);
 | 
						  ax25_delete(pp);
 | 
				
			||||||
	  return;
 | 
						  return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Normal case - put in queue for radio transmission.
 | 
					// Normal case - put in queue for radio transmission.
 | 
				
			||||||
// Error if trying to transmit to a radio channel which was not configured.
 | 
					// 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);
 | 
						  text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	  dw_printf ("ERROR - Request to transmit on invalid radio channel %d.\n", chan);
 | 
						  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");
 | 
						  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
 | 
					#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.
 | 
						  // Connected mode is allowed only with internal modems.
 | 
				
			||||||
	  text_color_set(DW_COLOR_ERROR);
 | 
						  text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	  dw_printf ("ERROR - Request to transmit on invalid radio channel %d.\n", chan);
 | 
						  dw_printf ("ERROR - Request to transmit on invalid radio channel %d.\n", chan);
 | 
				
			||||||
| 
						 | 
					@ -648,7 +672,7 @@ void lm_seize_request (int chan)
 | 
				
			||||||
#endif
 | 
					#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.
 | 
						  // Connected mode is allowed only with internal modems.
 | 
				
			||||||
	  text_color_set(DW_COLOR_ERROR);
 | 
						  text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	  dw_printf ("ERROR - Request to transmit on invalid radio channel %d.\n", chan);
 | 
						  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);
 | 
						text_color_set(DW_COLOR_DEBUG);
 | 
				
			||||||
	dw_printf ("tq_wait_while_empty (%d) : enter critical section\n", chan);
 | 
						dw_printf ("tq_wait_while_empty (%d) : enter critical section\n", chan);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dw_mutex_lock (&tq_mutex);
 | 
						dw_mutex_lock (&tq_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -944,7 +968,7 @@ static int tq_is_empty (int chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int p;
 | 
						int p;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_RADIO_CHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (p=0; p<TQ_NUM_PRIO; p++) {
 | 
						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.
 | 
						// 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);
 | 
						  text_color_set(DW_COLOR_DEBUG);
 | 
				
			||||||
	  dw_printf ("INTERNAL ERROR - tq_count(%d, %d, \"%s\", \"%s\", %d)\n", chan, prio, source, dest, bytes);
 | 
						  dw_printf ("INTERNAL ERROR - tq_count(%d, %d, \"%s\", \"%s\", %d)\n", chan, prio, source, dest, bytes);
 | 
				
			||||||
	  return (0);
 | 
						  return (0);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -819,10 +819,10 @@ static void xmit_object_report (int i, int first_time)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (save_tt_config_p->obj_xmit_chan >= 0) {
 | 
						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 {
 | 
						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, ">", sizeof(stemp));
 | 
				
			||||||
	strlcat (stemp, APP_TOCALL, 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));
 | 
						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. */
 | 
					/* Fake TT gateway config. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										30
									
								
								src/xmit.c
								
								
								
								
							
							
						
						
									
										30
									
								
								src/xmit.c
								
								
								
								
							| 
						 | 
					@ -88,24 +88,24 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int xmit_slottime[MAX_CHANS];	/* Slot time in 10 mS units for persistence algorithm. */
 | 
					static int xmit_slottime[MAX_RADIO_CHANS];	/* Slot time in 10 mS units for persistence algorithm. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int xmit_persist[MAX_CHANS];	/* Sets probability for transmitting after each */
 | 
					static int xmit_persist[MAX_RADIO_CHANS];	/* Sets probability for transmitting after each */
 | 
				
			||||||
					/* slot time delay.  Transmit if a random number */
 | 
										/* slot time delay.  Transmit if a random number */
 | 
				
			||||||
					/* in range of 0 - 255 <= persist value.  */
 | 
										/* in range of 0 - 255 <= persist value.  */
 | 
				
			||||||
					/* Otherwise wait another slot time and try again. */
 | 
										/* 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. */
 | 
										/* 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 */
 | 
										/* are done sending the data.  This is to avoid */
 | 
				
			||||||
					/* dropping PTT too soon and chopping off the end */
 | 
										/* dropping PTT too soon and chopping off the end */
 | 
				
			||||||
					/* of the frame.  Again 10 mS units. */
 | 
										/* 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 */
 | 
										/* Often called baud rate which is equivalent for */
 | 
				
			||||||
					/* 1200 & 9600 cases but could be different with other */
 | 
										/* 1200 & 9600 cases but could be different with other */
 | 
				
			||||||
					/* modulation techniques. */
 | 
										/* modulation techniques. */
 | 
				
			||||||
| 
						 | 
					@ -211,11 +211,11 @@ void xmit_init (struct audio_s *p_modem, int debug_xmit_packet)
 | 
				
			||||||
	int ad;
 | 
						int ad;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if __WIN32__
 | 
					#if __WIN32__
 | 
				
			||||||
	HANDLE xmit_th[MAX_CHANS];
 | 
						HANDLE xmit_th[MAX_RADIO_CHANS];
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
	//pthread_attr_t attr;
 | 
						//pthread_attr_t attr;
 | 
				
			||||||
	//struct sched_param sp;
 | 
						//struct sched_param sp;
 | 
				
			||||||
	pthread_t xmit_tid[MAX_CHANS];
 | 
						pthread_t xmit_tid[MAX_RADIO_CHANS];
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	//int e;
 | 
						//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?
 | 
					 * 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_bits_per_sec[j] = p_modem->achan[j].baud;
 | 
				
			||||||
	  xmit_slottime[j] = p_modem->achan[j].slottime;
 | 
						  xmit_slottime[j] = p_modem->achan[j].slottime;
 | 
				
			||||||
	  xmit_persist[j] = p_modem->achan[j].persist;
 | 
						  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.
 | 
					// 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 (p_modem->chan_medium[j] == MEDIUM_RADIO) {
 | 
				
			||||||
#if __WIN32__
 | 
					#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)
 | 
					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;
 | 
						  xmit_txdelay[channel] = value;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void xmit_set_persist (int channel, int 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;
 | 
						  xmit_persist[channel] = value;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void xmit_set_slottime (int channel, int 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;
 | 
						  xmit_slottime[channel] = value;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void xmit_set_txtail (int channel, int 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;
 | 
						  xmit_txtail[channel] = value;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void xmit_set_fulldup (int channel, int 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;
 | 
						  xmit_fulldup[channel] = value;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,3 +25,4 @@ WantedBy=multi-user.target
 | 
				
			||||||
DefaultInstance=1
 | 
					DefaultInstance=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# alternate version:  https://www.f4fxl.org/start-direwolf-at-boot-the-systemd-way/
 | 
					# alternate version:  https://www.f4fxl.org/start-direwolf-at-boot-the-systemd-way/
 | 
				
			||||||
 | 
					# or:  https://groups.io/g/direwolf/message/9883
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -524,7 +524,7 @@ if(OPTIONAL_TEST)
 | 
				
			||||||
    ${CUSTOM_SRC_DIR}/pfilter.c
 | 
					    ${CUSTOM_SRC_DIR}/pfilter.c
 | 
				
			||||||
    ${CUSTOM_SRC_DIR}/telemetry.c
 | 
					    ${CUSTOM_SRC_DIR}/telemetry.c
 | 
				
			||||||
    ${CUSTOM_SRC_DIR}/decode_aprs.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}/dwgpsnmea.c
 | 
				
			||||||
    ${CUSTOM_SRC_DIR}/dwgps.c
 | 
					    ${CUSTOM_SRC_DIR}/dwgps.c
 | 
				
			||||||
    ${CUSTOM_SRC_DIR}/dwgpsd.c
 | 
					    ${CUSTOM_SRC_DIR}/dwgpsd.c
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue