Sync Mac CM108 changes with latest dev branch

This commit is contained in:
Martin Cooper 2024-05-13 09:52:43 -07:00
commit 8ae7de98fc
25 changed files with 2876 additions and 756 deletions

View File

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

View File

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

2
.gitignore vendored
View File

@ -109,5 +109,5 @@ $RECYCLE.BIN/
*.dSYM *.dSYM
# cmake # cmake
build/ build*/
tmp/ tmp/

View File

@ -2,6 +2,12 @@
# Revision History # # Revision History #
## Version 1.8 -- Development Version
### New Features: ###
- [http://www.aprs.org/aprs11/tocalls.txt](http://www.aprs.org/aprs11/tocalls.txt) has been abandoned since the end of 2021. [https://github.com/aprsorg/aprs-deviceid](https://github.com/aprsorg/aprs-deviceid) is now considered to be the authoritative source of truth for the vendor/model encoding.
## Version 1.7 -- October 2023 ## ## Version 1.7 -- October 2023 ##

View File

@ -1,10 +1,10 @@
cmake_minimum_required(VERSION 3.1.0) cmake_minimum_required(VERSION 3.5.0)
project(direwolf) project(direwolf)
# configure version # configure version
set(direwolf_VERSION_MAJOR "1") set(direwolf_VERSION_MAJOR "1")
set(direwolf_VERSION_MINOR "7") set(direwolf_VERSION_MINOR "8")
set(direwolf_VERSION_PATCH "0") set(direwolf_VERSION_PATCH "0")
set(direwolf_VERSION_SUFFIX "Development") set(direwolf_VERSION_SUFFIX "Development")
@ -320,6 +320,14 @@ else()
set(HAMLIB_LIBRARIES "") set(HAMLIB_LIBRARIES "")
endif() endif()
find_package(gpiod)
if(GPIOD_FOUND)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_GPIOD")
else()
set(GPIOD_INCLUDE_DIRS "")
set(GPIOD_LIBRARIES "")
endif()
if(LINUX) if(LINUX)
find_package(ALSA REQUIRED) find_package(ALSA REQUIRED)
if(ALSA_FOUND) if(ALSA_FOUND)

View File

@ -7,10 +7,10 @@ string(REGEX REPLACE "\n" ";" files "${files}")
foreach(file ${files}) foreach(file ${files})
message(STATUS "Uninstalling $ENV{DESTDIR}${file}") message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
exec_program( execute_process(
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" COMMAND "@CMAKE_COMMAND@" -E remove "$ENV{DESTDIR}${file}"
OUTPUT_VARIABLE rm_out OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval RESULT_VARIABLE rm_retval
) )
if(NOT "${rm_retval}" STREQUAL 0) if(NOT "${rm_retval}" STREQUAL 0)
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")

View File

@ -5,9 +5,9 @@ elseif(NOT DEFINED C_GCC AND CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(C_GCC 1) set(C_GCC 1)
elseif(NOT DEFINED C_MSVC AND CMAKE_CXX_COMPILER_ID MATCHES "MSVC") elseif(NOT DEFINED C_MSVC AND CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(C_MSVC 1) set(C_MSVC 1)
if(MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS_EQUAL 1929) if(MSVC_VERSION GREATER 1919 AND MSVC_VERSION LESS 1926)
set(VS2019 ON) set(VS2019 ON)
elseif(MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS_EQUAL 1919) elseif(MSVC_VERSION GREATER 1910 AND MSVC_VERSION LESS 1919)
set(VS2017 ON) set(VS2017 ON)
elseif(MSVC_VERSION GREATER 1899 AND MSVC_VERSION LESS 1910) elseif(MSVC_VERSION GREATER 1899 AND MSVC_VERSION LESS 1910)
set(VS2015 ON) set(VS2015 ON)

View File

@ -0,0 +1,23 @@
# - Try to find libgpiod
# Once done this will define
# GPIOD_FOUND - System has libgpiod
# GPIOD_INCLUDE_DIRS - The libgpiod include directories
# GPIOD_LIBRARIES - The libraries needed to use libgpiod
# GPIOD_DEFINITIONS - Compiler switches required for using libgpiod
find_package(PkgConfig)
pkg_check_modules(PC_GPIOD QUIET gpiod)
find_path(GPIOD_INCLUDE_DIR gpiod.h)
find_library(GPIOD_LIBRARY NAMES gpiod)
include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set GPIOD_FOUND to TRUE
# if all listed variables are TRUE
find_package_handle_standard_args(gpiod DEFAULT_MSG
GPIOD_LIBRARY GPIOD_INCLUDE_DIR)
mark_as_advanced(GPIOD_INCLUDE_DIR GPIOD_LIBRARY)
set(GPIOD_LIBRARIES ${GPIOD_LIBRARY})
set(GPIOD_INCLUDE_DIRS ${GPIOD_INCLUDE_DIR})

View File

@ -16,7 +16,7 @@
# #
# 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.yaml at application start up time.
# #
# 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.
@ -25,17 +25,17 @@
include(ExternalProject) include(ExternalProject)
set(TOCALLS_TXT "tocalls.txt") set(TOCALLS_YAML "tocalls.yaml")
set(SYMBOLS-NEW_TXT "symbols-new.txt") set(SYMBOLS-NEW_TXT "symbols-new.txt")
set(SYMBOLSX_TXT "symbolsX.txt") set(SYMBOLSX_TXT "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)
# see conf/install_conf.cmake as example # see conf/install_conf.cmake as example
file(COPY "${CUSTOM_DATA_DIR}/${TOCALLS_TXT}" DESTINATION "${CUSTOM_BINARY_DATA_DIR}") file(COPY "${CUSTOM_DATA_DIR}/${TOCALLS_YAML}" DESTINATION "${CUSTOM_BINARY_DATA_DIR}")
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}")
install(FILES "${CUSTOM_BINARY_DATA_DIR}/${TOCALLS_TXT}" DESTINATION ${INSTALL_DATA_DIR}) install(FILES "${CUSTOM_BINARY_DATA_DIR}/${TOCALLS_YAML}" 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})

18
data/README.txt Normal file
View File

@ -0,0 +1,18 @@
tocalls.yaml contains the encoding for the device/system/software
identifier which created the packet.
Knowing what generated the packet is very useful for troubleshooting.
TNCs, digipeaters, and IGates must not change this.
For MIC-E format, well... it's complicated.
See Understanding-APRS-Packets.pdf. Too long to repeat here.
For all other packet types, the AX.25 destination, or "tocall" field
contains a code for what generated the packet.
This is of the form AP????. For example, APDW18 for direwolf 1.8.
The database of identifiers is currently maintained by Hessu, OH7LZB.
You can update your local copy by running:
wget https://raw.githubusercontent.com/aprsorg/aprs-deviceid/main/tocalls.yaml

View File

@ -1,326 +0,0 @@
<title>
APRS TO-CALL VERSION NUMBERS 14 Dec 2021
---------------------------------------------------------------------
WB4APR
</title>
<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
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
16 Sep 21 Added APY05D for Yaesu FT5D series
04 Sep 21 APLOxx LoRa KISS TNC/Tracker https://github.com/SQ9MDD/TTGO-T-Beam-LoRa-APRS
24 Aug 21 Added APLSxx SARIMESH http://www.sarimesh.net
22 Aug 21 Added APE2Ax for VA3NNW's Email-2-APRS ap
30 Jun 21 Added APCNxx for carNET by DG5OAW
14 Jun 21 Added APN2xx for NOSaprs JNOS 2.0 - VE4KLM
24 Apr 21 Added APMPAD for DF1JSL's WXBot clone and extension
20 Apr 21 Added APLCxx for APRScube by DL3DCW
19 Apr 21 Added APVMxx for DRCC-DVM Voice (Digital Radio China Club)
13 Apr 21 Added APIxxx for all Dstar ICOMS (APRS via DPRS)
23 MAr 20 Added APW9xx For 9A9Y Weather Tracker
16 Feb 21 Added API970 for I com 9700
2020 Added APHBLx,APIZCI,APLGxx,APLTxx,APNVxx,APY300,APESPG,APESPW
APGDTx,APOSWx,APOSBx,APBT62,APCLUB,APMQxx
2019 Added APTPNx,APJ8xx,APBSDx,APNKMX,APAT51,APMGxx,APTCMA,
APATxx,APQTHx,APLIGx
2018 added APRARX,APELKx,APGBLN,APBKxx,APERSx,APTCHE
2017 Added APHWxx,APDVxx,APPICO,APBMxx,APP6xx,APTAxx,APOCSG,APCSMS,
APPMxx,APOFF,APDTMF,APRSON,APDIGI,APSAT,APTBxx,APIExx,
APSFxx
2016 added APYSxx,APINxx,APNICx,APTKPT,APK004,APFPRS,APCDS0,APDNOx
2015 Added APSTPO,APAND1,APDRxx,APZ247,APHTxx,APMTxx,APZMAJ
APB2MF,APR2MF,APAVT5
</version_notes>
<description>
In APRS, the AX.25 Destination address is not used for packet
routing as is normally done in AX.25. So APRS uses it for two
things. The initial APxxxx is used as a group identifier to make
APRS packets instanantly recognizable on shared channels. Most
applicaitons ignore all non APRS packets. The remaining 4 xxxx
bytes of the field are available to indicate the software version
number or application. The following applications have requested
a TOCALL number series:
Authors with similar alphabetic requirements are encouraged to share
their address space with other software. Work out agreements amongst
yourselves and keep me informed.
</description>
<tocalls>
APn 3rd digit is a number
AP1WWX TAPR T-238+ WX station
AP1MAJ Martyn M1MAJ DeLorme inReach Tracker
AP4Rxy APRS4R software interface
APnnnD Painter Engineering uSmartDigi D-Gate DSTAR Gateway
APnnnU Painter Engineering uSmartDigi Digipeater
APA APAFxx AFilter.
APAGxx AGATE
APAGWx SV2AGW's AGWtracker
APALxx Alinco DR-620/635 internal TNC digis. "Hachi" ,JF1AJE
APAXxx AFilterX.
APAHxx AHub
APAND1 APRSdroid (pre-release) http://aprsdroid.org/
APAMxx Altus Metrum GPS trackers
APATAR ATA-R APRS Digipeater by TA7W/OH2UDS and TA6AEU
APAT8x for Anytone. 81 for 878 HT
APAT51 for Anytone AT-D578UV APRS mobile radio
APAVT5 SainSonic AP510 which is a 1watt tracker
APAWxx AGWPE
APB APBxxx Beacons or Rabbit TCPIP micros?
APB2MF DL2MF - MF2APRS Radiosonde for balloons
APBLxx BigRedBee BeeLine
APBLO MOdel Rocketry K7RKT
APBKxx PY5BK Bravo Tracker in Brazil
APBPQx John G8BPQ Digipeater/IGate
APBMxx BrandMeister DMR Server for R3ABM
APBSDx HamBSD https://hambsd.org/
APBT62 BTech DMR 6x2
APC APCxxx Cellular applications
APCBBx VE7UDP Blackberry Applications
APCDS0 Leon Lessing ZS6LMG's cell tracker
APCLEY EYTraker GPRS/GSM tracker by ZS6EY
APCLEZ Telit EZ10 GSM application ZS6CEY
APCLUB Brazil APRS network
APCLWX EYWeather GPRS/GSM WX station by ZS6EY
APCNxx for carNET by DG5OAW
APCSMS for Cosmos (used for sending commands @USNA)
APCSS for AMSAT cubesats https://cubesatsim.org
APCWP8 John GM7HHB, WinphoneAPRS
APCYxx Cybiko applications
APD APD4xx UP4DAR platform
APDDxx DV-RPTR Modem and Control Center Software
APDFxx Automatic DF units
APDGxx D-Star Gateways by G4KLX ircDDB
APDHxx WinDV (DUTCH*Star DV Node for Windows)
APDInn DIXPRS - Bela, HA5DI
APDIGI Used by PSAT2 to indicate the digi is ON
APDIGI digi ON for PSAT2 and QIKCOM-2
APDKxx KI4LKF g2_ircddb Dstar gateway software
APDNOx APRSduino by DO3SWW
APDOxx ON8JL Standalone DStar Node
APDPRS D-Star originated posits
APDRxx APRSdroid Android App http://aprsdroid.org/
APDSXX SP9UOB for dsDigi and ds-tracker
APDTxx APRStouch Tone (DTMF)
APDTMF digi off mode on QIKCOM2 and DTMF ON
APDUxx U2APRS by JA7UDE
APDVxx OE6PLD's SSTV with APRS status exchange
APDWxx DireWolf, WB2OSZ
APE APExxx Telemetry devices
APE2Ax VA3NNW's Email-2-APRS ap
APECAN Pecan Pico APRS Balloon Tracker
APELKx WB8ELK balloons
APERXQ Experimental tracker by PE1RXQ
APERSx Runner tracking by Jason,KG7YKZ
APESPG ESP SmartBeacon APRS-IS Client
APESPW ESP Weather Station APRS-IS Client
APF APFxxx Firenet
APFGxx Flood Gage (KP4DJT)
APFIxx for APRS.FI OH7LZB, Hessu
APFPRS for FreeDV by Jeroen PE1RXQ
APG APGxxx Gates, etc
APGOxx for AA3NJ PDA application
APGBLN for NW5W's GoBalloon
APGDTx for VK4FAST's Graphic Data Terminal
APH APHKxx for LA1BR tracker/digipeater
APHAXn SM2APRS by PY2UEP
APHBLx for DMR Gateway by Eric - KF7EEL
APHTxx HMTracker by IU0AAC
APHWxx for use in "HamWAN
API API282 for ICOM IC-2820
API31 for ICOM ID-31
API410 for ICOM ID-4100
API51 for ICOM ID-51
API510 for ICOM ID-5100
API710 for ICOM IC-7100
API80 for ICOM IC-80
API880 for ICOM ID-880
API910 for ICOM IC-9100
API92 for ICOM IC-92
API970 for ICOM 9700
APICQx for ICQ
APICxx HA9MCQ's Pic IGate
APIExx W7KMV's PiAPRS system
APINxx PinPoint by AB0WV
APIZCI hymTR IZCI Tracker by TA7W/OH2UDS and TA6AEU
APJ APJ8xx Jordan / KN4CRD JS8Call application
APJAxx JavAPRS
APJExx JeAPRS
APJIxx jAPRSIgate
APJSxx javAPRSSrvr
APJYnn KA2DDO Yet another APRS system
APK APK0xx Kenwood TH-D7's
APK003 Kenwood TH-D72
APK004 Kenwood TH-D74
APK005 Kenwood TH-D75
APK1xx Kenwood D700's
APK102 Kenwood D710
APKRAM KRAMstuff.com - Mark. G7LEU
APL APLCxx APRScube by DL3DCW
APLGxx LoRa Gateway/Digipeater OE5BPA
APLIGx LightAPRS - TA2MUN and TA9OHC
APLOxx LoRa KISS TNC/Tracker
APLQRU Charlie - QRU Server
APLMxx WA0TQG transceiver controller
APLSxx SARIMESH ( http://www.sarimesh.net )
APLTxx LoRa Tracker - OE5BPA
APM APMxxx MacAPRS,
APMGxx PiCrumbs and MiniGate - Alex, AB0TJ
APMIxx SQ3PLX http://microsat.com.pl/
APMPAD DF1JSL's WXBot clone and extension
APMQxx Ham Radio of Things WB2OSZ
APMTxx LZ1PPL for tracker
APN APNxxx Network nodes, digis, etc
APN2xx NOSaprs for JNOS 2.0 - VE4KLM
APN3xx Kantronics KPC-3 rom versions
APN9xx Kantronics KPC-9612 Roms
APNAxx WB6ZSU's APRServe
APNDxx DIGI_NED
APNICx SQ5EKU http://sq5eku.blogspot.com/
APNK01 Kenwood D700 (APK101) type
APNK80 KAM version 8.0
APNKMP KAM+
APNKMX KAM-XL
APNMxx MJF TNC roms
APNPxx Paccom TNC roms
APNTxx SV2AGW's TNT tnc as a digi
APNUxx UIdigi
APNVxx SQ8L's VP digi and Nodes
APNXxx TNC-X (K6DBG)
APNWxx SQ3FYK.com WX/Digi and SQ3PLX http://microsat.com.pl/
APO APRSpoint
APOFF Used by PSAT and PSAT2 to indicate the digi is OFF
APOLUx for OSCAR satellites for AMSAT-LU by LU9DO
APOAxx OpenAPRS - Greg Carter
APOCSG For N0AGI's APRS to POCSAG project
APOD1w Open Track with 1 wire WX
APOSBx openSPOT3 by HA2NON at sharkrf.com
APOSWx openSPOT2
APOTxx Open Track
APOU2k Open Track for Ultimeter
APOZxx www.KissOZ.dk Tracker. OZ1EKD and OZ7HVO
APP APP6xx for APRSlib
APPICx DB1NTO' PicoAPRS
APPMxx DL1MX's RTL-SDR pytohon Igate
APPTxx KetaiTracker by JF6LZE, Takeki (msg capable)
APQ APQxxx Earthquake data
APQTHx W8WJB's QTH.app
APR APR8xx APRSdos versions 800+
APR2MF DL2MF - MF2APRS Radiosonde WX reporting
APRARX VK5QI's radiosonde tracking
APRDxx APRSdata, APRSdr
APRGxx aprsg igate software, OH2GVE
APRHH2 HamHud 2
APRKxx APRStk
APRNOW W5GGW ipad application
APRRTx RPC electronics
APRS Generic, (obsolete. Digis should use APNxxx instead)
APRSON Used by PSAT to indicate the DIGI is ON
APRXxx >40 APRSmax
APRXxx <39 for OH2MQK's igate
APRTLM used in MIM's and Mic-lites, etc
APRtfc APRStraffic
APRSTx APRStt (Touch tone)
APS APSxxx APRS+SA, etc
APSARx ZL4FOX's SARTRACK
APSAT digi ON for QIKCOM-1
APSCxx aprsc APRS-IS core server (OH7LZB, OH2MQK)
APSFxx F5OPV embedded devices - was APZ40
APSK63 APRS Messenger -over-PSK63
APSK25 APRS Messenger GMSK-250
APSMSx Paul Dufresne's SMSGTE - SMS Gateway
APSTMx for W7QO's Balloon trackers
APSTPO for N0AGI Satellite Tracking and Operations
APT APT2xx Tiny Track II
APT3xx Tiny Track III
APTAxx K4ATM's tiny track
APTBxx TinyAPRS by BG5HHP Was APTAxx till Sep 2017
APTCHE PU3IKE in Brazil TcheTracker/Tcheduino
APTCMA CAPI tracker - PU1CMA Brazil
APTIGR TigerTrack
APTKPT TrackPoint N0LP
APTPNx TARPN Packet Node Tracker by KN4ORB http://tarpn.net/
APTTxx Tiny Track
APTWxx Byons WXTrac
APTVxx for ATV/APRN and SSTV applications
APU APU1xx UIview 16 bit applications
APU2xx UIview 32 bit apps
APU3xx UIview terminal program
APUDRx NW Digital Radio's UDR (APRS/Dstar)
APV APVxxx Voice over Internet applications
APVMxx DRCC-DVM Digital Voice (Digital Radio China Club)
APVRxx for IRLP
APVLxx for I-LINK
APVExx for ECHO link
APW APWxxx WinAPRS, etc
APW9xx 9A9Y Weather Tracker
APWAxx APRSISCE Android version
APWSxx DF4IAN's WS2300 WX station
APWMxx APRSISCE KJ4ERJ
APWWxx APRSISCE win32 version
APX APXnnn Xastir
APXRnn Xrouter
APY APYxxx Yaesu Radios
APY008 Yaesu VX-8 series
APY01D Yaesu FT1D series
APY02D Yaesu FT2D series
APY03D Yaesu FT3D series
APY05D Yaesu FT5D series
APY100 Yaesu FTM-100D series
APY300 Yaesu FTM-300D series
APY350 Yaesu FTM-350 series
APY400 Yaesu FTM-400D series
APZ APZxxx Experimental
APZ200 old versions of JNOS
APZ247 for UPRS NR0Q
APZ0xx Xastir (old versions. See APX)
APZMAJ Martyn M1MAJ DeLorme inReach Tracker
APZMDM github/codec2_talkie - product code not registered
APZMDR for HaMDR trackers - hessu * hes.iki.fi]
APZPAD Smart Palm
APZTKP TrackPoint, Nick N0LP (Balloon tracking)(depricated)
APZWIT MAP27 radio (Mountain Rescue) EI7IG
APZWKR GM1WKR NetSked application
</tocalls>
<notes>
</notes>
<altnets>
REGISTERED TOCALL ALTNETS:
--------------------------
ALTNETS are uses of the AX-25 tocall to distinguish specialized
traffic that may be flowing on the APRS-IS, but that are not intended
to be part of normal APRS distribution to all normal APRS software
operating in normal (default) modes. Proper APRS software that
honors this design are supposed to IGNORE all ALTNETS unless the
particular operator has selected an ALTNET to monitor for.
An example is when testing; an author may want to transmit objects
all over his map for on-air testing, but does not want these to
clutter everyone's maps or databases. He could use the ALTNET of
"TEST" and client APRS software that respects the ALTNET concept
should ignore these packets.
An ALTNET is defined to be ANY AX.25 TOCALL that is NOT one of the
normal APRS TOCALL's. The normal TOCALL's that APRS is supposed to
process are: ALL, BEACON, CQ, QST, GPSxxx and of course APxxxx.
The following is a list of ALTNETS that may be of interest to other
users. This list is by no means complete, since ANY combination of
characters other than APxxxx are considered an ALTNET. But this list
can give consisntecy to ALTNETS that may be using the global APRS-IS
and need some special recognition. Here are some ideas:
</altnets>
<altnet_list>
SATERN - Salvation Army Altnet
AFMARS - Airforce Mars
AMARS - Army Mars
</altnet_list>

1637
data/tocalls.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@ include_directories(
${PORTAUDIO_INCLUDE_DIRS} ${PORTAUDIO_INCLUDE_DIRS}
${SNDIO_INCLUDE_DIRS} ${SNDIO_INCLUDE_DIRS}
${CUSTOM_GEOTRANZ_DIR} ${CUSTOM_GEOTRANZ_DIR}
${GPIOD_INCLUDE_DIRS}
) )
if(WIN32 OR CYGWIN) if(WIN32 OR CYGWIN)
@ -38,6 +39,7 @@ list(APPEND direwolf_SOURCES
beacon.c beacon.c
config.c config.c
decode_aprs.c decode_aprs.c
deviceid.c
dedupe.c dedupe.c
demod_9600.c demod_9600.c
demod_afsk.c demod_afsk.c
@ -162,6 +164,7 @@ target_link_libraries(direwolf
${ALSA_LIBRARIES} ${ALSA_LIBRARIES}
${UDEV_LIBRARIES} ${UDEV_LIBRARIES}
${PORTAUDIO_LIBRARIES} ${PORTAUDIO_LIBRARIES}
${GPIOD_LIBRARIES}
${SNDIO_LIBRARIES} ${SNDIO_LIBRARIES}
${AVAHI_LIBRARIES} ${AVAHI_LIBRARIES}
) )
@ -176,6 +179,7 @@ endif()
# decode_aprs # decode_aprs
list(APPEND decode_aprs_SOURCES list(APPEND decode_aprs_SOURCES
decode_aprs.c decode_aprs.c
deviceid.c
ais.c ais.c
kiss_frame.c kiss_frame.c
ax25_pad.c ax25_pad.c
@ -360,6 +364,7 @@ list(APPEND atest_SOURCES
ax25_pad.c ax25_pad.c
ax25_pad2.c ax25_pad2.c
decode_aprs.c decode_aprs.c
deviceid.c
dwgpsnmea.c dwgpsnmea.c
dwgps.c dwgps.c
dwgpsd.c dwgpsd.c

View File

@ -28,7 +28,8 @@
enum ptt_method_e { enum ptt_method_e {
PTT_METHOD_NONE, /* VOX or no transmit. */ PTT_METHOD_NONE, /* VOX or no transmit. */
PTT_METHOD_SERIAL, /* Serial port RTS or DTR. */ PTT_METHOD_SERIAL, /* Serial port RTS or DTR. */
PTT_METHOD_GPIO, /* General purpose I/O, Linux only. */ PTT_METHOD_GPIO, /* General purpose I/O using sysfs, deprecated after 2020, Linux only. */
PTT_METHOD_GPIOD, /* General purpose I/O, using libgpiod, Linux only. */
PTT_METHOD_LPT, /* Parallel printer port, Linux only. */ PTT_METHOD_LPT, /* Parallel printer port, Linux only. */
PTT_METHOD_HAMLIB, /* HAMLib, Linux only. */ PTT_METHOD_HAMLIB, /* HAMLib, Linux only. */
PTT_METHOD_CM108 }; /* GPIO pin of CM108/CM119/etc. Linux only. */ PTT_METHOD_CM108 }; /* GPIO pin of CM108/CM119/etc. Linux only. */
@ -74,16 +75,23 @@ struct audio_s {
/* Properties of the sound device. */ /* Properties of the sound device. */
int defined; /* Was device defined? */ int defined; /* Was device defined? 0=no. >0 for yes. */
/* First one defaults to yes. */ /* First channel defaults to 2 for yes with default config. */
/* 1 means it was defined by user. */
int copy_from; /* >=0 means copy contents from another audio device. */
/* In this case we don't have device names, below. */
/* Num channels, samples/sec, and bit/sample are copied from */
/* original device and can't be changed. */
/* -1 for normal case. */
char adevice_in[80]; /* Name of the audio input device (or file?). */ char adevice_in[80]; /* Name of the audio input device (or file?). */
/* TODO: Can be "-" to read from stdin. */ /* Can be udp:nnn for UDP or "-" to read from stdin. */
char adevice_out[80]; /* Name of the audio output device (or file?). */ char adevice_out[80]; /* Name of the audio output device (or file?). */
int num_channels; /* Should be 1 for mono or 2 for stereo. */ int num_channels; /* Should be 1 for mono or 2 for stereo. */
int samples_per_sec; /* Audio sampling rate. Typically 11025, 22050, or 44100. */ int samples_per_sec; /* Audio sampling rate. Typically 11025, 22050, 44100, or 48000. */
int bits_per_sample; /* 8 (unsigned char) or 16 (signed short). */ int bits_per_sample; /* 8 (unsigned char) or 16 (signed short). */
} adev[MAX_ADEVS]; } adev[MAX_ADEVS];
@ -304,6 +312,7 @@ struct audio_s {
/* the case for CubieBoard where it was longer. */ /* the case for CubieBoard where it was longer. */
/* This is filled in by ptt_init so we don't have to */ /* This is filled in by ptt_init so we don't have to */
/* recalculate it each time we access it. */ /* recalculate it each time we access it. */
/* Also GPIO chip name for GPIOD method. Looks like 'gpiochip4' */
/* This could probably be collapsed into ptt_device instead of being separate. */ /* This could probably be collapsed into ptt_device instead of being separate. */

View File

@ -4642,6 +4642,8 @@ static void dm_frame (ax25_dlsm_t *S, int f)
if (f == 1) { if (f == 1) {
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("%s doesn't understand AX.25 v2.2. Trying v2.0 ...\n", S->addrs[PEERCALL]); dw_printf ("%s doesn't understand AX.25 v2.2. Trying v2.0 ...\n", S->addrs[PEERCALL]);
dw_printf ("You can avoid this failed attempt and speed up the\n");
dw_printf ("process by putting \"V20 %s\" in the configuration file.\n", S->addrs[PEERCALL]);
INIT_T1V_SRT; INIT_T1V_SRT;
@ -4930,6 +4932,8 @@ static void frmr_frame (ax25_dlsm_t *S)
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("%s doesn't understand AX.25 v2.2. Trying v2.0 ...\n", S->addrs[PEERCALL]); dw_printf ("%s doesn't understand AX.25 v2.2. Trying v2.0 ...\n", S->addrs[PEERCALL]);
dw_printf ("You can avoid this failed attempt and speed up the\n");
dw_printf ("process by putting \"V20 %s\" in the configuration file.\n", S->addrs[PEERCALL]);
INIT_T1V_SRT; INIT_T1V_SRT;

View File

@ -2579,6 +2579,59 @@ int ax25_get_c2 (packet_t this_p)
} }
/*------------------------------------------------------------------
*
* Function: ax25_set_pid
*
* Purpose: Set protocol ID in packet.
*
* Inputs: this_p - pointer to packet object.
*
* pid - usually 0xF0 for APRS or 0xCF for NET/ROM.
*
* AX.25: "The Protocol Identifier (PID) field appears in information
* frames (I and UI) only. It identifies which kind of
* Layer 3 protocol, if any, is in use."
*
*------------------------------------------------------------------*/
void ax25_set_pid (packet_t this_p, int pid)
{
assert (this_p->magic1 == MAGIC);
assert (this_p->magic2 == MAGIC);
// Some applications set this to 0 which is an error.
// Change 0 to 0xF0 meaning no layer 3 protocol.
if (pid == 0) {
pid = AX25_PID_NO_LAYER_3;
}
// Sanity check: is it I or UI frame?
if (this_p->frame_len == 0) return;
ax25_frame_type_t frame_type;
cmdres_t cr; // command or response.
char description[64];
int pf; // Poll/Final.
int nr, ns; // Sequence numbers.
frame_type = ax25_frame_type (this_p, &cr, description, &pf, &nr, &ns);
if (frame_type != frame_type_I && frame_type != frame_type_U_UI) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("ax25_set_pid(0x%2x): Packet type is not I or UI.\n", pid);
return;
}
// TODO: handle 2 control byte case.
if (this_p->num_addr >= 2) {
this_p->frame_data[ax25_get_pid_offset(this_p)] = pid;
}
}
/*------------------------------------------------------------------ /*------------------------------------------------------------------
* *
* Function: ax25_get_pid * Function: ax25_get_pid

View File

@ -66,6 +66,7 @@
#define AX25_UI_FRAME 3 /* Control field value. */ #define AX25_UI_FRAME 3 /* Control field value. */
#define AX25_PID_NO_LAYER_3 0xf0 /* protocol ID used for APRS */ #define AX25_PID_NO_LAYER_3 0xf0 /* protocol ID used for APRS */
#define AX25_PID_NETROM 0xcf /* protocol ID used for NET/ROM */
#define AX25_PID_SEGMENTATION_FRAGMENT 0x08 #define AX25_PID_SEGMENTATION_FRAGMENT 0x08
#define AX25_PID_ESCAPE_CHARACTER 0xff #define AX25_PID_ESCAPE_CHARACTER 0xff
@ -427,6 +428,7 @@ extern int ax25_is_null_frame (packet_t this_p);
extern int ax25_get_control (packet_t this_p); extern int ax25_get_control (packet_t this_p);
extern int ax25_get_c2 (packet_t this_p); extern int ax25_get_c2 (packet_t this_p);
extern void ax25_set_pid (packet_t this_p, int pid);
extern int ax25_get_pid (packet_t this_p); extern int ax25_get_pid (packet_t this_p);
extern int ax25_get_frame_len (packet_t this_p); extern int ax25_get_frame_len (packet_t this_p);

View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2021 John Langner, WB2OSZ // Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 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
@ -755,12 +755,13 @@ void config_init (char *fname, struct audio_s *p_audio_config,
strlcpy (p_audio_config->adev[adevice].adevice_out, DEFAULT_ADEVICE, sizeof(p_audio_config->adev[adevice].adevice_out)); strlcpy (p_audio_config->adev[adevice].adevice_out, DEFAULT_ADEVICE, sizeof(p_audio_config->adev[adevice].adevice_out));
p_audio_config->adev[adevice].defined = 0; p_audio_config->adev[adevice].defined = 0;
p_audio_config->adev[adevice].copy_from = -1;
p_audio_config->adev[adevice].num_channels = DEFAULT_NUM_CHANNELS; /* -2 stereo */ p_audio_config->adev[adevice].num_channels = DEFAULT_NUM_CHANNELS; /* -2 stereo */
p_audio_config->adev[adevice].samples_per_sec = DEFAULT_SAMPLES_PER_SEC; /* -r option */ p_audio_config->adev[adevice].samples_per_sec = DEFAULT_SAMPLES_PER_SEC; /* -r option */
p_audio_config->adev[adevice].bits_per_sample = DEFAULT_BITS_PER_SAMPLE; /* -8 option for 8 instead of 16 bits */ p_audio_config->adev[adevice].bits_per_sample = DEFAULT_BITS_PER_SAMPLE; /* -8 option for 8 instead of 16 bits */
} }
p_audio_config->adev[0].defined = 1; p_audio_config->adev[0].defined = 2; // 2 means it was done by default and not the user's config file.
for (channel=0; channel<MAX_CHANS; channel++) { for (channel=0; channel<MAX_CHANS; channel++) {
int ot, it; int ot, it;
@ -925,10 +926,13 @@ void config_init (char *fname, struct audio_s *p_audio_config,
p_misc_config->maxframe_extended = AX25_K_MAXFRAME_EXTENDED_DEFAULT; /* Max frames to send before ACK. mod 128 "Window" size. */ p_misc_config->maxframe_extended = AX25_K_MAXFRAME_EXTENDED_DEFAULT; /* Max frames to send before ACK. mod 128 "Window" size. */
p_misc_config->maxv22 = AX25_N2_RETRY_DEFAULT / 3; /* Max SABME before falling back to SABM. */ p_misc_config->maxv22 = AX25_N2_RETRY_DEFAULT / 3; /* Send SABME this many times before falling back to SABM. */
p_misc_config->v20_addrs = NULL; /* Go directly to v2.0 for stations listed. */ p_misc_config->v20_addrs = NULL; /* Go directly to v2.0 for stations listed */
/* without trying v2.2 first. */
p_misc_config->v20_count = 0; p_misc_config->v20_count = 0;
p_misc_config->noxid_addrs = NULL; /* Don't send XID to these stations. */ p_misc_config->noxid_addrs = NULL; /* Don't send XID to these stations. */
/* Might work with a partial v2.2 implementation */
/* on the other end. */
p_misc_config->noxid_count = 0; p_misc_config->noxid_count = 0;
/* /*
@ -1013,6 +1017,10 @@ void config_init (char *fname, struct audio_s *p_audio_config,
* ADEVICE plughw:2,0 plughw:3,0 -- different in/out for a channel or channel pair. * ADEVICE plughw:2,0 plughw:3,0 -- different in/out for a channel or channel pair.
* ADEVICE1 udp:7355 default -- from Software defined radio (SDR) via UDP. * ADEVICE1 udp:7355 default -- from Software defined radio (SDR) via UDP.
* *
* New in 1.8: Ability to map to another audio device.
* This allows multiple modems (i.e. data speeds) on the same audio interface.
*
* ADEVICEn = n -- Copy from different already defined channel.
*/ */
/* Note that ALSA name can contain comma such as hw:1,0 */ /* Note that ALSA name can contain comma such as hw:1,0 */
@ -1040,17 +1048,42 @@ void config_init (char *fname, struct audio_s *p_audio_config,
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
// Do not allow same adevice to be defined more than once.
// Overriding the default for adevice 0 is ok.
// In that case definded was 2. That's why we check for 1, not just non-zero.
if (p_audio_config->adev[adevice].defined == 1) { // 1 means defined by user.
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file: ADEVICE%d can't be defined more than once. Line %d.\n", adevice, line);
continue;
}
p_audio_config->adev[adevice].defined = 1; p_audio_config->adev[adevice].defined = 1;
/* First channel of device is valid. */ // New case for release 1.8.
p_audio_config->chan_medium[ADEVFIRSTCHAN(adevice)] = MEDIUM_RADIO;
strlcpy (p_audio_config->adev[adevice].adevice_in, t, sizeof(p_audio_config->adev[adevice].adevice_in)); if (strcmp(t, "=") == 0) {
strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out)); t = split(NULL,0);
if (t != NULL) {
t = split(NULL,0); }
if (t != NULL) {
///////// to be continued.... FIXME
}
else {
/* First channel of device is valid. */
// This might be changed to UDP or STDIN when the device name is examined.
p_audio_config->chan_medium[ADEVFIRSTCHAN(adevice)] = MEDIUM_RADIO;
strlcpy (p_audio_config->adev[adevice].adevice_in, t, sizeof(p_audio_config->adev[adevice].adevice_in));
strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out)); strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out));
t = split(NULL,0);
if (t != NULL) {
// Different audio devices for receive and transmit.
strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out));
}
} }
} }
@ -1809,6 +1842,45 @@ void config_init (char *fname, struct audio_s *p_audio_config,
} }
p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_GPIO; p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_GPIO;
#endif #endif
}
else if (strcasecmp(t, "GPIOD") == 0) {
#if __WIN32__
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: %s with GPIOD is only available on Linux.\n", line, otname);
#else
#if defined(USE_GPIOD)
t = split(NULL,0);
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: Missing GPIO chip name for %s.\n", line, otname);
dw_printf ("Use the \"gpioinfo\" command to get a list of gpio chip names and corresponding I/O lines.\n");
continue;
}
strlcpy(p_audio_config->achan[channel].octrl[ot].out_gpio_name, t,
sizeof(p_audio_config->achan[channel].octrl[ot].out_gpio_name));
t = split(NULL,0);
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Config file line %d: Missing GPIO number for %s.\n", line, otname);
continue;
}
if (*t == '-') {
p_audio_config->achan[channel].octrl[ot].out_gpio_num = atoi(t+1);
p_audio_config->achan[channel].octrl[ot].ptt_invert = 1;
}
else {
p_audio_config->achan[channel].octrl[ot].out_gpio_num = atoi(t);
p_audio_config->achan[channel].octrl[ot].ptt_invert = 0;
}
p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_GPIOD;
#else
text_color_set(DW_COLOR_ERROR);
dw_printf ("Application was not built with optional support for GPIOD.\n");
dw_printf ("Install packages gpiod and libgpiod-dev, remove 'build' subdirectory, then rebuild.\n");
#endif /* USE_GPIOD*/
#endif /* __WIN32__ */
} }
else if (strcasecmp(t, "LPT") == 0) { else if (strcasecmp(t, "LPT") == 0) {
@ -2132,6 +2204,9 @@ void config_init (char *fname, struct audio_s *p_audio_config,
/* /*
* DWAIT n - Extra delay for receiver squelch. n = 10 mS units. * DWAIT n - Extra delay for receiver squelch. n = 10 mS units.
*
* Why did I do this? Just add more to TXDELAY.
* Now undocumented in User Guide. Might disappear someday.
*/ */
else if (strcasecmp(t, "DWAIT") == 0) { else if (strcasecmp(t, "DWAIT") == 0) {
@ -2167,14 +2242,20 @@ void config_init (char *fname, struct audio_s *p_audio_config,
continue; continue;
} }
n = atoi(t); n = atoi(t);
if (n >= 0 && n <= 255) { if (n >= 5 && n < 50) {
// 0 = User has no clue. This would be no delay.
// 10 = Default.
// 50 = Half second. User might think it is mSec and use 100.
p_audio_config->achan[channel].slottime = n; p_audio_config->achan[channel].slottime = n;
} }
else { else {
p_audio_config->achan[channel].slottime = DEFAULT_SLOTTIME; p_audio_config->achan[channel].slottime = DEFAULT_SLOTTIME;
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: Invalid delay time for persist algorithm. Using %d.\n", dw_printf ("Line %d: Invalid delay time for persist algorithm. Using default %d.\n",
line, p_audio_config->achan[channel].slottime); line, p_audio_config->achan[channel].slottime);
dw_printf ("Read the Dire Wolf User Guide, \"Radio Channel - Transmit Timing\"\n");
dw_printf ("section, to understand what this means.\n");
dw_printf ("Why don't you just use the default?\n");
} }
} }
@ -2191,14 +2272,17 @@ void config_init (char *fname, struct audio_s *p_audio_config,
continue; continue;
} }
n = atoi(t); n = atoi(t);
if (n >= 0 && n <= 255) { if (n >= 5 && n <= 250) {
p_audio_config->achan[channel].persist = n; p_audio_config->achan[channel].persist = n;
} }
else { else {
p_audio_config->achan[channel].persist = DEFAULT_PERSIST; p_audio_config->achan[channel].persist = DEFAULT_PERSIST;
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: Invalid probability for persist algorithm. Using %d.\n", dw_printf ("Line %d: Invalid probability for persist algorithm. Using default %d.\n",
line, p_audio_config->achan[channel].persist); line, p_audio_config->achan[channel].persist);
dw_printf ("Read the Dire Wolf User Guide, \"Radio Channel - Transmit Timing\"\n");
dw_printf ("section, to understand what this means.\n");
dw_printf ("Why don't you just use the default?\n");
} }
} }
@ -2216,6 +2300,23 @@ void config_init (char *fname, struct audio_s *p_audio_config,
} }
n = atoi(t); n = atoi(t);
if (n >= 0 && n <= 255) { if (n >= 0 && n <= 255) {
text_color_set(DW_COLOR_ERROR);
if (n < 10) {
dw_printf ("Line %d: Setting TXDELAY this small is a REALLY BAD idea if you want other stations to hear you.\n",
line);
dw_printf ("Read the Dire Wolf User Guide, \"Radio Channel - Transmit Timing\"\n");
dw_printf ("section, to understand what this means.\n");
dw_printf ("Why don't you just use the default rather than reducing reliability?\n");
}
else if (n >= 100) {
dw_printf ("Line %d: Keeping with tradition, going back to the 1980s, TXDELAY is in 10 millisecond units.\n",
line);
dw_printf ("Line %d: The value %d would be %.3f seconds which seems rather excessive. Are you sure you want that?\n",
line, n, (double)n * 10. / 1000.);
dw_printf ("Read the Dire Wolf User Guide, \"Radio Channel - Transmit Timing\"\n");
dw_printf ("section, to understand what this means.\n");
dw_printf ("Why don't you just use the default?\n");
}
p_audio_config->achan[channel].txdelay = n; p_audio_config->achan[channel].txdelay = n;
} }
else { else {
@ -2240,6 +2341,22 @@ void config_init (char *fname, struct audio_s *p_audio_config,
} }
n = atoi(t); n = atoi(t);
if (n >= 0 && n <= 255) { if (n >= 0 && n <= 255) {
if (n < 5) {
dw_printf ("Line %d: Setting TXTAIL that small is a REALLY BAD idea if you want other stations to hear you.\n",
line);
dw_printf ("Read the Dire Wolf User Guide, \"Radio Channel - Transmit Timing\"\n");
dw_printf ("section, to understand what this means.\n");
dw_printf ("Why don't you just use the default rather than reducing reliability?\n");
}
else if (n >= 50) {
dw_printf ("Line %d: Keeping with tradition, going back to the 1980s, TXTAIL is in 10 millisecond units.\n",
line);
dw_printf ("Line %d: The value %d would be %.3f seconds which seems rather excessive. Are you sure you want that?\n",
line, n, (double)n * 10. / 1000.);
dw_printf ("Read the Dire Wolf User Guide, \"Radio Channel - Transmit Timing\"\n");
dw_printf ("section, to understand what this means.\n");
dw_printf ("Why don't you just use the default?\n");
}
p_audio_config->achan[channel].txtail = n; p_audio_config->achan[channel].txtail = n;
} }
else { else {
@ -2794,7 +2911,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
dw_printf ("Config file: FILTER IG ... on line %d.\n", line); dw_printf ("Config file: FILTER IG ... on line %d.\n", line);
dw_printf ("Warning! Don't mess with IS>RF filtering unless you are an expert and have an unusual situation.\n"); dw_printf ("Warning! Don't mess with IS>RF filtering unless you are an expert and have an unusual situation.\n");
dw_printf ("Warning! The default is fine for nearly all situations.\n"); dw_printf ("Warning! The default is fine for nearly all situations.\n");
dw_printf ("Warning! Be sure to read carefully and understand Successful-APRS-Gateway-Operation.pdf .\n"); dw_printf ("Warning! Be sure to read carefully and understand \"Successful-APRS-Gateway-Operation.pdf\" .\n");
dw_printf ("Warning! If you insist, be sure to add \" | i/180 \" so you don't break messaging.\n"); dw_printf ("Warning! If you insist, be sure to add \" | i/180 \" so you don't break messaging.\n");
} }
else { else {
@ -2834,7 +2951,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
dw_printf ("Warning! Don't mess with RF>IS filtering unless you are an expert and have an unusual situation.\n"); dw_printf ("Warning! Don't mess with RF>IS filtering unless you are an expert and have an unusual situation.\n");
dw_printf ("Warning! Expected behavior is for everything to go from RF to IS.\n"); dw_printf ("Warning! Expected behavior is for everything to go from RF to IS.\n");
dw_printf ("Warning! The default is fine for nearly all situations.\n"); dw_printf ("Warning! The default is fine for nearly all situations.\n");
dw_printf ("Warning! Be sure to read carefully and understand Successful-APRS-Gateway-Operation.pdf .\n"); dw_printf ("Warning! Be sure to read carefully and understand \"Successful-APRS-Gateway-Operation.pdf\" .\n");
} }
else { else {
to_chan = isdigit(*t) ? atoi(t) : -999; to_chan = isdigit(*t) ? atoi(t) : -999;
@ -4470,6 +4587,13 @@ void config_init (char *fname, struct audio_s *p_audio_config,
if (t != NULL && strlen(t) > 0) { if (t != NULL && strlen(t) > 0) {
p_igate_config->t2_filter = strdup (t); p_igate_config->t2_filter = strdup (t);
text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: Warning - IGFILTER is a rarely needed expert level feature.\n", line);
dw_printf ("If you don't have a special situation and a good understanding of\n");
dw_printf ("how this works, you probably should not be messing with it.\n");
dw_printf ("The default behavior is appropriate for most situations.\n");
dw_printf ("Please read \"Successful-APRS-IGate-Operation.pdf\".\n");
} }
} }

View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2017, 2022 John Langner, WB2OSZ // Copyright (C) 2011, 2012, 2013, 2014, 2015, 2017, 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
@ -56,7 +56,7 @@
#include "decode_aprs.h" #include "decode_aprs.h"
#include "telemetry.h" #include "telemetry.h"
#include "ais.h" #include "ais.h"
#include "deviceid.h"
#define TRUE 1 #define TRUE 1
#define FALSE 0 #define FALSE 0
@ -124,7 +124,6 @@ static double get_longitude_9 (char *p, int quiet);
static time_t get_timestamp (decode_aprs_t *A, char *p); static time_t get_timestamp (decode_aprs_t *A, char *p);
static int get_maidenhead (decode_aprs_t *A, char *p); static int get_maidenhead (decode_aprs_t *A, char *p);
static int data_extension_comment (decode_aprs_t *A, char *pdext); static int data_extension_comment (decode_aprs_t *A, char *pdext);
static void decode_tocall (decode_aprs_t *A, char *dest);
//static void get_symbol (decode_aprs_t *A, char dti, char *src, char *dest); //static void get_symbol (decode_aprs_t *A, char dti, char *src, char *dest);
static void process_comment (decode_aprs_t *A, char *pstart, int clen); static void process_comment (decode_aprs_t *A, char *pstart, int clen);
@ -292,7 +291,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
} }
/* /*
* Application might be in the destination field for most message types. * Device/Application is in the destination field for most packet types.
* MIC-E format has part of location in the destination field. * MIC-E format has part of location in the destination field.
*/ */
@ -303,7 +302,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
break; break;
default: default:
decode_tocall (A, A->g_dest); deviceid_decode_dest (A->g_dest, A->g_mfr, sizeof(A->g_mfr));
break; break;
} }
@ -1392,7 +1391,6 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
int cust_msg = 0; int cust_msg = 0;
const char *std_text[8] = {"Emergency", "Priority", "Special", "Committed", "Returning", "In Service", "En Route", "Off Duty" }; const char *std_text[8] = {"Emergency", "Priority", "Special", "Committed", "Returning", "In Service", "En Route", "Off Duty" };
const char *cust_text[8] = {"Emergency", "Custom-6", "Custom-5", "Custom-4", "Custom-3", "Custom-2", "Custom-1", "Custom-0" }; const char *cust_text[8] = {"Emergency", "Custom-6", "Custom-5", "Custom-4", "Custom-3", "Custom-2", "Custom-1", "Custom-0" };
unsigned char *pfirst, *plast;
strlcpy (A->g_data_type_desc, "MIC-E", sizeof(A->g_data_type_desc)); strlcpy (A->g_data_type_desc, "MIC-E", sizeof(A->g_data_type_desc));
@ -1622,126 +1620,43 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
A->g_course = n; A->g_course = n;
// The rest is a comment which can have other information cryptically embedded.
// Remove any trailing CR, which I would argue, violates the protocol spec.
// It is essential to keep trailing spaces. e.g. VX-8 suffix is "_ "
char mcomment[256];
strlcpy (mcomment, info + sizeof(struct aprs_mic_e_s), sizeof(mcomment));
if (mcomment[strlen(mcomment)-1] == '\r') {
mcomment[strlen(mcomment)-1] = '\0';
}
/* Now try to pick out manufacturer and other optional items. */ /* Now try to pick out manufacturer and other optional items. */
/* The telemetry field, in the original spec, is no longer used. */ /* The telemetry field, in the original spec, is no longer used. */
strlcpy (A->g_mfr, "Unknown manufacturer", sizeof(A->g_mfr)); char trimmed[256]; // Comment with vendor/model removed.
deviceid_decode_mice (mcomment, trimmed, sizeof(trimmed), A->g_mfr, sizeof(A->g_mfr));
pfirst = info + sizeof(struct aprs_mic_e_s);
plast = info + ilen - 1;
/* Carriage return character at the end is not mentioned in spec. */
/* Remove if found because it messes up extraction of manufacturer. */
/* Don't drop trailing space because that is used for Yaesu VX-8. */
/* As I recall, the IGate function trims trailing spaces. */
/* That would be bad for this particular model. Maybe I'm mistaken? */
if (*plast == '\r') plast--;
#define isT(c) ((c) == ' ' || (c) == '>' || (c) == ']' || (c) == '`' || (c) == '\'') // Possible altitude at beginning of remaining comment.
// Three base 91 characters followed by }
// Last Updated Dec. 2021
// This does not change very often but I'm wondering if we could parse if (strlen(trimmed) >=4 &&
// http://www.aprs.org/aprs12/mic-e-types.txt similar to how we use tocalls.txt. isdigit91(trimmed[0]) &&
isdigit91(trimmed[1]) &&
isdigit91(trimmed[2]) &&
trimmed[3] == '}') {
// TODO: Use https://github.com/aprsorg/aprs-deviceid rather than hardcoding. A->g_altitude_ft = DW_METERS_TO_FEET((trimmed[0]-33)*91*91 + (trimmed[1]-33)*91 + (trimmed[2]-33) - 10000);
if (isT(*pfirst)) { process_comment (A, trimmed+4, strlen(trimmed) - 4);
return;
// "legacy" formats.
if (*pfirst == ' ' ) { strlcpy (A->g_mfr, "Original MIC-E", sizeof(A->g_mfr)); pfirst++; }
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-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 == ']' && *plast == '=') { strlcpy (A->g_mfr, "Kenwood TM-D710", sizeof(A->g_mfr)); pfirst++; plast--; }
else if (*pfirst == ']' ) { strlcpy (A->g_mfr, "Kenwood TM-D700", sizeof(A->g_mfr)); pfirst++; }
// ` should be used for message capable devices.
else if (*pfirst == '`' && *(plast-1) == '_' && *plast == ' ') { strlcpy (A->g_mfr, "Yaesu VX-8", sizeof(A->g_mfr)); pfirst++; plast-=2; }
else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '"') { strlcpy (A->g_mfr, "Yaesu FTM-350", sizeof(A->g_mfr)); pfirst++; plast-=2; }
else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '#') { strlcpy (A->g_mfr, "Yaesu VX-8G", sizeof(A->g_mfr)); pfirst++; plast-=2; }
else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '$') { strlcpy (A->g_mfr, "Yaesu FT1D", sizeof(A->g_mfr)); pfirst++; plast-=2; }
else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '%') { strlcpy (A->g_mfr, "Yaesu FTM-400DR", sizeof(A->g_mfr)); pfirst++; plast-=2; }
else if (*pfirst == '`' && *(plast-1) == '_' && *plast == ')') { strlcpy (A->g_mfr, "Yaesu FTM-100D", sizeof(A->g_mfr)); pfirst++; plast-=2; }
else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '(') { strlcpy (A->g_mfr, "Yaesu FT2D", 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 == '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 == '5') { strlcpy (A->g_mfr, "Anytone D578UV", sizeof(A->g_mfr)); pfirst++; plast-=2; }
else if (*pfirst == '`' ) { strlcpy (A->g_mfr, "Generic Mic-Emsg", sizeof(A->g_mfr)); pfirst++; }
// ' 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 == '3') { strlcpy (A->g_mfr, "Byonics TinyTrack3", sizeof(A->g_mfr)); pfirst++; plast-=2; }
else if (*pfirst == '\'' && *(plast-1) == '|' && *plast == '4') { strlcpy (A->g_mfr, "Byonics TinyTrack4", sizeof(A->g_mfr)); pfirst++; plast-=2; }
else if (*pfirst == '\'' && *(plast-1) == ':' && *plast == '4') { strlcpy (A->g_mfr, "SCS GmbH & Co. P4dragon DR-7400 modems", sizeof(A->g_mfr)); pfirst++; plast-=2; }
else if (*pfirst == '\'' && *(plast-1) == ':' && *plast == '8') { strlcpy (A->g_mfr, "SCS GmbH & Co. P4dragon DR-7800 modems", sizeof(A->g_mfr)); pfirst++; plast-=2; }
else if (*pfirst == '\'' ) { strlcpy (A->g_mfr, "Generic McTrackr", sizeof(A->g_mfr)); pfirst++; }
else if ( *(plast-1) == '\\' ) { strlcpy (A->g_mfr, "Hamhud ?", sizeof(A->g_mfr)); pfirst++; plast-=2; }
else if ( *(plast-1) == '/' ) { strlcpy (A->g_mfr, "Argent ?", sizeof(A->g_mfr)); pfirst++; plast-=2; }
else if ( *(plast-1) == '^' ) { strlcpy (A->g_mfr, "HinzTec anyfrog", sizeof(A->g_mfr)); pfirst++; plast-=2; }
else if ( *(plast-1) == '*' ) { strlcpy (A->g_mfr, "APOZxx www.KissOZ.dk Tracker. OZ1EKD and OZ7HVO", sizeof(A->g_mfr)); pfirst++; plast-=2; }
else if ( *(plast-1) == '~' ) { strlcpy (A->g_mfr, "Unknown OTHER", sizeof(A->g_mfr)); pfirst++; plast-=2; }
} }
/* process_comment (A, trimmed, strlen(trimmed));
* An optional altitude is next.
* It is three base-91 digits followed by "}".
* The TM-D710A might have encoding bug. This was observed:
*
* KJ4ETP-9>SUUP9Q,KE4OTZ-3,WIDE1*,WIDE2-1,qAR,KI4HDU-2:`oV$n6:>/]"7&}162.475MHz <Knox,TN> clintserman@gmail=
* N 35 50.9100, W 083 58.0800, 25 MPH, course 230, alt 945 ft, 162.475MHz
*
* KJ4ETP-9>SUUP6Y,GRNTOP-3*,WIDE2-1,qAR,KI4HDU-2:`oU~nT >/]<0x9a>xt}162.475MHz <Knox,TN> clintserman@gmail=
* Invalid character in MIC-E altitude. Must be in range of '!' to '{'.
* N 35 50.6900, W 083 57.9800, 29 MPH, course 204, alt 3280843 ft, 162.475MHz
*
* KJ4ETP-9>SUUP6Y,N4NEQ-3,K4EGA-1,WIDE2*,qAS,N5CWH-1:`oU~nT >/]?xt}162.475MHz <Knox,TN> clintserman@gmail=
* N 35 50.6900, W 083 57.9800, 29 MPH, course 204, alt 808497 ft, 162.475MHz
*
* KJ4ETP-9>SUUP2W,KE4OTZ-3,WIDE1*,WIDE2-1,qAR,KI4HDU-2:`oV2o"J>/]"7)}162.475MHz <Knox,TN> clintserman@gmail=
* N 35 50.2700, W 083 58.2200, 35 MPH, course 246, alt 955 ft, 162.475MHz
*
* Note the <0x9a> which is outside of the 7-bit ASCII range. Clearly very wrong.
*/
if (plast > pfirst && pfirst[3] == '}') { } // end aprs_mic_e
A->g_altitude_ft = DW_METERS_TO_FEET((pfirst[0]-33)*91*91 + (pfirst[1]-33)*91 + (pfirst[2]-33) - 10000);
if ( ! isdigit91(pfirst[0]) || ! isdigit91(pfirst[1]) || ! isdigit91(pfirst[2]))
{
if ( ! A->g_quiet) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Invalid character in MIC-E altitude. Must be in range of '!' to '{'.\n");
dw_printf("Bogus altitude of %.0f changed to unknown.\n", A->g_altitude_ft);
}
A->g_altitude_ft = G_UNKNOWN;
}
pfirst += 4;
}
process_comment (A, (char*)pfirst, (int)(plast - pfirst) + 1);
}
/*------------------------------------------------------------------ /*------------------------------------------------------------------
@ -4251,221 +4166,6 @@ static int data_extension_comment (decode_aprs_t *A, char *pdext)
} }
/*------------------------------------------------------------------
*
* Function: decode_tocall
*
* Purpose: Extract application from the destination.
*
* Inputs: dest - Destination address.
* Don't care if SSID is present or not.
*
* Outputs: A->g_mfr
*
* Description: For maximum flexibility, we will read the
* data file at run time rather than compiling it in.
*
* For the most recent version, download from:
*
* http://www.aprs.org/aprs11/tocalls.txt
*
* Windows version: File must be in current working directory.
*
* Linux version: Search order is current working directory then
* /usr/local/share/direwolf
* /usr/share/direwolf/tocalls.txt
*
* Mac: Like Linux and then
* /opt/local/share/direwolf
*
*------------------------------------------------------------------*/
// If I was more ambitious, this would dynamically allocate enough
// storage based on the file contents. Just stick in a constant for
// now. This takes an insignificant amount of space and
// I don't anticipate tocalls.txt growing that quickly.
// Version 1.4 - add message if too small instead of silently ignoring the rest.
// Dec. 2016 tocalls.txt has 153 destination addresses.
#define MAX_TOCALLS 250
static struct tocalls_s {
unsigned char len;
char prefix[7];
char *description;
} tocalls[MAX_TOCALLS];
static int num_tocalls = 0;
// Make sure the array is null terminated.
// If search order is changed, do the same in symbols.c for consistency.
static const char *search_locations[] = {
(const char *) "tocalls.txt", // CWD
(const char *) "data/tocalls.txt", // Windows with CMake
(const char *) "../data/tocalls.txt", // ?
#ifndef __WIN32__
(const char *) "/usr/local/share/direwolf/tocalls.txt",
(const char *) "/usr/share/direwolf/tocalls.txt",
#endif
#if __APPLE__
// https://groups.yahoo.com/neo/groups/direwolf_packet/conversations/messages/2458
// Adding the /opt/local tree since macports typically installs there. Users might want their
// INSTALLDIR (see Makefile.macosx) to mirror that. If so, then we need to search the /opt/local
// path as well.
(const char *) "/opt/local/share/direwolf/tocalls.txt",
#endif
(const char *) NULL // Important - Indicates end of list.
};
static int tocall_cmp (const void *px, const void *py)
{
const struct tocalls_s *x = (struct tocalls_s *)px;
const struct tocalls_s *y = (struct tocalls_s *)py;
if (x->len != y->len) return (y->len - x->len);
return (strcmp(x->prefix, y->prefix));
}
static void decode_tocall (decode_aprs_t *A, char *dest)
{
FILE *fp = 0;
int n = 0;
static int first_time = 1;
char stuff[100];
char *p = NULL;
char *r = NULL;
//dw_printf("debug: decode_tocall(\"%s\")\n", dest);
/*
* Extract the calls and descriptions from the file.
*
* Use only lines with exactly these formats:
*
* APN Network nodes, digis, etc
* APWWxx APRSISCE win32 version
* | | |
* 00000000001111111111
* 01234567890123456789...
*
* Matching will be with only leading upper case and digits.
*/
// TODO: Look for this in multiple locations.
// For example, if application was installed in /usr/local/bin,
// we might want to put this in /usr/local/share/aprs
// If search strategy changes, be sure to keep symbols_init in sync.
if (first_time) {
n = 0;
fp = NULL;
do {
if(search_locations[n] == NULL) break;
fp = fopen(search_locations[n++], "r");
} while (fp == NULL);
if (fp != NULL) {
while (fgets(stuff, sizeof(stuff), fp) != NULL && num_tocalls < MAX_TOCALLS) {
p = stuff + strlen(stuff) - 1;
while (p >= stuff && (*p == '\r' || *p == '\n')) {
*p-- = '\0';
}
// dw_printf("debug: %s\n", stuff);
if (stuff[0] == ' ' &&
stuff[4] == ' ' &&
stuff[5] == ' ' &&
stuff[6] == 'A' &&
stuff[7] == 'P' &&
stuff[12] == ' ' &&
stuff[13] == ' ' ) {
p = stuff + 6;
r = tocalls[num_tocalls].prefix;
while (isupper((int)(*p)) || isdigit((int)(*p))) {
*r++ = *p++;
}
*r = '\0';
if (strlen(tocalls[num_tocalls].prefix) > 2) {
tocalls[num_tocalls].description = strdup(stuff+14);
tocalls[num_tocalls].len = strlen(tocalls[num_tocalls].prefix);
// dw_printf("debug %d: %d '%s' -> '%s'\n", num_tocalls, tocalls[num_tocalls].len, tocalls[num_tocalls].prefix, tocalls[num_tocalls].description);
num_tocalls++;
}
}
else if (stuff[0] == ' ' &&
stuff[1] == 'A' &&
stuff[2] == 'P' &&
isupper((int)(stuff[3])) &&
stuff[4] == ' ' &&
stuff[5] == ' ' &&
stuff[6] == ' ' &&
stuff[12] == ' ' &&
stuff[13] == ' ' ) {
p = stuff + 1;
r = tocalls[num_tocalls].prefix;
while (isupper((int)(*p)) || isdigit((int)(*p))) {
*r++ = *p++;
}
*r = '\0';
if (strlen(tocalls[num_tocalls].prefix) > 2) {
tocalls[num_tocalls].description = strdup(stuff+14);
tocalls[num_tocalls].len = strlen(tocalls[num_tocalls].prefix);
// dw_printf("debug %d: %d '%s' -> '%s'\n", num_tocalls, tocalls[num_tocalls].len, tocalls[num_tocalls].prefix, tocalls[num_tocalls].description);
num_tocalls++;
}
}
if (num_tocalls == MAX_TOCALLS) { // oops. might have discarded some.
text_color_set(DW_COLOR_ERROR);
dw_printf("MAX_TOCALLS needs to be larger than %d to handle contents of 'tocalls.txt'.\n", MAX_TOCALLS);
}
}
fclose(fp);
/*
* Sort by decreasing length so the search will go
* from most specific to least specific.
* Example: APY350 or APY008 would match those specific
* models before getting to the more generic APY.
*/
qsort (tocalls, num_tocalls, sizeof(struct tocalls_s), tocall_cmp);
}
else {
if ( ! A->g_quiet) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Warning: Could not open 'tocalls.txt'.\n");
dw_printf("System types in the destination field will not be decoded.\n");
}
}
first_time = 0;
//for (n=0; n<num_tocalls; n++) {
// dw_printf("sorted %d: %d '%s' -> '%s'\n", n, tocalls[n].len, tocalls[n].prefix, tocalls[n].description);
//}
}
for (n=0; n<num_tocalls; n++) {
if (strncmp(dest, tocalls[n].prefix, tocalls[n].len) == 0) {
strlcpy (A->g_mfr, tocalls[n].description, sizeof(A->g_mfr));
return;
}
}
} /* end decode_tocall */
@ -4513,7 +4213,7 @@ static void substr_se (char *dest, const char *src, int start, int endp1)
* clen - Length of comment or -1 to take it all. * clen - Length of comment or -1 to take it all.
* *
* Outputs: A->g_telemetry - Base 91 telemetry |ss1122| * Outputs: A->g_telemetry - Base 91 telemetry |ss1122|
* A->g_altitude_ft - from /A=123456 * A->g_altitude_ft - from /A=123456 or /A=-12345
* A->g_lat - Might be adjusted from !DAO! * A->g_lat - Might be adjusted from !DAO!
* A->g_lon - Might be adjusted from !DAO! * A->g_lon - Might be adjusted from !DAO!
* A->g_aprstt_loc - Private extension to !DAO! * A->g_aprstt_loc - Private extension to !DAO!
@ -4543,6 +4243,10 @@ static void substr_se (char *dest, const char *src, int start, int endp1)
* Protocol reference, end of chapter 6. * Protocol reference, end of chapter 6.
* *
* /A=123456 Altitude * /A=123456 Altitude
* /A=-12345 Enhancement - There are many places on the earth's
* surface but the APRS spec has no provision for negative
* numbers. I propose having 5 digits for a consistent
* field width. 6 would be excessive.
* *
* What can appear in a comment? * What can appear in a comment?
* *
@ -4708,7 +4412,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen)
dw_printf("%s:%d: %s\n", __FILE__, __LINE__, emsg); dw_printf("%s:%d: %s\n", __FILE__, __LINE__, emsg);
} }
e = regcomp (&alt_re, "/A=[0-9][0-9][0-9][0-9][0-9][0-9]", REG_EXTENDED); e = regcomp (&alt_re, "/A=[0-9-][0-9][0-9][0-9][0-9][0-9]", REG_EXTENDED);
if (e) { if (e) {
regerror (e, &alt_re, emsg, sizeof(emsg)); regerror (e, &alt_re, emsg, sizeof(emsg));
dw_printf("%s:%d: %s\n", __FILE__, __LINE__, emsg); dw_printf("%s:%d: %s\n", __FILE__, __LINE__, emsg);
@ -5068,7 +4772,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen)
} }
/* /*
* Altitude in feet. /A=123456 * Altitude in feet. /A=123456 or /A=-12345
*/ */
if (regexec (&alt_re, A->g_comment, MAXMATCH, match, 0) == 0) if (regexec (&alt_re, A->g_comment, MAXMATCH, match, 0) == 0)
@ -5186,7 +4890,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen)
* *
* Function: main * Function: main
* *
* Purpose: Main program for standalone test program. * Purpose: Main program for standalone application to parse and explain APRS packets.
* *
* Inputs: stdin for raw data to decode. * Inputs: stdin for raw data to decode.
* This is in the usual display format either from * This is in the usual display format either from
@ -5332,6 +5036,7 @@ int main (int argc, char *argv[])
// If you don't like the text colors, use 0 instead of 1 here. // If you don't like the text colors, use 0 instead of 1 here.
text_color_init(1); text_color_init(1);
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
deviceid_init();
while (fgets(stuff, sizeof(stuff), stdin) != NULL) while (fgets(stuff, sizeof(stuff), stdin) != NULL)
{ {

668
src/deviceid.c Normal file
View File

@ -0,0 +1,668 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2023 John Langner, WB2OSZ
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
/*------------------------------------------------------------------
*
* File: deviceid.c
*
* Purpose: Determine the device identifier from the destination field,
* or from prefix/suffix for MIC-E format.
*
* Description: Orginally this used the tocalls.txt file and was part of decode_aprs.c.
* For release 1.8, we use tocalls.yaml and this is split into a separate file.
*
*------------------------------------------------------------------*/
//#define TEST 1 // Standalone test. $ gcc -DTEST deviceid.c && ./a.out
#if TEST
#define HAVE_STRLCPY 1 // prevent defining in direwolf.h
#define HAVE_STRLCAT 1
#endif
#include "direwolf.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "deviceid.h"
#include "textcolor.h"
static void unquote (int line, char *pin, char *pout);
static int tocall_cmp (const void *px, const void *py);
static int mice_cmp (const void *px, const void *py);
/*------------------------------------------------------------------
*
* Function: main
*
* Purpose: A little self-test used during development.
*
* Description: Read the yaml file. Decipher a few typical values.
*
*------------------------------------------------------------------*/
#if TEST
// So we don't need to link with any other files.
#define dw_printf printf
void text_color_set(dw_color_t) { return; }
void strlcpy(char *dst, char *src, size_t dlen) {
strcpy (dst, src);
}
void strlcat(char *dst, char *src, size_t dlen) {
strcat (dst, src);
}
int main (int argc, char *argv[])
{
char device[80];
char comment_out[80];
deviceid_init ();
dw_printf ("\n");
dw_printf ("Testing ...\n");
// MIC-E Legacy (really Kenwood).
deviceid_decode_mice (">Comment", comment_out, sizeof(comment_out), device, sizeof(device));
dw_printf ("%s %s\n", comment_out, device);
assert (strcmp(comment_out, "Comment") == 0);
assert (strcmp(device, "Kenwood TH-D7A") == 0);
deviceid_decode_mice (">Comment^", comment_out, sizeof(comment_out), device, sizeof(device));
dw_printf ("%s %s\n", comment_out, device);
assert (strcmp(comment_out, "Comment") == 0);
assert (strcmp(device, "Kenwood TH-D74") == 0);
deviceid_decode_mice ("]Comment", comment_out, sizeof(comment_out), device, sizeof(device));
dw_printf ("%s %s\n", comment_out, device);
assert (strcmp(comment_out, "Comment") == 0);
assert (strcmp(device, "Kenwood TM-D700") == 0);
deviceid_decode_mice ("]Comment=", comment_out, sizeof(comment_out), device, sizeof(device));
dw_printf ("%s %s\n", comment_out, device);
assert (strcmp(comment_out, "Comment") == 0);
assert (strcmp(device, "Kenwood TM-D710") == 0);
deviceid_decode_mice ("]\"4V}=", comment_out, sizeof(comment_out), device, sizeof(device));
dw_printf ("%s %s\n", comment_out, device);
assert (strcmp(comment_out, "\"4V}") == 0);
assert (strcmp(device, "Kenwood TM-D710") == 0);
// Modern MIC-E.
deviceid_decode_mice ("`Comment_\"", comment_out, sizeof(comment_out), device, sizeof(device));
dw_printf ("%s %s\n", comment_out, device);
assert (strcmp(comment_out, "Comment") == 0);
assert (strcmp(device, "Yaesu FTM-350") == 0);
deviceid_decode_mice ("`Comment_ ", comment_out, sizeof(comment_out), device, sizeof(device));
dw_printf ("%s %s\n", comment_out, device);
assert (strcmp(comment_out, "Comment") == 0);
assert (strcmp(device, "Yaesu VX-8") == 0);
deviceid_decode_mice ("'Comment|3", comment_out, sizeof(comment_out), device, sizeof(device));
dw_printf ("%s %s\n", comment_out, device);
assert (strcmp(comment_out, "Comment") == 0);
assert (strcmp(device, "Byonics TinyTrak3") == 0);
deviceid_decode_mice ("Comment", comment_out, sizeof(comment_out), device, sizeof(device));
dw_printf ("%s %s\n", comment_out, device);
assert (strcmp(comment_out, "Comment") == 0);
assert (strcmp(device, "UNKNOWN vendor/model") == 0);
// Tocall
deviceid_decode_dest ("APDW18", device, sizeof(device));
dw_printf ("%s\n", device);
assert (strcmp(device, "WB2OSZ DireWolf") == 0);
deviceid_decode_dest ("APD123", device, sizeof(device));
dw_printf ("%s\n", device);
assert (strcmp(device, "Open Source aprsd") == 0);
// null for Vendor.
deviceid_decode_dest ("APAX", device, sizeof(device));
dw_printf ("%s\n", device);
assert (strcmp(device, "AFilterX") == 0);
deviceid_decode_dest ("APA123", device, sizeof(device));
dw_printf ("%s\n", device);
assert (strcmp(device, "UNKNOWN vendor/model") == 0);
dw_printf ("\n");
dw_printf ("Success!\n");
exit (EXIT_SUCCESS);
}
#endif // TEST
// Structures to hold mapping from encoded form to vendor and model.
// The .yaml file has two separate sections for MIC-E but they can
// both be handled as a single more general case.
struct mice {
char prefix[4]; // The legacy form has 1 prefix character.
// The newer form has none. (more accurately ` or ')
char suffix[4]; // The legacy form has 0 or 1.
// The newer form has 2.
char *vendor;
char *model;
};
struct tocalls {
char tocall[8]; // Up to 6 characters. Some may have wildcards at the end.
// Most often they are trailing "??" or "?" or "???" in one case.
// Sometimes there is trailing "nnn". Does that imply digits only?
// Sometimes we see a trailing "*". Is "*" different than "?"?
// There are a couple bizzare cases like APnnnD which can
// create an ambigious situation. APMPAD, APRFGD, APY0[125]D.
// Screw them if they can't follow the rules. I'm not putting in a special case.
char *vendor;
char *model;
};
static struct mice *pmice = NULL; // Pointer to array.
static int mice_count = 0; // Number of allocated elements.
static int mice_index = -1; // Current index for filling in.
static struct tocalls *ptocalls = NULL; // Pointer to array.
static int tocalls_count = 0; // Number of allocated elements.
static int tocalls_index = -1; // Current index for filling in.
/*------------------------------------------------------------------
*
* Function: deviceid_init
*
* Purpose: Called once at startup to read the tocalls.yaml file which was obtained from
* https://github.com/aprsorg/aprs-deviceid .
*
* Inputs: tocalls.yaml with OS specific directory search list.
*
* Outputs: static variables listed above.
*
* Description: For maximum flexibility, we will read the
* data file at run time rather than compiling it in.
*
*------------------------------------------------------------------*/
// Make sure the array is null terminated.
// If search order is changed, do the same in symbols.c for consistency.
// fopen is perfectly happy with / in file path when running on Windows.
static const char *search_locations[] = {
(const char *) "tocalls.yaml", // Current working directory
(const char *) "data/tocalls.yaml", // Windows with CMake
(const char *) "../data/tocalls.yaml", // Source tree
#ifndef __WIN32__
(const char *) "/usr/local/share/direwolf/tocalls.yaml",
(const char *) "/usr/share/direwolf/tocalls.yaml",
#endif
#if __APPLE__
// https://groups.yahoo.com/neo/groups/direwolf_packet/conversations/messages/2458
// Adding the /opt/local tree since macports typically installs there. Users might want their
// INSTALLDIR (see Makefile.macosx) to mirror that. If so, then we need to search the /opt/local
// path as well.
(const char *) "/opt/local/share/direwolf/tocalls.yaml",
#endif
(const char *) NULL // Important - Indicates end of list.
};
void deviceid_init(void)
{
FILE *fp = NULL;
for (int n = 0; search_locations[n] != NULL && fp == NULL; n++) {
#if TEST
text_color_set(DW_COLOR_INFO);
dw_printf ("Trying %s\n", search_locations[n]);
#endif
fp = fopen(search_locations[n], "r");
#if TEST
if (fp != NULL) {
dw_printf ("Opened %s\n", search_locations[n]);
}
#endif
};
if (fp == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Could not open any of these file locations:\n");
for (int n = 0; search_locations[n] != NULL; n++) {
dw_printf (" %s\n", search_locations[n]);
}
dw_printf("It won't be possible to extract device identifiers from packets.\n");
return;
};
// Read file first time to get number of items.
// Allocate required space.
// Rewind.
// Read file second time to gather data.
enum { no_section, mice_section, tocalls_section} section = no_section;
char stuff[80];
for (int pass = 1; pass <=2; pass++) {
int line = 0; // Line number within file.
while (fgets(stuff, sizeof(stuff), fp)) {
line++;
// Remove trailing CR/LF or spaces.
char *p = stuff + strlen(stuff) - 1;
while (p >= (char*)stuff && (*p == '\r' || *p == '\n' || *p == ' ')) {
*p-- = '\0';
}
// Ignore comment lines.
if (stuff[0] == '#') {
continue;
}
#if TEST
//dw_printf ("%d: %s\n", line, stuff);
#endif
// This is not very robust; everything better be in exactly the right format.
if (strncmp(stuff, "mice:", strlen("mice:")) == 0) {
section = mice_section;
#if TEST
dw_printf ("Pass %d, line %d, MIC-E section\n", pass, line);
#endif
}
else if (strncmp(stuff, "micelegacy:", strlen("micelegacy:")) == 0) {
section = mice_section; // treat both same.
#if TEST
dw_printf ("Pass %d, line %d, Legacy MIC-E section\n", pass, line);
#endif
}
else if (strncmp(stuff, "tocalls:", strlen("tocalls:")) == 0) {
section = tocalls_section;
#if TEST
dw_printf ("Pass %d, line %d, TOCALLS section\n", pass, line);
#endif
}
// The first property of an item is preceded by " - ".
if (pass == 1 && strncmp(stuff, " - ", 3) == 0) {
switch (section) {
case no_section: break;
case mice_section: mice_count++; break;
case tocalls_section: tocalls_count++; break;
}
}
if (pass == 2) {
switch (section) {
case no_section:
break;
case mice_section:
if (strncmp(stuff, " - ", 3) == 0) {
mice_index++;
assert (mice_index >= 0 && mice_index < mice_count);
}
if (strncmp(stuff+3, "prefix: ", strlen("prefix: ")) == 0) {
unquote (line, stuff+3+8, pmice[mice_index].prefix);
}
else if (strncmp(stuff+3, "suffix: ", strlen("suffix: ")) == 0) {
unquote (line, stuff+3+8, pmice[mice_index].suffix);
}
else if (strncmp(stuff+3, "vendor: ", strlen("vendor: ")) == 0) {
pmice[mice_index].vendor = strdup(stuff+3+8);
}
else if (strncmp(stuff+3, "model: ", strlen("model: ")) == 0) {
pmice[mice_index].model = strdup(stuff+3+7);
}
break;
case tocalls_section:
if (strncmp(stuff, " - ", 3) == 0) {
tocalls_index++;
assert (tocalls_index >= 0 && tocalls_index < tocalls_count);
}
if (strncmp(stuff+3, "tocall: ", strlen("tocall: ")) == 0) {
// Remove trailing wildcard characters ? * n
char *r = stuff + strlen(stuff) - 1;
while (r >= (char*)stuff && (*r == '?' || *r == '*' || *r == 'n')) {
*r-- = '\0';
}
strlcpy (ptocalls[tocalls_index].tocall, stuff+3+8, sizeof(ptocalls[tocalls_index].tocall));
// Remove trailing CR/LF or spaces.
char *p = stuff + strlen(stuff) - 1;
while (p >= (char*)stuff && (*p == '\r' || *p == '\n' || *p == ' ')) {
*p-- = '\0';
}
}
else if (strncmp(stuff+3, "vendor: ", strlen("vendor: ")) == 0) {
ptocalls[tocalls_index].vendor = strdup(stuff+3+8);
}
else if (strncmp(stuff+3, "model: ", strlen("model: ")) == 0) {
ptocalls[tocalls_index].model = strdup(stuff+3+7);
}
break;
}
}
} // while(fgets
if (pass == 1) {
#if TEST
dw_printf ("deviceid sizes %d %d\n", mice_count, tocalls_count);
#endif
pmice = calloc(sizeof(struct mice), mice_count);
ptocalls = calloc(sizeof(struct tocalls), tocalls_count);
rewind (fp);
section = no_section;
}
} // for pass = 1 or 2
fclose (fp);
assert (mice_index == mice_count - 1);
assert (tocalls_index == tocalls_count - 1);
// MIC-E Legacy needs to be sorted so those with suffix come first.
qsort (pmice, mice_count, sizeof(struct mice), mice_cmp);
// Sort tocalls by decreasing length so the search will go from most specific to least specific.
// Example: APY350 or APY008 would match those specific models before getting to the more generic APY.
qsort (ptocalls, tocalls_count, sizeof(struct tocalls), tocall_cmp);
#if TEST
dw_printf ("MIC-E:\n");
for (int i = 0; i < mice_count; i++) {
dw_printf ("%s %s %s\n", pmice[i].suffix, pmice[i].vendor, pmice[i].model);
}
dw_printf ("TOCALLS:\n");
for (int i = 0; i < tocalls_count; i++) {
dw_printf ("%s %s %s\n", ptocalls[i].tocall, ptocalls[i].vendor, ptocalls[i].model);
}
#endif
return;
} // end deviceid_init
/*------------------------------------------------------------------
*
* Function: unquote
*
* Purpose: Remove surrounding quotes and undo any escapes.
*
* Inputs: line - File line number for error message.
*
* in - String with quotes. Might contain \ escapes.
*
* Outputs: out - Quotes and escapes removed.
* Limited to 2 characters to avoid buffer overflow.
*
* Examples: in out
* "_#" _#
* "_\"" _"
* "=" =
*
*------------------------------------------------------------------*/
static void unquote (int line, char *pin, char *pout)
{
int count = 0;
*pout = '\0';
if (*pin != '"') {
text_color_set(DW_COLOR_ERROR);
dw_printf("Missing leading \" for %s on line %d.\n", pin, line);
return;
}
pin++;
while (*pin != '\0' && *pin != '\"' && count < 2) {
if (*pin == '\\') {
pin++;
}
*pout++ = *pin++;
count++;
}
*pout = '\0';
if (*pin != '"') {
text_color_set(DW_COLOR_ERROR);
dw_printf("Missing trailing \" or string too long on line %d.\n", line);
return;
}
}
// Used to sort the tocalls by length.
// When length is equal, alphabetically.
static int tocall_cmp (const void *px, const void *py)
{
const struct tocalls *x = (struct tocalls *)px;
const struct tocalls *y = (struct tocalls *)py;
if (strlen(x->tocall) != strlen(y->tocall)) {
return (strlen(y->tocall) - strlen(x->tocall));
}
return (strcmp(x->tocall, y->tocall));
}
// Used to sort the suffixes by length.
// Longer at the top.
// Example check for >xxx^ before >xxx .
static int mice_cmp (const void *px, const void *py)
{
const struct mice *x = (struct mice *)px;
const struct mice *y = (struct mice *)py;
return (strlen(y->suffix) - strlen(x->suffix));
}
/*------------------------------------------------------------------
*
* Function: deviceid_decode_dest
*
* Purpose: Find vendor/model for destination address of form APxxxx.
*
* Inputs: dest - Destination address. No SSID.
*
* device_size - Amount of space available for result to avoid buffer overflow.
*
* Outputs: device - Vendor and model.
*
* Description: With the exception of MIC-E format, we expect to find the vendor/model in the
* AX.25 destination field. The form should be APxxxx.
*
* Search the list looking for the maximum length match.
* For example,
* APXR = Xrouter
* APX = Xastir
*
*------------------------------------------------------------------*/
void deviceid_decode_dest (char *dest, char *device, size_t device_size)
{
*device = '\0';
if (ptocalls == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf("deviceid_decode_dest called without any deviceid data.\n");
return;
}
for (int n = 0; n < tocalls_count; n++) {
if (strncmp(dest, ptocalls[n].tocall, strlen(ptocalls[n].tocall)) == 0) {
if (ptocalls[n].vendor != NULL) {
strlcpy (device, ptocalls[n].vendor, device_size);
}
if (ptocalls[n].vendor != NULL && ptocalls[n].model != NULL) {
strlcat (device, " ", device_size);
}
if (ptocalls[n].model != NULL) {
strlcat (device, ptocalls[n].model, device_size);
}
return;
}
}
strlcpy (device, "UNKNOWN vendor/model", device_size);
} // end deviceid_decode_dest
/*------------------------------------------------------------------
*
* Function: deviceid_decode_mice
*
* Purpose: Find vendor/model for MIC-E comment.
*
* Inputs: comment - MIC-E comment that might have vendor/model encoded as
* a prefix and/or suffix.
*
* trimmed_size - Amount of space available for result to avoid buffer overflow.
*
* device_size - Amount of space available for result to avoid buffer overflow.
*
* Outputs: trimmed - Final comment with device vendor/model removed.
*
* device - Vendor and model.
*
* Description: This has a tortured history.
*
* The Kenwood TH-D7A put ">" at the beginning of the comment.
* The Kenwood TM-D700 put "]" at the beginning of the comment.
* Later Kenwood models also added a single suffix character
* using a character very unlikely to appear at the end of a comment.
*
* The later convention, used by everyone else, is to have a prefix of ` or '
* and a suffix of two characters. The suffix characters need to be
* something very unlikely to be found at the end of a comment.
*
* A receiving device is expected to remove those extra characters
* before displaying the comment.
*
* References: http://www.aprs.org/aprs12/mic-e-types.txt
* http://www.aprs.org/aprs12/mic-e-examples.txt
*
*------------------------------------------------------------------*/
// The strncmp documentation doesn't mention behavior if length is zero.
// Do our own just to be safe.
static inline int strncmp_z (char *a, char *b, size_t len)
{
int result = 0;
if (len > 0) {
result = strncmp(a, b, len);
}
//dw_printf ("Comparing '%s' and '%s' len %d result %d\n", a, b, len, result);
return result;
}
void deviceid_decode_mice (char *comment, char *trimmed, size_t trimmed_size, char *device, size_t device_size)
{
*device = '\0';
if (ptocalls == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf("deviceid_decode_mice called without any deviceid data.\n");
return;
}
// The Legacy format has an explicit prefix in the table.
// For others, it must be ` or ' to indicate whether messaging capable.
for (int n = 0; n < mice_count; n++) {
if ((strlen(pmice[n].prefix) != 0 && // Legacy
strncmp_z(comment, // prefix from table
pmice[n].prefix,
strlen(pmice[n].prefix)) == 0 &&
strncmp_z(comment + strlen(comment) - strlen(pmice[n].suffix), // possible suffix
pmice[n].suffix,
strlen(pmice[n].suffix)) == 0) ||
(strlen(pmice[n].prefix) == 0 && // Later
(comment[0] == '`' || comment[0] == '\'') && // prefix ` or '
strncmp_z(comment + strlen(comment) - strlen(pmice[n].suffix), // suffix
pmice[n].suffix,
strlen(pmice[n].suffix)) == 0) ) {
if (pmice[n].vendor != NULL) {
strlcpy (device, pmice[n].vendor, device_size);
}
if (pmice[n].vendor != NULL && pmice[n].model != NULL) {
strlcat (device, " ", device_size);
}
if (pmice[n].model != NULL) {
strlcat (device, pmice[n].model, device_size);
}
// Remove any prefix/suffix and return what remains.
strlcpy (trimmed, comment + 1, trimmed_size);
trimmed[strlen(comment) - 1 - strlen(pmice[n].suffix)] = '\0';
return;
}
}
// Not found.
strlcpy (device, "UNKNOWN vendor/model", device_size);
} // end deviceid_decode_mice
// end deviceid.c

6
src/deviceid.h Normal file
View File

@ -0,0 +1,6 @@
// deviceid.h
void deviceid_init(void);
void deviceid_decode_dest (char *dest, char *device, size_t device_size);
void deviceid_decode_mice (char *comment, char *trimmed, size_t trimmed_size, char *device, size_t device_size);

View File

@ -129,6 +129,7 @@
#include "dwsock.h" #include "dwsock.h"
#include "dns_sd_dw.h" #include "dns_sd_dw.h"
#include "dlq.h" // for fec_type_t definition. #include "dlq.h" // for fec_type_t definition.
#include "deviceid.h"
//static int idx_decoded = 0; //static int idx_decoded = 0;
@ -186,7 +187,7 @@ static int d_u_opt = 0; /* "-d u" command line option to print UTF-8 also in h
static int d_p_opt = 0; /* "-d p" option for dumping packets over radio. */ static int d_p_opt = 0; /* "-d p" option for dumping packets over radio. */
static int q_h_opt = 0; /* "-q h" Quiet, suppress the "heard" line with audio level. */ static int q_h_opt = 0; /* "-q h" Quiet, suppress the "heard" line with audio level. */
static int q_d_opt = 0; /* "-q d" Quiet, suppress the printing of decoded of APRS packets. */ static int q_d_opt = 0; /* "-q d" Quiet, suppress the printing of description of APRS packets. */
static int A_opt_ais_to_obj = 0; /* "-A" Convert received AIS to APRS "Object Report." */ static int A_opt_ais_to_obj = 0; /* "-A" Convert received AIS to APRS "Object Report." */
@ -302,24 +303,27 @@ int main (int argc, char *argv[])
text_color_init(t_opt); text_color_init(t_opt);
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
//dw_printf ("Dire Wolf version %d.%d (%s) BETA TEST 7\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); //dw_printf ("Dire Wolf version %d.%d (%s) BETA TEST 7\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
//dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "G", __DATE__); dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "A", __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 || USE_GPIOD
dw_printf ("Includes optional support for: "); dw_printf ("Includes optional support for: ");
#if defined(ENABLE_GPSD) #if defined(ENABLE_GPSD)
dw_printf (" gpsd"); dw_printf (" gpsd");
#endif #endif
#if defined(USE_HAMLIB) #if defined(USE_HAMLIB)
dw_printf (" hamlib"); dw_printf (" hamlib");
#endif #endif
#if defined(USE_CM108) #if defined(USE_CM108)
dw_printf (" cm108-ptt"); dw_printf (" cm108-ptt");
#endif #endif
#if (USE_AVAHI_CLIENT|USE_MACOS_DNSSD) #if defined(USE_GPIOD)
dw_printf (" libgpiod");
#endif
#if (USE_AVAHI_CLIENT|USE_MACOS_DNSSD)
dw_printf (" dns-sd"); dw_printf (" dns-sd");
#endif #endif
dw_printf ("\n"); dw_printf ("\n");
#endif #endif
@ -982,6 +986,7 @@ int main (int argc, char *argv[])
* Files not supported at this time. * Files not supported at this time.
* Can always "cat" the file and pipe it into stdin. * Can always "cat" the file and pipe it into stdin.
*/ */
deviceid_init();
err = audio_open (&audio_config); err = audio_open (&audio_config);
if (err < 0) { if (err < 0) {
@ -1708,7 +1713,7 @@ static void usage (char **argv)
dw_printf (" d d = APRStt (DTMF to APRS object translation).\n"); dw_printf (" d d = APRStt (DTMF to APRS object translation).\n");
dw_printf (" -q Quiet (suppress output) options:\n"); dw_printf (" -q Quiet (suppress output) options:\n");
dw_printf (" h h = Heard line with the audio level.\n"); dw_printf (" h h = Heard line with the audio level.\n");
dw_printf (" d d = Decoding of APRS packets.\n"); dw_printf (" d d = Description of APRS packets.\n");
dw_printf (" x x = Silence FX.25 information.\n"); dw_printf (" x x = Silence FX.25 information.\n");
dw_printf (" -t n Text colors. 0=disabled. 1=default. 2,3,4,... alternatives.\n"); dw_printf (" -t n Text colors. 0=disabled. 1=default. 2,3,4,... alternatives.\n");
dw_printf (" Use 9 to test compatibility with your terminal.\n"); dw_printf (" Use 9 to test compatibility with your terminal.\n");

106
src/ptt.c
View File

@ -162,6 +162,10 @@
#include <hamlib/rig.h> #include <hamlib/rig.h>
#endif #endif
#ifdef USE_GPIOD
#include <gpiod.h>
#endif
/* So we can have more common code for fd. */ /* So we can have more common code for fd. */
typedef int HANDLE; typedef int HANDLE;
#define INVALID_HANDLE_VALUE (-1) #define INVALID_HANDLE_VALUE (-1)
@ -468,6 +472,20 @@ void export_gpio(int ch, int ot, int invert, int direction)
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Error writing \"%s\" to %s, errno=%d\n", stemp, gpio_export_path, e); dw_printf ("Error writing \"%s\" to %s, errno=%d\n", stemp, gpio_export_path, e);
dw_printf ("%s\n", strerror(e)); dw_printf ("%s\n", strerror(e));
if (e == 22) {
// It appears that error 22 occurs when sysfs gpio is not available.
// (See https://github.com/wb2osz/direwolf/issues/503)
//
// The solution might be to use the new gpiod approach.
dw_printf ("It looks like gpio with sysfs is not supported on this operating system.\n");
dw_printf ("Rather than the following form, in the configuration file,\n");
dw_printf (" PTT GPIO %s\n", stemp);
dw_printf ("try using gpiod form instead. e.g.\n");
dw_printf (" PTT GPIOD gpiochip0 %s\n", stemp);
dw_printf ("You can get a list of gpio chip names and corresponding I/O lines with \"gpioinfo\" command.\n");
}
exit (1); exit (1);
} }
} }
@ -634,6 +652,31 @@ void export_gpio(int ch, int ot, int invert, int direction)
get_access_to_gpio (gpio_value_path); get_access_to_gpio (gpio_value_path);
} }
#if defined(USE_GPIOD)
int gpiod_probe(const char *chip_name, int line_number)
{
struct gpiod_chip *chip;
chip = gpiod_chip_open_by_name(chip_name);
if (chip == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Can't open GPIOD chip %s.\n", chip_name);
return -1;
}
struct gpiod_line *line;
line = gpiod_chip_get_line(chip, line_number);
if (line == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Can't get GPIOD line %d.\n", line_number);
return -1;
}
if (ptt_debug_level >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf("GPIOD probe OK. Chip: %s line: %d\n", chip_name, line_number);
}
return 0;
}
#endif /* USE_GPIOD */
#endif /* not __WIN32__ */ #endif /* not __WIN32__ */
@ -650,7 +693,8 @@ void export_gpio(int ch, int ot, int invert, int direction)
* ptt_method Method for PTT signal. * ptt_method Method for PTT signal.
* PTT_METHOD_NONE - not configured. Could be using VOX. * PTT_METHOD_NONE - not configured. Could be using VOX.
* PTT_METHOD_SERIAL - serial (com) port. * PTT_METHOD_SERIAL - serial (com) port.
* PTT_METHOD_GPIO - general purpose I/O. * PTT_METHOD_GPIO - general purpose I/O (sysfs).
* PTT_METHOD_GPIOD - general purpose I/O (libgpiod).
* PTT_METHOD_LPT - Parallel printer port. * PTT_METHOD_LPT - Parallel printer port.
* PTT_METHOD_HAMLIB - HAMLib rig control. * PTT_METHOD_HAMLIB - HAMLib rig control.
* PTT_METHOD_CM108 - GPIO pins of CM108 etc. USB Audio. * PTT_METHOD_CM108 - GPIO pins of CM108 etc. USB Audio.
@ -729,12 +773,13 @@ void ptt_init (struct audio_s *audio_config_p)
if (ptt_debug_level >= 2) { if (ptt_debug_level >= 2) {
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("ch=%d, %s method=%d, device=%s, line=%d, gpio=%d, lpt_bit=%d, invert=%d\n", dw_printf ("ch=%d, %s method=%d, device=%s, line=%d, name=%s, gpio=%d, lpt_bit=%d, invert=%d\n",
ch, ch,
otnames[ot], otnames[ot],
audio_config_p->achan[ch].octrl[ot].ptt_method, audio_config_p->achan[ch].octrl[ot].ptt_method,
audio_config_p->achan[ch].octrl[ot].ptt_device, audio_config_p->achan[ch].octrl[ot].ptt_device,
audio_config_p->achan[ch].octrl[ot].ptt_line, audio_config_p->achan[ch].octrl[ot].ptt_line,
audio_config_p->achan[ch].octrl[ot].out_gpio_name,
audio_config_p->achan[ch].octrl[ot].out_gpio_num, audio_config_p->achan[ch].octrl[ot].out_gpio_num,
audio_config_p->achan[ch].octrl[ot].ptt_lpt_bit, audio_config_p->achan[ch].octrl[ot].ptt_lpt_bit,
audio_config_p->achan[ch].octrl[ot].ptt_invert); audio_config_p->achan[ch].octrl[ot].ptt_invert);
@ -880,7 +925,28 @@ void ptt_init (struct audio_s *audio_config_p)
if (using_gpio) { if (using_gpio) {
get_access_to_gpio ("/sys/class/gpio/export"); get_access_to_gpio ("/sys/class/gpio/export");
} }
#if defined(USE_GPIOD)
// GPIOD
for (ch = 0; ch < MAX_CHANS; ch++) {
if (save_audio_config_p->chan_medium[ch] == MEDIUM_RADIO) {
for (int ot = 0; ot < NUM_OCTYPES; ot++) {
if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_GPIOD) {
const char *chip_name = audio_config_p->achan[ch].octrl[ot].out_gpio_name;
int line_number = audio_config_p->achan[ch].octrl[ot].out_gpio_num;
int rc = gpiod_probe(chip_name, line_number);
if (rc < 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Disable PTT for channel %d\n", ch);
audio_config_p->achan[ch].octrl[ot].ptt_method = PTT_METHOD_NONE;
} else {
// Set initial state off ptt_set will invert output signal if appropriate.
ptt_set (ot, ch, 0);
}
}
}
}
}
#endif /* USE_GPIOD */
/* /*
* We should now be able to create the device nodes for * We should now be able to create the device nodes for
* the pins we want to use. * the pins we want to use.
@ -1123,7 +1189,27 @@ void ptt_init (struct audio_s *audio_config_p)
if (audio_config_p->chan_medium[ch] == MEDIUM_RADIO) { if (audio_config_p->chan_medium[ch] == MEDIUM_RADIO) {
if(audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_method == PTT_METHOD_NONE) { if(audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_method == PTT_METHOD_NONE) {
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("Note: PTT not configured for channel %d. (Ignore this if using VOX.)\n", ch); dw_printf ("\n");
dw_printf ("Note: PTT not configured for channel %d. (OK if using VOX.)\n", ch);
dw_printf ("When using VOX, ensure that it adds very little delay (e.g. 10-20) milliseconds\n");
dw_printf ("between the time that transmit audio ends and PTT is deactivated.\n");
dw_printf ("For example, if using a SignaLink USB, turn the DLY control all the\n");
dw_printf ("way counter clockwise.\n");
dw_printf ("\n");
dw_printf ("Using VOX built in to the radio is a VERY BAD idea. This is intended\n");
dw_printf ("for voice operation, with gaps in the sound, and typically has a delay of about a\n");
dw_printf ("half second between the time the audio stops and the transmitter is turned off.\n");
dw_printf ("When using APRS your transmiter will be sending a quiet carrier for\n");
dw_printf ("about a half second after your packet ends. This may interfere with the\n");
dw_printf ("the next station to transmit. This is being inconsiderate.\n");
dw_printf ("\n");
dw_printf ("If you are trying to use VOX with connected mode packet, expect\n");
dw_printf ("frustration and disappointment. Connected mode involves rapid responses\n");
dw_printf ("which you will probably miss because your transmitter is still on when\n");
dw_printf ("the response is being transmitted.\n");
dw_printf ("\n");
dw_printf ("Read the User Guide 'Transmit Timing' section for more details.\n");
dw_printf ("\n");
} }
} }
} }
@ -1298,6 +1384,18 @@ void ptt_set (int ot, int chan, int ptt_signal)
close (fd); close (fd);
} }
#if defined(USE_GPIOD)
if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_GPIOD) {
const char *chip = save_audio_config_p->achan[chan].octrl[ot].out_gpio_name;
int line = save_audio_config_p->achan[chan].octrl[ot].out_gpio_num;
int rc = gpiod_ctxless_set_value(chip, line, ptt, false, "direwolf", NULL, NULL);
if (ptt_debug_level >= 1) {
text_color_set(DW_COLOR_DEBUG);
dw_printf("PTT_METHOD_GPIOD chip: %s line: %d ptt: %d rc: %d\n", chip, line, ptt, rc);
}
}
#endif /* USE_GPIOD */
#endif #endif
/* /*

View File

@ -379,7 +379,7 @@ static void debug_print (fromto_t fromto, int client, struct agwpe_s *pmsg, int
case 'C': strlcpy (datakind, "AX.25 Connection Received", sizeof(datakind)); break; case 'C': strlcpy (datakind, "AX.25 Connection Received", sizeof(datakind)); break;
case 'D': strlcpy (datakind, "Connected AX.25 Data", sizeof(datakind)); break; case 'D': strlcpy (datakind, "Connected AX.25 Data", sizeof(datakind)); break;
case 'd': strlcpy (datakind, "Disconnected", sizeof(datakind)); break; case 'd': strlcpy (datakind, "Disconnected", sizeof(datakind)); break;
case 'M': strlcpy (datakind, "Monitored Connected Information", sizeof(datakind)); break; case 'I': strlcpy (datakind, "Monitored Connected Information", sizeof(datakind)); break;
case 'S': strlcpy (datakind, "Monitored Supervisory Information", sizeof(datakind)); break; case 'S': strlcpy (datakind, "Monitored Supervisory Information", sizeof(datakind)); break;
case 'U': strlcpy (datakind, "Monitored Unproto Information", sizeof(datakind)); break; case 'U': strlcpy (datakind, "Monitored Unproto Information", sizeof(datakind)); break;
case 'T': strlcpy (datakind, "Monitoring Own Information", sizeof(datakind)); break; case 'T': strlcpy (datakind, "Monitoring Own Information", sizeof(datakind)); break;
@ -820,7 +820,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
/* Stick in extra byte for the "TNC" to use. */ /* Stick in extra byte for the "TNC" to use. */
agwpe_msg.data[0] = 0; agwpe_msg.data[0] = chan << 4; // Was 0. Fixed in 1.8.
memcpy (agwpe_msg.data + 1, fbuf, (size_t)flen); memcpy (agwpe_msg.data + 1, fbuf, (size_t)flen);
if (debug_client) { if (debug_client) {
@ -1559,6 +1559,7 @@ static THREAD_F cmd_listen_thread (void *arg)
case MEDIUM_RADIO: case MEDIUM_RADIO:
{ {
// Misleading if using stdin or udp.
char stemp[100]; char stemp[100];
int a = ACHAN2ADEV(j); int a = ACHAN2ADEV(j);
// If I was really ambitious, some description could be provided. // If I was really ambitious, some description could be provided.
@ -1593,12 +1594,7 @@ static THREAD_F cmd_listen_thread (void *arg)
break; break;
default: default:
{ ; // Only list valid channels.
// could elaborate with hostname, etc.
char stemp[100];
snprintf (stemp, sizeof(stemp), "Port%d INVALID CHANNEL;", j+1);
strlcat (reply.info, stemp, sizeof(reply.info));
}
break; break;
} // switch } // switch
@ -1721,6 +1717,7 @@ static THREAD_F cmd_listen_thread (void *arg)
packet_t pp; packet_t pp;
int pid = cmd.hdr.pid;
strlcpy (stemp, cmd.hdr.call_from, sizeof(stemp)); strlcpy (stemp, cmd.hdr.call_from, sizeof(stemp));
strlcat (stemp, ">", sizeof(stemp)); strlcat (stemp, ">", sizeof(stemp));
strlcat (stemp, cmd.hdr.call_to, sizeof(stemp)); strlcat (stemp, cmd.hdr.call_to, sizeof(stemp));
@ -1734,33 +1731,41 @@ static THREAD_F cmd_listen_thread (void *arg)
strlcat (stemp, p, sizeof(stemp)); strlcat (stemp, p, sizeof(stemp));
p += 10; p += 10;
} }
// At this point, p now points to info part after digipeaters.
// Issue 527: NET/ROM routing broadcasts are binary info so we can't treat as string.
// Originally, I just appended the information part.
// That was fine until NET/ROM, with binary data, came along.
// Now we set the information field after creating the packet object.
strlcat (stemp, ":", sizeof(stemp)); strlcat (stemp, ":", sizeof(stemp));
strlcat (stemp, p, sizeof(stemp)); strlcat (stemp, " ", sizeof(stemp));
//text_color_set(DW_COLOR_DEBUG); //text_color_set(DW_COLOR_DEBUG);
//dw_printf ("Transmit '%s'\n", stemp); //dw_printf ("Transmit '%s'\n", stemp);
pp = ax25_from_text (stemp, 1); pp = ax25_from_text (stemp, 1);
if (pp == NULL) { if (pp == NULL) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Failed to create frame from AGW 'V' message.\n"); dw_printf ("Failed to create frame from AGW 'V' message.\n");
break;
} }
else {
/* This goes into the low priority queue because it is an original. */ ax25_set_info (pp, (unsigned char*)p, data_len - ndigi * 10);
// Issue 527: NET/ROM routing broadcasts use PID 0xCF which was not preserved here.
ax25_set_pid (pp, pid);
/* Note that the protocol has no way to set the "has been used" */ /* This goes into the low priority queue because it is an original. */
/* bits in the digipeater fields. */
/* This explains why the digipeating option is grayed out in */ /* Note that the protocol has no way to set the "has been used" */
/* xastir when using the AGW interface. */ /* bits in the digipeater fields. */
/* The current version uses only the 'V' message, not 'K' for transmitting. */
tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp); /* This explains why the digipeating option is grayed out in */
/* xastir when using the AGW interface. */
/* The current version uses only the 'V' message, not 'K' for transmitting. */
} tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp);
} }
break; break;
@ -1784,11 +1789,17 @@ static THREAD_F cmd_listen_thread (void *arg)
// 00=Port 1 // 00=Port 1
// 16=Port 2 // 16=Port 2
// //
// I don't know what that means; we already a port number in the header. // The seems to be redundant; we already a port number in the header.
// Anyhow, the original code here added one to cmd.data to get the // Anyhow, the original code here added one to cmd.data to get the
// first byte of the frame. Unfortunately, it did not subtract one from // first byte of the frame. Unfortunately, it did not subtract one from
// cmd.hdr.data_len so we ended up sending an extra byte. // cmd.hdr.data_len so we ended up sending an extra byte.
// TODO: Right now I just use the port (channel) number in the header.
// What if the second one is inconsistent?
// - Continue to ignore port number at beginning of data?
// - Use second one instead?
// - Error message if a mismatch?
memset (&alevel, 0xff, sizeof(alevel)); memset (&alevel, 0xff, sizeof(alevel));
pp = ax25_from_frame ((unsigned char *)cmd.data+1, data_len - 1, alevel); pp = ax25_from_frame ((unsigned char *)cmd.data+1, data_len - 1, alevel);
@ -1888,7 +1899,7 @@ static THREAD_F cmd_listen_thread (void *arg)
unsigned char num_digi; /* Expect to be in range 1 to 7. Why not up to 8? */ unsigned char num_digi; /* Expect to be in range 1 to 7. Why not up to 8? */
char dcall[7][10]; char dcall[7][10];
} }
#if 1
// October 2017. gcc ??? complained: // October 2017. gcc ??? complained:
// warning: dereferencing pointer 'v' does break strict-aliasing rules // warning: dereferencing pointer 'v' does break strict-aliasing rules
// Try adding this attribute to get rid of the warning. // Try adding this attribute to get rid of the warning.
@ -1896,7 +1907,6 @@ static THREAD_F cmd_listen_thread (void *arg)
// Let me know. Maybe we could put in a compiler version check here. // Let me know. Maybe we could put in a compiler version check here.
__attribute__((__may_alias__)) __attribute__((__may_alias__))
#endif
*v = (struct via_info *)cmd.data; *v = (struct via_info *)cmd.data;
char callsigns[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN]; char callsigns[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN];
@ -2005,19 +2015,7 @@ static THREAD_F cmd_listen_thread (void *arg)
{ {
int pid = cmd.hdr.pid; int pid = cmd.hdr.pid;
(void)(pid);
/* The AGW protocol spec says, */
/* "AX.25 PID 0x00 or 0xF0 for AX.25 0xCF NETROM and others" */
/* BUG: In theory, the AX.25 PID octet should be set from this. */
/* All examples seen (above) have 0. */
/* The AX.25 protocol spec doesn't list 0 as a valid value. */
/* We always send 0xf0, meaning no layer 3. */
/* Maybe we should have an ax25_set_pid function for cases when */
/* it is neither 0 nor 0xf0. */
char stemp[AX25_MAX_PACKET_LEN]; char stemp[AX25_MAX_PACKET_LEN];
packet_t pp;
strlcpy (stemp, cmd.hdr.call_from, sizeof(stemp)); strlcpy (stemp, cmd.hdr.call_from, sizeof(stemp));
strlcat (stemp, ">", sizeof(stemp)); strlcat (stemp, ">", sizeof(stemp));
@ -2025,21 +2023,29 @@ static THREAD_F cmd_listen_thread (void *arg)
cmd.data[data_len] = '\0'; cmd.data[data_len] = '\0';
// Issue 527: NET/ROM routing broadcasts are binary info so we can't treat as string.
// Originally, I just appended the information part as a text string.
// That was fine until NET/ROM, with binary data, came along.
// Now we set the information field after creating the packet object.
strlcat (stemp, ":", sizeof(stemp)); strlcat (stemp, ":", sizeof(stemp));
strlcat (stemp, cmd.data, sizeof(stemp)); strlcat (stemp, " ", sizeof(stemp));
//text_color_set(DW_COLOR_DEBUG); //text_color_set(DW_COLOR_DEBUG);
//dw_printf ("Transmit '%s'\n", stemp); //dw_printf ("Transmit '%s'\n", stemp);
pp = ax25_from_text (stemp, 1); packet_t pp = ax25_from_text (stemp, 1);
if (pp == NULL) { if (pp == NULL) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Failed to create frame from AGW 'M' message.\n"); dw_printf ("Failed to create frame from AGW 'M' message.\n");
} }
else {
tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp); ax25_set_info (pp, (unsigned char*)cmd.data, data_len);
} // Issue 527: NET/ROM routing broadcasts use PID 0xCF which was not preserved here.
ax25_set_pid (pp, pid);
tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp);
} }
break; break;

