mirror of https://github.com/wb2osz/direwolf.git
				
				
				
			
						commit
						7a64cd0fce
					
				| 
						 | 
					@ -80,15 +80,7 @@ jobs:
 | 
				
			||||||
              build_type: 'Release',
 | 
					              build_type: 'Release',
 | 
				
			||||||
              cmake_extra_flags: ''
 | 
					              cmake_extra_flags: ''
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          - {
 | 
					
 | 
				
			||||||
              name: 'Ubuntu 18.04',
 | 
					 | 
				
			||||||
              os: ubuntu-18.04,
 | 
					 | 
				
			||||||
              cc: 'gcc',
 | 
					 | 
				
			||||||
              cxx: 'g++',
 | 
					 | 
				
			||||||
              arch: 'x86_64',
 | 
					 | 
				
			||||||
              build_type: 'Release',
 | 
					 | 
				
			||||||
              cmake_extra_flags: ''
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: checkout
 | 
					      - name: checkout
 | 
				
			||||||
        uses: actions/checkout@v2
 | 
					        uses: actions/checkout@v2
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,64 @@
 | 
				
			||||||
 | 
					# For most projects, this workflow file will not need changing; you simply need
 | 
				
			||||||
 | 
					# to commit it to your repository.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# You may wish to alter this file to override the set of languages analyzed,
 | 
				
			||||||
 | 
					# or to provide custom queries or build logic.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# ******** NOTE ********
 | 
				
			||||||
 | 
					# We have attempted to detect the languages in your repository. Please check
 | 
				
			||||||
 | 
					# the `language` matrix defined below to confirm you have the correct set of
 | 
				
			||||||
 | 
					# supported CodeQL languages.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					name: "CodeQL - Python"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  push:
 | 
				
			||||||
 | 
					    branches: [ dev ]
 | 
				
			||||||
 | 
					  pull_request:
 | 
				
			||||||
 | 
					    # The branches below must be a subset of the branches above
 | 
				
			||||||
 | 
					    branches: [ dev ]
 | 
				
			||||||
 | 
					  schedule:
 | 
				
			||||||
 | 
					    - cron: '25 8 * * 4'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  analyze:
 | 
				
			||||||
 | 
					    name: Analyze
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    permissions:
 | 
				
			||||||
 | 
					      actions: read
 | 
				
			||||||
 | 
					      contents: read
 | 
				
			||||||
 | 
					      security-events: write
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    strategy:
 | 
				
			||||||
 | 
					      fail-fast: false
 | 
				
			||||||
 | 
					      matrix:
 | 
				
			||||||
 | 
					        language: [ 'python' ]
 | 
				
			||||||
 | 
					        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
 | 
				
			||||||
 | 
					        # Learn more about CodeQL language support at https://git.io/codeql-language-support
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					    - name: Checkout repository
 | 
				
			||||||
 | 
					      uses: actions/checkout@v3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Initializes the CodeQL tools for scanning.
 | 
				
			||||||
 | 
					    - name: Initialize CodeQL
 | 
				
			||||||
 | 
					      uses: github/codeql-action/init@v3
 | 
				
			||||||
 | 
					      with:
 | 
				
			||||||
 | 
					        languages: ${{ matrix.language }}
 | 
				
			||||||
 | 
					        setup-python-dependencies: true
 | 
				
			||||||
 | 
					        # If you wish to specify custom queries, you can do so here or in a config file.
 | 
				
			||||||
 | 
					        # By default, queries listed here will override any specified in a config file.
 | 
				
			||||||
 | 
					        # Prefix the list here with "+" to use these queries and those in the config file.
 | 
				
			||||||
 | 
					        # queries: ./path/to/local/query, your-org/your-repo/queries@main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
 | 
				
			||||||
 | 
					    # If this step fails, then you should remove it and run the build manually (see below)
 | 
				
			||||||
 | 
					    - name: Autobuild
 | 
				
			||||||
 | 
					      uses: github/codeql-action/autobuild@v3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # ℹ️ Command-line programs to run using the OS shell.
 | 
				
			||||||
 | 
					    # 📚 https://git.io/JvXDl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Perform CodeQL Analysis
 | 
				
			||||||
 | 
					      uses: github/codeql-action/analyze@v3
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@
 | 
				
			||||||
# the `language` matrix defined below to confirm you have the correct set of
 | 
					# the `language` matrix defined below to confirm you have the correct set of
 | 
				
			||||||
# supported CodeQL languages.
 | 
					# supported CodeQL languages.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
name: "CodeQL"
 | 
					name: "CodeQL - CPP"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
on:
 | 
					on:
 | 
				
			||||||
  push:
 | 
					  push:
 | 
				
			||||||
| 
						 | 
					@ -32,19 +32,20 @@ jobs:
 | 
				
			||||||
    strategy:
 | 
					    strategy:
 | 
				
			||||||
      fail-fast: false
 | 
					      fail-fast: false
 | 
				
			||||||
      matrix:
 | 
					      matrix:
 | 
				
			||||||
        language: [ 'cpp', 'python' ]
 | 
					        language: [ 'cpp' ]
 | 
				
			||||||
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
 | 
					        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
 | 
				
			||||||
        # Learn more about CodeQL language support at https://git.io/codeql-language-support
 | 
					        # Learn more about CodeQL language support at https://git.io/codeql-language-support
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
    - name: Checkout repository
 | 
					    - name: Checkout repository
 | 
				
			||||||
      uses: actions/checkout@v2
 | 
					      uses: actions/checkout@v3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Initializes the CodeQL tools for scanning.
 | 
					    # Initializes the CodeQL tools for scanning.
 | 
				
			||||||
    - name: Initialize CodeQL
 | 
					    - name: Initialize CodeQL
 | 
				
			||||||
      uses: github/codeql-action/init@v1
 | 
					      uses: github/codeql-action/init@v3
 | 
				
			||||||
      with:
 | 
					      with:
 | 
				
			||||||
        languages: ${{ matrix.language }}
 | 
					        languages: ${{ matrix.language }}
 | 
				
			||||||
 | 
					        setup-python-dependencies: true
 | 
				
			||||||
        # If you wish to specify custom queries, you can do so here or in a config file.
 | 
					        # If you wish to specify custom queries, you can do so here or in a config file.
 | 
				
			||||||
        # By default, queries listed here will override any specified in a config file.
 | 
					        # By default, queries listed here will override any specified in a config file.
 | 
				
			||||||
        # Prefix the list here with "+" to use these queries and those in the config file.
 | 
					        # Prefix the list here with "+" to use these queries and those in the config file.
 | 
				
			||||||
| 
						 | 
					@ -53,7 +54,7 @@ jobs:
 | 
				
			||||||
    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
 | 
					    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
 | 
				
			||||||
    # If this step fails, then you should remove it and run the build manually (see below)
 | 
					    # If this step fails, then you should remove it and run the build manually (see below)
 | 
				
			||||||
    - name: Autobuild
 | 
					    - name: Autobuild
 | 
				
			||||||
      uses: github/codeql-action/autobuild@v1
 | 
					      uses: github/codeql-action/autobuild@v3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # ℹ️ Command-line programs to run using the OS shell.
 | 
					    # ℹ️ Command-line programs to run using the OS shell.
 | 
				
			||||||
    # 📚 https://git.io/JvXDl
 | 
					    # 📚 https://git.io/JvXDl
 | 
				
			||||||
| 
						 | 
					@ -70,4 +71,4 @@ jobs:
 | 
				
			||||||
       make test
 | 
					       make test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - name: Perform CodeQL Analysis
 | 
					    - name: Perform CodeQL Analysis
 | 
				
			||||||
      uses: github/codeql-action/analyze@v1
 | 
					      uses: github/codeql-action/analyze@v3
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										42
									
								
								CHANGES.md
								
								
								
								
							
							
						
						
									
										42
									
								
								CHANGES.md
								
								
								
								
							| 
						 | 
					@ -2,20 +2,39 @@
 | 
				
			||||||
# Revision History #
 | 
					# Revision History #
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Version 1.7  --  Under Development ('dev' branch) ##
 | 
					## Version 1.7  --  October 2023 ##
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### New Documentation: ###
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Additional documentation location to slow down growth of main repository.  [https://github.com/wb2osz/direwolf-doc](https://github.com/wb2osz/direwolf-doc) .   These are more oriented toward achieving a goal and understanding, as opposed to the User Guide which describes the functionality.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- ***APRS Digipeaters***
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- ***Internal Packet Routing***
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- ***Radio Interface Guide***
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- ***Successful IGate Operation***
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- ***Understanding APRS Packets***
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### New Features: ###
 | 
					### New Features: ###
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Additional documentation location to slow down growth of main repository.  [https://github.com/wb2osz/direwolf-doc](https://github.com/wb2osz/direwolf-doc)
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- New ICHANNEL configuration option to map a KISS client application channel to APRS-IS. Packets from APRS-IS will be presented to client applications as the specified channel. Packets sent, by client applications, to that channel will go to APRS-IS rather than a radio channel.  Details in ***Internal-Packet-Routing.pdf***.
 | 
					- New ICHANNEL configuration option to map a KISS client application channel to APRS-IS. Packets from APRS-IS will be presented to client applications as the specified channel. Packets sent, by client applications, to that channel will go to APRS-IS rather than a radio channel.  Details in ***Internal-Packet-Routing.pdf***.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- New variable speed option for gen_packets. For example,  "-v 5,0.1" would generate packets from 5% too slow to 5% too fast with increments of 0.1.  Some implementations might have imprecise timing.  Use this to test how well TNCs tolerate sloppy timing.
 | 
					- New variable speed option for gen_packets. For example,  "-v 5,0.1" would generate packets from 5% too slow to 5% too fast with increments of 0.1.  Some implementations might have imprecise timing.  Use this to test how well TNCs tolerate sloppy timing.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Improved Layer 2 Protocol [(IL2P)](https://en.wikipedia.org/wiki/FX.25_Forward_Error_Correction).  Use "-I 1" on command line to enable transmit for first channel.  Compatible with Nino TNC for 1200 and 9600 bps.
 | 
					- Improved Layer 2 Protocol [(IL2P)](https://en.wikipedia.org/wiki/FX.25_Forward_Error_Correction).    Compatible with Nino TNC for 1200 and 9600 bps.  Use "-I 1" on command line to enable transmit for first channel.  For more general case, add to config file (simplified version, see User Guide for more details):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Limited support for CM109/CM119 GPIO PTT on Windows.
 | 
					    > After:   "CHANNEL 1"   (or other channel)
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					    > Add:     "IL2PTX 1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Limited support for CM108/CM119 GPIO PTT on Windows.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Dire Wolf now advertises itself using DNS Service Discovery.  This allows suitable APRS / Packet Radio applications to find a network KISS TNC without knowing the IP address or TCP port.    Thanks to Hessu for providing this.  Currently available only for Linux and Mac OSX.  [Read all about it here.](https://github.com/hessu/aprs-specs/blob/master/TCP-KISS-DNS-SD.md)
 | 
					- Dire Wolf now advertises itself using DNS Service Discovery.  This allows suitable APRS / Packet Radio applications to find a network KISS TNC without knowing the IP address or TCP port.    Thanks to Hessu for providing this.  Currently available only for Linux and Mac OSX.  [Read all about it here.](https://github.com/hessu/aprs-specs/blob/master/TCP-KISS-DNS-SD.md)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,6 +49,21 @@
 | 
				
			||||||
    > Add:     "FX25TX 1" (or 16 or 32 or 64)
 | 
					    > Add:     "FX25TX 1" (or 16 or 32 or 64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Bugs Fixed: ###
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- The t/m packet filter incorrectly included bulletins.  It now allows only "messages" to specific stations.  Use of t/m is discouraged.  i/180 is the preferred filter for messages to users recently heard locally.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Packet filtering now skips over any third party header before classifying packet types.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Fixed build for Alpine Linux.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Notes: ###
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The Windows binary distribution now uses gcc (MinGW) version 11.3.0.
 | 
				
			||||||
 | 
					The Windows version is built for both 32 and 64 bit operating systems.
 | 
				
			||||||
 | 
					Use the 64 bit version if possible; it runs considerably faster.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Version 1.6  --  October 2020 ##
 | 
					## Version 1.6  --  October 2020 ##
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### New Build Procedure: ###
 | 
					### New Build Procedure: ###
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
cmake_minimum_required(VERSION 3.1.0)
 | 
					cmake_minimum_required(VERSION 3.5.0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
project(direwolf)
 | 
					project(direwolf)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -265,9 +265,33 @@ endif(WIN32 OR CYGWIN)
 | 
				
			||||||
# requirements
 | 
					# requirements
 | 
				
			||||||
 | 
					
 | 
				
			||||||
include(CheckSymbolExists)
 | 
					include(CheckSymbolExists)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Some platforms provide their own strlcpy & strlcat. (BSD, MacOSX)
 | 
					# Some platforms provide their own strlcpy & strlcat. (BSD, MacOSX)
 | 
				
			||||||
# Others don't so we provide our own. (Most, but not all Linux)
 | 
					# Others don't so we provide our own. (Windows, most, but not all Linux)
 | 
				
			||||||
# Define the preprocessor macro so libgps does not supply its own version.
 | 
					# Here we detect whether these are provided by the OS and set a symbol
 | 
				
			||||||
 | 
					# so that:
 | 
				
			||||||
 | 
					#  (1) libgps does not supply its own version.
 | 
				
			||||||
 | 
					#  (2) we know whether we need to supply our own copy.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This was all working fine until these were added to the gnu c library 2.38.
 | 
				
			||||||
 | 
					# References:
 | 
				
			||||||
 | 
					#  - https://www.gnu.org/software/libc/sources.html
 | 
				
			||||||
 | 
					#  - https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=NEWS;hb=HEAD
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This test is not detecting them for glibc 2.38 resulting in a conflict.
 | 
				
			||||||
 | 
					# Why?  Are they declared in a different file or in some strange way?
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This is how they are declared in include/string.h:
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#	extern __typeof (strlcpy) __strlcpy;
 | 
				
			||||||
 | 
					#	libc_hidden_proto (__strlcpy)
 | 
				
			||||||
 | 
					#	extern __typeof (strlcat) __strlcat;
 | 
				
			||||||
 | 
					#	libc_hidden_proto (__strlcat)
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Apparently cmake does not recognize this style.
 | 
				
			||||||
 | 
					# Keep this here for BSD type systems where it behaves as expected.
 | 
				
			||||||
 | 
					# We will need to add a hack in direwolf.h to define these if glibc version >= 2.38.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
check_symbol_exists(strlcpy string.h HAVE_STRLCPY)
 | 
					check_symbol_exists(strlcpy string.h HAVE_STRLCPY)
 | 
				
			||||||
if(HAVE_STRLCPY)
 | 
					if(HAVE_STRLCPY)
 | 
				
			||||||
    add_compile_options(-DHAVE_STRLCPY)
 | 
					    add_compile_options(-DHAVE_STRLCPY)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,3 +28,9 @@ SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0d8c", GROUP="audio", MODE="0660"
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Read the User Guide and run the "cm108" application for more information.
 | 
					# Read the User Guide and run the "cm108" application for more information.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Same thing for the "All In One Cable."
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="7388", GROUP="audio", MODE="0660"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -98,6 +98,11 @@
 | 
				
			||||||
%C%# Many people will simply use the default sound device.
 | 
					%C%# Many people will simply use the default sound device.
 | 
				
			||||||
%C%# Some might want to use an alternative device by choosing it here.
 | 
					%C%# Some might want to use an alternative device by choosing it here.
 | 
				
			||||||
%C%#
 | 
					%C%#
 | 
				
			||||||
 | 
					%C%#
 | 
				
			||||||
 | 
					%C%# Many examples of radio interfaces and PTT options can be found in: 
 | 
				
			||||||
 | 
					%C%# https://github.com/wb2osz/direwolf-doc/blob/main/Radio-Interface-Guide.pdf
 | 
				
			||||||
 | 
					%C%#
 | 
				
			||||||
 | 
					%C%#
 | 
				
			||||||
%R% ---------- Windows ----------
 | 
					%R% ---------- Windows ----------
 | 
				
			||||||
%R%
 | 
					%R%
 | 
				
			||||||
%W%# When the Windows version starts up, it displays something like
 | 
					%W%# When the Windows version starts up, it displays something like
 | 
				
			||||||
| 
						 | 
					@ -382,8 +387,8 @@
 | 
				
			||||||
%C%# This is not a global setting.
 | 
					%C%# This is not a global setting.
 | 
				
			||||||
%C%# It applies only the the most recent CHANNEL specified.
 | 
					%C%# It applies only the the most recent CHANNEL specified.
 | 
				
			||||||
%C%#
 | 
					%C%#
 | 
				
			||||||
%C%#	0 - Don't try to repair.
 | 
					%C%#	0 - Don't try to repair.  (default)
 | 
				
			||||||
%C%#	1 - Attempt to fix single bit error.  (default)
 | 
					%C%#	1 - Attempt to fix single bit error.
 | 
				
			||||||
%C%#
 | 
					%C%#
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
%C%#FIX_BITS 0
 | 
					%C%#FIX_BITS 0
 | 
				
			||||||
| 
						 | 
					@ -458,10 +463,10 @@
 | 
				
			||||||
%C%# the "#" from the beginning of the line below.
 | 
					%C%# the "#" from the beginning of the line below.
 | 
				
			||||||
%C%#
 | 
					%C%#
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
%C%#DIGIPEAT 0 0 ^WIDE[3-7]-[1-7]$|^TEST$ ^WIDE[12]-[12]$ TRACE
 | 
					%C%#DIGIPEAT 0 0 ^WIDE[3-7]-[1-7]$|^TEST$ ^WIDE[12]-[12]$
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
%C%# See User Guide for more explanation of what this means and how
 | 
					%C%# See User Guide and "APRS-Digipeaters.pdf" for more explanation of what
 | 
				
			||||||
%C%# it can be customized for your particular needs.
 | 
					%C%# this means and how it can be customized for your particular needs.
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
%C%# Traditional connected mode packet radio uses a different
 | 
					%C%# Traditional connected mode packet radio uses a different
 | 
				
			||||||
| 
						 | 
					@ -505,7 +510,7 @@
 | 
				
			||||||
%C%# To relay messages from the Internet to radio, you need to add
 | 
					%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
 | 
					%C%#IGTXVIA 0 WIDE1-1,WIDE2-1
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
%C%
 | 
					%C%
 | 
				
			||||||
%C%# Finally, we don't want to flood the radio channel.
 | 
					%C%# Finally, we don't want to flood the radio channel.
 | 
				
			||||||
| 
						 | 
					@ -529,82 +534,4 @@
 | 
				
			||||||
%C%#
 | 
					%C%#
 | 
				
			||||||
%C%# See separate "APRStt-Implementation-Notes" document for details.
 | 
					%C%# See separate "APRStt-Implementation-Notes" document for details.
 | 
				
			||||||
%C%#
 | 
					%C%#
 | 
				
			||||||
%C%
 | 
					
 | 
				
			||||||
%C%#
 | 
					 | 
				
			||||||
%C%# Sample gateway configuration based on:
 | 
					 | 
				
			||||||
%C%#
 | 
					 | 
				
			||||||
%C%#	http://www.aprs.org/aprstt/aprstt-coding24.txt
 | 
					 | 
				
			||||||
%C%#	http://www.aprs.org/aprs-jamboree-2013.html
 | 
					 | 
				
			||||||
%C%#
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%# Define specific points.
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%TTPOINT  B01  37^55.37N  81^7.86W
 | 
					 | 
				
			||||||
%C%TTPOINT  B7495088  42.605237  -71.34456
 | 
					 | 
				
			||||||
%C%TTPOINT  B934  42.605237  -71.34456
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%TTPOINT B901  42.661279  -71.364452
 | 
					 | 
				
			||||||
%C%TTPOINT B902  42.660411  -71.364419
 | 
					 | 
				
			||||||
%C%TTPOINT B903  42.659046  -71.364452
 | 
					 | 
				
			||||||
%C%TTPOINT B904  42.657578  -71.364602
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%# For location at given bearing and distance from starting point.
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%TTVECTOR  B5bbbddd  37^55.37N  81^7.86W  0.01  mi
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%# For location specified by x, y coordinates.
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%TTGRID   Byyyxxx    37^50.00N  81^00.00W  37^59.99N  81^09.99W
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%# UTM location for Lowell-Dracut-Tyngsborough State Forest.
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%TTUTM  B6xxxyyy  19T  10  300000  4720000
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%# Location for the corral.
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%TTCORRAL   37^55.50N  81^7.00W  0^0.02N
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%# Compact messages - Fixed locations xx and object yyy where
 | 
					 | 
				
			||||||
%C%#   	Object numbers 100 - 199	= bicycle
 | 
					 | 
				
			||||||
%C%#	Object numbers 200 - 299	= fire truck
 | 
					 | 
				
			||||||
%C%#	Others				= dog
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%TTMACRO  xx1yy  B9xx*AB166*AA2B4C5B3B0A1yy
 | 
					 | 
				
			||||||
%C%TTMACRO  xx2yy  B9xx*AB170*AA3C4C7C3B0A2yy
 | 
					 | 
				
			||||||
%C%TTMACRO  xxyyy  B9xx*AB180*AA3A6C4A0Ayyy
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%TTMACRO  z  Cz
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%# Receive on channel 0, Transmit object reports on channel 1 with optional via path.
 | 
					 | 
				
			||||||
%C%# You probably want to put in a transmit delay on the APRStt channel so it
 | 
					 | 
				
			||||||
%C%# it doesn't start sending a response before the user releases PTT.
 | 
					 | 
				
			||||||
%C%# This is in 10 ms units so 100 means 1000 ms = 1 second.
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%#TTOBJ 0 1 WIDE1-1
 | 
					 | 
				
			||||||
%C%#CHANNEL 0
 | 
					 | 
				
			||||||
%C%#DWAIT 100
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%# Advertise gateway position with beacon.
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%# OBEACON DELAY=0:15 EVERY=10:00 VIA=WIDE1-1 OBJNAME=WB2OSZ-tt SYMBOL=APRStt LAT=42^37.14N LONG=71^20.83W COMMENT="APRStt Gateway"
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%# Sample speech responses.
 | 
					 | 
				
			||||||
%C%# Default is Morse code "R" for received OK and "?" for all errors.
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
%C%#TTERR  OK               SPEECH  Message Received.
 | 
					 | 
				
			||||||
%C%#TTERR  D_MSG            SPEECH  D not implemented.
 | 
					 | 
				
			||||||
%C%#TTERR  INTERNAL         SPEECH  Internal error.
 | 
					 | 
				
			||||||
%C%#TTERR  MACRO_NOMATCH    SPEECH  No definition for digit sequence.
 | 
					 | 
				
			||||||
%C%#TTERR  BAD_CHECKSUM     SPEECH  Bad checksum on call.
 | 
					 | 
				
			||||||
%C%#TTERR  INVALID_CALL     SPEECH  Invalid callsign.
 | 
					 | 
				
			||||||
%C%#TTERR  INVALID_OBJNAME  SPEECH  Invalid object name.
 | 
					 | 
				
			||||||
%C%#TTERR  INVALID_SYMBOL   SPEECH  Invalid symbol.
 | 
					 | 
				
			||||||
%C%#TTERR  INVALID_LOC      SPEECH  Invalid location.
 | 
					 | 
				
			||||||
%C%#TTERR  NO_CALL          SPEECH  No call or object name.
 | 
					 | 
				
			||||||
%C%#TTERR  SATSQ            SPEECH  Satellite square must be 4 digits.
 | 
					 | 
				
			||||||
%C%#TTERR  SUFFIX_NO_CALL   SPEECH  Send full call before using suffix.
 | 
					 | 
				
			||||||
%C%
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,19 @@
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					# Update: 1 May 2023  (still 1.7 dev version)
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# The original intention was to allow an easy way to download the most
 | 
				
			||||||
 | 
					# recent versions of some files.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# "update-data" would only work once.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# These locations are no longer being maintained:
 | 
				
			||||||
 | 
					#	http://www.aprs.org/aprs11/tocalls.txt		-- 14 Dec 2021
 | 
				
			||||||
 | 
					#	http://www.aprs.org/symbols/symbols-new.txt	-- 17 Mar 2021
 | 
				
			||||||
 | 
					#	http://www.aprs.org/symbols/symbolsX.txt	-- 25 Nov 2015
 | 
				
			||||||
 | 
					# so there is no reason to provide a capability grab the latest version.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Rather than fixing an obsolete capability, it will just be removed.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
# The destination field is often used to identify the manufacturer/model.
 | 
					# The destination field is often used to identify the manufacturer/model.
 | 
				
			||||||
# These are not hardcoded into Dire Wolf.  Instead they are read from
 | 
					# These are not hardcoded into Dire Wolf.  Instead they are read from
 | 
				
			||||||
# a file called tocalls.txt at application start up time.
 | 
					# a file called tocalls.txt at application start up time.
 | 
				
			||||||
| 
						 | 
					@ -6,24 +21,13 @@
 | 
				
			||||||
# The original permanent symbols are built in but the "new" symbols,
 | 
					# The original permanent symbols are built in but the "new" symbols,
 | 
				
			||||||
# using overlays, are often updated.  These are also read from files.
 | 
					# using overlays, are often updated.  These are also read from files.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# You can obtain an updated copy by typing "make data-update".
 | 
					
 | 
				
			||||||
# This is not part of the normal build process.  You have to do this explicitly.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# The locations below appear to be the most recent.
 | 
					 | 
				
			||||||
# The copy at http://www.aprs.org/tocalls.txt is out of date.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
include(ExternalProject)
 | 
					include(ExternalProject)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
set(TOCALLS_TXT "tocalls.txt")
 | 
					set(TOCALLS_TXT "tocalls.txt")
 | 
				
			||||||
set(TOCALLS_TXT_BKP "tocalls.txt.old")
 | 
					 | 
				
			||||||
set(TOCALLS_URL "http://www.aprs.org/aprs11/tocalls.txt")
 | 
					 | 
				
			||||||
set(SYMBOLS-NEW_TXT "symbols-new.txt")
 | 
					set(SYMBOLS-NEW_TXT "symbols-new.txt")
 | 
				
			||||||
set(SYMBOLS-NEW_TXT_BKP "symbols-new.txt.old")
 | 
					 | 
				
			||||||
set(SYMBOLS-NEW_URL "http://www.aprs.org/symbols/symbols-new.txt")
 | 
					 | 
				
			||||||
set(SYMBOLSX_TXT "symbolsX.txt")
 | 
					set(SYMBOLSX_TXT "symbolsX.txt")
 | 
				
			||||||
set(SYMBOLSX_TXT_BKP "symbolsX.txt.old")
 | 
					 | 
				
			||||||
set(SYMBOLSX_URL "http://www.aprs.org/symbols/symbolsX.txt")
 | 
					 | 
				
			||||||
set(CUSTOM_BINARY_DATA_DIR "${CMAKE_BINARY_DIR}/data")
 | 
					set(CUSTOM_BINARY_DATA_DIR "${CMAKE_BINARY_DIR}/data")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# we can also move to a separate cmake file and use file(download)
 | 
					# we can also move to a separate cmake file and use file(download)
 | 
				
			||||||
| 
						 | 
					@ -32,63 +36,6 @@ file(COPY "${CUSTOM_DATA_DIR}/${TOCALLS_TXT}" DESTINATION "${CUSTOM_BINARY_DATA_
 | 
				
			||||||
file(COPY "${CUSTOM_DATA_DIR}/${SYMBOLS-NEW_TXT}" DESTINATION "${CUSTOM_BINARY_DATA_DIR}")
 | 
					file(COPY "${CUSTOM_DATA_DIR}/${SYMBOLS-NEW_TXT}" DESTINATION "${CUSTOM_BINARY_DATA_DIR}")
 | 
				
			||||||
file(COPY "${CUSTOM_DATA_DIR}/${SYMBOLSX_TXT}" DESTINATION "${CUSTOM_BINARY_DATA_DIR}")
 | 
					file(COPY "${CUSTOM_DATA_DIR}/${SYMBOLSX_TXT}" DESTINATION "${CUSTOM_BINARY_DATA_DIR}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_custom_target(data_rename
 | 
					 | 
				
			||||||
  COMMAND ${CMAKE_COMMAND} -E rename "${CUSTOM_BINARY_DATA_DIR}/${TOCALLS_TXT}" "${CUSTOM_BINARY_DATA_DIR}/${TOCALLS_TXT_BKP}"
 | 
					 | 
				
			||||||
  COMMAND ${CMAKE_COMMAND} -E rename "${CUSTOM_BINARY_DATA_DIR}/${SYMBOLS-NEW_TXT}" "${CUSTOM_BINARY_DATA_DIR}/${SYMBOLS-NEW_TXT_BKP}"
 | 
					 | 
				
			||||||
  COMMAND ${CMAKE_COMMAND} -E rename "${CUSTOM_BINARY_DATA_DIR}/${SYMBOLSX_TXT}" "${CUSTOM_BINARY_DATA_DIR}/${SYMBOLSX_TXT_BKP}"
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ExternalProject_Add(download_tocalls
 | 
					 | 
				
			||||||
  DEPENDS data_rename
 | 
					 | 
				
			||||||
  URL ${TOCALLS_URL}
 | 
					 | 
				
			||||||
  PREFIX ""
 | 
					 | 
				
			||||||
  DOWNLOAD_DIR "${CUSTOM_BINARY_DATA_DIR}"
 | 
					 | 
				
			||||||
  DOWNLOAD_NAME "${TOCALLS_TXT}"
 | 
					 | 
				
			||||||
  DOWNLOAD_NO_EXTRACT 0
 | 
					 | 
				
			||||||
  EXCLUDE_FROM_ALL 1
 | 
					 | 
				
			||||||
  UPDATE_COMMAND ""
 | 
					 | 
				
			||||||
  PATCH_COMMAND ""
 | 
					 | 
				
			||||||
  CONFIGURE_COMMAND ""
 | 
					 | 
				
			||||||
  BUILD_COMMAND ""
 | 
					 | 
				
			||||||
  INSTALL_COMMAND ""
 | 
					 | 
				
			||||||
  TEST_COMMAND ""
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ExternalProject_Add(download_symbols-new
 | 
					 | 
				
			||||||
  DEPENDS data_rename
 | 
					 | 
				
			||||||
  URL ${SYMBOLS-NEW_URL}
 | 
					 | 
				
			||||||
  PREFIX ""
 | 
					 | 
				
			||||||
  DOWNLOAD_DIR "${CUSTOM_BINARY_DATA_DIR}"
 | 
					 | 
				
			||||||
  DOWNLOAD_NAME "${SYMBOLS-NEW_TXT}"
 | 
					 | 
				
			||||||
  DOWNLOAD_NO_EXTRACT 0
 | 
					 | 
				
			||||||
  EXCLUDE_FROM_ALL 1
 | 
					 | 
				
			||||||
  UPDATE_COMMAND ""
 | 
					 | 
				
			||||||
  PATCH_COMMAND ""
 | 
					 | 
				
			||||||
  CONFIGURE_COMMAND ""
 | 
					 | 
				
			||||||
  BUILD_COMMAND ""
 | 
					 | 
				
			||||||
  INSTALL_COMMAND ""
 | 
					 | 
				
			||||||
  TEST_COMMAND ""
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ExternalProject_Add(download_symbolsx
 | 
					 | 
				
			||||||
  DEPENDS data_rename
 | 
					 | 
				
			||||||
  URL ${SYMBOLSX_URL}
 | 
					 | 
				
			||||||
  PREFIX ""
 | 
					 | 
				
			||||||
  DOWNLOAD_DIR "${CUSTOM_BINARY_DATA_DIR}"
 | 
					 | 
				
			||||||
  DOWNLOAD_NAME "${SYMBOLSX_TXT}"
 | 
					 | 
				
			||||||
  DOWNLOAD_NO_EXTRACT 0
 | 
					 | 
				
			||||||
  EXCLUDE_FROM_ALL 1
 | 
					 | 
				
			||||||
  UPDATE_COMMAND ""
 | 
					 | 
				
			||||||
  PATCH_COMMAND ""
 | 
					 | 
				
			||||||
  CONFIGURE_COMMAND ""
 | 
					 | 
				
			||||||
  BUILD_COMMAND ""
 | 
					 | 
				
			||||||
  INSTALL_COMMAND ""
 | 
					 | 
				
			||||||
  TEST_COMMAND ""
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
add_custom_target(update-data)
 | 
					 | 
				
			||||||
add_dependencies(update-data data_rename download_tocalls download_symbols-new download_symbolsx)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
install(FILES "${CUSTOM_BINARY_DATA_DIR}/${TOCALLS_TXT}" DESTINATION ${INSTALL_DATA_DIR})
 | 
					install(FILES "${CUSTOM_BINARY_DATA_DIR}/${TOCALLS_TXT}" DESTINATION ${INSTALL_DATA_DIR})
 | 
				
			||||||
install(FILES "${CUSTOM_BINARY_DATA_DIR}/${SYMBOLS-NEW_TXT}" DESTINATION ${INSTALL_DATA_DIR})
 | 
					install(FILES "${CUSTOM_BINARY_DATA_DIR}/${SYMBOLS-NEW_TXT}" DESTINATION ${INSTALL_DATA_DIR})
 | 
				
			||||||
install(FILES "${CUSTOM_BINARY_DATA_DIR}/${SYMBOLSX_TXT}" DESTINATION ${INSTALL_DATA_DIR})
 | 
					install(FILES "${CUSTOM_BINARY_DATA_DIR}/${SYMBOLSX_TXT}" DESTINATION ${INSTALL_DATA_DIR})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@ APRS TO-CALL VERSION NUMBERS                              14 Dec 2021
 | 
				
			||||||
                                                               WB4APR
 | 
					                                                               WB4APR
 | 
				
			||||||
</title>
 | 
					</title>
 | 
				
			||||||
<version_notes>
 | 
					<version_notes>
 | 
				
			||||||
 | 
					07 Jun 23 Added APK005 for Kenwood TH-D75
 | 
				
			||||||
14 Dec 21 Added APATAR ATA-R APRS Digipeater by TA7W/OH2UDS and TA6AEU
 | 
					14 Dec 21 Added APATAR ATA-R APRS Digipeater by TA7W/OH2UDS and TA6AEU
 | 
				
			||||||
26 Sep 21 Added APRRDZ EPS32 https://github.com/dl9rdz/rdz_ttgo_sonde
 | 
					26 Sep 21 Added APRRDZ EPS32 https://github.com/dl9rdz/rdz_ttgo_sonde
 | 
				
			||||||
18 Sep 21 Added APCSS  for AMSAT Cubesat Simulator https://cubesatsim.org
 | 
					18 Sep 21 Added APCSS  for AMSAT Cubesat Simulator https://cubesatsim.org
 | 
				
			||||||
| 
						 | 
					@ -156,6 +157,7 @@ yourselves and keep me informed.
 | 
				
			||||||
 APK  APK0xx  Kenwood TH-D7's
 | 
					 APK  APK0xx  Kenwood TH-D7's
 | 
				
			||||||
      APK003  Kenwood TH-D72
 | 
					      APK003  Kenwood TH-D72
 | 
				
			||||||
      APK004  Kenwood TH-D74
 | 
					      APK004  Kenwood TH-D74
 | 
				
			||||||
 | 
					      APK005  Kenwood TH-D75
 | 
				
			||||||
      APK1xx  Kenwood D700's
 | 
					      APK1xx  Kenwood D700's
 | 
				
			||||||
      APK102  Kenwood D710
 | 
					      APK102  Kenwood D710
 | 
				
			||||||
      APKRAM  KRAMstuff.com - Mark. G7LEU
 | 
					      APKRAM  KRAMstuff.com - Mark. G7LEU
 | 
				
			||||||
| 
						 | 
					@ -278,6 +280,7 @@ yourselves and keep me informed.
 | 
				
			||||||
      APZ247  for UPRS NR0Q
 | 
					      APZ247  for UPRS NR0Q
 | 
				
			||||||
      APZ0xx  Xastir (old versions. See APX)
 | 
					      APZ0xx  Xastir (old versions. See APX)
 | 
				
			||||||
      APZMAJ  Martyn M1MAJ DeLorme inReach Tracker
 | 
					      APZMAJ  Martyn M1MAJ DeLorme inReach Tracker
 | 
				
			||||||
 | 
					      APZMDM  github/codec2_talkie  - product code not registered
 | 
				
			||||||
      APZMDR  for HaMDR trackers - hessu * hes.iki.fi]
 | 
					      APZMDR  for HaMDR trackers - hessu * hes.iki.fi]
 | 
				
			||||||
      APZPAD  Smart Palm 
 | 
					      APZPAD  Smart Palm 
 | 
				
			||||||
      APZTKP  TrackPoint, Nick N0LP (Balloon tracking)(depricated)
 | 
					      APZTKP  TrackPoint, Nick N0LP (Balloon tracking)(depricated)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
# Documentation for Dire Wolf #
 | 
					# Documentation for Dire Wolf #
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Click on the document name to view in your web browser or the link following to download the PDF file.
 | 
					Click on the document name to view in your web browser or the link following to download the PDF file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -154,6 +154,14 @@ and a couple things that can be done about it.
 | 
				
			||||||
    Here, we take a closer look at some of the frames on the TNC Test CD in hopes of gaining some insights into why some are easily decoded and others are more difficult.
 | 
					    Here, we take a closer look at some of the frames on the TNC Test CD in hopes of gaining some insights into why some are easily decoded and others are more difficult.
 | 
				
			||||||
    There are a lot of ugly signals out there.   Many can be improved by decreasing the transmit volume.   Others are just plain weird and you have to wonder how they are being generated.
 | 
					    There are a lot of ugly signals out there.   Many can be improved by decreasing the transmit volume.   Others are just plain weird and you have to wonder how they are being generated.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Additional Documentation for Dire Wolf Software TNC #
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When there was little documentation, it was all added to the source code repository [https://github.com/wb2osz/direwolf/tree/master/doc](https://github.com/wb2osz/direwolf/tree/master/doc) 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The growing number of documentation files and revisions are making the source code repository very large which means long download times.  Additional documentation, not tied to a specific release, is now being added to  [https://github.com/wb2osz/direwolf-doc](https://github.com/wb2osz/direwolf-doc) 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Questions?  Experiences to share?  ##
 | 
					## Questions?  Experiences to share?  ##
 | 
				
			||||||
 
 | 
					 
 | 
				
			||||||
Here are some good places to ask questions and share your experiences:
 | 
					Here are some good places to ask questions and share your experiences:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -32,9 +32,22 @@ if(LINUX)
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
  endif()
 | 
					  endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  add_library(misc STATIC
 | 
					  # Add_library doesn't like to get an empty source file list.
 | 
				
			||||||
    ${misc_SOURCES}
 | 
					  # I tried several variations on this theme to test whether the list
 | 
				
			||||||
    )
 | 
					  # was not empty and was not successful in getting it to work
 | 
				
			||||||
 | 
					  # on both Alpine and RPi.
 | 
				
			||||||
 | 
					  #if("${misc_SOURCES}")
 | 
				
			||||||
 | 
					  # This is less elegant and less maintainable but it works.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if ((NOT HAVE_STRLCPY) OR (NOT HAVE_STRLCAT))
 | 
				
			||||||
 | 
					    add_library(misc STATIC
 | 
				
			||||||
 | 
					      ${misc_SOURCES}
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					  else()
 | 
				
			||||||
 | 
					    set(MISC_LIBRARIES "" CACHE INTERNAL "")
 | 
				
			||||||
 | 
					  endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
elseif(WIN32 OR CYGWIN) # windows
 | 
					elseif(WIN32 OR CYGWIN) # windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -208,7 +208,8 @@ typedef unsigned long int reg_syntax_t;
 | 
				
			||||||
   some interfaces).  When a regexp is compiled, the syntax used is
 | 
					   some interfaces).  When a regexp is compiled, the syntax used is
 | 
				
			||||||
   stored in the pattern buffer, so changing this does not affect
 | 
					   stored in the pattern buffer, so changing this does not affect
 | 
				
			||||||
   already-compiled regexps.  */
 | 
					   already-compiled regexps.  */
 | 
				
			||||||
REGEX_VARIABLE_IMPEXP reg_syntax_t re_syntax_options;
 | 
					//REGEX_VARIABLE_IMPEXP reg_syntax_t re_syntax_options;
 | 
				
			||||||
 | 
					extern reg_syntax_t re_syntax_options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Define combinations of the above bits for the standard possibilities.
 | 
					/* Define combinations of the above bits for the standard possibilities.
 | 
				
			||||||
   (The [[[ comments delimit what gets put into the Texinfo file, so
 | 
					   (The [[[ comments delimit what gets put into the Texinfo file, so
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,6 +37,10 @@ Data rate in bits/sec.  Standard values are 300, 1200, 2400, 4800, 9600.
 | 
				
			||||||
4800 bps uses 8PSK based on V.27 standard.
 | 
					4800 bps uses 8PSK based on V.27 standard.
 | 
				
			||||||
.P
 | 
					.P
 | 
				
			||||||
9600 bps and up uses K9NG/G3RUH standard.
 | 
					9600 bps and up uses K9NG/G3RUH standard.
 | 
				
			||||||
 | 
					.P
 | 
				
			||||||
 | 
					AIS for ship Automatic Identification System.
 | 
				
			||||||
 | 
					.P
 | 
				
			||||||
 | 
					EAS for Emergency Alert System (EAS) Specific Area Message Encoding (SAME).
 | 
				
			||||||
.RE
 | 
					.RE
 | 
				
			||||||
.RE
 | 
					.RE
 | 
				
			||||||
.PD
 | 
					.PD
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,6 +46,8 @@ Data rate in bits/sec for first channel.  Standard values are 300, 1200, 2400, 4
 | 
				
			||||||
4800 bps uses 8PSK based on V.27 standard.
 | 
					4800 bps uses 8PSK based on V.27 standard.
 | 
				
			||||||
.P
 | 
					.P
 | 
				
			||||||
9600 bps and up uses K9NG/G3RUH standard.
 | 
					9600 bps and up uses K9NG/G3RUH standard.
 | 
				
			||||||
 | 
					.P
 | 
				
			||||||
 | 
					EAS for Emergency Alert System (EAS) Specific Area Message Encoding (SAME).
 | 
				
			||||||
.RE
 | 
					.RE
 | 
				
			||||||
.RE
 | 
					.RE
 | 
				
			||||||
.PD
 | 
					.PD
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										68
									
								
								src/atest.c
								
								
								
								
							
							
						
						
									
										68
									
								
								src/atest.c
								
								
								
								
							| 
						 | 
					@ -2,7 +2,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, 2019, 2021, 2022  John Langner, WB2OSZ
 | 
					//    Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2019, 2021, 2022, 2023  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
 | 
				
			||||||
| 
						 | 
					@ -276,12 +276,12 @@ int main (int argc, char *argv[])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case 'B':				/* -B for data Bit rate */
 | 
					            case 'B':				/* -B for data Bit rate */
 | 
				
			||||||
						/* Also implies modem type based on speed. */
 | 
											/* Also implies modem type based on speed. */
 | 
				
			||||||
						/* Special case "AIS" rather than number. */
 | 
											/* Special cases AIS, EAS rather than number. */
 | 
				
			||||||
	      if (strcasecmp(optarg, "AIS") == 0) {
 | 
						      if (strcasecmp(optarg, "AIS") == 0) {
 | 
				
			||||||
	        B_opt = 12345;	// See special case below.
 | 
						        B_opt = 0xA15A15;	// See special case below.
 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
	      else if (strcasecmp(optarg, "EAS") == 0) {
 | 
						      else if (strcasecmp(optarg, "EAS") == 0) {
 | 
				
			||||||
	        B_opt = 23456;	// See special case below.
 | 
						        B_opt = 0xEA5EA5;	// See special case below.
 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
	      else {
 | 
						      else {
 | 
				
			||||||
	        B_opt = atoi(optarg);
 | 
						        B_opt = atoi(optarg);
 | 
				
			||||||
| 
						 | 
					@ -425,11 +425,6 @@ int main (int argc, char *argv[])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        my_audio_config.achan[0].baud = B_opt;
 | 
					        my_audio_config.achan[0].baud = B_opt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (my_audio_config.achan[0].baud < MIN_BAUD || my_audio_config.achan[0].baud > MAX_BAUD) {
 | 
					 | 
				
			||||||
	  text_color_set(DW_COLOR_ERROR);
 | 
					 | 
				
			||||||
          dw_printf ("Use a more reasonable bit rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD);
 | 
					 | 
				
			||||||
          exit (EXIT_FAILURE);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* We have similar logic in direwolf.c, config.c, gen_packets.c, and atest.c, */
 | 
						/* We have similar logic in direwolf.c, config.c, gen_packets.c, and atest.c, */
 | 
				
			||||||
	/* that need to be kept in sync.  Maybe it could be a common function someday. */
 | 
						/* that need to be kept in sync.  Maybe it could be a common function someday. */
 | 
				
			||||||
| 
						 | 
					@ -438,7 +433,6 @@ int main (int argc, char *argv[])
 | 
				
			||||||
	  my_audio_config.achan[0].modem_type = MODEM_AFSK;
 | 
						  my_audio_config.achan[0].modem_type = MODEM_AFSK;
 | 
				
			||||||
	  my_audio_config.achan[0].mark_freq = 1615;
 | 
						  my_audio_config.achan[0].mark_freq = 1615;
 | 
				
			||||||
	  my_audio_config.achan[0].space_freq = 1785;
 | 
						  my_audio_config.achan[0].space_freq = 1785;
 | 
				
			||||||
	  //strlcpy (my_audio_config.achan[0].profiles, "A", sizeof(my_audio_config.achan[0].profiles));
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else if (my_audio_config.achan[0].baud < 600) {		// e.g. HF SSB packet
 | 
						else if (my_audio_config.achan[0].baud < 600) {		// e.g. HF SSB packet
 | 
				
			||||||
	  my_audio_config.achan[0].modem_type = MODEM_AFSK;
 | 
						  my_audio_config.achan[0].modem_type = MODEM_AFSK;
 | 
				
			||||||
| 
						 | 
					@ -446,13 +440,11 @@ int main (int argc, char *argv[])
 | 
				
			||||||
	  my_audio_config.achan[0].space_freq = 1800;
 | 
						  my_audio_config.achan[0].space_freq = 1800;
 | 
				
			||||||
	  // Previously we had a "D" which was fine tuned for 300 bps.
 | 
						  // Previously we had a "D" which was fine tuned for 300 bps.
 | 
				
			||||||
	  // In v1.7, it's not clear if we should use "B" or just stick with "A".
 | 
						  // In v1.7, it's not clear if we should use "B" or just stick with "A".
 | 
				
			||||||
	  //strlcpy (my_audio_config.achan[0].profiles, "B", sizeof(my_audio_config.achan[0].profiles));
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else if (my_audio_config.achan[0].baud < 1800) {	// common 1200
 | 
						else if (my_audio_config.achan[0].baud < 1800) {	// common 1200
 | 
				
			||||||
	  my_audio_config.achan[0].modem_type = MODEM_AFSK;
 | 
						  my_audio_config.achan[0].modem_type = MODEM_AFSK;
 | 
				
			||||||
	  my_audio_config.achan[0].mark_freq = DEFAULT_MARK_FREQ;
 | 
						  my_audio_config.achan[0].mark_freq = DEFAULT_MARK_FREQ;
 | 
				
			||||||
	  my_audio_config.achan[0].space_freq = DEFAULT_SPACE_FREQ;
 | 
						  my_audio_config.achan[0].space_freq = DEFAULT_SPACE_FREQ;
 | 
				
			||||||
	// Should default to E+ or something similar later.
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else if (my_audio_config.achan[0].baud < 3600) {
 | 
						else if (my_audio_config.achan[0].baud < 3600) {
 | 
				
			||||||
	  my_audio_config.achan[0].modem_type = MODEM_QPSK;
 | 
						  my_audio_config.achan[0].modem_type = MODEM_QPSK;
 | 
				
			||||||
| 
						 | 
					@ -466,14 +458,14 @@ int main (int argc, char *argv[])
 | 
				
			||||||
	  my_audio_config.achan[0].space_freq = 0;
 | 
						  my_audio_config.achan[0].space_freq = 0;
 | 
				
			||||||
	  strlcpy (my_audio_config.achan[0].profiles, "", sizeof(my_audio_config.achan[0].profiles));
 | 
						  strlcpy (my_audio_config.achan[0].profiles, "", sizeof(my_audio_config.achan[0].profiles));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else if (my_audio_config.achan[0].baud == 12345) {	// Hack for different use of 9600
 | 
						else if (my_audio_config.achan[0].baud == 0xA15A15) {	// Hack for different use of 9600
 | 
				
			||||||
	  my_audio_config.achan[0].modem_type = MODEM_AIS;
 | 
						  my_audio_config.achan[0].modem_type = MODEM_AIS;
 | 
				
			||||||
	  my_audio_config.achan[0].baud = 9600;
 | 
						  my_audio_config.achan[0].baud = 9600;
 | 
				
			||||||
	  my_audio_config.achan[0].mark_freq = 0;
 | 
						  my_audio_config.achan[0].mark_freq = 0;
 | 
				
			||||||
	  my_audio_config.achan[0].space_freq = 0;
 | 
						  my_audio_config.achan[0].space_freq = 0;
 | 
				
			||||||
	  strlcpy (my_audio_config.achan[0].profiles, " ", sizeof(my_audio_config.achan[0].profiles));	// avoid getting default later.
 | 
						  strlcpy (my_audio_config.achan[0].profiles, " ", sizeof(my_audio_config.achan[0].profiles));	// avoid getting default later.
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else if (my_audio_config.achan[0].baud == 23456) {
 | 
						else if (my_audio_config.achan[0].baud == 0xEA5EA5) {
 | 
				
			||||||
	  my_audio_config.achan[0].modem_type = MODEM_EAS;
 | 
						  my_audio_config.achan[0].modem_type = MODEM_EAS;
 | 
				
			||||||
	  my_audio_config.achan[0].baud = 521;	// Actually 520.83 but we have an integer field here.
 | 
						  my_audio_config.achan[0].baud = 521;	// Actually 520.83 but we have an integer field here.
 | 
				
			||||||
						// Will make more precise in afsk demod init.
 | 
											// Will make more precise in afsk demod init.
 | 
				
			||||||
| 
						 | 
					@ -488,6 +480,12 @@ int main (int argc, char *argv[])
 | 
				
			||||||
	  strlcpy (my_audio_config.achan[0].profiles, " ", sizeof(my_audio_config.achan[0].profiles));	// avoid getting default later.
 | 
						  strlcpy (my_audio_config.achan[0].profiles, " ", sizeof(my_audio_config.achan[0].profiles));	// avoid getting default later.
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (my_audio_config.achan[0].baud < MIN_BAUD || my_audio_config.achan[0].baud > MAX_BAUD) {
 | 
				
			||||||
 | 
						  text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
 | 
					          dw_printf ("Use a more reasonable bit rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD);
 | 
				
			||||||
 | 
					          exit (EXIT_FAILURE);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * -g option means force g3RUH regardless of speed.
 | 
					 * -g option means force g3RUH regardless of speed.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -759,7 +757,7 @@ int audio_get (int a)
 | 
				
			||||||
 * This is called when we have a good frame.
 | 
					 * This is called when we have a good frame.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, int is_fx25, retry_t retries, char *spectrum)
 | 
					void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, fec_type_t fec_type, retry_t retries, char *spectrum)
 | 
				
			||||||
{	
 | 
					{	
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	char stemp[500];
 | 
						char stemp[500];
 | 
				
			||||||
| 
						 | 
					@ -828,29 +826,31 @@ void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alev
 | 
				
			||||||
	  strlcat (heard, ")", sizeof(heard));
 | 
						  strlcat (heard, ")", sizeof(heard));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (my_audio_config.achan[chan].fix_bits == RETRY_NONE && my_audio_config.achan[chan].passall == 0) {
 | 
						switch (fec_type) {
 | 
				
			||||||
	  dw_printf ("%s audio level = %s     %s\n", heard, alevel_text, spectrum);
 | 
					
 | 
				
			||||||
	}
 | 
						  case fec_type_fx25:
 | 
				
			||||||
	else if (is_fx25) {
 | 
						    dw_printf ("%s audio level = %s   FX.25  %s\n", heard, alevel_text, spectrum);
 | 
				
			||||||
	  dw_printf ("%s audio level = %s     %s\n", heard, alevel_text, spectrum);
 | 
						    break;
 | 
				
			||||||
	}
 | 
					
 | 
				
			||||||
	else {
 | 
						  case fec_type_il2p:
 | 
				
			||||||
	  assert (retries >= RETRY_NONE && retries <= RETRY_MAX);
 | 
						    dw_printf ("%s audio level = %s   IL2P  %s\n", heard, alevel_text, spectrum);
 | 
				
			||||||
	  dw_printf ("%s audio level = %s   [%s]   %s\n", heard, alevel_text, retry_text[(int)retries], spectrum);
 | 
						    break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  case fec_type_none:
 | 
				
			||||||
 | 
						  default:
 | 
				
			||||||
 | 
						    if (my_audio_config.achan[chan].fix_bits == RETRY_NONE && my_audio_config.achan[chan].passall == 0) {
 | 
				
			||||||
 | 
						      // No fix_bits or passall specified.
 | 
				
			||||||
 | 
						      dw_printf ("%s audio level = %s     %s\n", heard, alevel_text, spectrum);
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
 | 
						    else {
 | 
				
			||||||
 | 
						      assert (retries >= RETRY_NONE && retries <= RETRY_MAX);   // validate array index.
 | 
				
			||||||
 | 
						      dw_printf ("%s audio level = %s   [%s]   %s\n", heard, alevel_text, retry_text[(int)retries], spectrum);
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
 | 
						    break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//#if defined(EXPERIMENT_G) || defined(EXPERIMENT_H)
 | 
					 | 
				
			||||||
//	int j;
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//	for (j=0; j<MAX_SUBCHANS; j++) {
 | 
					 | 
				
			||||||
//	  if (spectrum[j] == '|') {
 | 
					 | 
				
			||||||
//	    count[j]++;
 | 
					 | 
				
			||||||
//	  }
 | 
					 | 
				
			||||||
//	}
 | 
					 | 
				
			||||||
//#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Display non-APRS packets in a different color.
 | 
					// Display non-APRS packets in a different color.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										22
									
								
								src/audio.h
								
								
								
								
							
							
						
						
									
										22
									
								
								src/audio.h
								
								
								
								
							| 
						 | 
					@ -151,6 +151,17 @@ struct audio_s {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct achan_param_s {
 | 
						struct achan_param_s {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    // Currently, we have a fixed mapping from audio sources to channel.
 | 
				
			||||||
 | 
						    //
 | 
				
			||||||
 | 
						    //		ADEVICE		CHANNEL (mono)		(stereo)
 | 
				
			||||||
 | 
						    //		0		0			0, 1
 | 
				
			||||||
 | 
						    //		1		2			2, 3
 | 
				
			||||||
 | 
						    //		2		4			4, 5
 | 
				
			||||||
 | 
						    //
 | 
				
			||||||
 | 
						    // A future feauture might allow the user to specify a different audio source.
 | 
				
			||||||
 | 
						    // This would allow multiple modems (with associated channel) to share an audio source.
 | 
				
			||||||
 | 
						    // 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. */
 | 
						    char mycall[AX25_MAX_ADDR_LEN];      /* Call associated with this radio channel. */
 | 
				
			||||||
                                	/* Could all be the same or different. */
 | 
					                                	/* Could all be the same or different. */
 | 
				
			||||||
| 
						 | 
					@ -415,7 +426,8 @@ struct audio_s {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DEFAULT_BITS_PER_SAMPLE	16
 | 
					#define DEFAULT_BITS_PER_SAMPLE	16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DEFAULT_FIX_BITS RETRY_INVERT_SINGLE
 | 
					#define DEFAULT_FIX_BITS RETRY_NONE	// Interesting research project but even a single bit fix up
 | 
				
			||||||
 | 
										// will occasionally let corrupted packets through.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* 
 | 
					/* 
 | 
				
			||||||
 * Standard for AFSK on VHF FM. 
 | 
					 * Standard for AFSK on VHF FM. 
 | 
				
			||||||
| 
						 | 
					@ -445,11 +457,11 @@ struct audio_s {
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DEFAULT_DWAIT		0
 | 
					#define DEFAULT_DWAIT		0
 | 
				
			||||||
#define DEFAULT_SLOTTIME	10
 | 
					#define DEFAULT_SLOTTIME	10	// *10mS = 100mS
 | 
				
			||||||
#define DEFAULT_PERSIST		63
 | 
					#define DEFAULT_PERSIST		63
 | 
				
			||||||
#define DEFAULT_TXDELAY		30
 | 
					#define DEFAULT_TXDELAY		30	// *10mS = 300mS
 | 
				
			||||||
#define DEFAULT_TXTAIL		10	
 | 
					#define DEFAULT_TXTAIL		10	// *10mS = 100mS	
 | 
				
			||||||
#define DEFAULT_FULLDUP		0
 | 
					#define DEFAULT_FULLDUP		0	// false = half duplex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* 
 | 
					/* 
 | 
				
			||||||
 * Note that we have two versions of these in audio.c and audio_win.c.
 | 
					 * Note that we have two versions of these in audio.c and audio_win.c.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										131
									
								
								src/ax25_link.c
								
								
								
								
							
							
						
						
									
										131
									
								
								src/ax25_link.c
								
								
								
								
							| 
						 | 
					@ -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  John Langner, WB2OSZ
 | 
					//    Copyright (C) 2016, 2017, 2018, 2023  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
 | 
				
			||||||
| 
						 | 
					@ -194,14 +194,16 @@
 | 
				
			||||||
// Debug switches for different types of information.
 | 
					// Debug switches for different types of information.
 | 
				
			||||||
// Should have command line options instead of changing source and recompiling.
 | 
					// Should have command line options instead of changing source and recompiling.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int s_debug_protocol_errors = 1;	// Less serious Protocol errors.
 | 
					static int s_debug_protocol_errors = 0;	// Less serious Protocol errors.
 | 
				
			||||||
					// Useful for debugging but unnecessarily alarming other times.
 | 
										// Useful for debugging but unnecessarily alarming other times.
 | 
				
			||||||
 | 
										// Was it intentially left on for release 1.6?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int s_debug_client_app = 0;	// Interaction with client application.
 | 
					static int s_debug_client_app = 0;	// Interaction with client application.
 | 
				
			||||||
					// dl_connect_request, dl_data_request, dl_data_indication, etc.
 | 
										// dl_connect_request, dl_data_request, dl_data_indication, etc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int s_debug_radio = 0;		// Received frames and channel busy status.
 | 
					static int s_debug_radio = 0;		// Received frames and channel busy status.
 | 
				
			||||||
					// lm_data_indication, lm_channel_busy
 | 
										// lm_data_indication, lm_channel_busy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int s_debug_variables = 0;	// Variables, state changes.
 | 
					static int s_debug_variables = 0;	// Variables, state changes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int s_debug_retry = 0;		// Related to lost I frames, REJ, SREJ, timeout, resending.
 | 
					static int s_debug_retry = 0;		// Related to lost I frames, REJ, SREJ, timeout, resending.
 | 
				
			||||||
| 
						 | 
					@ -250,7 +252,7 @@ typedef struct ax25_dlsm_s {
 | 
				
			||||||
						// notifications about state changes.
 | 
											// notifications about state changes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	char addrs[AX25_MAX_REPEATERS][AX25_MAX_ADDR_LEN];
 | 
						char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN];
 | 
				
			||||||
						// Up to 10 addresses, same order as in frame.
 | 
											// Up to 10 addresses, same order as in frame.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int num_addr;				// Number of addresses.  Should be in range 2 .. 10.
 | 
						int num_addr;				// Number of addresses.  Should be in range 2 .. 10.
 | 
				
			||||||
| 
						 | 
					@ -594,6 +596,8 @@ static int AX25MODULO(int n, int m, const char *file, const char *func, int line
 | 
				
			||||||
#define PAUSE_TM201	pause_tm201(S, __func__, __LINE__)
 | 
					#define PAUSE_TM201	pause_tm201(S, __func__, __LINE__)
 | 
				
			||||||
#define RESUME_TM201	resume_tm201(S, __func__, __LINE__)
 | 
					#define RESUME_TM201	resume_tm201(S, __func__, __LINE__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: add SELECT_T1_VALUE	for debugging.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void dl_data_indication (ax25_dlsm_t *S, int pid, char *data, int len);
 | 
					static void dl_data_indication (ax25_dlsm_t *S, int pid, char *data, int len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1075,10 +1079,29 @@ void dl_disconnect_request (dlq_item_t *E)
 | 
				
			||||||
	  case 	state_1_awaiting_connection:
 | 
						  case 	state_1_awaiting_connection:
 | 
				
			||||||
	  case 	state_5_awaiting_v22_connection:
 | 
						  case 	state_5_awaiting_v22_connection:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: "requeue."  Not sure what to do here.
 | 
					// Erratum: The protocol spec says "requeue."  If we put disconnect req back in the
 | 
				
			||||||
// If we put it back in the queue we will get it back again probably still in same state.
 | 
					// queue we will probably get it back again here while still in same state.
 | 
				
			||||||
// Need a way to defer it until the next state change.
 | 
					// I don't think we would want to delay it until the next state transition.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Suppose someone tried to connect to another station, which is not responding, and decided to cancel
 | 
				
			||||||
 | 
					// before all of the SABMe retries were used up.  I think we would want to transmit a DISC, send a disc
 | 
				
			||||||
 | 
					// notice to the user, and go directly into disconnected state, rather than into awaiting release.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// New code v1.7 dev, May 6 2023
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    text_color_set(DW_COLOR_INFO);
 | 
				
			||||||
 | 
						    dw_printf ("Stream %d: In progress connection attempt to %s terminated by user.\n", S->stream_id, S->addrs[PEERCALL]);
 | 
				
			||||||
 | 
						    discard_i_queue (S);
 | 
				
			||||||
 | 
						    SET_RC(0);
 | 
				
			||||||
 | 
						    int p1 = 1;
 | 
				
			||||||
 | 
						    int nopid0 = 0;
 | 
				
			||||||
 | 
						    packet_t pp15 = ax25_u_frame (S->addrs, S->num_addr, cr_cmd, frame_type_U_DISC, p1, nopid0, NULL, 0);
 | 
				
			||||||
 | 
						    lm_data_request (S->chan, TQ_PRIO_1_LO, pp15);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    STOP_T1;	// started in establish_data_link.
 | 
				
			||||||
 | 
						    STOP_T3;	// probably don't need.
 | 
				
			||||||
 | 
						    enter_new_state (S, state_0_disconnected, __func__, __LINE__);
 | 
				
			||||||
 | 
						    server_link_terminated (S->chan, S->client, S->addrs[PEERCALL], S->addrs[OWNCALL], 0);
 | 
				
			||||||
	    break;
 | 
						    break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  case 	state_2_awaiting_release:
 | 
						  case 	state_2_awaiting_release:
 | 
				
			||||||
| 
						 | 
					@ -1580,13 +1603,49 @@ void dl_unregister_callsign (dlq_item_t *E)
 | 
				
			||||||
 *		- Incoming connected data, from application still in the queue.
 | 
					 *		- Incoming connected data, from application still in the queue.
 | 
				
			||||||
 *		- I frames which have been transmitted but not yet acknowledged.
 | 
					 *		- I frames which have been transmitted but not yet acknowledged.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
					 * Confusion:	https://github.com/wb2osz/direwolf/issues/427
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		There are different, inconsistent versions of the protocol spec.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		One of them simply has:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *			CallFrom is our call
 | 
				
			||||||
 | 
					 *			CallTo is the call of the other station
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		A more detailed version has the same thing in the table of fields:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *			CallFrom	10 bytes	Our CallSign
 | 
				
			||||||
 | 
					 *			CallTo		10 bytes	Other CallSign
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		(My first implementation went with that.)
 | 
				
			||||||
 | 
					 *		
 | 
				
			||||||
 | 
					 *		HOWEVER, shortly after that, is contradictory information:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *			Careful must be exercised to fill correctly both the CallFrom
 | 
				
			||||||
 | 
					 *			and CallTo fields to match the ones of an existing connection,
 | 
				
			||||||
 | 
					 *			otherwise AGWPE won’t return any information at all from this query.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *			The order of the CallFrom and CallTo is not trivial, it should
 | 
				
			||||||
 | 
					 *			reflect the order used to start the connection, so
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *			  *  If we started the connection CallFrom=US and CallTo=THEM
 | 
				
			||||||
 | 
					 *			  *  If the other end started the connection CallFrom=THEM and CallTo=US
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		This seems to make everything unnecessarily more complicated.
 | 
				
			||||||
 | 
					 *		We should only care about the stream going from the local station to the
 | 
				
			||||||
 | 
					 *		remote station.  Why would it matter who reqested the link?  The state
 | 
				
			||||||
 | 
					 *		machine doesn't even contain this information so the TNC doesn't know.
 | 
				
			||||||
 | 
					 *		The client app interface needs to behave differently for the two cases.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		The new code, below, May 2023, should handle both of those cases.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 *------------------------------------------------------------------------------*/
 | 
					 *------------------------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void dl_outstanding_frames_request (dlq_item_t *E)
 | 
					void dl_outstanding_frames_request (dlq_item_t *E)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	ax25_dlsm_t *S;
 | 
						ax25_dlsm_t *S;
 | 
				
			||||||
	int ok_to_create = 0;	// must exist already.
 | 
						const int ok_to_create = 0;	// must exist already.
 | 
				
			||||||
 | 
						int reversed_addrs = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (s_debug_client_app) {
 | 
						if (s_debug_client_app) {
 | 
				
			||||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
						  text_color_set(DW_COLOR_DEBUG);
 | 
				
			||||||
| 
						 | 
					@ -1594,12 +1653,28 @@ void dl_outstanding_frames_request (dlq_item_t *E)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	S = get_link_handle (E->addrs, E->num_addr, E->chan, E->client, ok_to_create);
 | 
						S = get_link_handle (E->addrs, E->num_addr, E->chan, E->client, ok_to_create);
 | 
				
			||||||
 | 
						if (S != NULL) {
 | 
				
			||||||
 | 
						  reversed_addrs = 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else {
 | 
				
			||||||
 | 
						  // Try swapping the addresses.
 | 
				
			||||||
 | 
						  // this is communicating with the client app, not over the air,
 | 
				
			||||||
 | 
						  // so we don't need to worry about digipeaters.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (S == NULL) {
 | 
						  char swapped[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN];
 | 
				
			||||||
	  text_color_set(DW_COLOR_ERROR);
 | 
						  memset (swapped, 0, sizeof(swapped));
 | 
				
			||||||
	  dw_printf ("Can't get outstanding frames for %s -> %s, chan %d\n", E->addrs[OWNCALL], E->addrs[PEERCALL], E->chan);
 | 
						  strlcpy (swapped[PEERCALL], E->addrs[OWNCALL], sizeof(swapped[PEERCALL]));
 | 
				
			||||||
	  server_outstanding_frames_reply (E->chan, E->client, E->addrs[OWNCALL], E->addrs[PEERCALL], 0);
 | 
						  strlcpy (swapped[OWNCALL], E->addrs[PEERCALL], sizeof(swapped[OWNCALL]));
 | 
				
			||||||
	  return;
 | 
						  S = get_link_handle (swapped, E->num_addr, E->chan, E->client, ok_to_create);
 | 
				
			||||||
 | 
						  if (S != NULL) {
 | 
				
			||||||
 | 
						    reversed_addrs = 1;
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
						  else {
 | 
				
			||||||
 | 
						    text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
 | 
						    dw_printf ("Can't get outstanding frames for %s -> %s, chan %d\n", E->addrs[OWNCALL], E->addrs[PEERCALL], E->chan);
 | 
				
			||||||
 | 
						    server_outstanding_frames_reply (E->chan, E->client, E->addrs[OWNCALL], E->addrs[PEERCALL], 0);
 | 
				
			||||||
 | 
						    return;
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Add up these
 | 
					// Add up these
 | 
				
			||||||
| 
						 | 
					@ -1628,7 +1703,13 @@ void dl_outstanding_frames_request (dlq_item_t *E)
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	server_outstanding_frames_reply (S->chan, S->client, S->addrs[OWNCALL], S->addrs[PEERCALL], count1 + count2);
 | 
						if (reversed_addrs) {
 | 
				
			||||||
 | 
						  // Other end initiated the link.
 | 
				
			||||||
 | 
						  server_outstanding_frames_reply (S->chan, S->client, S->addrs[PEERCALL], S->addrs[OWNCALL], count1 + count2);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else {
 | 
				
			||||||
 | 
						  server_outstanding_frames_reply (S->chan, S->client, S->addrs[OWNCALL], S->addrs[PEERCALL], count1 + count2);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // end dl_outstanding_frames_request
 | 
					} // end dl_outstanding_frames_request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6148,7 +6229,7 @@ static void select_t1_value (ax25_dlsm_t *S)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    // This goes up exponentially if implemented as documented!
 | 
						    // This goes up exponentially if implemented as documented!
 | 
				
			||||||
	    // For example, if we were trying to connect to a station which is not there, we
 | 
						    // For example, if we were trying to connect to a station which is not there, we
 | 
				
			||||||
	    // would retry after 3, the 8, 16, 32, ...  and not time out for over an hour.
 | 
						    // would retry after 3, then 8, 16, 32, ...  and not time out for over an hour.
 | 
				
			||||||
	    // That's ridiculous.   Let's try increasing it by a quarter second each time.
 | 
						    // That's ridiculous.   Let's try increasing it by a quarter second each time.
 | 
				
			||||||
	    // We now give up after about a minute.
 | 
						    // We now give up after about a minute.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6165,12 +6246,30 @@ static void select_t1_value (ax25_dlsm_t *S)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// See  https://groups.io/g/direwolf/topic/100782658#8542
 | 
				
			||||||
 | 
					// Perhaps the demands of file transfer lead to this problem.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// "Temporary" hack.
 | 
				
			||||||
 | 
					// Automatic fine tuning of t1v generally works well, but on very rare occasions, it gets wildly out of control.
 | 
				
			||||||
 | 
					// Until I have more time to properly diagnose this, add some guardrails so it does not go flying off a cliff.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The initial value of t1v is frack + frack * 2 (number of digipeateers in path)
 | 
				
			||||||
 | 
					// If anything, it should automatically be adjusted down.
 | 
				
			||||||
 | 
					// Let's say, something smells fishy if it exceeds twice that initial value.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: Add some instrumentation to record where this was called from and all the values in the printf below.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 1
 | 
				
			||||||
 | 
						if (S->t1v < 0.25 || S->t1v > 2 * (g_misc_config_p->frack * (2 * (S->num_addr - 2) + 1)) ) {
 | 
				
			||||||
 | 
						    INIT_T1V_SRT;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
	if (S->t1v < 0.99 || S->t1v > 30) {
 | 
						if (S->t1v < 0.99 || S->t1v > 30) {
 | 
				
			||||||
	  text_color_set(DW_COLOR_ERROR);
 | 
						  text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	  dw_printf ("INTERNAL ERROR?  Stream %d: select_t1_value, rc = %d, t1 remaining = %.3f, old srt = %.3f, new srt = %.3f, Extreme new t1v = %.3f\n",
 | 
						  dw_printf ("INTERNAL ERROR?  Stream %d: select_t1_value, rc = %d, t1 remaining = %.3f, old srt = %.3f, new srt = %.3f, Extreme new t1v = %.3f\n",
 | 
				
			||||||
		S->stream_id, S->rc, S->t1_remaining_when_last_stopped, old_srt, S->srt, S->t1v);
 | 
							S->stream_id, S->rc, S->t1_remaining_when_last_stopped, old_srt, S->srt, S->t1v);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
} /* end select_t1_value */
 | 
					} /* end select_t1_value */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										28
									
								
								src/cm108.c
								
								
								
								
							
							
						
						
									
										28
									
								
								src/cm108.c
								
								
								
								
							| 
						 | 
					@ -34,6 +34,7 @@
 | 
				
			||||||
 *	We have a few commercial products:
 | 
					 *	We have a few commercial products:
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		DINAH		https://hamprojects.info/dinah/
 | 
					 *		DINAH		https://hamprojects.info/dinah/
 | 
				
			||||||
 | 
					 *		PAUL		https://hamprojects.info/paul/
 | 
				
			||||||
 *		DMK URI		http://www.dmkeng.com/URI_Order_Page.htm
 | 
					 *		DMK URI		http://www.dmkeng.com/URI_Order_Page.htm
 | 
				
			||||||
 *		RB-USB RIM	http://www.repeater-builder.com/products/usb-rim-lite.html
 | 
					 *		RB-USB RIM	http://www.repeater-builder.com/products/usb-rim-lite.html
 | 
				
			||||||
 *		RA-35		http://www.masterscommunications.com/products/radio-adapter/ra35.html
 | 
					 *		RA-35		http://www.masterscommunications.com/products/radio-adapter/ra35.html
 | 
				
			||||||
| 
						 | 
					@ -93,6 +94,12 @@
 | 
				
			||||||
 *	with a single USB Audio Adapter, but does not automatically handle the multiple device case.
 | 
					 *	with a single USB Audio Adapter, but does not automatically handle the multiple device case.
 | 
				
			||||||
 *	Manual configuration needs to be used in this case.
 | 
					 *	Manual configuration needs to be used in this case.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
					 *	Here is something new and interesting.  The All in One cable (AIOC).
 | 
				
			||||||
 | 
					 *	https://github.com/skuep/AIOC/tree/master
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *	A microcontroller is used to emulate a CM108-compatible soundcard
 | 
				
			||||||
 | 
					 *	and a serial port.  It fits right on the side of a Bao Feng or similar.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 *---------------------------------------------------------------*/
 | 
					 *---------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "direwolf.h"
 | 
					#include "direwolf.h"
 | 
				
			||||||
| 
						 | 
					@ -178,6 +185,11 @@ static int cm108_write (char *name, int iomask, int iodata);
 | 
				
			||||||
#define SSS_PID2 0x1607
 | 
					#define SSS_PID2 0x1607
 | 
				
			||||||
#define SSS_PID3 0x160b
 | 
					#define SSS_PID3 0x160b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// https://github.com/skuep/AIOC/blob/master/stm32/aioc-fw/Src/usb_descriptors.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AIOC_VID 0x1209
 | 
				
			||||||
 | 
					#define AIOC_PID 0x7388
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//	Device		VID	PID		Number of GPIO
 | 
					//	Device		VID	PID		Number of GPIO
 | 
				
			||||||
//	------		---	---		--------------
 | 
					//	------		---	---		--------------
 | 
				
			||||||
| 
						 | 
					@ -217,7 +229,9 @@ static int cm108_write (char *name, int iomask, int iodata);
 | 
				
			||||||
							|| p == CMEDIA_PID_CM119A \
 | 
												|| p == CMEDIA_PID_CM119A \
 | 
				
			||||||
							|| p == CMEDIA_PID_CM119B )) \
 | 
												|| p == CMEDIA_PID_CM119B )) \
 | 
				
			||||||
				 || \
 | 
									 || \
 | 
				
			||||||
				  (v == SSS_VID && (p == SSS_PID1 || p == SSS_PID2 || p == SSS_PID3))  )
 | 
									  (v == SSS_VID && (p == SSS_PID1 || p == SSS_PID2 || p == SSS_PID3)) \
 | 
				
			||||||
 | 
									 || \
 | 
				
			||||||
 | 
									  (v == AIOC_VID && p == AIOC_PID)  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Look out for null source pointer, and avoid buffer overflow on destination.
 | 
					// Look out for null source pointer, and avoid buffer overflow on destination.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -243,6 +257,12 @@ static void substr_se (char *dest, const char *src, int start, int endp1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Maximum length of name for PTT HID.
 | 
				
			||||||
 | 
					// For Linux, this was originally 17 to handle names like /dev/hidraw3.
 | 
				
			||||||
 | 
					// Windows has more complicated names.  The longest I saw was 95 but longer have been reported.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MAXX_HIDRAW_NAME_LEN 128
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Result of taking inventory of USB soundcards and USB HIDs.
 | 
					 * Result of taking inventory of USB soundcards and USB HIDs.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -258,7 +278,8 @@ struct thing_s {
 | 
				
			||||||
				// Oversized to silence a compiler warning.
 | 
									// Oversized to silence a compiler warning.
 | 
				
			||||||
	char plughw2[72];	// With name rather than number.
 | 
						char plughw2[72];	// With name rather than number.
 | 
				
			||||||
	char devpath[128];	// Kernel dev path.  Does not include /sys mount point.
 | 
						char devpath[128];	// Kernel dev path.  Does not include /sys mount point.
 | 
				
			||||||
	char devnode_hidraw[128]; // e.g. /dev/hidraw3  -  for Linux - was length 17
 | 
						char devnode_hidraw[MAXX_HIDRAW_NAME_LEN];
 | 
				
			||||||
 | 
									// e.g. /dev/hidraw3  -  for Linux - was length 17
 | 
				
			||||||
				// The Windows path for a HID looks like this, lengths up to 95 seen.
 | 
									// The Windows path for a HID looks like this, lengths up to 95 seen.
 | 
				
			||||||
				// \\?\hid#vid_0d8c&pid_000c&mi_03#8&164d11c9&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
 | 
									// \\?\hid#vid_0d8c&pid_000c&mi_03#8&164d11c9&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
 | 
				
			||||||
	char devnode_usb[25];	// e.g. /dev/bus/usb/001/012
 | 
						char devnode_usb[25];	// e.g. /dev/bus/usb/001/012
 | 
				
			||||||
| 
						 | 
					@ -852,7 +873,7 @@ void cm108_find_ptt (char *output_audio_device, char *ptt_device,  int ptt_devic
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Errors:	A descriptive error message will be printed for any problem.
 | 
					 * Errors:	A descriptive error message will be printed for any problem.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Future:	For our initial implementation we are making the simplifying
 | 
					 * Shortcut:	For our initial implementation we are making the simplifying
 | 
				
			||||||
 *		restriction of using only one GPIO pin per device and limit
 | 
					 *		restriction of using only one GPIO pin per device and limit
 | 
				
			||||||
 *		configuration to PTT only.
 | 
					 *		configuration to PTT only.
 | 
				
			||||||
 *		Longer term, we might want to have DCD, and maybe other
 | 
					 *		Longer term, we might want to have DCD, and maybe other
 | 
				
			||||||
| 
						 | 
					@ -882,7 +903,6 @@ int cm108_set_gpio_pin (char *name, int num, int state)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	iomask = 1 << (num - 1);	// 0=input, 1=output
 | 
						iomask = 1 << (num - 1);	// 0=input, 1=output
 | 
				
			||||||
	iodata = state << (num - 1);	// 0=low, 1=high
 | 
						iodata = state << (num - 1);	// 0=low, 1=high
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return (cm108_write (name, iomask, iodata));
 | 
						return (cm108_write (name, iomask, iodata));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  /* end cm108_set_gpio_pin */
 | 
					}  /* end cm108_set_gpio_pin */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/config.c
								
								
								
								
							
							
						
						
									
										27
									
								
								src/config.c
								
								
								
								
							| 
						 | 
					@ -709,6 +709,14 @@ static char *split (char *string, int rest_of_line)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *--------------------------------------------------------------------*/
 | 
					 *--------------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void rtfm()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
 | 
						dw_printf ("See online documentation:\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 ("    additional topics:    https://github.com/wb2osz/direwolf-doc\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void config_init (char *fname, struct audio_s *p_audio_config, 
 | 
					void config_init (char *fname, struct audio_s *p_audio_config, 
 | 
				
			||||||
			struct digi_config_s *p_digi_config,
 | 
								struct digi_config_s *p_digi_config,
 | 
				
			||||||
| 
						 | 
					@ -970,7 +978,8 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
	  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 config 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");
 | 
				
			||||||
	  return;
 | 
						  rtfm();
 | 
				
			||||||
 | 
						  exit(EXIT_FAILURE);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	dw_printf ("\nReading config file %s\n", filepath);
 | 
						dw_printf ("\nReading config file %s\n", filepath);
 | 
				
			||||||
| 
						 | 
					@ -1027,7 +1036,8 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
	    if (t == NULL) {
 | 
						    if (t == NULL) {
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf ("Config file: Missing name of audio device for ADEVICE command on line %d.\n", line);
 | 
						      dw_printf ("Config file: Missing name of audio device for ADEVICE command on line %d.\n", line);
 | 
				
			||||||
	      continue;
 | 
						      rtfm();
 | 
				
			||||||
 | 
						      exit(EXIT_FAILURE);
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    p_audio_config->adev[adevice].defined = 1;
 | 
						    p_audio_config->adev[adevice].defined = 1;
 | 
				
			||||||
| 
						 | 
					@ -1986,7 +1996,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
	      dw_printf ("Config file line %d: %s with CM108 is only available when USB Audio GPIO support is enabled.\n", line, otname);
 | 
						      dw_printf ("Config file line %d: %s with CM108 is only available when USB Audio GPIO support is enabled.\n", line, otname);
 | 
				
			||||||
	      dw_printf ("You must rebuild direwolf with CM108 Audio Adapter GPIO PTT support.\n");
 | 
						      dw_printf ("You must rebuild direwolf with CM108 Audio Adapter GPIO PTT support.\n");
 | 
				
			||||||
	      dw_printf ("See Interface Guide for details.\n");
 | 
						      dw_printf ("See Interface Guide for details.\n");
 | 
				
			||||||
 | 
						      rtfm();
 | 
				
			||||||
	      exit (EXIT_FAILURE);
 | 
						      exit (EXIT_FAILURE);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
| 
						 | 
					@ -2370,7 +2380,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
	    p_audio_config->achan[channel].il2p_invert_polarity = 0;
 | 
						    p_audio_config->achan[channel].il2p_invert_polarity = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    while ((t = split(NULL,0)) != NULL) {
 | 
						    while ((t = split(NULL,0)) != NULL) {
 | 
				
			||||||
	      for (char *c = t; *t != '\0'; c++) {
 | 
						      for (char *c = t; *c != '\0'; c++) {
 | 
				
			||||||
	        switch (*c) {
 | 
						        switch (*c) {
 | 
				
			||||||
	          case '+':
 | 
						          case '+':
 | 
				
			||||||
	            p_audio_config->achan[channel].il2p_invert_polarity = 0;
 | 
						            p_audio_config->achan[channel].il2p_invert_polarity = 0;
 | 
				
			||||||
| 
						 | 
					@ -2513,7 +2523,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
	        text_color_set(DW_COLOR_ERROR);
 | 
						        text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	        dw_printf ("Config file, line %d: Preemptive digipeating DROP option is discouraged.\n", line);
 | 
						        dw_printf ("Config file, line %d: Preemptive digipeating DROP option is discouraged.\n", line);
 | 
				
			||||||
	        dw_printf ("It can create a via path which is misleading about the actual path taken.\n");
 | 
						        dw_printf ("It can create a via path which is misleading about the actual path taken.\n");
 | 
				
			||||||
	        dw_printf ("TRACE is the best choice for this feature.\n");
 | 
						        dw_printf ("PREEMPT is the best choice for this feature.\n");
 | 
				
			||||||
	        p_digi_config->preempt[from_chan][to_chan] = PREEMPT_DROP;
 | 
						        p_digi_config->preempt[from_chan][to_chan] = PREEMPT_DROP;
 | 
				
			||||||
	        t = split(NULL,0);
 | 
						        t = split(NULL,0);
 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
| 
						 | 
					@ -2521,11 +2531,11 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
				
			||||||
	        text_color_set(DW_COLOR_ERROR);
 | 
						        text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	        dw_printf ("Config file, line %d: Preemptive digipeating MARK option is discouraged.\n", line);
 | 
						        dw_printf ("Config file, line %d: Preemptive digipeating MARK option is discouraged.\n", line);
 | 
				
			||||||
	        dw_printf ("It can create a via path which is misleading about the actual path taken.\n");
 | 
						        dw_printf ("It can create a via path which is misleading about the actual path taken.\n");
 | 
				
			||||||
	        dw_printf ("TRACE is the best choice for this feature.\n");
 | 
						        dw_printf ("PREEMPT is the best choice for this feature.\n");
 | 
				
			||||||
	        p_digi_config->preempt[from_chan][to_chan] = PREEMPT_MARK;
 | 
						        p_digi_config->preempt[from_chan][to_chan] = PREEMPT_MARK;
 | 
				
			||||||
	        t = split(NULL,0);
 | 
						        t = split(NULL,0);
 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
	      else if (strcasecmp(t, "TRACE") == 0) {
 | 
						      else if ((strcasecmp(t, "TRACE") == 0) || (strncasecmp(t, "PREEMPT", 7) == 0)){
 | 
				
			||||||
	        p_digi_config->preempt[from_chan][to_chan] = PREEMPT_TRACE;
 | 
						        p_digi_config->preempt[from_chan][to_chan] = PREEMPT_TRACE;
 | 
				
			||||||
	        t = split(NULL,0);
 | 
						        t = split(NULL,0);
 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
| 
						 | 
					@ -5765,8 +5775,9 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_
 | 
				
			||||||
	  else if (strcasecmp(keyword, "POWER") == 0) {
 | 
						  else if (strcasecmp(keyword, "POWER") == 0) {
 | 
				
			||||||
	    b->power = atoi(value);
 | 
						    b->power = atoi(value);
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	  else if (strcasecmp(keyword, "HEIGHT") == 0) {
 | 
						  else if (strcasecmp(keyword, "HEIGHT") == 0) {	// This is in feet.
 | 
				
			||||||
	    b->height = atoi(value);
 | 
						    b->height = atoi(value);
 | 
				
			||||||
 | 
						    // TODO: ability to add units suffix, e.g.  10m
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	  else if (strcasecmp(keyword, "GAIN") == 0) {
 | 
						  else if (strcasecmp(keyword, "GAIN") == 0) {
 | 
				
			||||||
	    b->gain = atoi(value);
 | 
						    b->gain = atoi(value);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,7 @@
 | 
				
			||||||
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO:  Better error messages for examples here: http://lists.tapr.org/pipermail/aprssig_lists.tapr.org/2023-July/date.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*------------------------------------------------------------------
 | 
					/*------------------------------------------------------------------
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -169,7 +170,9 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//dw_printf ("DEBUG decode_aprs info=\"%s\"\n", pinfo);
 | 
						//dw_printf ("DEBUG decode_aprs info=\"%s\"\n", pinfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memset (A, 0, sizeof (*A));
 | 
						if (third_party_src == NULL) {
 | 
				
			||||||
 | 
						  memset (A, 0, sizeof (*A));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	A->g_quiet = quiet;
 | 
						A->g_quiet = quiet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -234,6 +237,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
 | 
				
			||||||
	    memcpy(payload_src, (char*)pinfo+1, sizeof(payload_src)-1);
 | 
						    memcpy(payload_src, (char*)pinfo+1, sizeof(payload_src)-1);
 | 
				
			||||||
	    char *q = strchr(payload_src, '>');
 | 
						    char *q = strchr(payload_src, '>');
 | 
				
			||||||
	    if (q != NULL) *q = '\0';
 | 
						    if (q != NULL) *q = '\0';
 | 
				
			||||||
 | 
						    A->g_has_thirdparty_header = 1;
 | 
				
			||||||
	    decode_aprs (A, pp_payload, quiet, payload_src);	// 1 means used recursively
 | 
						    decode_aprs (A, pp_payload, quiet, payload_src);	// 1 means used recursively
 | 
				
			||||||
	    ax25_delete (pp_payload);
 | 
						    ax25_delete (pp_payload);
 | 
				
			||||||
	    return;
 | 
						    return;
 | 
				
			||||||
| 
						 | 
					@ -318,6 +322,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
 | 
				
			||||||
	      {	     
 | 
						      {	     
 | 
				
			||||||
	        aprs_ll_pos (A, pinfo, info_len);
 | 
						        aprs_ll_pos (A, pinfo, info_len);
 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
 | 
						      A->g_packet_type = packet_type_position;
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -330,10 +335,12 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
 | 
				
			||||||
	      if (strncmp((char*)pinfo, "$ULTW", 5) == 0)
 | 
						      if (strncmp((char*)pinfo, "$ULTW", 5) == 0)
 | 
				
			||||||
	      {
 | 
						      {
 | 
				
			||||||
		aprs_ultimeter (A, (char*)pinfo, info_len);		// TODO: produce obsolete error.
 | 
							aprs_ultimeter (A, (char*)pinfo, info_len);		// TODO: produce obsolete error.
 | 
				
			||||||
 | 
						        A->g_packet_type = packet_type_weather;
 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
	      else
 | 
						      else
 | 
				
			||||||
	      {
 | 
						      {
 | 
				
			||||||
	        aprs_raw_nmea (A, pinfo, info_len);
 | 
						        aprs_raw_nmea (A, pinfo, info_len);
 | 
				
			||||||
 | 
						        A->g_packet_type = packet_type_position;
 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -341,17 +348,20 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
 | 
				
			||||||
	    case '`':		/* Current Mic-E Data (not used in TM-D700) */
 | 
						    case '`':		/* Current Mic-E Data (not used in TM-D700) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      aprs_mic_e (A, pp, pinfo, info_len);
 | 
						      aprs_mic_e (A, pp, pinfo, info_len);
 | 
				
			||||||
 | 
						      A->g_packet_type = packet_type_position;
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case ')':		/* Item. */
 | 
						    case ')':		/* Item. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      aprs_item (A, pinfo, info_len);
 | 
						      aprs_item (A, pinfo, info_len);
 | 
				
			||||||
 | 
						      A->g_packet_type = packet_type_item;
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
	    case '/':		/* Position with timestamp (no APRS messaging) */
 | 
						    case '/':		/* Position with timestamp (no APRS messaging) */
 | 
				
			||||||
	    case '@':		/* Position with timestamp (with APRS messaging) */
 | 
						    case '@':		/* Position with timestamp (with APRS messaging) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      aprs_ll_pos_time (A, pinfo, info_len);
 | 
						      aprs_ll_pos_time (A, pinfo, info_len);
 | 
				
			||||||
 | 
						      A->g_packet_type = packet_type_position;
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -360,42 +370,76 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
 | 
				
			||||||
				/* Telemetry metadata. */
 | 
									/* Telemetry metadata. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      aprs_message (A, pinfo, info_len, quiet);
 | 
						      aprs_message (A, pinfo, info_len, quiet);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						      switch (A->g_message_subtype) {
 | 
				
			||||||
 | 
							case message_subtype_message:
 | 
				
			||||||
 | 
							case message_subtype_ack:
 | 
				
			||||||
 | 
							case message_subtype_rej:
 | 
				
			||||||
 | 
						          A->g_packet_type = packet_type_message;
 | 
				
			||||||
 | 
						          break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case message_subtype_nws:
 | 
				
			||||||
 | 
						           A->g_packet_type = packet_type_nws;
 | 
				
			||||||
 | 
						           break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case message_subtype_bulletin:
 | 
				
			||||||
 | 
						        default:
 | 
				
			||||||
 | 
							  break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case message_subtype_telem_parm:
 | 
				
			||||||
 | 
							case message_subtype_telem_unit:
 | 
				
			||||||
 | 
							case message_subtype_telem_eqns:
 | 
				
			||||||
 | 
							case message_subtype_telem_bits:
 | 
				
			||||||
 | 
						          A->g_packet_type = packet_type_telemetry;
 | 
				
			||||||
 | 
						          break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case message_subtype_directed_query:
 | 
				
			||||||
 | 
						          A->g_packet_type = packet_type_query;
 | 
				
			||||||
 | 
						          break;
 | 
				
			||||||
 | 
						      }
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case ';':		/* Object */
 | 
						    case ';':		/* Object */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      aprs_object (A, pinfo, info_len);
 | 
						      aprs_object (A, pinfo, info_len);
 | 
				
			||||||
 | 
						      A->g_packet_type = packet_type_object;
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case '<':		/* Station Capabilities */
 | 
						    case '<':		/* Station Capabilities */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      aprs_station_capabilities (A, (char*)pinfo, info_len);
 | 
						      aprs_station_capabilities (A, (char*)pinfo, info_len);
 | 
				
			||||||
 | 
						      A->g_packet_type = packet_type_capabilities;
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case '>':		/* Status Report */
 | 
						    case '>':		/* Status Report */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      aprs_status_report (A, (char*)pinfo, info_len);
 | 
						      aprs_status_report (A, (char*)pinfo, info_len);
 | 
				
			||||||
 | 
						      A->g_packet_type = packet_type_status;
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    
 | 
						    
 | 
				
			||||||
	    case '?':		/* General Query */
 | 
						    case '?':		/* General Query */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      aprs_general_query (A, (char*)pinfo, info_len, quiet);
 | 
						      aprs_general_query (A, (char*)pinfo, info_len, quiet);
 | 
				
			||||||
 | 
						      A->g_packet_type = packet_type_query;
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
	    case 'T':		/* Telemetry */
 | 
						    case 'T':		/* Telemetry */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      aprs_telemetry (A, (char*)pinfo, info_len, quiet);
 | 
						      aprs_telemetry (A, (char*)pinfo, info_len, quiet);
 | 
				
			||||||
 | 
						      A->g_packet_type = packet_type_telemetry;
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case '_':		/* Positionless Weather Report */
 | 
						    case '_':		/* Positionless Weather Report */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      aprs_positionless_weather_report (A, pinfo, info_len);
 | 
						      aprs_positionless_weather_report (A, pinfo, info_len);
 | 
				
			||||||
 | 
						      A->g_packet_type = packet_type_weather;
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case '{':		/* user defined data */
 | 
						    case '{':		/* user defined data */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      aprs_user_defined (A, (char*)pinfo, info_len);
 | 
						      aprs_user_defined (A, (char*)pinfo, info_len);
 | 
				
			||||||
 | 
						      A->g_packet_type = packet_type_userdefined;
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case 't':		/* Raw touch tone data - NOT PART OF STANDARD */
 | 
						    case 't':		/* Raw touch tone data - NOT PART OF STANDARD */
 | 
				
			||||||
| 
						 | 
					@ -404,6 +448,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
 | 
				
			||||||
				/* Might move into user defined data, above. */
 | 
									/* Might move into user defined data, above. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      aprs_raw_touch_tone (A, (char*)pinfo, info_len);
 | 
						      aprs_raw_touch_tone (A, (char*)pinfo, info_len);
 | 
				
			||||||
 | 
						      // no packet type for t/ filter
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case 'm':		/* Morse Code data - NOT PART OF STANDARD */
 | 
						    case 'm':		/* Morse Code data - NOT PART OF STANDARD */
 | 
				
			||||||
| 
						 | 
					@ -413,6 +458,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
 | 
				
			||||||
				/* Might move into user defined data, above. */
 | 
									/* Might move into user defined data, above. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      aprs_morse_code (A, (char*)pinfo, info_len);
 | 
						      aprs_morse_code (A, (char*)pinfo, info_len);
 | 
				
			||||||
 | 
						      // no packet type for t/ filter
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    //case '}':		/* third party header */
 | 
						    //case '}':		/* third party header */
 | 
				
			||||||
| 
						 | 
					@ -504,15 +550,17 @@ void decode_aprs_print (decode_aprs_t *A) {
 | 
				
			||||||
	//dw_printf ("DEBUG decode_aprs_print stemp3=%s mfr=%s\n", stemp, A->g_mfr);
 | 
						//dw_printf ("DEBUG decode_aprs_print stemp3=%s mfr=%s\n", stemp, A->g_mfr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (strlen(A->g_mfr) > 0) {
 | 
						if (strlen(A->g_mfr) > 0) {
 | 
				
			||||||
	  if (strcmp(A->g_dest, "APRS") == 0  ||  strcmp(A->g_dest, "BEACON") == 0) {
 | 
						  if (strcmp(A->g_dest, "APRS") == 0  ||
 | 
				
			||||||
 | 
							strcmp(A->g_dest, "BEACON") == 0 ||
 | 
				
			||||||
 | 
							strcmp(A->g_dest, "ID") == 0) {
 | 
				
			||||||
	    strlcat (stemp, "\nUse of \"", sizeof(stemp));
 | 
						    strlcat (stemp, "\nUse of \"", sizeof(stemp));
 | 
				
			||||||
	    strlcat (stemp, A->g_dest, sizeof(stemp));
 | 
						    strlcat (stemp, A->g_dest, sizeof(stemp));
 | 
				
			||||||
	    strlcat (stemp, "\" in the destination field is obsolete.", sizeof(stemp));
 | 
						    strlcat (stemp, "\" in the destination field is obsolete.", sizeof(stemp));
 | 
				
			||||||
	    strlcat (stemp, "  You can help to improve the quality of APRS signals.", sizeof(stemp));
 | 
						    strlcat (stemp, "  You can help to improve the quality of APRS signals.", sizeof(stemp));
 | 
				
			||||||
	    strlcat (stemp, "\nTell the sender (", sizeof(stemp));
 | 
						    strlcat (stemp, "\nTell the sender (", sizeof(stemp));
 | 
				
			||||||
	    strlcat (stemp, A->g_src, sizeof(stemp));
 | 
						    strlcat (stemp, A->g_src, sizeof(stemp));
 | 
				
			||||||
	    strlcat (stemp, ") to use the proper product code from", sizeof(stemp));
 | 
						    strlcat (stemp, ") to use the proper product identifier from", sizeof(stemp));
 | 
				
			||||||
	    strlcat (stemp, " http://www.aprs.org/aprs11/tocalls.txt", sizeof(stemp));
 | 
						    strlcat (stemp, " https://github.com/aprsorg/aprs-deviceid ", sizeof(stemp));
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	  else {
 | 
						  else {
 | 
				
			||||||
	    strlcat (stemp, ", ", sizeof(stemp));
 | 
						    strlcat (stemp, ", ", sizeof(stemp));
 | 
				
			||||||
| 
						 | 
					@ -537,7 +585,7 @@ void decode_aprs_print (decode_aprs_t *A) {
 | 
				
			||||||
	  /* http://eng.usna.navy.mil/~bruninga/aprs/aprs11.html */
 | 
						  /* http://eng.usna.navy.mil/~bruninga/aprs/aprs11.html */
 | 
				
			||||||
	  /* "The Antenna Gain in the PHG format on page 28 is in dBi." */
 | 
						  /* "The Antenna Gain in the PHG format on page 28 is in dBi." */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  snprintf (phg, sizeof(phg), ", %d W height=%d %ddBi %s", A->g_power, A->g_height, A->g_gain, A->g_directivity);
 | 
						  snprintf (phg, sizeof(phg), ", %d W height(HAAT)=%dft=%.0fm %ddBi %s", A->g_power, A->g_height, DW_FEET_TO_METERS(A->g_height), A->g_gain, A->g_directivity);
 | 
				
			||||||
	  strlcat (stemp, phg, sizeof(stemp));
 | 
						  strlcat (stemp, phg, sizeof(stemp));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1091,7 +1139,9 @@ static void aprs_raw_nmea (decode_aprs_t *A, unsigned char *info, int ilen)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Function:	aprs_mic_e
 | 
					 * Function:	aprs_mic_e
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Purpose:	Decode MIC-E (also Kenwood D7 & D700) packet.
 | 
					 * Purpose:	Decode MIC-E (e.g. Kenwood D7 & D700) packet.
 | 
				
			||||||
 | 
					 *		This format is an overzelous quest to make the packet as short as possible.
 | 
				
			||||||
 | 
					 *		It uses non-printable characters and hacks wrapped in kludges.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Inputs:	info 	- Pointer to Information field.
 | 
					 * Inputs:	info 	- Pointer to Information field.
 | 
				
			||||||
 *		ilen 	- Information field length.
 | 
					 *		ilen 	- Information field length.
 | 
				
			||||||
| 
						 | 
					@ -1100,31 +1150,120 @@ static void aprs_raw_nmea (decode_aprs_t *A, unsigned char *info, int ilen)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Description:	
 | 
					 * Description:	
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		Destination Address Field - 
 | 
					 *		AX.25 Destination Address Field -
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		The 7-byte Destination Address field contains
 | 
					 *		The 6-byte Destination Address field contains
 | 
				
			||||||
 *		the following encoded information:
 | 
					 *		the following encoded information:
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		* The 6 latitude digits.
 | 
					 *			Byte 1: Lat digit 1, message bit A
 | 
				
			||||||
 *		* A 3-bit Mic-E message identifier, specifying one of 7 Standard Mic-E
 | 
					 *			Byte 2: Lat digit 2, message bit B
 | 
				
			||||||
 *		   Message Codes or one of 7 Custom Message Codes or an Emergency
 | 
					 *			Byte 3: Lat digit 3, message bit C
 | 
				
			||||||
 *		   Message Code.
 | 
					 *			Byte 4: Lat digit 4, N/S lat indicator
 | 
				
			||||||
 *		* The North/South and West/East Indicators.
 | 
					 *			Byte 5: Lat digit 5, Longitude offset
 | 
				
			||||||
 *		* The Longitude Offset Indicator.
 | 
					 *			Byte 6: Lat digit 6, W/E Long indicator
 | 
				
			||||||
 *		* The generic APRS digipeater path code.
 | 
					 * *
 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 *		"Although the destination address appears to be quite unconventional, it is
 | 
					 *		"Although the destination address appears to be quite unconventional, it is
 | 
				
			||||||
 *		still a valid AX.25 address, consisting only of printable 7-bit ASCII values."
 | 
					 *		still a valid AX.25 address, consisting only of printable 7-bit ASCII values."
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * References:	Mic-E TYPE CODES -- http://www.aprs.org/aprs12/mic-e-types.txt
 | 
					 *		AX.25 Information Field - Starts with ' or `
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *			This is up to date with the 24 Aug 16 version mentioning the TH-D74.
 | 
					 *			Bytes 1,2,3: Longitude
 | 
				
			||||||
 | 
					 *			Bytes 4,5,6: Speed and Course
 | 
				
			||||||
 | 
					 *			Byte 6: Symbol code
 | 
				
			||||||
 | 
					 *			Byte 7: Symbol Table ID
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		The rest of it is a complicated comment field which can hold various information
 | 
				
			||||||
 | 
					 *		and must be intrepreted in a particular order.  At this point we look for any
 | 
				
			||||||
 | 
					 *		prefix and/or suffix to identify the equipment type.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 		References:	Mic-E TYPE CODES -- http://www.aprs.org/aprs12/mic-e-types.txt
 | 
				
			||||||
 | 
					 *				Mic-E TEST EXAMPLES -- http://www.aprs.org/aprs12/mic-e-examples.txt
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		Next, we have what Addedum 1.2 calls the "type byte."  This prefix can be
 | 
				
			||||||
 | 
					 *			space	Original MIC-E.
 | 
				
			||||||
 | 
					 *			>	Kenwood HT.
 | 
				
			||||||
 | 
					 *			]	Kenwood Mobile.
 | 
				
			||||||
 | 
					 *			none.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		We also need to look at the last byte or two
 | 
				
			||||||
 | 
					 *		for a possible suffix to distinguish equipment types.  Examples:
 | 
				
			||||||
 | 
					 *			>......		is D7
 | 
				
			||||||
 | 
					 *			>......=	is D72
 | 
				
			||||||
 | 
					 *			>......^	is D74
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		For other brands, it gets worse.  There might a 2 character suffix.
 | 
				
			||||||
 | 
					 *		The prefix indicates whether messaging-capable.  Examples:
 | 
				
			||||||
 | 
					 *			`....._.%	Yaesu FTM-400DR
 | 
				
			||||||
 | 
					 *			`......_)	Yaesu FTM-100D
 | 
				
			||||||
 | 
					 *			`......_3	Yaesu FT5D
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *			'......|3	Byonics TinyTrack3
 | 
				
			||||||
 | 
					 *			'......|4	Byonics TinyTrack4
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		Any prefix and suffix must be removed before futher processsing.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		Pick one: MIC-E Telemetry Data or "Status Text" (called a comment everywhere else).
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		If the character after the symbol table id is "," (comma) or 0x1d, we have telemetry.
 | 
				
			||||||
 | 
					 *		(Is this obsoleted by the base-91 telemetry?)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *			`	Two 2-character hexadecimal numbers. (Channels 1 & 3)
 | 
				
			||||||
 | 
					 *			'	Five 2-character hexadecimal numbers.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		Anything left over is a comment which can contain various types of information.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		If present, the MIC-E compressed altitude must be first.
 | 
				
			||||||
 | 
					 *		It is three base-91 characters followed by "}".
 | 
				
			||||||
 | 
					 *		Examples:    "4T}	"4T}	]"4T}
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		We can also have frequency specification  --  http://www.aprs.org/info/freqspec.txt
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Warning:	Some Kenwood radios add CR at the end, in apparent violation of the spec.
 | 
				
			||||||
 | 
					 *		Watch out so it doesn't get included when looking for equipment type suffix.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		Mic-E TEST EXAMPLES -- http://www.aprs.org/aprs12/mic-e-examples.txt 
 | 
					 *		Mic-E TEST EXAMPLES -- http://www.aprs.org/aprs12/mic-e-examples.txt 
 | 
				
			||||||
 *		
 | 
					 *		
 | 
				
			||||||
 * Examples:	`b9Z!4y>/>"4N}Paul's_TH-D7
 | 
					 * Examples:	Observed on the air.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * TODO:	Destination SSID can contain generic digipeater path.
 | 
					 *		KB1HNZ-9>TSSP5P,W1IMD,WIDE1,KQ1L-8,N3LLO-3,WIDE2*:`b6,l}#>/]"48}449.225MHz<0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff>=<0x0d>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		`       b6,    l}#   >/     ]         "48}    449.225MHz   ......    =       <0x0d>
 | 
				
			||||||
 | 
					 *		mic-e  long.   cs    sym    prefix    alt.    freq         comment   suffix   must-ignore
 | 
				
			||||||
 | 
					 *		                            Kenwood                                  D710
 | 
				
			||||||
 | 
					 *---------------
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		N1JDU-9>ECCU8Y,W1MHL*,WIDE2-1:'cZ<0x7f>l#H>/]Go fly a kite!<0x0d>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		'      cZ<0x7f>   l#H     >/     ]         .....                 <0x0d>
 | 
				
			||||||
 | 
					 *		mic-e  long.      cs      sym    prefix    comment   no-suffix   must-ignore
 | 
				
			||||||
 | 
					 *		                                 Kenwood              D700
 | 
				
			||||||
 | 
					 *---------------
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		KC1HHO-7>T2PX5R,WA1PLE-4,WIDE1*,WIDE2-1:`c_snp(k/`"4B}official relay station NTS_(<0x0d>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		`       c_s     np(  k/     `       "4B}      .......   _(       <0x0d>
 | 
				
			||||||
 | 
					 *		mic-e  long.    cs   sym    prefix   alt      comment   suffix   must-ignore
 | 
				
			||||||
 | 
					 *		                                                         FT2D
 | 
				
			||||||
 | 
					 *---------------
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		N1CMD-12>T3PQ1Y,KA1GJU-3,WIDE1,WA1PLE-4*:`cP#l!Fk/'"7H}|!%&-']|!w`&!|3
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		`      cP#      l!F   k/     '       "7H}    |!%&-']|         !w`&!   |3
 | 
				
			||||||
 | 
					 *		mic-e  long.    cs   sym    prefix   alt     base91telemetry  DAO     suffix
 | 
				
			||||||
 | 
					 *		                                                                      TinyTrack3
 | 
				
			||||||
 | 
					 *---------------
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		 W1STJ-3>T2UR4X,WA1PLE-4,WIDE1*,WIDE2-1:`c@&l#.-/`"5,}146.685MHz T100 -060 146.520 Simplex or Voice Alert_%<0x0d>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		`      c@&     l#.   -/     `        "5,}    146.685MHz T100 -060     ..............  _%       <0x0d>
 | 
				
			||||||
 | 
					 *		mic-e  long.    cs   sym    prefix   alt     frequency-specification     comment     suffix   must-ignore
 | 
				
			||||||
 | 
					 *		                                                                                    FTM-400DR
 | 
				
			||||||
 | 
					 *---------------
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * TODO:	Destination SSID can contain generic digipeater path.  (?)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Bugs:	Doesn't handle ambiguous position.  "space" treated as zero.
 | 
					 * Bugs:	Doesn't handle ambiguous position.  "space" treated as zero.
 | 
				
			||||||
 *		Invalid data results in a message but latitude is not set to unknown.
 | 
					 *		Invalid data results in a message but latitude is not set to unknown.
 | 
				
			||||||
| 
						 | 
					@ -1507,6 +1646,8 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
 | 
				
			||||||
// This does not change very often but I'm wondering if we could parse
 | 
					// This does not change very often but I'm wondering if we could parse
 | 
				
			||||||
// http://www.aprs.org/aprs12/mic-e-types.txt similar to how we use tocalls.txt.
 | 
					// http://www.aprs.org/aprs12/mic-e-types.txt similar to how we use tocalls.txt.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO:  Use https://github.com/aprsorg/aprs-deviceid rather than hardcoding.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (isT(*pfirst)) {
 | 
						if (isT(*pfirst)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// "legacy" formats.
 | 
					// "legacy" formats.
 | 
				
			||||||
| 
						 | 
					@ -1515,6 +1656,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  else if (*pfirst == '>'                       && *plast == '=')  { strlcpy (A->g_mfr, "Kenwood TH-D72", sizeof(A->g_mfr)); pfirst++; plast--; }
 | 
						  else if (*pfirst == '>'                       && *plast == '=')  { strlcpy (A->g_mfr, "Kenwood TH-D72", sizeof(A->g_mfr)); pfirst++; plast--; }
 | 
				
			||||||
	  else if (*pfirst == '>'                       && *plast == '^')  { strlcpy (A->g_mfr, "Kenwood TH-D74", sizeof(A->g_mfr)); pfirst++; plast--; }
 | 
						  else if (*pfirst == '>'                       && *plast == '^')  { strlcpy (A->g_mfr, "Kenwood TH-D74", sizeof(A->g_mfr)); pfirst++; plast--; }
 | 
				
			||||||
 | 
						  else if (*pfirst == '>'                       && *plast == '&')  { strlcpy (A->g_mfr, "Kenwood TH-D75", sizeof(A->g_mfr)); pfirst++; plast--; }
 | 
				
			||||||
	  else if (*pfirst == '>'                                       )  { strlcpy (A->g_mfr, "Kenwood TH-D7A", sizeof(A->g_mfr)); pfirst++; }
 | 
						  else if (*pfirst == '>'                                       )  { strlcpy (A->g_mfr, "Kenwood TH-D7A", sizeof(A->g_mfr)); pfirst++; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  else if (*pfirst == ']'                       && *plast == '=')  { strlcpy (A->g_mfr, "Kenwood TM-D710", sizeof(A->g_mfr)); pfirst++; plast--; }
 | 
						  else if (*pfirst == ']'                       && *plast == '=')  { strlcpy (A->g_mfr, "Kenwood TM-D710", sizeof(A->g_mfr)); pfirst++; plast--; }
 | 
				
			||||||
| 
						 | 
					@ -1532,6 +1674,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
 | 
				
			||||||
	  else if (*pfirst == '`'  && *(plast-1) == '_' && *plast == '0')  { strlcpy (A->g_mfr, "Yaesu FT3D", sizeof(A->g_mfr)); pfirst++; plast-=2; }
 | 
						  else if (*pfirst == '`'  && *(plast-1) == '_' && *plast == '0')  { strlcpy (A->g_mfr, "Yaesu FT3D", sizeof(A->g_mfr)); pfirst++; plast-=2; }
 | 
				
			||||||
	  else if (*pfirst == '`'  && *(plast-1) == '_' && *plast == '3')  { strlcpy (A->g_mfr, "Yaesu FT5D", sizeof(A->g_mfr)); pfirst++; plast-=2; }
 | 
						  else if (*pfirst == '`'  && *(plast-1) == '_' && *plast == '3')  { strlcpy (A->g_mfr, "Yaesu FT5D", sizeof(A->g_mfr)); pfirst++; plast-=2; }
 | 
				
			||||||
	  else if (*pfirst == '`'  && *(plast-1) == '_' && *plast == '1')  { strlcpy (A->g_mfr, "Yaesu FTM-300D", sizeof(A->g_mfr)); pfirst++; plast-=2; }
 | 
						  else if (*pfirst == '`'  && *(plast-1) == '_' && *plast == '1')  { strlcpy (A->g_mfr, "Yaesu FTM-300D", sizeof(A->g_mfr)); pfirst++; plast-=2; }
 | 
				
			||||||
 | 
						  else if (*pfirst == '`'  && *(plast-1) == '_' && *plast == '5')  { strlcpy (A->g_mfr, "Yaesu FTM-500D", sizeof(A->g_mfr)); pfirst++; plast-=2; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  else if (*pfirst == '`'  && *(plast-1) == ' ' && *plast == 'X')  { strlcpy (A->g_mfr, "AP510", sizeof(A->g_mfr)); pfirst++; plast-=2; }
 | 
						  else if (*pfirst == '`'  && *(plast-1) == ' ' && *plast == 'X')  { strlcpy (A->g_mfr, "AP510", sizeof(A->g_mfr)); pfirst++; plast-=2; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1540,6 +1683,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ' should be used for trackers (not message capable).
 | 
					// ' should be used for trackers (not message capable).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  else if (*pfirst == '\'' && *(plast-1) == '(' && *plast == '5')  { strlcpy (A->g_mfr, "Anytone D578UV", sizeof(A->g_mfr)); pfirst++; plast-=2; }
 | 
				
			||||||
	  else if (*pfirst == '\'' && *(plast-1) == '(' && *plast == '8')  { strlcpy (A->g_mfr, "Anytone D878UV", sizeof(A->g_mfr)); pfirst++; plast-=2; }
 | 
						  else if (*pfirst == '\'' && *(plast-1) == '(' && *plast == '8')  { strlcpy (A->g_mfr, "Anytone D878UV", sizeof(A->g_mfr)); pfirst++; plast-=2; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  else if (*pfirst == '\'' && *(plast-1) == '|' && *plast == '3')  { strlcpy (A->g_mfr, "Byonics TinyTrack3", sizeof(A->g_mfr)); pfirst++; plast-=2; }
 | 
						  else if (*pfirst == '\'' && *(plast-1) == '|' && *plast == '3')  { strlcpy (A->g_mfr, "Byonics TinyTrack3", sizeof(A->g_mfr)); pfirst++; plast-=2; }
 | 
				
			||||||
| 
						 | 
					@ -1628,10 +1772,19 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
 | 
				
			||||||
 *		It's a lot more complicated with different types of addressees
 | 
					 *		It's a lot more complicated with different types of addressees
 | 
				
			||||||
 *		and replies with acknowledgement or rejection.
 | 
					 *		and replies with acknowledgement or rejection.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		There is even a special case for telemetry metadata.
 | 
					 *		Is it an elegant generalization to lump all of these special cases
 | 
				
			||||||
 | 
					 *		together or was it a big mistake that will cause confusion and incorrect
 | 
				
			||||||
 | 
					 *		implementations?  The decision to put telemetry metadata here is baffling.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Cases:	:xxxxxxxxx:PARM.		Telemetry metadata, parameter name
 | 
					 * Cases:	:BLNxxxxxx: ...			Bulletin.
 | 
				
			||||||
 | 
					 *		:NWSxxxxxx: ...			National Weather Service Bulletin.
 | 
				
			||||||
 | 
					 *							http://www.aprs.org/APRS-docs/WX.TXT
 | 
				
			||||||
 | 
					 *		:SKYxxxxxx: ...			Need reference.
 | 
				
			||||||
 | 
					 *		:CWAxxxxxx: ...			Need reference.
 | 
				
			||||||
 | 
					 *		:BOMxxxxxx: ...			Australian version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		:xxxxxxxxx:PARM.		Telemetry metadata, parameter name
 | 
				
			||||||
 *		:xxxxxxxxx:UNIT.		Telemetry metadata, unit/label
 | 
					 *		:xxxxxxxxx:UNIT.		Telemetry metadata, unit/label
 | 
				
			||||||
 *		:xxxxxxxxx:EQNS.		Telemetry metadata, Equation Coefficients
 | 
					 *		:xxxxxxxxx:EQNS.		Telemetry metadata, Equation Coefficients
 | 
				
			||||||
 *		:xxxxxxxxx:BITS.		Telemetry metadata, Bit Sense/Project Name
 | 
					 *		:xxxxxxxxx:BITS.		Telemetry metadata, Bit Sense/Project Name
 | 
				
			||||||
| 
						 | 
					@ -1647,7 +1800,8 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
 | 
				
			||||||
 *		:xxxxxxxxx: ... {mm}aa		Message with new style message number and ack.
 | 
					 *		:xxxxxxxxx: ... {mm}aa		Message with new style message number and ack.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Reference:	See new message id style:  http://www.aprs.org/aprs11/replyacks.txt
 | 
					 * Reference:	http://www.aprs.org/txt/messages101.txt
 | 
				
			||||||
 | 
					 *		http://www.aprs.org/aprs11/replyacks.txt	<-- New (1999) adding ack to outgoing message.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *------------------------------------------------------------------*/
 | 
					 *------------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1736,6 +1890,62 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	strlcpy (A->g_addressee, addressee, sizeof(A->g_addressee));
 | 
						strlcpy (A->g_addressee, addressee, sizeof(A->g_addressee));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Addressee starting with BLN or NWS is a bulletin.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
						if (strlen(addressee) >= 3 && strncmp(addressee,"BLN",3) == 0) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  // Interpret 3 cases of identifiers.
 | 
				
			||||||
 | 
						  // BLN9	"general bulletin" has a single digit.
 | 
				
			||||||
 | 
						  // BLNX	"announcement" has a single uppercase letter.
 | 
				
			||||||
 | 
						  // BLN9xxxxx	"group bulletin" has single digit group id and group name up to 5 characters.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  if (strlen(addressee) == 4 && isdigit(addressee[3])) {
 | 
				
			||||||
 | 
						    snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "General Bulletin with identifier \"%s\"", addressee+3);
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
						  else if (strlen(addressee) == 4 && isupper(addressee[3])) {
 | 
				
			||||||
 | 
						    snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Announcement with identifier \"%s\"", addressee+3);
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
						  if (strlen(addressee) >=5 && isdigit(addressee[3])) {
 | 
				
			||||||
 | 
						    snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Group Bulletin with identifier \"%c\", group name \"%s\"", addressee[3], addressee+4);
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
						  else {
 | 
				
			||||||
 | 
						    // Not one of the official formats.
 | 
				
			||||||
 | 
						    snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Bulletin with identifier \"%s\"", addressee+3);
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
						  A->g_message_subtype = message_subtype_bulletin;
 | 
				
			||||||
 | 
						  strlcpy (A->g_comment, p->message, sizeof(A->g_comment));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Weather bulletins have addressee starting with NWS, SKY, CWA, or BOM.
 | 
				
			||||||
 | 
						// The protocol spec and http://www.aprs.org/APRS-docs/WX.TXT state that
 | 
				
			||||||
 | 
						// the 3 letter prefix must be followed by a dash.
 | 
				
			||||||
 | 
						// However, https://www.aprs-is.net/WX/ also lists the underscore
 | 
				
			||||||
 | 
						// alternative for the compressed format.  Xastir implements this.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						else if (strlen(addressee) >= 3 && strncmp(addressee,"NWS",3) == 0) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  if (strlen(addressee) >=4 && addressee[3] == '-') {
 | 
				
			||||||
 | 
						    snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Weather bulletin with identifier \"%s\"", addressee+4);
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
						  else if (strlen(addressee) >=4 && addressee[3] == '_') {
 | 
				
			||||||
 | 
						    snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Compressed Weather bulletin with identifier \"%s\"", addressee+4);
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
						  else {
 | 
				
			||||||
 | 
						    snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Weather bulletin is missing - or _ after %.3s", addressee);
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
						  A->g_message_subtype = message_subtype_nws;
 | 
				
			||||||
 | 
						  strlcpy (A->g_comment, p->message, sizeof(A->g_comment));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						else if (strlen(addressee) >= 3 && (strncmp(addressee,"SKY",3) == 0 || strncmp(addressee,"CWA",3) == 0 || strncmp(addressee,"BOM",3) == 0)) {
 | 
				
			||||||
 | 
						  // SKY... or CWA...   https://www.aprs-is.net/WX/
 | 
				
			||||||
 | 
						  snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Weather bulletin with identifier \"%s\"", addressee+4);
 | 
				
			||||||
 | 
						  A->g_message_subtype = message_subtype_nws;
 | 
				
			||||||
 | 
						  strlcpy (A->g_comment, p->message, sizeof(A->g_comment));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Special message formats contain telemetry metadata.
 | 
					 * Special message formats contain telemetry metadata.
 | 
				
			||||||
| 
						 | 
					@ -1748,23 +1958,23 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q
 | 
				
			||||||
 * Why not use other characters after the "T" for metadata?
 | 
					 * Why not use other characters after the "T" for metadata?
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (strncmp(p->message,"PARM.",5) == 0) {
 | 
						else if (strncmp(p->message,"PARM.",5) == 0) {
 | 
				
			||||||
	  snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Parameter Name Message for \"%s\"", addressee);
 | 
						  snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Parameter Name for \"%s\"", addressee);
 | 
				
			||||||
	  A->g_message_subtype = message_subtype_telem_parm;
 | 
						  A->g_message_subtype = message_subtype_telem_parm;
 | 
				
			||||||
	  telemetry_name_message (addressee, p->message+5);
 | 
						  telemetry_name_message (addressee, p->message+5);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else if (strncmp(p->message,"UNIT.",5) == 0) {
 | 
						else if (strncmp(p->message,"UNIT.",5) == 0) {
 | 
				
			||||||
	  snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Unit/Label Message for \"%s\"", addressee);
 | 
						  snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Unit/Label for \"%s\"", addressee);
 | 
				
			||||||
	  A->g_message_subtype = message_subtype_telem_unit;
 | 
						  A->g_message_subtype = message_subtype_telem_unit;
 | 
				
			||||||
	  telemetry_unit_label_message (addressee, p->message+5);
 | 
						  telemetry_unit_label_message (addressee, p->message+5);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else if (strncmp(p->message,"EQNS.",5) == 0) {
 | 
						else if (strncmp(p->message,"EQNS.",5) == 0) {
 | 
				
			||||||
	  snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Equation Coefficients Message for \"%s\"", addressee);
 | 
						  snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Equation Coefficients for \"%s\"", addressee);
 | 
				
			||||||
	  A->g_message_subtype = message_subtype_telem_eqns;
 | 
						  A->g_message_subtype = message_subtype_telem_eqns;
 | 
				
			||||||
	  telemetry_coefficents_message (addressee, p->message+5, quiet);
 | 
						  telemetry_coefficents_message (addressee, p->message+5, quiet);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else if (strncmp(p->message,"BITS.",5) == 0) {
 | 
						else if (strncmp(p->message,"BITS.",5) == 0) {
 | 
				
			||||||
	  snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Bit Sense/Project Name Message for \"%s\"", addressee);
 | 
						  snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Bit Sense/Project Name for \"%s\"", addressee);
 | 
				
			||||||
	  A->g_message_subtype = message_subtype_telem_bits;
 | 
						  A->g_message_subtype = message_subtype_telem_bits;
 | 
				
			||||||
	  telemetry_bit_sense_message (addressee, p->message+5, quiet);
 | 
						  telemetry_bit_sense_message (addressee, p->message+5, quiet);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1788,10 +1998,12 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf("ERROR: \"%s\" must be lower case \"ack\"\n", p->message);
 | 
						      dw_printf("ERROR: \"%s\" must be lower case \"ack\"\n", p->message);
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	  strlcpy (A->g_message_number, p->message + 3, sizeof(A->g_message_number));
 | 
						  else {
 | 
				
			||||||
	  if (strlen(A->g_message_number) == 0) {
 | 
						    strlcpy (A->g_message_number, p->message + 3, sizeof(A->g_message_number));
 | 
				
			||||||
 | 
						    if (strlen(A->g_message_number) == 0) {
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf("ERROR: Message number is missing after \"ack\".\n");
 | 
						      dw_printf("ERROR: Message number is missing after \"ack\".\n");
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  // Xastir puts a carriage return on the end.
 | 
						  // Xastir puts a carriage return on the end.
 | 
				
			||||||
| 
						 | 
					@ -1812,10 +2024,12 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf("ERROR: \"%s\" must be lower case \"rej\"\n", p->message);
 | 
						      dw_printf("ERROR: \"%s\" must be lower case \"rej\"\n", p->message);
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	  strlcpy (A->g_message_number, p->message + 3, sizeof(A->g_message_number));
 | 
						  else {
 | 
				
			||||||
	  if (strlen(A->g_message_number) == 0) {
 | 
						    strlcpy (A->g_message_number, p->message + 3, sizeof(A->g_message_number));
 | 
				
			||||||
 | 
						    if (strlen(A->g_message_number) == 0) {
 | 
				
			||||||
	      text_color_set(DW_COLOR_ERROR);
 | 
						      text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	      dw_printf("ERROR: Message number is missing after \"rej\".\n");
 | 
						      dw_printf("ERROR: Message number is missing after \"rej\".\n");
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  // Xastir puts a carriage return on the end.
 | 
						  // Xastir puts a carriage return on the end.
 | 
				
			||||||
| 
						 | 
					@ -1847,7 +2061,7 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q
 | 
				
			||||||
// X>Y:}A>B::WA1XYX-15:Howdy y'all{toolong
 | 
					// X>Y:}A>B::WA1XYX-15:Howdy y'all{toolong
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	else {
 | 
						else {
 | 
				
			||||||
	  // Look for message number.
 | 
						  // Normal messaage case.  Look for message number.
 | 
				
			||||||
	  char *pno = strchr(p->message, '{');
 | 
						  char *pno = strchr(p->message, '{');
 | 
				
			||||||
	  if (pno != NULL) {
 | 
						  if (pno != NULL) {
 | 
				
			||||||
	    strlcpy (A->g_message_number, pno+1, sizeof(A->g_message_number));
 | 
						    strlcpy (A->g_message_number, pno+1, sizeof(A->g_message_number));
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,7 +24,8 @@ typedef struct decode_aprs_s {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int g_quiet;			/* Suppress error messages when decoding. */
 | 
						int g_quiet;			/* Suppress error messages when decoding. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        char g_src[AX25_MAX_ADDR_LEN];
 | 
					        char g_src[AX25_MAX_ADDR_LEN];	// In the case of a packet encapsulated by a 3rd party
 | 
				
			||||||
 | 
										// header, this is the encapsulated source.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        char g_dest[AX25_MAX_ADDR_LEN];
 | 
					        char g_dest[AX25_MAX_ADDR_LEN];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,10 +67,30 @@ typedef struct decode_aprs_s {
 | 
				
			||||||
					/* Also for Directed Station Query which is a */
 | 
										/* Also for Directed Station Query which is a */
 | 
				
			||||||
					/* special case of message. */
 | 
										/* special case of message. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This is so pfilter.c:filt_t does not need to duplicate the same work.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int g_has_thirdparty_header;
 | 
				
			||||||
 | 
						enum packet_type_e {
 | 
				
			||||||
 | 
								packet_type_none=0,
 | 
				
			||||||
 | 
								packet_type_position,
 | 
				
			||||||
 | 
								packet_type_weather,
 | 
				
			||||||
 | 
								packet_type_object,
 | 
				
			||||||
 | 
								packet_type_item,
 | 
				
			||||||
 | 
								packet_type_message,
 | 
				
			||||||
 | 
								packet_type_query,
 | 
				
			||||||
 | 
								packet_type_capabilities,
 | 
				
			||||||
 | 
								packet_type_status,
 | 
				
			||||||
 | 
								packet_type_telemetry,
 | 
				
			||||||
 | 
								packet_type_userdefined,
 | 
				
			||||||
 | 
								packet_type_nws
 | 
				
			||||||
 | 
							} g_packet_type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	enum message_subtype_e { message_subtype_invalid = 0,
 | 
						enum message_subtype_e { message_subtype_invalid = 0,
 | 
				
			||||||
				message_subtype_message,
 | 
									message_subtype_message,
 | 
				
			||||||
				message_subtype_ack,
 | 
									message_subtype_ack,
 | 
				
			||||||
				message_subtype_rej,
 | 
									message_subtype_rej,
 | 
				
			||||||
 | 
									message_subtype_bulletin,
 | 
				
			||||||
 | 
									message_subtype_nws,
 | 
				
			||||||
				message_subtype_telem_parm,
 | 
									message_subtype_telem_parm,
 | 
				
			||||||
				message_subtype_telem_unit,
 | 
									message_subtype_telem_unit,
 | 
				
			||||||
				message_subtype_telem_eqns,
 | 
									message_subtype_telem_eqns,
 | 
				
			||||||
| 
						 | 
					@ -90,8 +111,9 @@ typedef struct decode_aprs_s {
 | 
				
			||||||
        int g_power;			/* Transmitter power in watts. */
 | 
					        int g_power;			/* Transmitter power in watts. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        int g_height;			/* Antenna height above average terrain, feet. */
 | 
					        int g_height;			/* Antenna height above average terrain, feet. */
 | 
				
			||||||
 | 
										// TODO:  rename to g_height_ft
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        int g_gain;			/* Antenna gain in dB. */
 | 
					        int g_gain;			/* Antenna gain in dBi. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        char g_directivity[12];		/* Direction of max signal strength */
 | 
					        char g_directivity[12];		/* Direction of max signal strength */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -99,7 +121,7 @@ typedef struct decode_aprs_s {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        float g_altitude_ft;		/* Feet above median sea level.  */
 | 
					        float g_altitude_ft;		/* Feet above median sea level.  */
 | 
				
			||||||
					/* I used feet here because the APRS specification */
 | 
										/* I used feet here because the APRS specification */
 | 
				
			||||||
					/* has units of feet for alititude.  Meters would be */
 | 
										/* has units of feet for altitude.  Meters would be */
 | 
				
			||||||
					/* more natural to the other 96% of the world. */
 | 
										/* more natural to the other 96% of the world. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        char g_mfr[80];			/* Manufacturer or application. */
 | 
					        char g_mfr[80];			/* Manufacturer or application. */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										21
									
								
								src/demod.c
								
								
								
								
							
							
						
						
									
										21
									
								
								src/demod.c
								
								
								
								
							| 
						 | 
					@ -852,7 +852,6 @@ int demod_init (struct audio_s *pa)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define FSK_READ_ERR (256*256)
 | 
					#define FSK_READ_ERR (256*256)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
__attribute__((hot))
 | 
					__attribute__((hot))
 | 
				
			||||||
int demod_get_sample (int a)		
 | 
					int demod_get_sample (int a)		
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -862,7 +861,6 @@ int demod_get_sample (int a)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert (save_audio_config_p->adev[a].bits_per_sample == 8 || save_audio_config_p->adev[a].bits_per_sample == 16);
 | 
						assert (save_audio_config_p->adev[a].bits_per_sample == 8 || save_audio_config_p->adev[a].bits_per_sample == 16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (save_audio_config_p->adev[a].bits_per_sample == 8) {
 | 
						if (save_audio_config_p->adev[a].bits_per_sample == 8) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  x1 = audio_get(a);				
 | 
						  x1 = audio_get(a);				
 | 
				
			||||||
| 
						 | 
					@ -929,6 +927,21 @@ int demod_get_sample (int a)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *--------------------------------------------------------------------*/
 | 
					 *--------------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static volatile int mute_input[MAX_CHANS];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// New in 1.7.
 | 
				
			||||||
 | 
					// A few people have a really bad audio cross talk situation where they receive their own transmissions.
 | 
				
			||||||
 | 
					// It usually doesn't cause a problem but it is confusing to look at.
 | 
				
			||||||
 | 
					// "half duplex" setting applied only to the transmit logic.  i.e. wait for clear channel before sending.
 | 
				
			||||||
 | 
					// Receiving was still active.
 | 
				
			||||||
 | 
					// I think the simplest solution is to mute/unmute the audio input at this point if not full duplex.
 | 
				
			||||||
 | 
					// This is called from ptt_set for half duplex.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void demod_mute_input (int chan, int mute_during_xmit)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						assert (chan >= 0 && chan < MAX_CHANS);
 | 
				
			||||||
 | 
						mute_input[chan] = mute_during_xmit;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__attribute__((hot))
 | 
					__attribute__((hot))
 | 
				
			||||||
void demod_process_sample (int chan, int subchan, int sam)
 | 
					void demod_process_sample (int chan, int subchan, int sam)
 | 
				
			||||||
| 
						 | 
					@ -942,6 +955,10 @@ void demod_process_sample (int chan, int subchan, int sam)
 | 
				
			||||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
						assert (chan >= 0 && chan < MAX_CHANS);
 | 
				
			||||||
	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
						assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mute_input[chan]) {
 | 
				
			||||||
 | 
						  sam = 0;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	D = &demodulator_state[chan][subchan];
 | 
						D = &demodulator_state[chan][subchan];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,8 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int demod_init (struct audio_s *pa);
 | 
					int demod_init (struct audio_s *pa);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void demod_mute_input (int chan, int mute);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int demod_get_sample (int a);
 | 
					int demod_get_sample (int a);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void demod_process_sample (int chan, int subchan, int sam);
 | 
					void demod_process_sample (int chan, int subchan, int sam);
 | 
				
			||||||
| 
						 | 
					@ -15,3 +17,4 @@ void demod_process_sample (int chan, int subchan, int sam);
 | 
				
			||||||
void demod_print_agc (int chan, int subchan);
 | 
					void demod_print_agc (int chan, int subchan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
alevel_t demod_get_audio_level (int chan, int subchan);
 | 
					alevel_t demod_get_audio_level (int chan, int subchan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,6 +49,7 @@
 | 
				
			||||||
 *		Preemptive Digipeating  (new in version 0.8)
 | 
					 *		Preemptive Digipeating  (new in version 0.8)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *			http://www.aprs.org/aprs12/preemptive-digipeating.txt
 | 
					 *			http://www.aprs.org/aprs12/preemptive-digipeating.txt
 | 
				
			||||||
 | 
					 *			I ignored the part about the RR bits.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *------------------------------------------------------------------*/
 | 
					 *------------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -440,6 +441,10 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
 | 
				
			||||||
/* 
 | 
					/* 
 | 
				
			||||||
 * If preemptive digipeating is enabled, try matching my call 
 | 
					 * If preemptive digipeating is enabled, try matching my call 
 | 
				
			||||||
 * and aliases against all remaining unused digipeaters.
 | 
					 * and aliases against all remaining unused digipeaters.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Bob says: "GENERIC XXXXn-N DIGIPEATING should not do preemptive digipeating."
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * But consider this case:  https://github.com/wb2osz/direwolf/issues/488
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (preempt != PREEMPT_OFF) {
 | 
						if (preempt != PREEMPT_OFF) {
 | 
				
			||||||
| 
						 | 
					@ -465,13 +470,22 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      switch (preempt) {
 | 
						      switch (preempt) {
 | 
				
			||||||
	        case PREEMPT_DROP:	/* remove all prior */
 | 
						        case PREEMPT_DROP:	/* remove all prior */
 | 
				
			||||||
 | 
										// TODO: deprecate this option.  Result is misleading.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							  text_color_set (DW_COLOR_ERROR);
 | 
				
			||||||
 | 
							  dw_printf ("The digipeat DROP option will be removed in a future release.  Use PREEMPT for preemptive digipeating.\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	          while (r2 > AX25_REPEATER_1) {
 | 
						          while (r2 > AX25_REPEATER_1) {
 | 
				
			||||||
	            ax25_remove_addr (result, r2-1);
 | 
						            ax25_remove_addr (result, r2-1);
 | 
				
			||||||
 		    r2--;
 | 
					 		    r2--;
 | 
				
			||||||
	          }
 | 
						          }
 | 
				
			||||||
	          break;
 | 
						          break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	        case PREEMPT_MARK:
 | 
						        case PREEMPT_MARK:	// TODO: deprecate this option.  Result is misleading.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							  text_color_set (DW_COLOR_ERROR);
 | 
				
			||||||
 | 
							  dw_printf ("The digipeat MARK option will be removed in a future release.  Use PREEMPT for preemptive digipeating.\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	          r2--;
 | 
						          r2--;
 | 
				
			||||||
	          while (r2 >= AX25_REPEATER_1 && ax25_get_h(result,r2) == 0) {
 | 
						          while (r2 >= AX25_REPEATER_1 && ax25_get_h(result,r2) == 0) {
 | 
				
			||||||
	            ax25_set_h (result, r2);
 | 
						            ax25_set_h (result, r2);
 | 
				
			||||||
| 
						 | 
					@ -479,7 +493,12 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
 | 
				
			||||||
	          }
 | 
						          }
 | 
				
			||||||
	          break;
 | 
						          break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case PREEMPT_TRACE:	/* remove prior unused */
 | 
							case PREEMPT_TRACE:	/* My enhancement - remove prior unused digis. */
 | 
				
			||||||
 | 
										/* this provides an accurate path of where packet traveled. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Uh oh.  It looks like sample config files went out
 | 
				
			||||||
 | 
										// with this option.  Should it be renamed as
 | 
				
			||||||
 | 
										// PREEMPT which is more descriptive?
 | 
				
			||||||
	        default:
 | 
						        default:
 | 
				
			||||||
	          while (r2 > AX25_REPEATER_1 && ax25_get_h(result,r2-1) == 0) {
 | 
						          while (r2 > AX25_REPEATER_1 && ax25_get_h(result,r2-1) == 0) {
 | 
				
			||||||
	            ax25_remove_addr (result, r2-1);
 | 
						            ax25_remove_addr (result, r2-1);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -128,6 +128,7 @@
 | 
				
			||||||
#include "il2p.h"
 | 
					#include "il2p.h"
 | 
				
			||||||
#include "dwsock.h"
 | 
					#include "dwsock.h"
 | 
				
			||||||
#include "dns_sd_dw.h"
 | 
					#include "dns_sd_dw.h"
 | 
				
			||||||
 | 
					#include "dlq.h"		// for fec_type_t definition.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//static int idx_decoded = 0;
 | 
					//static int idx_decoded = 0;
 | 
				
			||||||
| 
						 | 
					@ -138,7 +139,7 @@ static BOOL cleanup_win (int);
 | 
				
			||||||
static void cleanup_linux (int);
 | 
					static void cleanup_linux (int);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void usage (char **argv);
 | 
					static void usage ();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(__SSE__) && !defined(__APPLE__)
 | 
					#if defined(__SSE__) && !defined(__APPLE__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -300,9 +301,9 @@ 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 4\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, "F", __DATE__);
 | 
						//dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "G", __DATE__);
 | 
				
			||||||
	//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
 | 
						dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(ENABLE_GPSD) || defined(USE_HAMLIB) || defined(USE_CM108) || USE_AVAHI_CLIENT || USE_MACOS_DNSSD
 | 
					#if defined(ENABLE_GPSD) || defined(USE_HAMLIB) || defined(USE_CM108) || USE_AVAHI_CLIENT || USE_MACOS_DNSSD
 | 
				
			||||||
| 
						 | 
					@ -387,7 +388,7 @@ int main (int argc, char *argv[])
 | 
				
			||||||
	      dw_printf ("\n");
 | 
						      dw_printf ("\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);
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -598,7 +599,7 @@ int main (int argc, char *argv[])
 | 
				
			||||||
          case '?':
 | 
					          case '?':
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            /* For '?' unknown option message was already printed. */
 | 
					            /* For '?' unknown option message was already printed. */
 | 
				
			||||||
            usage (argv);
 | 
					            usage ();
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  case 'd':				/* Set debug option. */
 | 
						  case 'd':				/* Set debug option. */
 | 
				
			||||||
| 
						 | 
					@ -742,7 +743,7 @@ int main (int argc, char *argv[])
 | 
				
			||||||
            /* Should not be here. */
 | 
					            /* Should not be here. */
 | 
				
			||||||
	    text_color_set(DW_COLOR_DEBUG);
 | 
						    text_color_set(DW_COLOR_DEBUG);
 | 
				
			||||||
            dw_printf("?? getopt returned character code 0%o ??\n", c);
 | 
					            dw_printf("?? getopt returned character code 0%o ??\n", c);
 | 
				
			||||||
            usage (argv);
 | 
					            usage ();
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
	}  /* end while(1) for options */
 | 
						}  /* end while(1) for options */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -987,6 +988,7 @@ int main (int argc, char *argv[])
 | 
				
			||||||
	  text_color_set(DW_COLOR_ERROR);
 | 
						  text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	  dw_printf ("Pointless to continue without audio device.\n");
 | 
						  dw_printf ("Pointless to continue without audio device.\n");
 | 
				
			||||||
	  SLEEP_SEC(5);
 | 
						  SLEEP_SEC(5);
 | 
				
			||||||
 | 
						  usage ();
 | 
				
			||||||
	  exit (1);
 | 
						  exit (1);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1178,7 +1180,7 @@ int main (int argc, char *argv[])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO:  Use only one printf per line so output doesn't get jumbled up with stuff from other threads.
 | 
					// TODO:  Use only one printf per line so output doesn't get jumbled up with stuff from other threads.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, int is_fx25, retry_t retries, char *spectrum)
 | 
					void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, fec_type_t fec_type, retry_t retries, char *spectrum)
 | 
				
			||||||
{	
 | 
					{	
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	char stemp[500];
 | 
						char stemp[500];
 | 
				
			||||||
| 
						 | 
					@ -1187,7 +1189,8 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
 | 
				
			||||||
	char heard[AX25_MAX_ADDR_LEN];
 | 
						char heard[AX25_MAX_ADDR_LEN];
 | 
				
			||||||
	//int j;
 | 
						//int j;
 | 
				
			||||||
	int h;
 | 
						int h;
 | 
				
			||||||
	char display_retries[32];
 | 
						char display_retries[32];				// Extra stuff before slice indicators.
 | 
				
			||||||
 | 
													// 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 >= -2 && subchan < MAX_SUBCHANS);
 | 
				
			||||||
| 
						 | 
					@ -1196,12 +1199,21 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
 | 
				
			||||||
     
 | 
					     
 | 
				
			||||||
	strlcpy (display_retries, "", sizeof(display_retries));
 | 
						strlcpy (display_retries, "", sizeof(display_retries));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (is_fx25) {
 | 
						switch (fec_type) {
 | 
				
			||||||
	  ;
 | 
						  case fec_type_fx25:
 | 
				
			||||||
	}
 | 
						    strlcpy (display_retries, " FX.25 ", sizeof(display_retries));
 | 
				
			||||||
	else if (audio_config.achan[chan].fix_bits != RETRY_NONE || audio_config.achan[chan].passall) {
 | 
						    break;
 | 
				
			||||||
	  assert (retries >= RETRY_NONE && retries <= RETRY_MAX);
 | 
						  case fec_type_il2p:
 | 
				
			||||||
	  snprintf (display_retries, sizeof(display_retries), " [%s] ", retry_text[(int)retries]);
 | 
						    strlcpy (display_retries, " IL2P ", sizeof(display_retries));
 | 
				
			||||||
 | 
						    break;
 | 
				
			||||||
 | 
						  case fec_type_none:
 | 
				
			||||||
 | 
						  default:
 | 
				
			||||||
 | 
						    // Possible fix_bits indication.
 | 
				
			||||||
 | 
						    if (audio_config.achan[chan].fix_bits != RETRY_NONE || audio_config.achan[chan].passall) {
 | 
				
			||||||
 | 
						      assert (retries >= RETRY_NONE && retries <= RETRY_MAX);
 | 
				
			||||||
 | 
						      snprintf (display_retries, sizeof(display_retries), " [%s] ", retry_text[(int)retries]);
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
 | 
						    break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ax25_format_addrs (pp, stemp);
 | 
						ax25_format_addrs (pp, stemp);
 | 
				
			||||||
| 
						 | 
					@ -1576,7 +1588,7 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
 | 
				
			||||||
 * However, if it used FEC mode (FX.25. IL2P), we have much higher level of
 | 
					 * However, if it used FEC mode (FX.25. IL2P), we have much higher level of
 | 
				
			||||||
 * confidence that it is correct.
 | 
					 * confidence that it is correct.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
	  if (ax25_is_aprs(pp) && ( retries == RETRY_NONE || is_fx25) ) {
 | 
						  if (ax25_is_aprs(pp) && ( retries == RETRY_NONE || fec_type == fec_type_fx25 || fec_type == fec_type_il2p) ) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    igate_send_rec_packet (chan, pp);
 | 
						    igate_send_rec_packet (chan, pp);
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
| 
						 | 
					@ -1597,7 +1609,7 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
 | 
				
			||||||
 * However, if it used FEC mode (FX.25. IL2P), we have much higher level of
 | 
					 * However, if it used FEC mode (FX.25. IL2P), we have much higher level of
 | 
				
			||||||
 * confidence that it is correct.
 | 
					 * confidence that it is correct.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
	  if (ax25_is_aprs(pp) && ( retries == RETRY_NONE || is_fx25) ) {
 | 
						  if (ax25_is_aprs(pp) && ( retries == RETRY_NONE || fec_type == fec_type_fx25 || fec_type == fec_type_il2p) ) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    digipeater (chan, pp);
 | 
						    digipeater (chan, pp);
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
| 
						 | 
					@ -1607,7 +1619,7 @@ 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 || is_fx25) {
 | 
						  if (retries == RETRY_NONE || fec_type == fec_type_fx25 || fec_type == fec_type_il2p) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    cdigipeater (chan, pp);
 | 
						    cdigipeater (chan, pp);
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
| 
						 | 
					@ -1732,16 +1744,16 @@ static void usage (char **argv)
 | 
				
			||||||
	dw_printf ("\n");
 | 
						dw_printf ("\n");
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
#if __WIN32__
 | 
					#if __WIN32__
 | 
				
			||||||
	dw_printf ("Complete documentation can be found in the 'doc' folder\n");
 | 
						dw_printf ("Documentation can be found in the 'doc' folder\n");
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
	// TODO: Could vary by platform and build options.
 | 
						// TODO: Could vary by platform and build options.
 | 
				
			||||||
	dw_printf ("Complete documentation can be found in /usr/local/share/doc/direwolf\n");
 | 
						dw_printf ("Documentation can be found in /usr/local/share/doc/direwolf\n");
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	dw_printf ("or online at https://github.com/wb2osz/direwolf/tree/master/doc\n");
 | 
						dw_printf ("or online at https://github.com/wb2osz/direwolf/tree/master/doc\n");
 | 
				
			||||||
 | 
						dw_printf ("additional topics: https://github.com/wb2osz/direwolf-doc\n");
 | 
				
			||||||
	text_color_set(DW_COLOR_INFO);
 | 
						text_color_set(DW_COLOR_INFO);
 | 
				
			||||||
	exit (EXIT_FAILURE);
 | 
						exit (EXIT_FAILURE);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
/* end direwolf.c */
 | 
					/* end direwolf.c */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -278,6 +278,8 @@ typedef pthread_mutex_t dw_mutex_t;
 | 
				
			||||||
/* Platform differences for string functions. */
 | 
					/* Platform differences for string functions. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Windows is missing a few which are available on Unix/Linux platforms.
 | 
				
			||||||
 | 
					// We provide our own copies when building on Windows.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if __WIN32__
 | 
					#if __WIN32__
 | 
				
			||||||
char *strsep(char **stringp, const char *delim);
 | 
					char *strsep(char **stringp, const char *delim);
 | 
				
			||||||
| 
						 | 
					@ -285,13 +287,49 @@ char *strtok_r(char *str, const char *delim, char **saveptr);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Don't recall why I added this for everyone rather than only for Windows.
 | 
					// Don't recall why I added this for everyone rather than only for Windows.
 | 
				
			||||||
 | 
					// Potential problem if some C library declares it a little differently.
 | 
				
			||||||
char *strcasestr(const char *S, const char *FIND);
 | 
					char *strcasestr(const char *S, const char *FIND);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// cmake determines whether strlcpy and strlcat are available
 | 
					// cmake tries to determine whether strlcpy and strlcat are provided by the C runtime library.
 | 
				
			||||||
// or if we need to supply our own.
 | 
					//
 | 
				
			||||||
 | 
					//	../CMakeLists.txt:check_symbol_exists(strlcpy string.h HAVE_STRLCPY)
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// It sets HAVE_STRLCPY and HAVE_STRLCAT if the corresponding functions are declared.
 | 
				
			||||||
 | 
					// Unfortunately this does not work right for glibc 2.38 which declares the functions
 | 
				
			||||||
 | 
					// like this:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	extern __typeof (strlcpy) __strlcpy;
 | 
				
			||||||
 | 
					//	libc_hidden_proto (__strlcpy)
 | 
				
			||||||
 | 
					//	extern __typeof (strlcat) __strlcat;
 | 
				
			||||||
 | 
					//	libc_hidden_proto (__strlcat)
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Rather than the normal way found in earlier versions:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	extern char *strcpy (char *__restrict __dest, const char *__restrict __src)
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Perhaps a later version of cmake will recognize this form but the version I'm
 | 
				
			||||||
 | 
					// using does not.
 | 
				
			||||||
 | 
					// So, our work around is to assume these functions are available for glibc >= 2.38.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// In theory, cmake should be able to find the version of the C runtime library, 
 | 
				
			||||||
 | 
					// but I could not get it to work.  So we have the test here.  We will still build
 | 
				
			||||||
 | 
					// own library with the strl... functions but this does not cause a problem
 | 
				
			||||||
 | 
					// because they have special debug names which will not cause a conflict.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __GLIBC__
 | 
				
			||||||
 | 
					#if (__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 38))
 | 
				
			||||||
 | 
					// These functions first added in 2.38.
 | 
				
			||||||
 | 
					//#warning "DEBUG - glibc >= 2.38"
 | 
				
			||||||
 | 
					#define HAVE_STRLCPY 1
 | 
				
			||||||
 | 
					#define HAVE_STRLCAT 1
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					//#warning "DEBUG - glibc < 2.38"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DEBUG_STRL 1	// Extra Debug version when using our own strlcpy, strlcat.
 | 
					#define DEBUG_STRL 1	// Extra Debug version when using our own strlcpy, strlcat.
 | 
				
			||||||
 | 
								// Should be ignored if not supplying our own.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef HAVE_STRLCPY	// Need to supply our own.
 | 
					#ifndef HAVE_STRLCPY	// Need to supply our own.
 | 
				
			||||||
#if DEBUG_STRL
 | 
					#if DEBUG_STRL
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										19
									
								
								src/dlq.c
								
								
								
								
							
							
						
						
									
										19
									
								
								src/dlq.c
								
								
								
								
							| 
						 | 
					@ -215,10 +215,10 @@ void dlq_init (void)
 | 
				
			||||||
 *				 display of audio level line.
 | 
					 *				 display of audio level line.
 | 
				
			||||||
 *				 Use -2 to indicate DTMF message.)
 | 
					 *				 Use -2 to indicate DTMF message.)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		is_fx25 - Was it from FX.25?  Need to know because
 | 
					 *		fec_type - Was it from FX.25 or IL2P?  Need to know because
 | 
				
			||||||
 *			  meaning of retries is different.
 | 
					 *			  meaning of retries is different.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		retries	- Level of bit correction used.
 | 
					 *		retries	- Level of correction used.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		spectrum - Display of how well multiple decoders did.
 | 
					 *		spectrum - Display of how well multiple decoders did.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -228,7 +228,7 @@ void dlq_init (void)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *--------------------------------------------------------------------*/
 | 
					 *--------------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, int is_fx25, retry_t retries, char *spectrum)
 | 
					void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, fec_type_t fec_type, retry_t retries, char *spectrum)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct dlq_item_s *pnew;
 | 
						struct dlq_item_s *pnew;
 | 
				
			||||||
| 
						 | 
					@ -278,7 +278,7 @@ void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alev
 | 
				
			||||||
	pnew->subchan = subchan;
 | 
						pnew->subchan = subchan;
 | 
				
			||||||
	pnew->pp = pp;
 | 
						pnew->pp = pp;
 | 
				
			||||||
	pnew->alevel = alevel;
 | 
						pnew->alevel = alevel;
 | 
				
			||||||
	pnew->is_fx25 = is_fx25;
 | 
						pnew->fec_type = fec_type;
 | 
				
			||||||
	pnew->retries = retries;
 | 
						pnew->retries = retries;
 | 
				
			||||||
	if (spectrum == NULL) 
 | 
						if (spectrum == NULL) 
 | 
				
			||||||
	  strlcpy(pnew->spectrum, "", sizeof(pnew->spectrum));
 | 
						  strlcpy(pnew->spectrum, "", sizeof(pnew->spectrum));
 | 
				
			||||||
| 
						 | 
					@ -728,8 +728,7 @@ void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int n
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Purpose:     Register callsigns that we will recognize for incoming connection requests.
 | 
					 * Purpose:     Register callsigns that we will recognize for incoming connection requests.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Inputs:	addrs		- Source (owncall), destination (peercall),
 | 
					 * Inputs:	addr		- Callsign to [un]register.
 | 
				
			||||||
 *				  and possibly digipeaters.
 | 
					 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		chan		- Channel, 0 is first.
 | 
					 *		chan		- Channel, 0 is first.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -749,7 +748,7 @@ void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int n
 | 
				
			||||||
 *--------------------------------------------------------------------*/
 | 
					 *--------------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void dlq_register_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client)
 | 
					void dlq_register_callsign (char *addr, int chan, int client)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct dlq_item_s *pnew;
 | 
						struct dlq_item_s *pnew;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -773,7 +772,7 @@ void dlq_register_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pnew->type = DLQ_REGISTER_CALLSIGN;
 | 
						pnew->type = DLQ_REGISTER_CALLSIGN;
 | 
				
			||||||
	pnew->chan = chan;
 | 
						pnew->chan = chan;
 | 
				
			||||||
	strlcpy (pnew->addrs[0], addr, AX25_MAX_ADDR_LEN);
 | 
						strlcpy (pnew->addrs[0], addr, sizeof(pnew->addrs[0]));
 | 
				
			||||||
	pnew->num_addr = 1;
 | 
						pnew->num_addr = 1;
 | 
				
			||||||
	pnew->client = client;
 | 
						pnew->client = client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -784,7 +783,7 @@ void dlq_register_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client)
 | 
				
			||||||
} /* end dlq_register_callsign */
 | 
					} /* end dlq_register_callsign */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void dlq_unregister_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client)
 | 
					void dlq_unregister_callsign (char *addr, int chan, int client)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct dlq_item_s *pnew;
 | 
						struct dlq_item_s *pnew;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -808,7 +807,7 @@ void dlq_unregister_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pnew->type = DLQ_UNREGISTER_CALLSIGN;
 | 
						pnew->type = DLQ_UNREGISTER_CALLSIGN;
 | 
				
			||||||
	pnew->chan = chan;
 | 
						pnew->chan = chan;
 | 
				
			||||||
	strlcpy (pnew->addrs[0], addr, AX25_MAX_ADDR_LEN);
 | 
						strlcpy (pnew->addrs[0], addr, sizeof(pnew->addrs[0]));
 | 
				
			||||||
	pnew->num_addr = 1;
 | 
						pnew->num_addr = 1;
 | 
				
			||||||
	pnew->client = client;
 | 
						pnew->client = client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/dlq.h
								
								
								
								
							
							
						
						
									
										11
									
								
								src/dlq.h
								
								
								
								
							| 
						 | 
					@ -33,10 +33,13 @@ typedef struct cdata_s {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Types of things that can be in queue. */
 | 
					/* Types of things that can be in queue. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef enum dlq_type_e {DLQ_REC_FRAME, DLQ_CONNECT_REQUEST, DLQ_DISCONNECT_REQUEST, DLQ_XMIT_DATA_REQUEST, DLQ_REGISTER_CALLSIGN, DLQ_UNREGISTER_CALLSIGN, DLQ_OUTSTANDING_FRAMES_REQUEST, DLQ_CHANNEL_BUSY, DLQ_SEIZE_CONFIRM, DLQ_CLIENT_CLEANUP} dlq_type_t;
 | 
					typedef enum dlq_type_e {DLQ_REC_FRAME, DLQ_CONNECT_REQUEST, DLQ_DISCONNECT_REQUEST, DLQ_XMIT_DATA_REQUEST, DLQ_REGISTER_CALLSIGN, DLQ_UNREGISTER_CALLSIGN, DLQ_OUTSTANDING_FRAMES_REQUEST, DLQ_CHANNEL_BUSY, DLQ_SEIZE_CONFIRM, DLQ_CLIENT_CLEANUP} dlq_type_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum fec_type_e {fec_type_none=0, fec_type_fx25=1, fec_type_il2p=2} fec_type_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* A queue item. */
 | 
					/* A queue item. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -68,7 +71,7 @@ typedef struct dlq_item_s {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	alevel_t alevel;		/* Audio level. */
 | 
						alevel_t alevel;		/* Audio level. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int is_fx25;			/* Was it from FX.25? */
 | 
						fec_type_t fec_type;		// Type of FEC for received signal: none, FX.25, or IL2P.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	retry_t retries;		/* Effort expended to get a valid CRC. */
 | 
						retry_t retries;		/* Effort expended to get a valid CRC. */
 | 
				
			||||||
					/* Bits changed for regular AX.25. */
 | 
										/* Bits changed for regular AX.25. */
 | 
				
			||||||
| 
						 | 
					@ -106,7 +109,7 @@ void dlq_init (void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, int is_fx25, retry_t retries, char *spectrum);
 | 
					void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, fec_type_t fec_type, retry_t retries, char *spectrum);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void dlq_connect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client, int pid);
 | 
					void dlq_connect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client, int pid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,9 +119,9 @@ void dlq_outstanding_frames_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client, int pid, char *xdata_ptr, int xdata_len);
 | 
					void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client, int pid, char *xdata_ptr, int xdata_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void dlq_register_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client);
 | 
					void dlq_register_callsign (char *addr, int chan, int client);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void dlq_unregister_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client);
 | 
					void dlq_unregister_callsign (char *addr, int chan, int client);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void dlq_channel_busy (int chan, int activity, int status);
 | 
					void dlq_channel_busy (int chan, int activity, int status);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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, 2016, 2019, 2021  John Langner, WB2OSZ
 | 
					//    Copyright (C) 2011, 2013, 2014, 2015, 2016, 2019, 2021, 2023  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
 | 
				
			||||||
| 
						 | 
					@ -118,13 +118,48 @@ static void send_packet (char *str)
 | 
				
			||||||
    	packet_t pp;
 | 
					    	packet_t pp;
 | 
				
			||||||
    	unsigned char fbuf[AX25_MAX_PACKET_LEN+2];
 | 
					    	unsigned char fbuf[AX25_MAX_PACKET_LEN+2];
 | 
				
			||||||
    	int flen;
 | 
					    	int flen;
 | 
				
			||||||
	int c;
 | 
						int c = 0;	// channel number.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (g_morse_wpm > 0) {
 | 
						if (g_morse_wpm > 0) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  // TODO: Why not use the destination field instead of command line option?
 | 
						  // Why not use the destination field instead of command line option?
 | 
				
			||||||
 | 
						  // For one thing, this is not in TNC-2 monitor format.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  morse_send (0, str, g_morse_wpm, 100, 100);
 | 
						  morse_send (c, str, g_morse_wpm, 100, 100);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else if (modem.achan[0].modem_type == MODEM_EAS) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Generate EAS SAME signal FOR RESEARCH AND TESTING ONLY!!!
 | 
				
			||||||
 | 
					// There could be legal consequences for sending unauhorized SAME
 | 
				
			||||||
 | 
					// over the radio so don't do it!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  // I'm expecting to see TNC 2 monitor format.
 | 
				
			||||||
 | 
						  // The source and destination are ignored.
 | 
				
			||||||
 | 
						  // The optional destination SSID is the number of times to repeat.
 | 
				
			||||||
 | 
						  // The user defined data type indicator can optionally be used
 | 
				
			||||||
 | 
						  // for compatibility with how it is received and presented to client apps.
 | 
				
			||||||
 | 
						  // Examples:
 | 
				
			||||||
 | 
						  //	X>X-3:{DEZCZC-WXR-RWT-033019-033017-033015-033013-033011-025011-025017-033007-033005-033003-033001-025009-025027-033009+0015-1691525-KGYX/NWS-
 | 
				
			||||||
 | 
						  //	X>X:NNNN
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  pp = ax25_from_text (str, 1);
 | 
				
			||||||
 | 
						  if (pp == NULL) {
 | 
				
			||||||
 | 
					            text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
 | 
					            dw_printf ("\"%s\" is not valid TNC2 monitoring format.\n", str);
 | 
				
			||||||
 | 
						    return;
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
						  unsigned char *pinfo;
 | 
				
			||||||
 | 
						  int info_len = ax25_get_info (pp, &pinfo);
 | 
				
			||||||
 | 
						  if (info_len >= 3 && strncmp((char*)pinfo, "{DE", 3) == 0) {
 | 
				
			||||||
 | 
						    pinfo += 3;
 | 
				
			||||||
 | 
						    info_len -= 3;
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  int repeat = ax25_get_ssid (pp, AX25_DESTINATION);
 | 
				
			||||||
 | 
						  if (repeat == 0) repeat = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  eas_send (c, pinfo, repeat, 500, 500);
 | 
				
			||||||
 | 
						  ax25_delete (pp);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else {
 | 
						else {
 | 
				
			||||||
	  pp = ax25_from_text (str, 1);
 | 
						  pp = ax25_from_text (str, 1);
 | 
				
			||||||
| 
						 | 
					@ -135,6 +170,9 @@ static void send_packet (char *str)
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	  flen = ax25_pack (pp, fbuf);
 | 
						  flen = ax25_pack (pp, fbuf);
 | 
				
			||||||
	  (void)flen;
 | 
						  (void)flen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  // If stereo, put same thing in each channel.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  for (c=0; c<modem.adev[0].num_channels; c++)
 | 
						  for (c=0; c<modem.adev[0].num_channels; c++)
 | 
				
			||||||
	  {
 | 
						  {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -282,23 +320,31 @@ int main(int argc, char **argv)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						// FIXME: options should not be order dependent.
 | 
											// FIXME: options should not be order dependent.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              modem.achan[0].baud = atoi(optarg);
 | 
					              if (strcasecmp(optarg, "EAS") == 0) {
 | 
				
			||||||
 | 
						        modem.achan[0].baud = 0xEA5EA5;	// See special case below.
 | 
				
			||||||
 | 
						      }
 | 
				
			||||||
 | 
						      else {
 | 
				
			||||||
 | 
						        modem.achan[0].baud = atoi(optarg);
 | 
				
			||||||
 | 
						      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              text_color_set(DW_COLOR_INFO); 
 | 
					              text_color_set(DW_COLOR_INFO); 
 | 
				
			||||||
              dw_printf ("Data rate set to %d bits / second.\n", modem.achan[0].baud);
 | 
					              dw_printf ("Data rate set to %d bits / second.\n", modem.achan[0].baud);
 | 
				
			||||||
              if (modem.achan[0].baud != 100 && (modem.achan[0].baud < MIN_BAUD || modem.achan[0].baud > MAX_BAUD)) {
 | 
					 | 
				
			||||||
                text_color_set(DW_COLOR_ERROR);
 | 
					 | 
				
			||||||
                dw_printf ("Use a more reasonable bit rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD);
 | 
					 | 
				
			||||||
                exit (EXIT_FAILURE);
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      /* We have similar logic in direwolf.c, config.c, gen_packets.c, and atest.c, */
 | 
						      /* We have similar logic in direwolf.c, config.c, gen_packets.c, and atest.c, */
 | 
				
			||||||
	      /* that need to be kept in sync.  Maybe it could be a common function someday. */
 | 
						      /* that need to be kept in sync.  Maybe it could be a common function someday. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      if (modem.achan[0].baud == 100) {
 | 
						      if (modem.achan[0].baud == 100) {			// What was this for?
 | 
				
			||||||
                  modem.achan[0].modem_type = MODEM_AFSK;
 | 
					                  modem.achan[0].modem_type = MODEM_AFSK;
 | 
				
			||||||
                  modem.achan[0].mark_freq = 1615;
 | 
					                  modem.achan[0].mark_freq = 1615;
 | 
				
			||||||
                  modem.achan[0].space_freq = 1785;
 | 
					                  modem.achan[0].space_freq = 1785;
 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
 | 
						      else if (modem.achan[0].baud == 0xEA5EA5) {
 | 
				
			||||||
 | 
							  modem.achan[0].baud = 521;			// Fine tuned later. 520.83333
 | 
				
			||||||
 | 
													// Proper fix is to make this float.
 | 
				
			||||||
 | 
					                  modem.achan[0].modem_type = MODEM_EAS;
 | 
				
			||||||
 | 
					                  modem.achan[0].mark_freq = 2083.3333;		// Ideally these should be floating point.
 | 
				
			||||||
 | 
					                  modem.achan[0].space_freq = 1562.5000 ;
 | 
				
			||||||
 | 
						      }
 | 
				
			||||||
	      else if (modem.achan[0].baud < 600) {
 | 
						      else if (modem.achan[0].baud < 600) {
 | 
				
			||||||
                  modem.achan[0].modem_type = MODEM_AFSK;
 | 
					                  modem.achan[0].modem_type = MODEM_AFSK;
 | 
				
			||||||
                  modem.achan[0].mark_freq = 1600;		// Typical for HF SSB
 | 
					                  modem.achan[0].mark_freq = 1600;		// Typical for HF SSB
 | 
				
			||||||
| 
						 | 
					@ -334,6 +380,11 @@ int main(int argc, char **argv)
 | 
				
			||||||
                  text_color_set(DW_COLOR_INFO); 
 | 
					                  text_color_set(DW_COLOR_INFO); 
 | 
				
			||||||
                  dw_printf ("Using scrambled baseband signal rather than AFSK.\n");
 | 
					                  dw_printf ("Using scrambled baseband signal rather than AFSK.\n");
 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
 | 
					              if (modem.achan[0].baud != 100 && (modem.achan[0].baud < MIN_BAUD || modem.achan[0].baud > MAX_BAUD)) {
 | 
				
			||||||
 | 
					                text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
 | 
					                dw_printf ("Use a more reasonable bit rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD);
 | 
				
			||||||
 | 
					                exit (EXIT_FAILURE);
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
              break;
 | 
					              break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case 'g':				/* -g for g3ruh scrambling */
 | 
					            case 'g':				/* -g for g3ruh scrambling */
 | 
				
			||||||
| 
						 | 
					@ -740,14 +791,23 @@ int main(int argc, char **argv)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else {
 | 
						else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  // This should send a total of 6.
 | 
				
			||||||
 | 
						  // Note that sticking in the user defined type {DE is optional.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  if (modem.achan[0].modem_type == MODEM_EAS) {
 | 
				
			||||||
 | 
						    send_packet ("X>X-3:{DEZCZC-WXR-RWT-033019-033017-033015-033013-033011-025011-025017-033007-033005-033003-033001-025009-025027-033009+0015-1691525-KGYX/NWS-");
 | 
				
			||||||
 | 
						    send_packet ("X>X-2:{DENNNN");
 | 
				
			||||||
 | 
						    send_packet ("X>X:NNNN");
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
						  else {
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Builtin default 4 packets.
 | 
					 * Builtin default 4 packets.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
						    send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog!  1 of 4");
 | 
				
			||||||
	  send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog!  1 of 4");
 | 
						    send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog!  2 of 4");
 | 
				
			||||||
	  send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog!  2 of 4");
 | 
						    send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog!  3 of 4");
 | 
				
			||||||
	  send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog!  3 of 4");
 | 
						    send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog!  4 of 4");
 | 
				
			||||||
	  send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog!  4 of 4");
 | 
						  }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	audio_file_close();
 | 
						audio_file_close();
 | 
				
			||||||
| 
						 | 
					@ -765,7 +825,7 @@ static void usage (char **argv)
 | 
				
			||||||
	dw_printf ("Options:\n");
 | 
						dw_printf ("Options:\n");
 | 
				
			||||||
	dw_printf ("  -a <number>   Signal amplitude in range of 0 - 200%%.  Default 50.\n");
 | 
						dw_printf ("  -a <number>   Signal amplitude in range of 0 - 200%%.  Default 50.\n");
 | 
				
			||||||
	dw_printf ("  -b <number>   Bits / second for data.  Default is %d.\n", DEFAULT_BAUD);
 | 
						dw_printf ("  -b <number>   Bits / second for data.  Default is %d.\n", DEFAULT_BAUD);
 | 
				
			||||||
	dw_printf ("  -B <number>   Bits / second for data.  Proper modem selected for 300, 1200, 2400, 4800, 9600.\n");
 | 
						dw_printf ("  -B <number>   Bits / second for data.  Proper modem selected for 300, 1200, 2400, 4800, 9600, EAS.\n");
 | 
				
			||||||
	dw_printf ("  -g            Scrambled baseband rather than AFSK.\n");
 | 
						dw_printf ("  -g            Scrambled baseband rather than AFSK.\n");
 | 
				
			||||||
	dw_printf ("  -j            2400 bps QPSK compatible with direwolf <= 1.5.\n");
 | 
						dw_printf ("  -j            2400 bps QPSK compatible with direwolf <= 1.5.\n");
 | 
				
			||||||
	dw_printf ("  -J            2400 bps QPSK compatible with MFJ-2400.\n");
 | 
						dw_printf ("  -J            2400 bps QPSK compatible with MFJ-2400.\n");
 | 
				
			||||||
| 
						 | 
					@ -788,6 +848,7 @@ static void usage (char **argv)
 | 
				
			||||||
	dw_printf ("the default built-in message. The format should correspond to\n");
 | 
						dw_printf ("the default built-in message. The format should correspond to\n");
 | 
				
			||||||
	dw_printf ("the standard packet monitoring representation such as,\n\n");
 | 
						dw_printf ("the standard packet monitoring representation such as,\n\n");
 | 
				
			||||||
	dw_printf ("    WB2OSZ-1>APDW12,WIDE2-2:!4237.14NS07120.83W#\n");
 | 
						dw_printf ("    WB2OSZ-1>APDW12,WIDE2-2:!4237.14NS07120.83W#\n");
 | 
				
			||||||
 | 
						dw_printf ("User defined content can't be used with -n option.\n");
 | 
				
			||||||
	dw_printf ("\n");
 | 
						dw_printf ("\n");
 | 
				
			||||||
	dw_printf ("Example:  gen_packets -o x.wav \n");
 | 
						dw_printf ("Example:  gen_packets -o x.wav \n");
 | 
				
			||||||
	dw_printf ("\n");
 | 
						dw_printf ("\n");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										161
									
								
								src/gen_tone.c
								
								
								
								
							
							
						
						
									
										161
									
								
								src/gen_tone.c
								
								
								
								
							| 
						 | 
					@ -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, 2014, 2015, 2016, 2019  John Langner, WB2OSZ
 | 
					//    Copyright (C) 2011, 2014, 2015, 2016, 2019, 2023  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
 | 
				
			||||||
| 
						 | 
					@ -70,6 +70,7 @@ static int ticks_per_sample[MAX_CHANS];	/* Same for both channels of same soundc
 | 
				
			||||||
static int ticks_per_bit[MAX_CHANS];
 | 
					static int ticks_per_bit[MAX_CHANS];
 | 
				
			||||||
static int f1_change_per_sample[MAX_CHANS];
 | 
					static int f1_change_per_sample[MAX_CHANS];
 | 
				
			||||||
static int f2_change_per_sample[MAX_CHANS];
 | 
					static int f2_change_per_sample[MAX_CHANS];
 | 
				
			||||||
 | 
					static float samples_per_symbol[MAX_CHANS];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static short sine_table[256];
 | 
					static short sine_table[256];
 | 
				
			||||||
| 
						 | 
					@ -198,8 +199,11 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets)
 | 
				
			||||||
	        ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / ((double)audio_config_p->achan[chan].baud * 0.5)) + 0.5);
 | 
						        ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / ((double)audio_config_p->achan[chan].baud * 0.5)) + 0.5);
 | 
				
			||||||
	        f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
 | 
						        f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
 | 
				
			||||||
	        f2_change_per_sample[chan] = f1_change_per_sample[chan];	// Not used.
 | 
						        f2_change_per_sample[chan] = f1_change_per_sample[chan];	// Not used.
 | 
				
			||||||
 | 
						        samples_per_symbol[chan] = 2. * (float)audio_config_p->adev[a].samples_per_sec / (float)audio_config_p->achan[chan].baud;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	        tone_phase[chan] = PHASE_SHIFT_45;	// Just to mimic first attempt.
 | 
						        tone_phase[chan] = PHASE_SHIFT_45;	// Just to mimic first attempt.
 | 
				
			||||||
 | 
												// ??? Why?  We are only concerned with the difference
 | 
				
			||||||
 | 
												// from one symbol to the next.
 | 
				
			||||||
	        break;
 | 
						        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      case MODEM_8PSK:
 | 
						      case MODEM_8PSK:
 | 
				
			||||||
| 
						 | 
					@ -211,6 +215,7 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets)
 | 
				
			||||||
	        ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / ((double)audio_config_p->achan[chan].baud / 3.)) + 0.5);
 | 
						        ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / ((double)audio_config_p->achan[chan].baud / 3.)) + 0.5);
 | 
				
			||||||
	        f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
 | 
						        f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
 | 
				
			||||||
	        f2_change_per_sample[chan] = f1_change_per_sample[chan];	// Not used.
 | 
						        f2_change_per_sample[chan] = f1_change_per_sample[chan];	// Not used.
 | 
				
			||||||
 | 
						        samples_per_symbol[chan] = 3. * (float)audio_config_p->adev[a].samples_per_sec / (float)audio_config_p->achan[chan].baud;
 | 
				
			||||||
	        break;
 | 
						        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      case MODEM_BASEBAND:
 | 
						      case MODEM_BASEBAND:
 | 
				
			||||||
| 
						 | 
					@ -220,11 +225,23 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets)
 | 
				
			||||||
		// Tone is half baud.
 | 
							// Tone is half baud.
 | 
				
			||||||
	        ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->achan[chan].baud ) + 0.5);
 | 
						        ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->achan[chan].baud ) + 0.5);
 | 
				
			||||||
	        f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].baud * 0.5 * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
 | 
						        f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].baud * 0.5 * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
 | 
				
			||||||
 | 
						        samples_per_symbol[chan] = (float)audio_config_p->adev[a].samples_per_sec / (float)audio_config_p->achan[chan].baud;
 | 
				
			||||||
 | 
						        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						      case MODEM_EAS:		//  EAS.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// TODO: Proper fix would be to use float for baud, mark, space.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						        ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / 520.833333333333 ) + 0.5);
 | 
				
			||||||
 | 
						        samples_per_symbol[chan] = (int)((audio_config_p->adev[a].samples_per_sec / 520.83333) + 0.5);
 | 
				
			||||||
 | 
						        f1_change_per_sample[chan] = (int) ((2083.33333333333 * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
 | 
				
			||||||
 | 
						        f2_change_per_sample[chan] = (int) ((1562.5000000 * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
 | 
				
			||||||
	        break;
 | 
						        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      default:		// AFSK
 | 
						      default:		// AFSK
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	        ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->achan[chan].baud ) + 0.5);
 | 
						        ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->achan[chan].baud ) + 0.5);
 | 
				
			||||||
 | 
						        samples_per_symbol[chan] = (float)audio_config_p->adev[a].samples_per_sec / (float)audio_config_p->achan[chan].baud;
 | 
				
			||||||
	        f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
 | 
						        f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
 | 
				
			||||||
	        f2_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].space_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
 | 
						        f2_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].space_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
 | 
				
			||||||
	        break;
 | 
						        break;
 | 
				
			||||||
| 
						 | 
					@ -285,9 +302,64 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *--------------------------------------------------------------------*/
 | 
					 *--------------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Interpolate between two values.
 | 
				
			||||||
 | 
					// My original approximation simply jumped between phases, producing a discontinuity,
 | 
				
			||||||
 | 
					// and increasing bandwidth.
 | 
				
			||||||
 | 
					// According to multiple sources, we should transition more gently.
 | 
				
			||||||
 | 
					// Below see see a rough approximation of:
 | 
				
			||||||
 | 
					//  * A step function, immediately going to new value.
 | 
				
			||||||
 | 
					//  * Linear interpoation.
 | 
				
			||||||
 | 
					//  * Raised cosine.  Square root of cosine is also mentioned.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	new	      -		    /		   --
 | 
				
			||||||
 | 
					//		      |		   /		  /
 | 
				
			||||||
 | 
					//		      |		  /		  |
 | 
				
			||||||
 | 
					//		      |		 /		  /
 | 
				
			||||||
 | 
					//	old	-------		/		--
 | 
				
			||||||
 | 
					//		step		linear		raised cosine
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Inputs are the old (previous value), new value, and a blending control
 | 
				
			||||||
 | 
					// 0 -> take old value
 | 
				
			||||||
 | 
					// 1 -> take new value.
 | 
				
			||||||
 | 
					// inbetween some sort of weighted average.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline float interpol8 (float oldv, float newv, float bc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Step function.
 | 
				
			||||||
 | 
						//return (newv);				// 78 on 11/7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert (bc >= 0);
 | 
				
			||||||
 | 
						assert (bc <= 1.1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (bc < 0) return (oldv);
 | 
				
			||||||
 | 
						if (bc > 1) return (newv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Linear interpolation, just for comparison.
 | 
				
			||||||
 | 
						//return (bc * newv + (1.0f - bc) * oldv);	// 39 on 11/7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float rc = 0.5f * (cosf(bc * M_PI - M_PI) + 1.0f);
 | 
				
			||||||
 | 
						float rrc = bc >= 0.5f
 | 
				
			||||||
 | 
									? 0.5f * (sqrtf(fabsf(cosf(bc * M_PI - M_PI))) + 1.0f)
 | 
				
			||||||
 | 
									: 0.5f * (-sqrtf(fabsf(cosf(bc * M_PI - M_PI))) + 1.0f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						(void)rrc;
 | 
				
			||||||
 | 
						return (rc * newv + (1.0f - bc) * oldv);	// 49 on 11/7
 | 
				
			||||||
 | 
						//return (rrc * newv + (1.0f - bc) * oldv);	// 55 on 11/7
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const int gray2phase_v26[4] = {0, 1, 3, 2};
 | 
					static const int gray2phase_v26[4] = {0, 1, 3, 2};
 | 
				
			||||||
static const int gray2phase_v27[8] = {1, 0, 2, 3, 6, 7, 5, 4};
 | 
					static const int gray2phase_v27[8] = {1, 0, 2, 3, 6, 7, 5, 4};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// #define PSKIQ 1  // not ready for prime time yet.
 | 
				
			||||||
 | 
					#if PSKIQ
 | 
				
			||||||
 | 
					static int xmit_octant[MAX_CHANS];	// absolute phase in 45 degree units.
 | 
				
			||||||
 | 
					static int xmit_prev_octant[MAX_CHANS];	// from previous symbol.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// For PSK, we generate the final signal by combining fixed frequency cosine and
 | 
				
			||||||
 | 
					// sine by the following weights.
 | 
				
			||||||
 | 
					static const float ci[8] = { 1,	.7071,	0,	-.7071,	-1,	-.7071,	0,	.7071	};
 | 
				
			||||||
 | 
					static const float sq[8] = { 0,	.7071,	1,	.7071,	0,	-.7071,	-1,	-.7071	};
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void tone_gen_put_bit (int chan, int dat)
 | 
					void tone_gen_put_bit (int chan, int dat)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -324,14 +396,28 @@ void tone_gen_put_bit (int chan, int dat)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  // All zero bits should give us steady 1800 Hz.
 | 
						  // All zero bits should give us steady 1800 Hz.
 | 
				
			||||||
	  // All one bits should flip phase by 180 degrees each time.
 | 
						  // All one bits should flip phase by 180 degrees each time.
 | 
				
			||||||
 | 
						  // For V.26B, add another 45 degrees.
 | 
				
			||||||
 | 
						  // This seems to work a little better.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  dibit = (save_bit[chan] << 1) | dat;
 | 
						  dibit = (save_bit[chan] << 1) | dat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  symbol = gray2phase_v26[dibit];
 | 
						  symbol = gray2phase_v26[dibit];	// 0 .. 3 for QPSK.
 | 
				
			||||||
 | 
					#if PSKIQ
 | 
				
			||||||
 | 
						  // One phase shift unit is 45 degrees.
 | 
				
			||||||
 | 
						  // Remember what it was last time and calculate new.
 | 
				
			||||||
 | 
						  // values 0 .. 7.
 | 
				
			||||||
 | 
						  xmit_prev_octant[chan] = xmit_octant[chan];
 | 
				
			||||||
 | 
						  xmit_octant[chan] += symbol * 2;
 | 
				
			||||||
 | 
						  if (save_audio_config_p->achan[chan].v26_alternative == V26_B) {
 | 
				
			||||||
 | 
						    xmit_octant[chan] += 1;
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
						  xmit_octant[chan] &= 0x7;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
	  tone_phase[chan] += symbol * PHASE_SHIFT_90;
 | 
						  tone_phase[chan] += symbol * PHASE_SHIFT_90;
 | 
				
			||||||
	  if (save_audio_config_p->achan[chan].v26_alternative == V26_B) {
 | 
						  if (save_audio_config_p->achan[chan].v26_alternative == V26_B) {
 | 
				
			||||||
	    tone_phase[chan] += PHASE_SHIFT_45;
 | 
						    tone_phase[chan] += PHASE_SHIFT_45;
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
	  bit_count[chan]++;
 | 
						  bit_count[chan]++;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -370,7 +456,9 @@ void tone_gen_put_bit (int chan, int dat)
 | 
				
			||||||
	  lfsr[chan] = (lfsr[chan] << 1) | (x & 1);
 | 
						  lfsr[chan] = (lfsr[chan] << 1) | (x & 1);
 | 
				
			||||||
	  dat = x;
 | 
						  dat = x;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	  
 | 
					#if PSKIQ
 | 
				
			||||||
 | 
						int blend = 1;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
	do {		/* until enough audio samples for this symbol. */
 | 
						do {		/* until enough audio samples for this symbol. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  int sam;
 | 
						  int sam;
 | 
				
			||||||
| 
						 | 
					@ -395,9 +483,58 @@ void tone_gen_put_bit (int chan, int dat)
 | 
				
			||||||
	      gen_tone_put_sample (chan, a, sam);
 | 
						      gen_tone_put_sample (chan, a, sam);
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case MODEM_QPSK:
 | 
						    case MODEM_EAS:
 | 
				
			||||||
	    case MODEM_8PSK:
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						      tone_phase[chan] += dat ? f1_change_per_sample[chan] : f2_change_per_sample[chan];
 | 
				
			||||||
 | 
					              sam = sine_table[(tone_phase[chan] >> 24) & 0xff];
 | 
				
			||||||
 | 
						      gen_tone_put_sample (chan, a, sam);
 | 
				
			||||||
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    case MODEM_QPSK:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if DEBUG2
 | 
				
			||||||
 | 
						      text_color_set(DW_COLOR_DEBUG);
 | 
				
			||||||
 | 
						      dw_printf ("tone_gen_put_bit %d PSK\n", __LINE__);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						      tone_phase[chan] += f1_change_per_sample[chan];
 | 
				
			||||||
 | 
					#if PSKIQ
 | 
				
			||||||
 | 
					#if 1  // blend JWL
 | 
				
			||||||
 | 
						      // remove loop invariant
 | 
				
			||||||
 | 
						      float old_i = ci[xmit_prev_octant[chan]];
 | 
				
			||||||
 | 
						      float old_q = sq[xmit_prev_octant[chan]];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						      float new_i = ci[xmit_octant[chan]];
 | 
				
			||||||
 | 
						      float new_q = sq[xmit_octant[chan]];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						      float b = blend / samples_per_symbol[chan];	// roughly 0 to 1
 | 
				
			||||||
 | 
						      blend++;
 | 
				
			||||||
 | 
						     // b = (b - 0.5) * 20 + 0.5;
 | 
				
			||||||
 | 
						     // if (b < 0) b = 0;
 | 
				
			||||||
 | 
						     // if (b > 1) b = 1;
 | 
				
			||||||
 | 
							// b = b > 0.5;
 | 
				
			||||||
 | 
							//b = 1;		// 78 decoded with this.
 | 
				
			||||||
 | 
										// only 39 without.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						      //float blended_i = new_i * b + old_i * (1.0f - b);
 | 
				
			||||||
 | 
						      //float blended_q = new_q * b + old_q * (1.0f - b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						      float blended_i = interpol8 (old_i, new_i, b);
 | 
				
			||||||
 | 
						      float blended_q = interpol8 (old_q, new_q, b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						      sam = blended_i * sine_table[((tone_phase[chan] - PHASE_SHIFT_90) >> 24) & 0xff] +
 | 
				
			||||||
 | 
						            blended_q * sine_table[(tone_phase[chan] >> 24) & 0xff];
 | 
				
			||||||
 | 
					#else  // jump
 | 
				
			||||||
 | 
						      sam = ci[xmit_octant[chan]] * sine_table[((tone_phase[chan] - PHASE_SHIFT_90) >> 24) & 0xff] +
 | 
				
			||||||
 | 
						            sq[xmit_octant[chan]] * sine_table[(tone_phase[chan] >> 24) & 0xff];
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					              sam = sine_table[(tone_phase[chan] >> 24) & 0xff];
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						      gen_tone_put_sample (chan, a, sam);
 | 
				
			||||||
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    case MODEM_8PSK:
 | 
				
			||||||
#if DEBUG2
 | 
					#if DEBUG2
 | 
				
			||||||
	      text_color_set(DW_COLOR_DEBUG);
 | 
						      text_color_set(DW_COLOR_DEBUG);
 | 
				
			||||||
	      dw_printf ("tone_gen_put_bit %d PSK\n", __LINE__);
 | 
						      dw_printf ("tone_gen_put_bit %d PSK\n", __LINE__);
 | 
				
			||||||
| 
						 | 
					@ -521,6 +658,20 @@ void gen_tone_put_sample (int chan, int a, int sam) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void gen_tone_put_quiet_ms (int chan, int time_ms) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int a = ACHAN2ADEV(chan);	/* device for channel. */
 | 
				
			||||||
 | 
						int sam = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int nsamples = (int) ((time_ms * (float)save_audio_config_p->adev[a].samples_per_sec / 1000.) + 0.5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (int j=0; j<nsamples; j++)  {
 | 
				
			||||||
 | 
						  gen_tone_put_sample (chan, a, sam);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Avoid abrupt change when it starts up again.
 | 
				
			||||||
 | 
						tone_phase[chan] = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*-------------------------------------------------------------------
 | 
					/*-------------------------------------------------------------------
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,3 +15,5 @@ int gen_tone_init (struct audio_s *pp, int amp, int gen_packets);
 | 
				
			||||||
void tone_gen_put_bit (int chan, int dat);
 | 
					void tone_gen_put_bit (int chan, int dat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void gen_tone_put_sample (int chan, int a, int sam);
 | 
					void gen_tone_put_sample (int chan, int a, int sam);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void gen_tone_put_quiet_ms (int chan, int time_ms);
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@
 | 
				
			||||||
#include "ax25_pad.h"	/* for packet_t, alevel_t */
 | 
					#include "ax25_pad.h"	/* for packet_t, alevel_t */
 | 
				
			||||||
#include "rrbb.h"
 | 
					#include "rrbb.h"
 | 
				
			||||||
#include "audio.h"		/* for struct audio_s */
 | 
					#include "audio.h"		/* for struct audio_s */
 | 
				
			||||||
 | 
					#include "dlq.h"		// for fec_type_t definition.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -62,6 +63,6 @@ int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, int slice,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Provided by the top level application to process a complete frame. */
 | 
					/* Provided by the top level application to process a complete frame. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t level, int is_fx25, retry_t retries, char *spectrum);
 | 
					void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t level, fec_type_t fec_type, retry_t retries, char *spectrum);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -295,4 +295,82 @@ static void send_bit_nrzi (int chan, int b)
 | 
				
			||||||
	number_of_bits_sent[chan]++;
 | 
						number_of_bits_sent[chan]++;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//  The rest of this is for EAS SAME.
 | 
				
			||||||
 | 
					//  This is sort of a logical place because it serializes a frame, but not in HDLC.
 | 
				
			||||||
 | 
					//  We have a parallel where SAME deserialization is in hdlc_rec.
 | 
				
			||||||
 | 
					//  Maybe both should be pulled out and moved to a same.c.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*-------------------------------------------------------------------
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Name:        eas_send
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Purpose:    	Serialize EAS SAME for transmission.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Inputs:	chan	- Radio channel number.
 | 
				
			||||||
 | 
					 *		str	- Character string to send.
 | 
				
			||||||
 | 
					 *		repeat	- Number of times to repeat with 1 sec quiet between.
 | 
				
			||||||
 | 
					 *		txdelay	- Delay (ms) from PTT to first preamble bit.
 | 
				
			||||||
 | 
					 *		txtail	- Delay (ms) from last data bit to PTT off.	
 | 
				
			||||||
 | 
					 *		
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns:	Total number of milliseconds to activate PTT.
 | 
				
			||||||
 | 
					 *		This includes delays before the first character
 | 
				
			||||||
 | 
					 *		and after the last to avoid chopping off part of it.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Description:	xmit_thread calls this instead of the usual hdlc_send
 | 
				
			||||||
 | 
					 *		when we have a special packet that means send EAS SAME
 | 
				
			||||||
 | 
					 *		code.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *--------------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void eas_put_byte (int chan, unsigned char b)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for (int n=0; n<8; n++) {
 | 
				
			||||||
 | 
						  tone_gen_put_bit (chan, (b & 1));
 | 
				
			||||||
 | 
						  b >>= 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int eas_send (int chan, unsigned char *str, int repeat, int txdelay, int txtail)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int bytes_sent = 0;
 | 
				
			||||||
 | 
						const int gap = 1000;
 | 
				
			||||||
 | 
						int gaps_sent = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gen_tone_put_quiet_ms (chan, txdelay);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (int r=0; r<repeat; r++ ) {
 | 
				
			||||||
 | 
						  for (int j=0; j<16; j++) {
 | 
				
			||||||
 | 
						    eas_put_byte (chan, 0xAB);
 | 
				
			||||||
 | 
						    bytes_sent++;
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  for (unsigned char *p = str; *p != '\0'; p++) {
 | 
				
			||||||
 | 
						    eas_put_byte (chan, *p);
 | 
				
			||||||
 | 
						    bytes_sent++;
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  if (r < repeat-1) {
 | 
				
			||||||
 | 
						    gen_tone_put_quiet_ms (chan, gap);
 | 
				
			||||||
 | 
						    gaps_sent++;
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gen_tone_put_quiet_ms (chan, txtail);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						audio_flush(ACHAN2ADEV(chan));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int elapsed = txdelay + (int) (bytes_sent * 8 * 1.92) + (gaps_sent * gap) + txtail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// dw_printf ("DEBUG:  EAS total time = %d ms\n", elapsed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (elapsed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  /* end eas_send */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* end hdlc_send.c */
 | 
					/* end hdlc_send.c */
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,10 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// In version 1.7 an extra layer of abstraction was added here.
 | 
					// In version 1.7 an extra layer of abstraction was added here.
 | 
				
			||||||
// Rather than calling hdlc_send_frame, we now use another function
 | 
					// Rather than calling hdlc_send_frame, we now use another function
 | 
				
			||||||
// which sends AX.25, FX.25, or IL2P depending on
 | 
					// which sends AX.25, FX.25, or IL2P depending on mode.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// eas_send fits here logically because it also serializes a packet.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "ax25_pad.h"
 | 
					#include "ax25_pad.h"
 | 
				
			||||||
#include "audio.h"
 | 
					#include "audio.h"
 | 
				
			||||||
| 
						 | 
					@ -12,6 +15,8 @@ int layer2_send_frame (int chan, packet_t pp, int bad_fcs, struct audio_s *audio
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int layer2_preamble_postamble (int chan, int flags, int finish, struct audio_s *audio_config_p);
 | 
					int layer2_preamble_postamble (int chan, int flags, int finish, struct audio_s *audio_config_p);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int eas_send (int chan, unsigned char *str, int repeat, int txdelay, int txtail);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* end hdlc_send.h */
 | 
					/* end hdlc_send.h */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										50
									
								
								src/igate.c
								
								
								
								
							
							
						
						
									
										50
									
								
								src/igate.c
								
								
								
								
							| 
						 | 
					@ -1564,9 +1564,9 @@ static void * igate_recv_thread (void *arg)
 | 
				
			||||||
					// See what happens with -2 and follow up on this.
 | 
										// See what happens with -2 and follow up on this.
 | 
				
			||||||
					// Do we need something else here?
 | 
										// Do we need something else here?
 | 
				
			||||||
		int slice = 0;
 | 
							int slice = 0;
 | 
				
			||||||
	        int is_fx25 = 0;
 | 
						        fec_type_t fec_type = fec_type_none;
 | 
				
			||||||
	        char spectrum[] = "APRS-IS";
 | 
						        char spectrum[] = "APRS-IS";
 | 
				
			||||||
	        dlq_rec_frame (ichan, subchan, slice, pp3, alevel, is_fx25, RETRY_NONE, spectrum);
 | 
						        dlq_rec_frame (ichan, subchan, slice, pp3, alevel, fec_type, RETRY_NONE, spectrum);
 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
	      else {
 | 
						      else {
 | 
				
			||||||
	        text_color_set(DW_COLOR_ERROR);
 | 
						        text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
| 
						 | 
					@ -1749,9 +1749,33 @@ static void * satgate_delay_thread (void *arg)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *--------------------------------------------------------------------*/
 | 
					 *--------------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: Use of "message" here is confusing because that term already
 | 
					
 | 
				
			||||||
// has a special meaning for APRS.  This could be an APRS message or
 | 
					// It is unforunate that the : data type indicator (DTI) was overloaded with
 | 
				
			||||||
// some other APRS data type.  Payload is already used.
 | 
					// so many different meanings.  Simply looking at the DTI is not adequate for
 | 
				
			||||||
 | 
					// determining whether a packet is a message.
 | 
				
			||||||
 | 
					// We need to exclude the other special cases of telemetry metadata,
 | 
				
			||||||
 | 
					// bulletins, and weather bulletins.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int is_message_message (char *infop)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (*infop != ':') return (0);
 | 
				
			||||||
 | 
						if (strlen(infop) < 11) return (0);	// too short for : addressee :
 | 
				
			||||||
 | 
						if (strlen(infop) >= 16) {
 | 
				
			||||||
 | 
						  if (strncmp(infop+10, ":PARM.", 6) == 0) return (0);
 | 
				
			||||||
 | 
						  if (strncmp(infop+10, ":UNIT.", 6) == 0) return (0);
 | 
				
			||||||
 | 
						  if (strncmp(infop+10, ":EQNS.", 6) == 0) return (0);
 | 
				
			||||||
 | 
						  if (strncmp(infop+10, ":BITS.", 6) == 0) return (0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (strlen(infop) >= 4) {
 | 
				
			||||||
 | 
						  if (strncmp(infop+1, "BLN", 3) == 0) return (0);
 | 
				
			||||||
 | 
						  if (strncmp(infop+1, "NWS", 3) == 0) return (0);
 | 
				
			||||||
 | 
						  if (strncmp(infop+1, "SKY", 3) == 0) return (0);
 | 
				
			||||||
 | 
						  if (strncmp(infop+1, "CWA", 3) == 0) return (0);
 | 
				
			||||||
 | 
						  if (strncmp(infop+1, "BOM", 3) == 0) return (0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return (1);		// message, including ack, rej
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void maybe_xmit_packet_from_igate (char *message, int to_chan)
 | 
					static void maybe_xmit_packet_from_igate (char *message, int to_chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -1800,9 +1824,6 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan)
 | 
				
			||||||
	    *gt = '\0';
 | 
						    *gt = '\0';
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// FIXME NO!
 | 
					 | 
				
			||||||
	///////ax25_get_addr_with_ssid (pp3, AX25_SOURCE, src);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Drop if path contains:
 | 
					 * Drop if path contains:
 | 
				
			||||||
 *	NOGATE or RFONLY - means IGate should not pass them.
 | 
					 *	NOGATE or RFONLY - means IGate should not pass them.
 | 
				
			||||||
| 
						 | 
					@ -1843,7 +1864,7 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan)
 | 
				
			||||||
 * If we recently transmitted a 'message' from some station,
 | 
					 * If we recently transmitted a 'message' from some station,
 | 
				
			||||||
 * send the position of the message sender when it comes along later.
 | 
					 * send the position of the message sender when it comes along later.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Some refer to this as a courtesy posit report but I don't
 | 
					 * Some refer to this as a "courtesy posit report" but I don't
 | 
				
			||||||
 * think that is an official term.
 | 
					 * think that is an official term.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * If we have a position report, look up the sender and see if we should
 | 
					 * If we have a position report, look up the sender and see if we should
 | 
				
			||||||
| 
						 | 
					@ -1988,10 +2009,7 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan)
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	    stats_rf_xmit_packets++;		// Any type of packet.
 | 
						    stats_rf_xmit_packets++;		// Any type of packet.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    // TEMP TEST: metadata temporarily allowed during testing.
 | 
						    if (is_message_message(pinfo)) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
	    if (*pinfo == ':' && ! is_telem_metadata(pinfo)) {
 | 
					 | 
				
			||||||
	    // temp test // if (*pinfo == ':') {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// We transmitted a "message."  Telemetry metadata is excluded.
 | 
					// We transmitted a "message."  Telemetry metadata is excluded.
 | 
				
			||||||
// Remember to pass along address of the sender later.
 | 
					// Remember to pass along address of the sender later.
 | 
				
			||||||
| 
						 | 
					@ -2449,6 +2467,8 @@ void ig_to_tx_remember (packet_t pp, int chan, int bydigi)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int ig_to_tx_allow (packet_t pp, int chan)
 | 
					static int ig_to_tx_allow (packet_t pp, int chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned short crc = ax25_dedupe_crc(pp);
 | 
						unsigned short crc = ax25_dedupe_crc(pp);
 | 
				
			||||||
| 
						 | 
					@ -2481,7 +2501,7 @@ static int ig_to_tx_allow (packet_t pp, int chan)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    /* We have a duplicate within some time period. */
 | 
						    /* We have a duplicate within some time period. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    if (*pinfo == ':' && ! is_telem_metadata((char*)pinfo)) {
 | 
						    if (is_message_message((char*)pinfo)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      /* I think I want to avoid the duplicate suppression for "messages." */
 | 
						      /* I think I want to avoid the duplicate suppression for "messages." */
 | 
				
			||||||
	      /* Suppose we transmit a message from station X and it doesn't get an ack back. */
 | 
						      /* Suppose we transmit a message from station X and it doesn't get an ack back. */
 | 
				
			||||||
| 
						 | 
					@ -2530,7 +2550,7 @@ static int ig_to_tx_allow (packet_t pp, int chan)
 | 
				
			||||||
	/* the normal limit for them. */
 | 
						/* the normal limit for them. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	increase_limit = 1;
 | 
						increase_limit = 1;
 | 
				
			||||||
	if (*pinfo == ':' && ! is_telem_metadata((char*)pinfo)) {
 | 
						if (is_message_message((char*)pinfo)) {
 | 
				
			||||||
	  increase_limit = 3;
 | 
						  increase_limit = 3;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -943,7 +943,7 @@ void tone_gen_put_bit (int chan, int data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This is called when a complete frame has been deserialized.
 | 
					// This is called when a complete frame has been deserialized.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, int is_fx25)
 | 
					void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, fec_type_t fec_type)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (rec_count < 0) return;	// Skip check before serdes test.
 | 
						if (rec_count < 0) return;	// Skip check before serdes test.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,7 +40,8 @@
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		The first byte of the frame contains:
 | 
					 *		The first byte of the frame contains:
 | 
				
			||||||
 *	
 | 
					 *	
 | 
				
			||||||
 *			* port number (radio channel) in upper nybble.
 | 
					 *			* radio channel in upper nybble.
 | 
				
			||||||
 | 
					 *				(KISS doc uses "port" but I don't like that because it has too many meanings.)
 | 
				
			||||||
 *			* command in lower nybble.
 | 
					 *			* command in lower nybble.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *	
 | 
					 *	
 | 
				
			||||||
| 
						 | 
					@ -954,7 +955,7 @@ void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int
 | 
				
			||||||
	  p = pmsg;
 | 
						  p = pmsg;
 | 
				
			||||||
	  if (*p == FEND) p++;
 | 
						  if (*p == FEND) p++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  dw_printf ("%s %s %s KISS client application, port %d, total length = %d\n",
 | 
						  dw_printf ("%s %s %s KISS client application, channel %d, total length = %d\n",
 | 
				
			||||||
			prefix[(int)fromto], function[p[0] & 0xf], direction[(int)fromto], 
 | 
								prefix[(int)fromto], function[p[0] & 0xf], direction[(int)fromto], 
 | 
				
			||||||
			(p[0] >> 4) & 0xf, msg_len);
 | 
								(p[0] >> 4) & 0xf, msg_len);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -224,7 +224,7 @@ void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	now = time(NULL);			// Get current time.
 | 
						now = time(NULL);			// Get current time.
 | 
				
			||||||
	(void)gmtime_r (&now, &tm);	
 | 
						(void)gmtime_r (&now, &tm);	
 | 
				
			||||||
 | 
					// FIXME:  https://github.com/wb2osz/direwolf/issues/473
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (g_daily_names) {
 | 
						if (g_daily_names) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										50
									
								
								src/mheard.c
								
								
								
								
							
							
						
						
									
										50
									
								
								src/mheard.c
								
								
								
								
							| 
						 | 
					@ -336,6 +336,52 @@ void mheard_save_rf (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, r
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hops = ax25_get_heard(pp) - AX25_SOURCE;
 | 
						hops = ax25_get_heard(pp) - AX25_SOURCE;
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *		Consider the following scenario:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		(1) We hear AA1PR-9 by a path of 4 digipeaters.
 | 
				
			||||||
 | 
					 *		    Looking closer, it's probably only two because there are left over WIDE1-0 and WIDE2-0.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *			Digipeater WIDE2 (probably N3LLO-3) audio level = 72(19/15)   [NONE]   _|||||___
 | 
				
			||||||
 | 
					 *			[0.3] AA1PR-9>APY300,K1EQX-7,WIDE1,N3LLO-3,WIDE2*,ARISS::ANSRVR   :cq hotg vt aprsthursday{01<0x0d>
 | 
				
			||||||
 | 
					 *			                             -----         -----
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		(2) APRS-IS sends a response to us.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *			[ig>tx] ANSRVR>APWW11,KJ4ERJ-15*,TCPIP*,qAS,KJ4ERJ-15::AA1PR-9  :N:HOTG 161 Messages Sent{JL}
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		(3) Here is our analysis of whether it should be sent to RF.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *			Was message addressee AA1PR-9 heard in the past 180 minutes, with 2 or fewer digipeater hops?
 | 
				
			||||||
 | 
					 *			No, AA1PR-9 was last heard over the radio with 4 digipeater hops 0 minutes ago.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		The wrong hop count caused us to drop a packet that should have been transmitted.
 | 
				
			||||||
 | 
					 *		We could put in a hack to not count the "WIDEn-0"  addresses.
 | 
				
			||||||
 | 
					 *		That is not correct because other prefixes could be used and we don't know
 | 
				
			||||||
 | 
					 *		what they are for other digipeaters.
 | 
				
			||||||
 | 
					 *		I think the best solution is to simply ignore the hop count.
 | 
				
			||||||
 | 
					 *		Maybe next release will have a major cleanup.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// HACK - Reduce hop count by number of used WIDEn-0 addresses.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (hops > 1) {
 | 
				
			||||||
 | 
						  for (int k = 0; k < ax25_get_num_repeaters(pp); k++) {
 | 
				
			||||||
 | 
						    char digi[AX25_MAX_ADDR_LEN];
 | 
				
			||||||
 | 
						    ax25_get_addr_no_ssid (pp, AX25_REPEATER_1 + k, digi);
 | 
				
			||||||
 | 
						    int ssid = ax25_get_ssid (pp, AX25_REPEATER_1 + k);
 | 
				
			||||||
 | 
						    int used = ax25_get_h (pp, AX25_REPEATER_1 + k);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    //text_color_set(DW_COLOR_DEBUG);
 | 
				
			||||||
 | 
						    //dw_printf ("Examining %s-%d  used=%d.\n", digi, ssid, used);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    if (used && strlen(digi) == 5 && strncmp(digi, "WIDE", 4) == 0 && isdigit(digi[4]) && ssid == 0) {
 | 
				
			||||||
 | 
						      hops--;
 | 
				
			||||||
 | 
						      //text_color_set(DW_COLOR_DEBUG);
 | 
				
			||||||
 | 
						      //dw_printf ("Decrease hop count to %d for problematic %s.\n", hops, digi);
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	mptr = mheard_ptr(source);
 | 
						mptr = mheard_ptr(source);
 | 
				
			||||||
	if (mptr == NULL) {
 | 
						if (mptr == NULL) {
 | 
				
			||||||
| 
						 | 
					@ -571,7 +617,7 @@ void mheard_save_is (char *ptext)
 | 
				
			||||||
 *					8 for RF_CNT.
 | 
					 *					8 for RF_CNT.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		time_limit	- Include only stations heard within this many minutes.
 | 
					 *		time_limit	- Include only stations heard within this many minutes.
 | 
				
			||||||
 *				  Typically 30 or 60.
 | 
					 *				  Typically 180.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Returns:	Number to be used in the statistics report.
 | 
					 * Returns:	Number to be used in the statistics report.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -649,7 +695,7 @@ int mheard_count (int max_hops, int time_limit)
 | 
				
			||||||
 *		callsign	- Callsign for station.
 | 
					 *		callsign	- Callsign for station.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		time_limit	- Include only stations heard within this many minutes.
 | 
					 *		time_limit	- Include only stations heard within this many minutes.
 | 
				
			||||||
 *				  Typically 30 or 60.
 | 
					 *				  Typically 180.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		max_hops	- Include only stations heard with this number of
 | 
					 *		max_hops	- Include only stations heard with this number of
 | 
				
			||||||
 *				  digipeater hops or less.  For reporting, we might use:
 | 
					 *				  digipeater hops or less.  For reporting, we might use:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,7 +116,8 @@ static struct audio_s          *save_audio_config_p;
 | 
				
			||||||
static struct {
 | 
					static struct {
 | 
				
			||||||
	packet_t packet_p;
 | 
						packet_t packet_p;
 | 
				
			||||||
	alevel_t alevel;
 | 
						alevel_t alevel;
 | 
				
			||||||
	int is_fx25;		// 1 for FX.25, 0 for regular AX.25.
 | 
						float speed_error;
 | 
				
			||||||
 | 
						fec_type_t fec_type;	// Type of FEC: none(0), fx25, il2p
 | 
				
			||||||
	retry_t retries;	// For the old "fix bits" strategy, this is the
 | 
						retry_t retries;	// For the old "fix bits" strategy, this is the
 | 
				
			||||||
				// number of bits that were modified to get a good CRC.
 | 
									// number of bits that were modified to get a good CRC.
 | 
				
			||||||
				// It would be 0 to something around 4.
 | 
									// It would be 0 to something around 4.
 | 
				
			||||||
| 
						 | 
					@ -306,14 +307,14 @@ void multi_modem_process_sample (int chan, int audio_sample)
 | 
				
			||||||
 *				 display of audio level line.
 | 
					 *				 display of audio level line.
 | 
				
			||||||
 *				 Use -2 to indicate DTMF message.)
 | 
					 *				 Use -2 to indicate DTMF message.)
 | 
				
			||||||
 *		retries	- Level of correction used.
 | 
					 *		retries	- Level of correction used.
 | 
				
			||||||
 *		is_fx25	- 1 for FX.25, 0 for normal AX.25.
 | 
					 *		fec_type	- none(0), fx25, il2p
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Description:	Add to list of candidates.  Best one will be picked later.
 | 
					 * Description:	Add to list of candidates.  Best one will be picked later.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *--------------------------------------------------------------------*/
 | 
					 *--------------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries, int is_fx25)
 | 
					void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries, fec_type_t fec_type)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	packet_t pp;
 | 
						packet_t pp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -346,12 +347,12 @@ void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned c
 | 
				
			||||||
	  pp = ax25_from_frame (fbuf, flen, alevel);
 | 
						  pp = ax25_from_frame (fbuf, flen, alevel);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: Eliminate function above and move code elsewhere?
 | 
					// TODO: Eliminate function above and move code elsewhere?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, int is_fx25)
 | 
					void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, fec_type_t fec_type)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (pp == NULL) {
 | 
						if (pp == NULL) {
 | 
				
			||||||
	  text_color_set(DW_COLOR_ERROR);
 | 
						  text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
| 
						 | 
					@ -386,7 +387,7 @@ void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t
 | 
				
			||||||
	    ax25_delete (pp);
 | 
						    ax25_delete (pp);
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	  else {
 | 
						  else {
 | 
				
			||||||
	    dlq_rec_frame (chan, subchan, slice, pp, alevel, is_fx25, retries, "");
 | 
						    dlq_rec_frame (chan, subchan, slice, pp, alevel, fec_type, retries, "");
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	  return;
 | 
						  return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -406,7 +407,7 @@ void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	candidate[chan][subchan][slice].packet_p = pp;
 | 
						candidate[chan][subchan][slice].packet_p = pp;
 | 
				
			||||||
	candidate[chan][subchan][slice].alevel = alevel;
 | 
						candidate[chan][subchan][slice].alevel = alevel;
 | 
				
			||||||
	candidate[chan][subchan][slice].is_fx25 = is_fx25;
 | 
						candidate[chan][subchan][slice].fec_type = fec_type;
 | 
				
			||||||
	candidate[chan][subchan][slice].retries = retries;
 | 
						candidate[chan][subchan][slice].retries = retries;
 | 
				
			||||||
	candidate[chan][subchan][slice].age = 0;
 | 
						candidate[chan][subchan][slice].age = 0;
 | 
				
			||||||
	candidate[chan][subchan][slice].crc = ax25_m_m_crc(pp);
 | 
						candidate[chan][subchan][slice].crc = ax25_m_m_crc(pp);
 | 
				
			||||||
| 
						 | 
					@ -443,6 +444,9 @@ static void pick_best_candidate (int chan)
 | 
				
			||||||
	int best_n, best_score;
 | 
						int best_n, best_score;
 | 
				
			||||||
	char spectrum[MAX_SUBCHANS*MAX_SLICERS+1];
 | 
						char spectrum[MAX_SUBCHANS*MAX_SLICERS+1];
 | 
				
			||||||
	int n, j, k;
 | 
						int n, j, k;
 | 
				
			||||||
 | 
						if (save_audio_config_p->achan[chan].num_slicers < 1) {
 | 
				
			||||||
 | 
						  save_audio_config_p->achan[chan].num_slicers = 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	int num_bars = save_audio_config_p->achan[chan].num_slicers * save_audio_config_p->achan[chan].num_subchan;
 | 
						int num_bars = save_audio_config_p->achan[chan].num_slicers * save_audio_config_p->achan[chan].num_subchan;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memset (spectrum, 0, sizeof(spectrum));
 | 
						memset (spectrum, 0, sizeof(spectrum));
 | 
				
			||||||
| 
						 | 
					@ -456,7 +460,7 @@ static void pick_best_candidate (int chan)
 | 
				
			||||||
	  if (candidate[chan][j][k].packet_p == NULL) {
 | 
						  if (candidate[chan][j][k].packet_p == NULL) {
 | 
				
			||||||
	    spectrum[n] = '_';
 | 
						    spectrum[n] = '_';
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	  else if (candidate[chan][j][k].is_fx25) {
 | 
						  else if (candidate[chan][j][k].fec_type != fec_type_none) {		// FX.25 or IL2P
 | 
				
			||||||
	    // FIXME: using retries both as an enum and later int too.
 | 
						    // FIXME: using retries both as an enum and later int too.
 | 
				
			||||||
	    if ((int)(candidate[chan][j][k].retries) <= 9) {
 | 
						    if ((int)(candidate[chan][j][k].retries) <= 9) {
 | 
				
			||||||
	      spectrum[n] = '0' + candidate[chan][j][k].retries;
 | 
						      spectrum[n] = '0' + candidate[chan][j][k].retries;
 | 
				
			||||||
| 
						 | 
					@ -464,7 +468,7 @@ static void pick_best_candidate (int chan)
 | 
				
			||||||
	    else {
 | 
						    else {
 | 
				
			||||||
	      spectrum[n] = '+';
 | 
						      spectrum[n] = '+';
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	  }
 | 
						  }									// AX.25 below
 | 
				
			||||||
	  else if (candidate[chan][j][k].retries == RETRY_NONE) {
 | 
						  else if (candidate[chan][j][k].retries == RETRY_NONE) {
 | 
				
			||||||
	    spectrum[n] = '|';
 | 
						    spectrum[n] = '|';
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
| 
						 | 
					@ -481,8 +485,8 @@ static void pick_best_candidate (int chan)
 | 
				
			||||||
	    candidate[chan][j][k].score = 0;
 | 
						    candidate[chan][j][k].score = 0;
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	  else {
 | 
						  else {
 | 
				
			||||||
	    if (candidate[chan][j][k].is_fx25) {
 | 
						    if (candidate[chan][j][k].fec_type != fec_type_none) {
 | 
				
			||||||
	      candidate[chan][j][k].score = 9000 - 100 * candidate[chan][j][k].retries;
 | 
						      candidate[chan][j][k].score = 9000 - 100 * candidate[chan][j][k].retries;		// has FEC
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    else {
 | 
						    else {
 | 
				
			||||||
	      /* Originally, this produced 0 for the PASSALL case. */
 | 
						      /* Originally, this produced 0 for the PASSALL case. */
 | 
				
			||||||
| 
						 | 
					@ -550,9 +554,9 @@ static void pick_best_candidate (int chan)
 | 
				
			||||||
		candidate[chan][j][k].packet_p);
 | 
							candidate[chan][j][k].packet_p);
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	  else {
 | 
						  else {
 | 
				
			||||||
	    dw_printf ("%d.%d.%d: ptr=%p, is_fx25=%d, retry=%d, age=%3d, crc=%04x, score=%d  %s\n", chan, j, k,
 | 
						    dw_printf ("%d.%d.%d: ptr=%p, fec_type=%d, retry=%d, age=%3d, crc=%04x, score=%d  %s\n", chan, j, k,
 | 
				
			||||||
		candidate[chan][j][k].packet_p,
 | 
							candidate[chan][j][k].packet_p,
 | 
				
			||||||
		candidate[chan][j][k].is_fx25,
 | 
							(int)(candidate[chan][j][k].fec_type),
 | 
				
			||||||
		(int)(candidate[chan][j][k].retries),
 | 
							(int)(candidate[chan][j][k].retries),
 | 
				
			||||||
		candidate[chan][j][k].age,
 | 
							candidate[chan][j][k].age,
 | 
				
			||||||
		candidate[chan][j][k].crc,
 | 
							candidate[chan][j][k].crc,
 | 
				
			||||||
| 
						 | 
					@ -611,7 +615,7 @@ static void pick_best_candidate (int chan)
 | 
				
			||||||
	  dlq_rec_frame (chan, j, k,
 | 
						  dlq_rec_frame (chan, j, k,
 | 
				
			||||||
		candidate[chan][j][k].packet_p,
 | 
							candidate[chan][j][k].packet_p,
 | 
				
			||||||
		candidate[chan][j][k].alevel,
 | 
							candidate[chan][j][k].alevel,
 | 
				
			||||||
		candidate[chan][j][k].is_fx25,
 | 
							candidate[chan][j][k].fec_type,
 | 
				
			||||||
		(int)(candidate[chan][j][k].retries),
 | 
							(int)(candidate[chan][j][k].retries),
 | 
				
			||||||
		spectrum);
 | 
							spectrum);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,8 +17,8 @@ void multi_modem_process_sample (int c, int audio_sample);
 | 
				
			||||||
int multi_modem_get_dc_average (int chan);
 | 
					int multi_modem_get_dc_average (int chan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Deprecated.  Replace with ...packet
 | 
					// Deprecated.  Replace with ...packet
 | 
				
			||||||
void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries, int is_fx25);
 | 
					void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries, fec_type_t fec_type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, int is_fx25);
 | 
					void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, fec_type_t fec_type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										257
									
								
								src/pfilter.c
								
								
								
								
							
							
						
						
									
										257
									
								
								src/pfilter.c
								
								
								
								
							| 
						 | 
					@ -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) 2015, 2016  John Langner, WB2OSZ
 | 
					//    Copyright (C) 2015, 2016, 2023  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
 | 
				
			||||||
| 
						 | 
					@ -546,7 +546,8 @@ static int parse_filter_spec (pfstate_t *pf)
 | 
				
			||||||
/* b - budlist */
 | 
					/* b - budlist */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	else if (pf->token_str[0] == 'b' && ispunct(pf->token_str[1])) {
 | 
						else if (pf->token_str[0] == 'b' && ispunct(pf->token_str[1])) {
 | 
				
			||||||
	  /* Budlist - source address */
 | 
						  /* Budlist - AX.25 source address */
 | 
				
			||||||
 | 
						  /* Could be different than source encapsulated by 3rd party header. */
 | 
				
			||||||
	  char addr[AX25_MAX_ADDR_LEN];
 | 
						  char addr[AX25_MAX_ADDR_LEN];
 | 
				
			||||||
	  ax25_get_addr_with_ssid (pf->pp, AX25_SOURCE, addr);
 | 
						  ax25_get_addr_with_ssid (pf->pp, AX25_SOURCE, addr);
 | 
				
			||||||
	  result = filt_bodgu (pf, addr);
 | 
						  result = filt_bodgu (pf, addr);
 | 
				
			||||||
| 
						 | 
					@ -572,7 +573,7 @@ static int parse_filter_spec (pfstate_t *pf)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	else if (pf->token_str[0] == 'd' && ispunct(pf->token_str[1])) {
 | 
						else if (pf->token_str[0] == 'd' && ispunct(pf->token_str[1])) {
 | 
				
			||||||
	  int n;
 | 
						  int n;
 | 
				
			||||||
	  // loop on all digipeaters
 | 
						  // Loop on all AX.25 digipeaters.
 | 
				
			||||||
	  result = 0;
 | 
						  result = 0;
 | 
				
			||||||
	  for (n = AX25_REPEATER_1; result == 0 && n < ax25_get_num_addr (pf->pp); n++) {
 | 
						  for (n = AX25_REPEATER_1; result == 0 && n < ax25_get_num_addr (pf->pp); n++) {
 | 
				
			||||||
	    // Consider only those with the H (has-been-used) bit set.
 | 
						    // Consider only those with the H (has-been-used) bit set.
 | 
				
			||||||
| 
						 | 
					@ -599,7 +600,7 @@ static int parse_filter_spec (pfstate_t *pf)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	else if (pf->token_str[0] == 'v' && ispunct(pf->token_str[1])) {
 | 
						else if (pf->token_str[0] == 'v' && ispunct(pf->token_str[1])) {
 | 
				
			||||||
	  int n;
 | 
						  int n;
 | 
				
			||||||
	  // loop on all digipeaters (mnemonic Via)
 | 
						  // loop on all AX.25 digipeaters (mnemonic Via)
 | 
				
			||||||
	  result = 0;
 | 
						  result = 0;
 | 
				
			||||||
	  for (n = AX25_REPEATER_1; result == 0 && n < ax25_get_num_addr (pf->pp); n++) {
 | 
						  for (n = AX25_REPEATER_1; result == 0 && n < ax25_get_num_addr (pf->pp); n++) {
 | 
				
			||||||
	    // This is different than the previous "d" filter.
 | 
						    // This is different than the previous "d" filter.
 | 
				
			||||||
| 
						 | 
					@ -623,10 +624,15 @@ static int parse_filter_spec (pfstate_t *pf)
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* g - Addressee of message. */
 | 
					/* g - Addressee of message. e.g. "BLN*" for bulletins. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	else if (pf->token_str[0] == 'g' && ispunct(pf->token_str[1])) {
 | 
						else if (pf->token_str[0] == 'g' && ispunct(pf->token_str[1])) {
 | 
				
			||||||
	  if (ax25_get_dti(pf->pp) == ':') {
 | 
						  if (pf->decoded.g_message_subtype == message_subtype_message ||
 | 
				
			||||||
 | 
						      pf->decoded.g_message_subtype == message_subtype_ack ||
 | 
				
			||||||
 | 
						      pf->decoded.g_message_subtype == message_subtype_rej ||
 | 
				
			||||||
 | 
						      pf->decoded.g_message_subtype == message_subtype_bulletin ||
 | 
				
			||||||
 | 
						      pf->decoded.g_message_subtype == message_subtype_nws ||
 | 
				
			||||||
 | 
						      pf->decoded.g_message_subtype == message_subtype_directed_query) {
 | 
				
			||||||
	    result = filt_bodgu (pf, pf->decoded.g_addressee);
 | 
						    result = filt_bodgu (pf, pf->decoded.g_addressee);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    if (s_debug >= 2) {
 | 
						    if (s_debug >= 2) {
 | 
				
			||||||
| 
						 | 
					@ -643,7 +649,7 @@ static int parse_filter_spec (pfstate_t *pf)
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* u - unproto (destination) */
 | 
					/* u - unproto (AX.25 destination) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	else if (pf->token_str[0] == 'u' && ispunct(pf->token_str[1])) {
 | 
						else if (pf->token_str[0] == 'u' && ispunct(pf->token_str[1])) {
 | 
				
			||||||
	  /* Probably want to exclude mic-e types */
 | 
						  /* Probably want to exclude mic-e types */
 | 
				
			||||||
| 
						 | 
					@ -668,7 +674,7 @@ static int parse_filter_spec (pfstate_t *pf)
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* t - type: position, weather, etc. */
 | 
					/* t - packet type: position, weather, telemetry, etc. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	else if (pf->token_str[0] == 't' && ispunct(pf->token_str[1])) {
 | 
						else if (pf->token_str[0] == 't' && ispunct(pf->token_str[1])) {
 | 
				
			||||||
	  
 | 
						  
 | 
				
			||||||
| 
						 | 
					@ -728,7 +734,7 @@ static int parse_filter_spec (pfstate_t *pf)
 | 
				
			||||||
	    (void) ax25_get_info (pf->pp, (unsigned char **)(&infop));
 | 
						    (void) ax25_get_info (pf->pp, (unsigned char **)(&infop));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    text_color_set(DW_COLOR_DEBUG);
 | 
						    text_color_set(DW_COLOR_DEBUG);
 | 
				
			||||||
	    if (*infop == ':' && ! is_telem_metadata(infop)) {
 | 
						    if (pf->decoded.g_packet_type == packet_type_message) {
 | 
				
			||||||
	      dw_printf ("   %s returns %s for message to %s\n", pf->token_str, bool2text(result), pf->decoded.g_addressee);
 | 
						      dw_printf ("   %s returns %s for message to %s\n", pf->token_str, bool2text(result), pf->decoded.g_addressee);
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	    else {
 | 
						    else {
 | 
				
			||||||
| 
						 | 
					@ -832,31 +838,17 @@ static int filt_bodgu (pfstate_t *pf, char *arg)
 | 
				
			||||||
 *		 0 = no
 | 
					 *		 0 = no
 | 
				
			||||||
 *		-1 = error detected
 | 
					 *		-1 = error detected
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Description:	The filter is based the type filtering described here:
 | 
					 * Description:	The filter is loosely based the type filtering described here:
 | 
				
			||||||
 *		http://www.aprs-is.net/javAPRSFilter.aspx
 | 
					 *		http://www.aprs-is.net/javAPRSFilter.aspx
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		Most of these simply check the first byte of the information part.
 | 
					 *		Mostly use g_packet_type and g_message_subtype from decode_aprs.
 | 
				
			||||||
 *		Trying to detect NWS information is a little trickier.
 | 
					 *
 | 
				
			||||||
 | 
					 * References:
 | 
				
			||||||
 *		http://www.aprs-is.net/WX/
 | 
					 *		http://www.aprs-is.net/WX/
 | 
				
			||||||
 *		http://wxsvr.aprs.net.au/protocol-new.html	
 | 
					 *		http://wxsvr.aprs.net.au/protocol-new.html	(has disappeared)
 | 
				
			||||||
 *		
 | 
					 *		
 | 
				
			||||||
 *------------------------------------------------------------------------------*/
 | 
					 *------------------------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Telemetry metadata is a special case of message. */
 | 
					 | 
				
			||||||
/* We want to categorize it as telemetry rather than message. */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int is_telem_metadata (char *infop)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (*infop != ':') return (0);
 | 
					 | 
				
			||||||
	if (strlen(infop) < 16) return (0);
 | 
					 | 
				
			||||||
	if (strncmp(infop+10, ":PARM.", 6) == 0) return (1);
 | 
					 | 
				
			||||||
	if (strncmp(infop+10, ":UNIT.", 6) == 0) return (1);
 | 
					 | 
				
			||||||
	if (strncmp(infop+10, ":EQNS.", 6) == 0) return (1);
 | 
					 | 
				
			||||||
	if (strncmp(infop+10, ":BITS.", 6) == 0) return (1);
 | 
					 | 
				
			||||||
	return (0);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int filt_t (pfstate_t *pf) 
 | 
					static int filt_t (pfstate_t *pf) 
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char src[AX25_MAX_ADDR_LEN];
 | 
						char src[AX25_MAX_ADDR_LEN];
 | 
				
			||||||
| 
						 | 
					@ -873,108 +865,60 @@ static int filt_t (pfstate_t *pf)
 | 
				
			||||||
	  switch (*f) {
 | 
						  switch (*f) {
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	    case 'p':				/* Position */
 | 
						    case 'p':				/* Position */
 | 
				
			||||||
	      if (*infop == '!') return (1);
 | 
						      if (pf->decoded.g_packet_type == packet_type_position) return(1);
 | 
				
			||||||
	      if (*infop == '/') return (1);
 | 
					 | 
				
			||||||
	      if (*infop == '=') return (1);
 | 
					 | 
				
			||||||
	      if (*infop == '@') return (1);
 | 
					 | 
				
			||||||
	      if (*infop == '\'') return (1);	// MIC-E
 | 
					 | 
				
			||||||
	      if (*infop == '`') return (1);	// MIC-E
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	      // What if we have "_" symbol code for weather?
 | 
					 | 
				
			||||||
	      // Still consider as position.
 | 
					 | 
				
			||||||
	      // The same packet can match more than one type here.
 | 
					 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case 'o':				/* Object */
 | 
						    case 'o':				/* Object */
 | 
				
			||||||
	      if (*infop == ';') return (1);
 | 
						      if (pf->decoded.g_packet_type == packet_type_object) return(1);
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case 'i':				/* Item */
 | 
						    case 'i':				/* Item */
 | 
				
			||||||
	      if (*infop == ')') return (1);
 | 
						      if (pf->decoded.g_packet_type == packet_type_item) return(1);
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case 'm':				/* Message */
 | 
						    case 'm':				// Any "message."
 | 
				
			||||||
	      if (*infop == ':' && ! is_telem_metadata(infop)) return (1);
 | 
						      if (pf->decoded.g_packet_type == packet_type_message) return(1);
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case 'q':				/* Query */
 | 
						    case 'q':				/* Query */
 | 
				
			||||||
	      if (*infop == '?') return (1);
 | 
						      if (pf->decoded.g_packet_type == packet_type_query) return(1);
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case 'c':				/* station Capabilities - my extension */
 | 
						    case 'c':				/* station Capabilities - my extension */
 | 
				
			||||||
						/* Most often used for IGate statistics. */
 | 
											/* Most often used for IGate statistics. */
 | 
				
			||||||
	      if (*infop == '<') return (1);
 | 
						      if (pf->decoded.g_packet_type == packet_type_capabilities) return(1);
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case 's':				/* Status */
 | 
						    case 's':				/* Status */
 | 
				
			||||||
	      if (*infop == '>') return (1);
 | 
						      if (pf->decoded.g_packet_type == packet_type_status) return(1);
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case 't':				/* Telemetry */
 | 
						    case 't':				/* Telemetry data or metadata */
 | 
				
			||||||
	      if (*infop == 'T') return (1);
 | 
						      if (pf->decoded.g_packet_type == packet_type_telemetry) return(1);
 | 
				
			||||||
	      if (is_telem_metadata(infop)) return (1);
 | 
					 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case 'u':				/* User-defined */
 | 
						    case 'u':				/* User-defined */
 | 
				
			||||||
	      if (*infop == '{') return (1);
 | 
						      if (pf->decoded.g_packet_type == packet_type_userdefined) return(1);
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case 'h':				/* third party Header - my extension */
 | 
						    case 'h':				/* has third party Header - my extension */
 | 
				
			||||||
	      if (*infop == '}') return (1);
 | 
						      if (pf->decoded.g_has_thirdparty_header) return (1);
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case 'w':				/* Weather */
 | 
						    case 'w':				/* Weather */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      if (*infop == '*') return (1);			// Peet Bros
 | 
						      if (pf->decoded.g_packet_type == packet_type_weather) return(1);
 | 
				
			||||||
	      if (*infop == '_') return (1);			// Weather report, no position.
 | 
					 | 
				
			||||||
	      if (strncmp(infop, "!!", 2) == 0) return(1);	// Ultimeter 2000.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	      /* '$' is normally raw GPS. Check for special case. */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	      if (strncmp(infop, "$ULTW", 5) == 0) return (1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	      /* Positions !=/@ with symbol code _ are weather. */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	      if (strchr("!=/@", *infop) != NULL &&
 | 
					 | 
				
			||||||
			pf->decoded.g_symbol_code == '_') return (1);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						      /* Positions !=/@  with symbol code _ are weather. */
 | 
				
			||||||
	      /* Object with _ symbol is also weather.  APRS protocol spec page 66. */
 | 
						      /* Object with _ symbol is also weather.  APRS protocol spec page 66. */
 | 
				
			||||||
 | 
						      // Can't use *infop because it would not work with 3rd party header.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      if (*infop == ';' &&
 | 
						      if ((pf->decoded.g_packet_type == packet_type_position ||
 | 
				
			||||||
			pf->decoded.g_symbol_code == '_') return (1);
 | 
						           pf->decoded.g_packet_type == packet_type_object) && pf->decoded.g_symbol_code == '_') return (1);
 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO: need more test cases at end for new weather cases.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case 'n':				/* NWS format */
 | 
						    case 'n':				/* NWS format */
 | 
				
			||||||
/*
 | 
						      if (pf->decoded.g_packet_type == packet_type_nws) return(1);
 | 
				
			||||||
 * This is the interesting case.
 | 
					 | 
				
			||||||
 * The source must be exactly 6 upper case letters, no SSID.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
	      if (strlen(src) != 6) break;
 | 
					 | 
				
			||||||
	      if (! isupper(src[0])) break;
 | 
					 | 
				
			||||||
	      if (! isupper(src[1])) break;
 | 
					 | 
				
			||||||
	      if (! isupper(src[2])) break;
 | 
					 | 
				
			||||||
	      if (! isupper(src[3])) break;
 | 
					 | 
				
			||||||
	      if (! isupper(src[4])) break;
 | 
					 | 
				
			||||||
	      if (! isupper(src[5])) break;
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * We can have a "message" with addressee starting with NWS, SKY, or BOM (Australian version.)
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
	      if (strncmp(infop, ":NWS", 4) == 0) return (1);
 | 
					 | 
				
			||||||
	      if (strncmp(infop, ":SKY", 4) == 0) return (1);
 | 
					 | 
				
			||||||
	      if (strncmp(infop, ":BOM", 4) == 0) return (1);
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * Or we can have an object.
 | 
					 | 
				
			||||||
 * It's not exactly clear how to distinguish this from other objects.
 | 
					 | 
				
			||||||
 * It looks like the first 3 characters of the source should be the same
 | 
					 | 
				
			||||||
 * as the first 3 characters of the addressee.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
	      if (infop[0] == ';' &&
 | 
					 | 
				
			||||||
		  infop[1] == src[0] &&
 | 
					 | 
				
			||||||
		  infop[2] == src[1] &&
 | 
					 | 
				
			||||||
		  infop[3] == src[2]) return (1);
 | 
					 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    default:
 | 
						    default:
 | 
				
			||||||
| 
						 | 
					@ -990,6 +934,7 @@ static int filt_t (pfstate_t *pf)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*------------------------------------------------------------------------------
 | 
					/*------------------------------------------------------------------------------
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Name:	filt_r
 | 
					 * Name:	filt_r
 | 
				
			||||||
| 
						 | 
					@ -1327,8 +1272,41 @@ static int filt_s (pfstate_t *pf)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		IMHO, the rules here are too restrictive.
 | 
					 *		IMHO, the rules here are too restrictive.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *		(1) The APRS-IS would send a "message" to my IGate only if the addressee
 | 
					 *		    The APRS-IS would send a "message" to my IGate only if the addressee
 | 
				
			||||||
 *		    has been heard nearby recently.  180 minutes, I believe.
 | 
					 *		    has been heard nearby recently.  180 minutes, I believe.
 | 
				
			||||||
 | 
					 *		    Why would I not want to transmit it?
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Discussion:	In retrospect, I think this is far too complicated.
 | 
				
			||||||
 | 
					 *		In a future release, I think at options other than time should be removed.
 | 
				
			||||||
 | 
					 *		Messages have more value than most packets.  Why reduce the chance of successful delivery?
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		Consider the following scenario:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		(1) We hear AA1PR-9 by a path of 4 digipeaters.
 | 
				
			||||||
 | 
					 *		    Looking closer, it's probably only two because there are left over WIDE1-0 and WIDE2-0.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *			Digipeater WIDE2 (probably N3LLO-3) audio level = 72(19/15)   [NONE]   _|||||___
 | 
				
			||||||
 | 
					 *			[0.3] AA1PR-9>APY300,K1EQX-7,WIDE1,N3LLO-3,WIDE2*,ARISS::ANSRVR   :cq hotg vt aprsthursday{01<0x0d>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		(2) APRS-IS sends a response to us.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *			[ig>tx] ANSRVR>APWW11,KJ4ERJ-15*,TCPIP*,qAS,KJ4ERJ-15::AA1PR-9  :N:HOTG 161 Messages Sent{JL}
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		(3) Here is our analysis of whether it should be sent to RF.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *			Was message addressee AA1PR-9 heard in the past 180 minutes, with 2 or fewer digipeater hops?
 | 
				
			||||||
 | 
					 *			No, AA1PR-9 was last heard over the radio with 4 digipeater hops 0 minutes ago.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		The wrong hop count caused us to drop a packet that should have been transmitted.
 | 
				
			||||||
 | 
					 *		We could put in a hack to not count the "WIDE*-0"  addresses.
 | 
				
			||||||
 | 
					 *		That is not correct because other prefixes could be used and we don't know
 | 
				
			||||||
 | 
					 *		what they are for other digipeaters.
 | 
				
			||||||
 | 
					 *		I think the best solution is to simply ignore the hop count.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Release 1.7:	I got overly ambitious and now realize this is just giving people too much
 | 
				
			||||||
 | 
					 *		"rope to hang themselves," drop messages unexpectedly, and accidentally break messaging.
 | 
				
			||||||
 | 
					 *		Change documentation to mention only the time limit.
 | 
				
			||||||
 | 
					 *		The other functionality will be undocumented and maybe disappear over time.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *------------------------------------------------------------------------------*/
 | 
					 *------------------------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1357,9 +1335,9 @@ static int filt_i (pfstate_t *pf)
 | 
				
			||||||
	double km = G_UNKNOWN;
 | 
						double km = G_UNKNOWN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	char src[AX25_MAX_ADDR_LEN];
 | 
						//char src[AX25_MAX_ADDR_LEN];
 | 
				
			||||||
	char *infop = NULL;
 | 
						//char *infop = NULL;
 | 
				
			||||||
	int info_len;
 | 
						//int info_len;
 | 
				
			||||||
	//char *f;
 | 
						//char *f;
 | 
				
			||||||
	//char addressee[AX25_MAX_ADDR_LEN];
 | 
						//char addressee[AX25_MAX_ADDR_LEN];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1433,20 +1411,7 @@ static int filt_i (pfstate_t *pf)
 | 
				
			||||||
 * Get source address and info part.
 | 
					 * Get source address and info part.
 | 
				
			||||||
 * Addressee has already been extracted into pf->decoded.g_addressee.
 | 
					 * Addressee has already been extracted into pf->decoded.g_addressee.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
						if (pf->decoded.g_packet_type != packet_type_message) return(0);
 | 
				
			||||||
	memset (src, 0, sizeof(src));
 | 
					 | 
				
			||||||
	ax25_get_addr_with_ssid (pf->pp, AX25_SOURCE, src);
 | 
					 | 
				
			||||||
	info_len = ax25_get_info (pf->pp, (unsigned char **)(&infop));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (infop == NULL) return (0);
 | 
					 | 
				
			||||||
	if (info_len < 1) return (0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Determine packet type.  We are interested only in "message."
 | 
					 | 
				
			||||||
// Telemetry metadata is not considered a message.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (*infop != ':') return (0);
 | 
					 | 
				
			||||||
	if (is_telem_metadata(infop)) return (0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(PFTEST) || defined(DIGITEST)	// TODO: test functionality too, not just syntax.
 | 
					#if defined(PFTEST) || defined(DIGITEST)	// TODO: test functionality too, not just syntax.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1486,7 +1451,7 @@ static int filt_i (pfstate_t *pf)
 | 
				
			||||||
 * the past minute, rather than the usual 180 minutes for the addressee.
 | 
					 * the past minute, rather than the usual 180 minutes for the addressee.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	was_heard = mheard_was_recently_nearby ("source", src, 1, 0, G_UNKNOWN, G_UNKNOWN, G_UNKNOWN);
 | 
						was_heard = mheard_was_recently_nearby ("source", pf->decoded.g_src, 1, 0, G_UNKNOWN, G_UNKNOWN, G_UNKNOWN);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (was_heard) return (0);
 | 
						if (was_heard) return (0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1640,24 +1605,29 @@ int main ()
 | 
				
			||||||
	pftest (112, "t/t", "WM1X>APU25N:@210147z4235.39N/07106.58W_359/000g000t027r000P000p000h89b10234/WX REPORT {UIV32N}<0x0d>", 0);
 | 
						pftest (112, "t/t", "WM1X>APU25N:@210147z4235.39N/07106.58W_359/000g000t027r000P000p000h89b10234/WX REPORT {UIV32N}<0x0d>", 0);
 | 
				
			||||||
	pftest (113, "t/w", "WM1X>APU25N:@210147z4235.39N/07106.58W_359/000g000t027r000P000p000h89b10234/WX REPORT {UIV32N}<0x0d>", 1);
 | 
						pftest (113, "t/w", "WM1X>APU25N:@210147z4235.39N/07106.58W_359/000g000t027r000P000p000h89b10234/WX REPORT {UIV32N}<0x0d>", 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Telemetry metadata is a special case of message. */
 | 
						/* Telemetry metadata should not be classified as message. */
 | 
				
			||||||
	pftest (114, "t/t", "KJ4SNT>APMI04::KJ4SNT   :PARM.Vin,Rx1h,Dg1h,Eff1h,Rx10m,O1,O2,O3,O4,I1,I2,I3,I4", 1);
 | 
						pftest (114, "t/t", "KJ4SNT>APMI04::KJ4SNT   :PARM.Vin,Rx1h,Dg1h,Eff1h,Rx10m,O1,O2,O3,O4,I1,I2,I3,I4", 1);
 | 
				
			||||||
	pftest (115, "t/m", "KJ4SNT>APMI04::KJ4SNT   :PARM.Vin,Rx1h,Dg1h,Eff1h,Rx10m,O1,O2,O3,O4,I1,I2,I3,I4", 0);
 | 
						pftest (115, "t/m", "KJ4SNT>APMI04::KJ4SNT   :PARM.Vin,Rx1h,Dg1h,Eff1h,Rx10m,O1,O2,O3,O4,I1,I2,I3,I4", 0);
 | 
				
			||||||
	pftest (116, "t/t", "KB1GKN-10>APRX27,UNCAN,WIDE1*:T#491,4.9,0.3,25.0,0.0,1.0,00000000", 1);
 | 
						pftest (116, "t/t", "KB1GKN-10>APRX27,UNCAN,WIDE1*:T#491,4.9,0.3,25.0,0.0,1.0,00000000", 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Bulletins should not be considered to be messages.  Was bug in 1.6. */
 | 
				
			||||||
 | 
						pftest (117, "t/m", "A>B::W1AW     :test", 1);
 | 
				
			||||||
 | 
						pftest (118, "t/m", "A>B::BLN      :test", 0);
 | 
				
			||||||
 | 
						pftest (119, "t/m", "A>B::NWS      :test", 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pftest (120, "t/p", "CWAPID>APRS::NWS-TTTTT:DDHHMMz,ADVISETYPE,zcs{seq#", 0);
 | 
						// https://www.aprs-is.net/WX/
 | 
				
			||||||
 | 
						pftest (121, "t/p", "CWAPID>APRS::NWS-TTTTT:DDHHMMz,ADVISETYPE,zcs{seq#", 0);
 | 
				
			||||||
	pftest (122, "t/p", "CWAPID>APRS::SKYCWA   :DDHHMMz,ADVISETYPE,zcs{seq#", 0);
 | 
						pftest (122, "t/p", "CWAPID>APRS::SKYCWA   :DDHHMMz,ADVISETYPE,zcs{seq#", 0);
 | 
				
			||||||
	pftest (123, "t/p", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", 0);
 | 
						pftest (123, "t/p", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", 0);
 | 
				
			||||||
	pftest (124, "t/n", "CWAPID>APRS::NWS-TTTTT:DDHHMMz,ADVISETYPE,zcs{seq#", 1);
 | 
						pftest (124, "t/n", "CWAPID>APRS::NWS-TTTTT:DDHHMMz,ADVISETYPE,zcs{seq#", 1);
 | 
				
			||||||
	pftest (125, "t/n", "CWAPID>APRS::SKYCWA   :DDHHMMz,ADVISETYPE,zcs{seq#", 1);
 | 
						pftest (125, "t/n", "CWAPID>APRS::SKYCWA   :DDHHMMz,ADVISETYPE,zcs{seq#", 1);
 | 
				
			||||||
	pftest (126, "t/n", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", 1);
 | 
						//pftest (126, "t/n", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", 1);
 | 
				
			||||||
	pftest (127, "t/",  "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", 0);
 | 
						pftest (127, "t/",  "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pftest (128, "t/c",  "S0RCE>DEST:<stationcapabilities", 1);
 | 
						pftest (128, "t/c",  "S0RCE>DEST:<stationcapabilities", 1);
 | 
				
			||||||
	pftest (129, "t/h",  "S0RCE>DEST:<stationcapabilities", 0);
 | 
						pftest (129, "t/h",  "S0RCE>DEST:<stationcapabilities", 0);
 | 
				
			||||||
	pftest (130, "t/h",  "S0RCE>DEST:}thirdpartyheaderwhatever", 1);
 | 
						pftest (130, "t/h",  "S0RCE>DEST:}WB2OSZ-5>APDW12,DIGI1,DIGI2*,DIGI3,DIGI4:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1);
 | 
				
			||||||
	pftest (131, "t/c",  "S0RCE>DEST:}thirdpartyheaderwhatever", 0);
 | 
						pftest (131, "t/c",  "S0RCE>DEST:}WB2OSZ-5>APDW12,DIGI1,DIGI2*,DIGI3,DIGI4:!4237.14NS07120.83W#PHG7140Chelmsford MA", 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pftest (140, "r/42.6/-71.3/10", "WB2OSZ-5>APDW12,WIDE1-1,WIDE2-1:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1);
 | 
						pftest (140, "r/42.6/-71.3/10", "WB2OSZ-5>APDW12,WIDE1-1,WIDE2-1:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1);
 | 
				
			||||||
	pftest (141, "r/42.6/-71.3/10", "WA1PLE-5>APWW10,W1MHL,N8VIM,WIDE2*:@022301h4208.75N/07115.16WoAPRS-IS for Win32", 0);
 | 
						pftest (141, "r/42.6/-71.3/10", "WA1PLE-5>APWW10,W1MHL,N8VIM,WIDE2*:@022301h4208.75N/07115.16WoAPRS-IS for Win32", 0);
 | 
				
			||||||
| 
						 | 
					@ -1712,13 +1682,13 @@ int main ()
 | 
				
			||||||
	pftest (203, "t/w t/w", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", -1);
 | 
						pftest (203, "t/w t/w", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", -1);
 | 
				
			||||||
	pftest (204, "r/42.6/-71.3", "WA1PLE-5>APWW10,W1MHL,N8VIM,WIDE2*:@022301h4208.75N/07115.16WoAPRS-IS for Win32", -1);
 | 
						pftest (204, "r/42.6/-71.3", "WA1PLE-5>APWW10,W1MHL,N8VIM,WIDE2*:@022301h4208.75N/07115.16WoAPRS-IS for Win32", -1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pftest (220, "i/30/8/42.6/-71.3/50", "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", 1);
 | 
						pftest (210, "i/30/8/42.6/-71.3/50", "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", 1);
 | 
				
			||||||
	pftest (222, "i/30/8/42.6/-71.3/",   "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", -1);
 | 
						pftest (212, "i/30/8/42.6/-71.3/",   "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", -1);
 | 
				
			||||||
	pftest (223, "i/30/8/42.6/-71.3",    "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", -1);
 | 
						pftest (213, "i/30/8/42.6/-71.3",    "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", -1);
 | 
				
			||||||
	pftest (224, "i/30/8/42.6/",         "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", -1);
 | 
						pftest (214, "i/30/8/42.6/",         "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", -1);
 | 
				
			||||||
	pftest (225, "i/30/8/42.6",          "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", -1);
 | 
						pftest (215, "i/30/8/42.6",          "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", -1);
 | 
				
			||||||
	pftest (226, "i/30/8/",              "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", 1);
 | 
						pftest (216, "i/30/8/",              "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", 1);
 | 
				
			||||||
	pftest (227, "i/30/8",               "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", 1);
 | 
						pftest (217, "i/30/8",               "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// FIXME: behaves differently on Windows and Linux.  Why?
 | 
						// FIXME: behaves differently on Windows and Linux.  Why?
 | 
				
			||||||
	// On Windows we have our own version of strsep because it's not in the MS library.
 | 
						// On Windows we have our own version of strsep because it's not in the MS library.
 | 
				
			||||||
| 
						 | 
					@ -1726,7 +1696,12 @@ int main ()
 | 
				
			||||||
	//pftest (228, "i/30/",                "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", 1);
 | 
						//pftest (228, "i/30/",                "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pftest (229, "i/30",                 "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", 1);
 | 
						pftest (229, "i/30",                 "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", 1);
 | 
				
			||||||
	pftest (230, "i/",                   "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", -1);
 | 
						pftest (230, "i/30",                 "X>X:}WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", 1);
 | 
				
			||||||
 | 
						pftest (231, "i/",                   "WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", -1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Besure bulletins and telemetry metadata don't get included.
 | 
				
			||||||
 | 
						pftest (234, "i/30", "KJ4SNT>APMI04::KJ4SNT   :PARM.Vin,Rx1h,Dg1h,Eff1h,Rx10m,O1,O2,O3,O4,I1,I2,I3,I4", 0);
 | 
				
			||||||
 | 
						pftest (235, "i/30", "A>B::BLN      :test", 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pftest (240, "s/", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
 | 
						pftest (240, "s/", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
 | 
				
			||||||
	pftest (241, "s/'/O/-/#/_", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
 | 
						pftest (241, "s/'/O/-/#/_", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
 | 
				
			||||||
| 
						 | 
					@ -1736,12 +1711,36 @@ int main ()
 | 
				
			||||||
	pftest (245, "s//", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
 | 
						pftest (245, "s//", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
 | 
				
			||||||
	pftest (246, "s///", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
 | 
						pftest (246, "s///", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Third party header - done properly in 1.7.
 | 
				
			||||||
 | 
						// Packet filter t/h is no longer a mutually exclusive packet type.
 | 
				
			||||||
 | 
						// Now it is an independent attribute and the encapsulated part is evaluated.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: to be continued...
 | 
						pftest (250, "o/home", "A>B:}WB2OSZ>APDW12,WIDE1-1,WIDE2-1:;home     *111111z4237.14N/07120.83W-Chelmsford MA", 1);
 | 
				
			||||||
 | 
						pftest (251, "t/p",    "A>B:}W1WRA-7>TRSY3T,WIDE1-1,WIDE2-1:`c-:l!hK\\>\"4b}=<0x0d>", 1);
 | 
				
			||||||
 | 
						pftest (252, "i/180",  "A>B:}WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", 1);
 | 
				
			||||||
 | 
						pftest (253, "t/m",  "A>B:}WB2OSZ-5>APDW14::W2UB     :Happy Birthday{001", 1);
 | 
				
			||||||
 | 
						pftest (254, "r/42.6/-71.3/10", "A>B:}WB2OSZ-5>APDW12,WIDE1-1,WIDE2-1:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1);
 | 
				
			||||||
 | 
						pftest (254, "r/42.6/-71.3/10", "A>B:}WA1PLE-5>APWW10,W1MHL,N8VIM,WIDE2*:@022301h4208.75N/07115.16WoAPRS-IS for Win32", 0);
 | 
				
			||||||
 | 
						pftest (255, "t/h",  "KB1GKN-10>APRX27,UNCAN,WIDE1*:T#491,4.9,0.3,25.0,0.0,1.0,00000000", 0);
 | 
				
			||||||
 | 
						pftest (256, "t/h",  "A>B:}KB1GKN-10>APRX27,UNCAN,WIDE1*:T#491,4.9,0.3,25.0,0.0,1.0,00000000", 1);
 | 
				
			||||||
 | 
						pftest (258, "t/t",  "A>B:}KB1GKN-10>APRX27,UNCAN,WIDE1*:T#491,4.9,0.3,25.0,0.0,1.0,00000000", 1);
 | 
				
			||||||
 | 
						pftest (259, "t/t",  "A>B:}KJ4SNT>APMI04::KJ4SNT   :PARM.Vin,Rx1h,Dg1h,Eff1h,Rx10m,O1,O2,O3,O4,I1,I2,I3,I4", 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pftest (270, "g/BLN*", "WB2OSZ>APDW17::BLN1xxxxx:bulletin text", 1);
 | 
				
			||||||
 | 
						pftest (271, "g/BLN*", "A>B:}WB2OSZ>APDW17::BLN1xxxxx:bulletin text", 1);
 | 
				
			||||||
 | 
						pftest (272, "g/BLN*", "A>B:}WB2OSZ>APDW17::W1AW     :xxxx", 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pftest (273, "g/NWS*", "WB2OSZ>APDW17::NWS-xxxxx:weather bulletin", 1);
 | 
				
			||||||
 | 
						pftest (274, "g/NWS*", "A>B:}WB2OSZ>APDW17::NWS-xxxxx:weather bulletin", 1);
 | 
				
			||||||
 | 
						pftest (275, "g/NWS*", "A>B:}WB2OSZ>APDW17::W1AW     :xxxx", 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: add b/ with 3rd party header.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: to be continued...  directed query ...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (error_count > 0) {
 | 
						if (error_count > 0) {
 | 
				
			||||||
	  text_color_set (DW_COLOR_ERROR);
 | 
						  text_color_set (DW_COLOR_ERROR);
 | 
				
			||||||
	  dw_printf ("\nPacket Filtering Test - FAILED!\n");
 | 
						  dw_printf ("\nPacket Filtering Test - FAILED!     %d errors\n", error_count);
 | 
				
			||||||
	  exit (EXIT_FAILURE);
 | 
						  exit (EXIT_FAILURE);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	text_color_set (DW_COLOR_REC);
 | 
						text_color_set (DW_COLOR_REC);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										35
									
								
								src/ptt.c
								
								
								
								
							
							
						
						
									
										35
									
								
								src/ptt.c
								
								
								
								
							| 
						 | 
					@ -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, 2016, 2017  John Langner, WB2OSZ
 | 
					//    Copyright (C) 2011, 2013, 2014, 2015, 2016, 2017, 2023  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
 | 
				
			||||||
| 
						 | 
					@ -176,6 +176,7 @@ typedef int HANDLE;
 | 
				
			||||||
#include "audio.h"
 | 
					#include "audio.h"
 | 
				
			||||||
#include "ptt.h"
 | 
					#include "ptt.h"
 | 
				
			||||||
#include "dlq.h"
 | 
					#include "dlq.h"
 | 
				
			||||||
 | 
					#include "demod.h"	// to mute recv audio during xmit if half duplex.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if __WIN32__
 | 
					#if __WIN32__
 | 
				
			||||||
| 
						 | 
					@ -990,6 +991,8 @@ void ptt_init (struct audio_s *audio_config_p)
 | 
				
			||||||
	    for (ot = 0; ot < NUM_OCTYPES; ot++) {
 | 
						    for (ot = 0; ot < NUM_OCTYPES; ot++) {
 | 
				
			||||||
	      if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_HAMLIB) {
 | 
						      if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_HAMLIB) {
 | 
				
			||||||
	        if (ot == OCTYPE_PTT) {
 | 
						        if (ot == OCTYPE_PTT) {
 | 
				
			||||||
 | 
							  int err = -1;
 | 
				
			||||||
 | 
							  int tries = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	          /* For "AUTO" model, try to guess what is out there. */
 | 
						          /* For "AUTO" model, try to guess what is out there. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1054,13 +1057,24 @@ void ptt_init (struct audio_s *audio_config_p)
 | 
				
			||||||
	            rig[ch][ot]->state.rigport.parm.serial.parity = RIG_PARITY_NONE;
 | 
						            rig[ch][ot]->state.rigport.parm.serial.parity = RIG_PARITY_NONE;
 | 
				
			||||||
	            rig[ch][ot]->state.rigport.parm.serial.handshake = RIG_HANDSHAKE_NONE;
 | 
						            rig[ch][ot]->state.rigport.parm.serial.handshake = RIG_HANDSHAKE_NONE;
 | 
				
			||||||
	          }
 | 
						          }
 | 
				
			||||||
	          int err = rig_open(rig[ch][ot]);
 | 
							  tries = 0;
 | 
				
			||||||
 | 
							  do {
 | 
				
			||||||
 | 
							    // Try up to 5 times, Hamlib can take a moment to finish init
 | 
				
			||||||
 | 
						            err = rig_open(rig[ch][ot]);
 | 
				
			||||||
 | 
							    if (++tries > 5) {
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							    } else if (err != RIG_OK) {
 | 
				
			||||||
 | 
								text_color_set(DW_COLOR_INFO);
 | 
				
			||||||
 | 
								dw_printf ("Retrying Hamlib Rig open...\n");
 | 
				
			||||||
 | 
								sleep (5);
 | 
				
			||||||
 | 
							    }
 | 
				
			||||||
 | 
							  } while (err != RIG_OK);
 | 
				
			||||||
	          if (err != RIG_OK) {
 | 
						          if (err != RIG_OK) {
 | 
				
			||||||
	            text_color_set(DW_COLOR_ERROR);
 | 
						            text_color_set(DW_COLOR_ERROR);
 | 
				
			||||||
	            dw_printf ("Hamlib Rig open error %d: %s\n", err, rigerror(err));
 | 
						            dw_printf ("Hamlib Rig open error %d: %s\n", err, rigerror(err));
 | 
				
			||||||
	            rig_cleanup (rig[ch][ot]);
 | 
						            rig_cleanup (rig[ch][ot]);
 | 
				
			||||||
	            rig[ch][ot] = NULL;
 | 
						            rig[ch][ot] = NULL;
 | 
				
			||||||
	            continue;
 | 
						            exit (1);
 | 
				
			||||||
	          }
 | 
						          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       		  /* Successful.  Later code should check for rig[ch][ot] not NULL. */
 | 
					       		  /* Successful.  Later code should check for rig[ch][ot] not NULL. */
 | 
				
			||||||
| 
						 | 
					@ -1141,6 +1155,8 @@ void ptt_init (struct audio_s *audio_config_p)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *--------------------------------------------------------------------*/
 | 
					 *--------------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// JWL - save status and new get_ptt function.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ptt_set (int ot, int chan, int ptt_signal)
 | 
					void ptt_set (int ot, int chan, int ptt_signal)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -1164,6 +1180,19 @@ void ptt_set (int ot, int chan, int ptt_signal)
 | 
				
			||||||
	  return;
 | 
						  return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// New in 1.7.
 | 
				
			||||||
 | 
					// A few people have a really bad audio cross talk situation where they receive their own transmissions.
 | 
				
			||||||
 | 
					// It usually doesn't cause a problem but it is confusing to look at.
 | 
				
			||||||
 | 
					// "half duplex" setting applied only to the transmit logic.  i.e. wait for clear channel before sending.
 | 
				
			||||||
 | 
					// Receiving was still active.
 | 
				
			||||||
 | 
					// I think the simplest solution is to mute/unmute the audio input at this point if not full duplex.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef TEST
 | 
				
			||||||
 | 
						if ( ot == OCTYPE_PTT && ! save_audio_config_p->achan[chan].fulldup) {
 | 
				
			||||||
 | 
						  demod_mute_input (chan, ptt_signal);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * The data link state machine has an interest in activity on the radio channel.
 | 
					 * The data link state machine has an interest in activity on the radio channel.
 | 
				
			||||||
 * This is a very convenient place to get that information.
 | 
					 * This is a very convenient place to get that information.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/recv.c
								
								
								
								
							
							
						
						
									
										10
									
								
								src/recv.c
								
								
								
								
							| 
						 | 
					@ -207,7 +207,7 @@ static void * recv_adev_thread (void *arg)
 | 
				
			||||||
	int eof;
 | 
						int eof;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	/* This audio device can have one (mono) or two (stereo) channels. */
 | 
						/* This audio device can have one (mono) or two (stereo) channels. */
 | 
				
			||||||
	/* Find number of the first channel. */
 | 
						/* Find number of the first channel and number of channels. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int first_chan =  ADEVFIRSTCHAN(a); 
 | 
						int first_chan =  ADEVFIRSTCHAN(a); 
 | 
				
			||||||
	int num_chan = save_pa->adev[a].num_channels;
 | 
						int num_chan = save_pa->adev[a].num_channels;
 | 
				
			||||||
| 
						 | 
					@ -234,6 +234,8 @@ static void * recv_adev_thread (void *arg)
 | 
				
			||||||
 	    if (audio_sample >= 256 * 256) 
 | 
					 	    if (audio_sample >= 256 * 256) 
 | 
				
			||||||
	      eof = 1;
 | 
						      eof = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    // Future?  provide more flexible mapping.
 | 
				
			||||||
 | 
						    // i.e. for each valid channel where audio_source[] is first_chan+c.
 | 
				
			||||||
	    multi_modem_process_sample(first_chan + c, audio_sample);
 | 
						    multi_modem_process_sample(first_chan + c, audio_sample);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -262,14 +264,14 @@ static void * recv_adev_thread (void *arg)
 | 
				
			||||||
	        aprs_tt_button (first_chan + c, tt);
 | 
						        aprs_tt_button (first_chan + c, tt);
 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	  }
 | 
						  }  // for c is just 0 or 0 then 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* When a complete frame is accumulated, */
 | 
							/* When a complete frame is accumulated, */
 | 
				
			||||||
		/* dlq_rec_frame, is called. */
 | 
							/* dlq_rec_frame, is called. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* recv_process, below, drains the queue. */
 | 
							/* recv_process, below, drains the queue. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	}
 | 
						}  // while !eof on audio stream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// What should we do now?
 | 
					// What should we do now?
 | 
				
			||||||
// Seimply terminate the application?  
 | 
					// Seimply terminate the application?  
 | 
				
			||||||
| 
						 | 
					@ -337,7 +339,7 @@ void recv_process (void)
 | 
				
			||||||
 *	- Digipeater.
 | 
					 *	- Digipeater.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		  app_process_rec_packet (pitem->chan, pitem->subchan, pitem->slice, pitem->pp, pitem->alevel, pitem->is_fx25, pitem->retries, pitem->spectrum);
 | 
							  app_process_rec_packet (pitem->chan, pitem->subchan, pitem->slice, pitem->pp, pitem->alevel, pitem->fec_type, pitem->retries, pitem->spectrum);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										49
									
								
								src/rrbb.c
								
								
								
								
							
							
						
						
									
										49
									
								
								src/rrbb.c
								
								
								
								
							| 
						 | 
					@ -51,8 +51,8 @@
 | 
				
			||||||
#define MAGIC2 0x56788765
 | 
					#define MAGIC2 0x56788765
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int new_count = 0;
 | 
					volatile static int new_count = 0;
 | 
				
			||||||
static int delete_count = 0;
 | 
					volatile static int delete_count = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/***********************************************************************************
 | 
					/***********************************************************************************
 | 
				
			||||||
| 
						 | 
					@ -425,6 +425,50 @@ alevel_t rrbb_get_audio_level (rrbb_t b)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/***********************************************************************************
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Name:	rrbb_set_speed_error
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Purpose:	Set speed error of the received frame.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Inputs:	b		Handle for bit array.
 | 
				
			||||||
 | 
					 *		speed_error	In percentage.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 ***********************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void rrbb_set_speed_error (rrbb_t b, float speed_error)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						assert (b != NULL);
 | 
				
			||||||
 | 
						assert (b->magic1 == MAGIC1);
 | 
				
			||||||
 | 
						assert (b->magic2 == MAGIC2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b->speed_error = speed_error;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/***********************************************************************************
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Name:	rrbb_get_speed_error
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Purpose:	Get speed error of the received frame.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Inputs:	b	Handle for bit array.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns:	speed error in percentage.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 ***********************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					float rrbb_get_speed_error (rrbb_t b)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						assert (b != NULL);
 | 
				
			||||||
 | 
						assert (b->magic1 == MAGIC1);
 | 
				
			||||||
 | 
						assert (b->magic2 == MAGIC2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (b->speed_error);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/***********************************************************************************
 | 
					/***********************************************************************************
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Name:	rrbb_get_is_scrambled	
 | 
					 * Name:	rrbb_get_is_scrambled	
 | 
				
			||||||
| 
						 | 
					@ -488,6 +532,7 @@ int rrbb_get_prev_descram (rrbb_t b)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* end rrbb.c */
 | 
					/* end rrbb.c */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,6 +33,7 @@ typedef struct rrbb_s {
 | 
				
			||||||
	int slice;		/* Which slicer. */
 | 
						int slice;		/* Which slicer. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	alevel_t alevel;	/* Received audio level at time of frame capture. */
 | 
						alevel_t alevel;	/* Received audio level at time of frame capture. */
 | 
				
			||||||
 | 
						float speed_error;	/* Received data speed error as percentage. */
 | 
				
			||||||
	unsigned int len;	/* Current number of samples in array. */
 | 
						unsigned int len;	/* Current number of samples in array. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int is_scrambled;	/* Is data scrambled G3RUH / K9NG style? */
 | 
						int is_scrambled;	/* Is data scrambled G3RUH / K9NG style? */
 | 
				
			||||||
| 
						 | 
					@ -84,6 +85,9 @@ int rrbb_get_slice (rrbb_t b);
 | 
				
			||||||
void rrbb_set_audio_level (rrbb_t b, alevel_t alevel);
 | 
					void rrbb_set_audio_level (rrbb_t b, alevel_t alevel);
 | 
				
			||||||
alevel_t rrbb_get_audio_level (rrbb_t b);
 | 
					alevel_t rrbb_get_audio_level (rrbb_t b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void rrbb_set_speed_error (rrbb_t b, float speed_error);
 | 
				
			||||||
 | 
					float rrbb_get_speed_error (rrbb_t b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int rrbb_get_is_scrambled (rrbb_t b);
 | 
					int rrbb_get_is_scrambled (rrbb_t b);
 | 
				
			||||||
int rrbb_get_descram_state (rrbb_t b);
 | 
					int rrbb_get_descram_state (rrbb_t b);
 | 
				
			||||||
int rrbb_get_prev_descram (rrbb_t b);
 | 
					int rrbb_get_prev_descram (rrbb_t b);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										23
									
								
								src/server.c
								
								
								
								
							
							
						
						
									
										23
									
								
								src/server.c
								
								
								
								
							| 
						 | 
					@ -1816,6 +1816,11 @@ static THREAD_F cmd_listen_thread (void *arg)
 | 
				
			||||||
	      
 | 
						      
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    case 'P':				/* Application Login  */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						      // Silently ignore it.
 | 
				
			||||||
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    case 'X':				/* Register CallSign  */
 | 
						    case 'X':				/* Register CallSign  */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      {
 | 
						      {
 | 
				
			||||||
| 
						 | 
					@ -1937,8 +1942,10 @@ static THREAD_F cmd_listen_thread (void *arg)
 | 
				
			||||||
	    case 'D': 				/* Send Connected Data */
 | 
						    case 'D': 				/* Send Connected Data */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      {
 | 
						      {
 | 
				
			||||||
	        char callsigns[2][AX25_MAX_ADDR_LEN];
 | 
						        char callsigns[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN];
 | 
				
			||||||
	        const int num_calls = 2;
 | 
						        memset (callsigns, 0, sizeof(callsigns));
 | 
				
			||||||
 | 
						        const int num_calls = 2;	// only first 2 used.  Digipeater path
 | 
				
			||||||
 | 
											// must be remembered from connect request.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	        strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
 | 
						        strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
 | 
				
			||||||
	        strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE]));
 | 
						        strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE]));
 | 
				
			||||||
| 
						 | 
					@ -1951,8 +1958,9 @@ static THREAD_F cmd_listen_thread (void *arg)
 | 
				
			||||||
	    case 'd': 				/* Disconnect, Terminate an AX.25 Connection */
 | 
						    case 'd': 				/* Disconnect, Terminate an AX.25 Connection */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      {
 | 
						      {
 | 
				
			||||||
	        char callsigns[2][AX25_MAX_ADDR_LEN];
 | 
						        char callsigns[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN];
 | 
				
			||||||
	        const int num_calls = 2;
 | 
						        memset (callsigns, 0, sizeof(callsigns));
 | 
				
			||||||
 | 
						        const int num_calls = 2;	// only first 2 used.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	        strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
 | 
						        strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
 | 
				
			||||||
	        strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE]));
 | 
						        strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE]));
 | 
				
			||||||
| 
						 | 
					@ -2102,15 +2110,14 @@ static THREAD_F cmd_listen_thread (void *arg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	      {
 | 
						      {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	        char callsigns[2][AX25_MAX_ADDR_LEN];
 | 
						        char callsigns[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN];
 | 
				
			||||||
	        const int num_calls = 2;
 | 
						        memset (callsigns, 0, sizeof(callsigns));
 | 
				
			||||||
 | 
						        const int num_calls = 2;	// only first 2 used.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	        strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
 | 
						        strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
 | 
				
			||||||
	        strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE]));
 | 
						        strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE]));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	        // Issue 169.  Proper implementation for 'Y'.
 | 
					 | 
				
			||||||
	        dlq_outstanding_frames_request (callsigns, num_calls, cmd.hdr.portx, client);
 | 
						        dlq_outstanding_frames_request (callsigns, num_calls, cmd.hdr.portx, client);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	      }
 | 
						      }
 | 
				
			||||||
	      break;
 | 
						      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
[Unit]
 | 
					[Unit]
 | 
				
			||||||
Description=Direwolf Sound Card-based AX.25 TNC
 | 
					Description=Direwolf Sound Card-based AX.25 TNC
 | 
				
			||||||
After=sound.target
 | 
					After=sound.target
 | 
				
			||||||
 | 
					After=network.target
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Service]
 | 
					[Service]
 | 
				
			||||||
EnvironmentFile=/etc/sysconfig/direwolf
 | 
					EnvironmentFile=/etc/sysconfig/direwolf
 | 
				
			||||||
| 
						 | 
					@ -22,3 +23,5 @@ ReadWritePaths=/var/log/direwolf
 | 
				
			||||||
[Install]
 | 
					[Install]
 | 
				
			||||||
WantedBy=multi-user.target
 | 
					WantedBy=multi-user.target
 | 
				
			||||||
DefaultInstance=1
 | 
					DefaultInstance=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# alternate version:  https://www.f4fxl.org/start-direwolf-at-boot-the-systemd-way/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,7 @@ set(TEST_CHECK-MODEM2400-a_FILE "check-modem2400-a")
 | 
				
			||||||
set(TEST_CHECK-MODEM2400-b_FILE "check-modem2400-b")
 | 
					set(TEST_CHECK-MODEM2400-b_FILE "check-modem2400-b")
 | 
				
			||||||
set(TEST_CHECK-MODEM2400-g_FILE "check-modem2400-g")
 | 
					set(TEST_CHECK-MODEM2400-g_FILE "check-modem2400-g")
 | 
				
			||||||
set(TEST_CHECK-MODEM4800_FILE "check-modem4800")
 | 
					set(TEST_CHECK-MODEM4800_FILE "check-modem4800")
 | 
				
			||||||
 | 
					set(TEST_CHECK-MODEMEAS_FILE "check-modemeas")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# generate the scripts that run the tests
 | 
					# generate the scripts that run the tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -101,6 +102,12 @@ configure_file(
 | 
				
			||||||
  @ONLY
 | 
					  @ONLY
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					configure_file(
 | 
				
			||||||
 | 
					  "${CUSTOM_TEST_SCRIPTS_DIR}/${TEST_CHECK-MODEMEAS_FILE}"
 | 
				
			||||||
 | 
					  "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEMEAS_FILE}${CUSTOM_SCRIPT_SUFFIX}"
 | 
				
			||||||
 | 
					  @ONLY
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# global includes
 | 
					# global includes
 | 
				
			||||||
# not ideal but not so slow
 | 
					# not ideal but not so slow
 | 
				
			||||||
| 
						 | 
					@ -498,6 +505,7 @@ add_test(check-modem2400-a "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM2400-a_F
 | 
				
			||||||
add_test(check-modem2400-b "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM2400-b_FILE}${CUSTOM_SCRIPT_SUFFIX}")
 | 
					add_test(check-modem2400-b "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM2400-b_FILE}${CUSTOM_SCRIPT_SUFFIX}")
 | 
				
			||||||
add_test(check-modem2400-g "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM2400-g_FILE}${CUSTOM_SCRIPT_SUFFIX}")
 | 
					add_test(check-modem2400-g "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM2400-g_FILE}${CUSTOM_SCRIPT_SUFFIX}")
 | 
				
			||||||
add_test(check-modem4800 "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM4800_FILE}${CUSTOM_SCRIPT_SUFFIX}")
 | 
					add_test(check-modem4800 "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEM4800_FILE}${CUSTOM_SCRIPT_SUFFIX}")
 | 
				
			||||||
 | 
					add_test(check-modemeas "${CUSTOM_TEST_BINARY_DIR}/${TEST_CHECK-MODEMEAS_FILE}${CUSTOM_SCRIPT_SUFFIX}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					@CUSTOM_SHELL_SHABANG@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@GEN_PACKETS_BIN@ -B EAS -o testeas.wav
 | 
				
			||||||
 | 
					@ATEST_BIN@ -B EAS -L 6 -G 6 testeas.wav
 | 
				
			||||||
		Loading…
	
		Reference in New Issue