View File

@ -147,6 +147,7 @@ list(APPEND dtest_SOURCES
${CUSTOM_SRC_DIR}/tq.c ${CUSTOM_SRC_DIR}/tq.c
${CUSTOM_SRC_DIR}/textcolor.c ${CUSTOM_SRC_DIR}/textcolor.c
${CUSTOM_SRC_DIR}/decode_aprs.c ${CUSTOM_SRC_DIR}/decode_aprs.c
${CUSTOM_SRC_DIR}/deviceid.c
${CUSTOM_SRC_DIR}/dwgpsnmea.c ${CUSTOM_SRC_DIR}/dwgpsnmea.c
${CUSTOM_SRC_DIR}/dwgps.c ${CUSTOM_SRC_DIR}/dwgps.c
${CUSTOM_SRC_DIR}/dwgpsd.c ${CUSTOM_SRC_DIR}/dwgpsd.c
@ -232,6 +233,7 @@ list(APPEND pftest_SOURCES
${CUSTOM_SRC_DIR}/textcolor.c ${CUSTOM_SRC_DIR}/textcolor.c
${CUSTOM_SRC_DIR}/fcs_calc.c ${CUSTOM_SRC_DIR}/fcs_calc.c
${CUSTOM_SRC_DIR}/decode_aprs.c ${CUSTOM_SRC_DIR}/decode_aprs.c
${CUSTOM_SRC_DIR}/deviceid.c
${CUSTOM_SRC_DIR}/dwgpsnmea.c ${CUSTOM_SRC_DIR}/dwgpsnmea.c
${CUSTOM_SRC_DIR}/dwgps.c ${CUSTOM_SRC_DIR}/dwgps.c
${CUSTOM_SRC_DIR}/dwgpsd.c ${CUSTOM_SRC_DIR}/dwgpsd.c
@ -522,6 +524,7 @@ if(OPTIONAL_TEST)
${CUSTOM_SRC_DIR}/pfilter.c ${CUSTOM_SRC_DIR}/pfilter.c
${CUSTOM_SRC_DIR}/telemetry.c ${CUSTOM_SRC_DIR}/telemetry.c
${CUSTOM_SRC_DIR}/decode_aprs.c ${CUSTOM_SRC_DIR}/decode_aprs.c
${CUSTOM_SRC_DIR}/deviceid.c.c
${CUSTOM_SRC_DIR}/dwgpsnmea.c ${CUSTOM_SRC_DIR}/dwgpsnmea.c
${CUSTOM_SRC_DIR}/dwgps.c ${CUSTOM_SRC_DIR}/dwgps.c
${CUSTOM_SRC_DIR}/dwgpsd.c ${CUSTOM_SRC_DIR}/dwgpsd.c
@ -574,6 +577,7 @@ if(OPTIONAL_TEST)
${CUSTOM_SRC_DIR}/fcs_calc.c ${CUSTOM_SRC_DIR}/fcs_calc.c
${CUSTOM_SRC_DIR}/ax25_pad.c ${CUSTOM_SRC_DIR}/ax25_pad.c
${CUSTOM_SRC_DIR}/decode_aprs.c ${CUSTOM_SRC_DIR}/decode_aprs.c
${CUSTOM_SRC_DIR}/deviceid.c
${CUSTOM_SRC_DIR}/dwgpsnmea.c ${CUSTOM_SRC_DIR}/dwgpsnmea.c
${CUSTOM_SRC_DIR}/dwgps.c ${CUSTOM_SRC_DIR}/dwgps.c
${CUSTOM_SRC_DIR}/dwgpsd.c ${CUSTOM_SRC_DIR}/dwgpsd.c