diff --git a/.github/workflows/codeql-analysis-python.yml b/.github/workflows/codeql-analysis-python.yml new file mode 100644 index 0000000..a47a8f8 --- /dev/null +++ b/.github/workflows/codeql-analysis-python.yml @@ -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 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7134f21..a86300f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -9,7 +9,7 @@ # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # -name: "CodeQL" +name: "CodeQL - CPP" on: push: @@ -32,19 +32,20 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'cpp', 'python' ] + language: [ 'cpp' ] # 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@v2 + uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + 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. @@ -53,7 +54,7 @@ jobs: # 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@v1 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -70,4 +71,4 @@ jobs: make test - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v3 diff --git a/.gitignore b/.gitignore index 659c845..b917a7a 100644 --- a/.gitignore +++ b/.gitignore @@ -109,5 +109,5 @@ $RECYCLE.BIN/ *.dSYM # cmake -build/ +build*/ tmp/ \ No newline at end of file diff --git a/CHANGES.md b/CHANGES.md index 4b78ca1..0903e9e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,12 @@ # 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 ## diff --git a/CMakeLists.txt b/CMakeLists.txt index 20aa28f..966fbaf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,10 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(direwolf) # configure version set(direwolf_VERSION_MAJOR "1") -set(direwolf_VERSION_MINOR "7") +set(direwolf_VERSION_MINOR "8") set(direwolf_VERSION_PATCH "0") set(direwolf_VERSION_SUFFIX "Development") @@ -320,6 +320,14 @@ else() set(HAMLIB_LIBRARIES "") 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) find_package(ALSA REQUIRED) if(ALSA_FOUND) diff --git a/cmake/include/uninstall.cmake.in b/cmake/include/uninstall.cmake.in index 2037e36..8ddc56a 100644 --- a/cmake/include/uninstall.cmake.in +++ b/cmake/include/uninstall.cmake.in @@ -7,10 +7,10 @@ string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") - exec_program( - "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + execute_process( + COMMAND "@CMAKE_COMMAND@" -E remove "$ENV{DESTDIR}${file}" OUTPUT_VARIABLE rm_out - RETURN_VALUE rm_retval + RESULT_VARIABLE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") diff --git a/cmake/modules/FindCompiler.cmake b/cmake/modules/FindCompiler.cmake index fe036e4..91e1b89 100644 --- a/cmake/modules/FindCompiler.cmake +++ b/cmake/modules/FindCompiler.cmake @@ -5,9 +5,9 @@ elseif(NOT DEFINED C_GCC AND CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(C_GCC 1) elseif(NOT DEFINED C_MSVC AND CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 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) - 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) elseif(MSVC_VERSION GREATER 1899 AND MSVC_VERSION LESS 1910) set(VS2015 ON) diff --git a/cmake/modules/Findgpiod.cmake b/cmake/modules/Findgpiod.cmake new file mode 100644 index 0000000..bf5be30 --- /dev/null +++ b/cmake/modules/Findgpiod.cmake @@ -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}) diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index 7972cc2..11a82a4 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -16,7 +16,7 @@ # # The destination field is often used to identify the manufacturer/model. # 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, # using overlays, are often updated. These are also read from files. @@ -25,17 +25,17 @@ include(ExternalProject) -set(TOCALLS_TXT "tocalls.txt") +set(TOCALLS_YAML "tocalls.yaml") set(SYMBOLS-NEW_TXT "symbols-new.txt") set(SYMBOLSX_TXT "symbolsX.txt") set(CUSTOM_BINARY_DATA_DIR "${CMAKE_BINARY_DIR}/data") # we can also move to a separate cmake file and use file(download) # 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}/${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}/${SYMBOLSX_TXT}" DESTINATION ${INSTALL_DATA_DIR}) diff --git a/data/README.txt b/data/README.txt new file mode 100644 index 0000000..9d4da43 --- /dev/null +++ b/data/README.txt @@ -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 diff --git a/data/tocalls.txt b/data/tocalls.txt deleted file mode 100644 index 169c986..0000000 --- a/data/tocalls.txt +++ /dev/null @@ -1,326 +0,0 @@ - -APRS TO-CALL VERSION NUMBERS 14 Dec 2021 ---------------------------------------------------------------------- - WB4APR - - -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 - - - - -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. - - - - - 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 - - - - - - -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: - - - - SATERN - Salvation Army Altnet - AFMARS - Airforce Mars - AMARS - Army Mars - \ No newline at end of file diff --git a/data/tocalls.yaml b/data/tocalls.yaml new file mode 100644 index 0000000..0df0478 --- /dev/null +++ b/data/tocalls.yaml @@ -0,0 +1,1637 @@ +# +# This is a machine-readable index of APRS device and software +# identification strings. For easy manual editing and validation, the +# master file is in YAML format. A conversion tool and pre-converted +# versions in XML and JSON are also provided for environments where those +# are more convenient to parse. +# +# This list is maintained by Hessu, OH7LZB, for the aprs.fi service. +# It is licensed under the CC BY-SA 2.0 license, so you're free to use +# it in any of your applications. For free. Just mention the source +# somewhere in the small print. +# http://creativecommons.org/licenses/by-sa/2.0/ +# + +--- + +# +# English shown names and descriptions for device classes +# +classes: + - class: wx + shown: Weather station + description: Dedicated weather station + + - class: tracker + shown: Tracker + description: Tracker device + + - class: rig + shown: Rig + description: Mobile or desktop radio + + - class: ht + shown: HT + description: Hand-held radio + + - class: app + shown: Mobile app + description: Mobile phone or tablet app + + - class: software + shown: Software + description: Desktop software + + - class: digi + shown: Digipeater + description: Digipeater software + + - class: igate + shown: iGate + description: iGate software + + - class: dstar + shown: D-Star + description: D-Star radio + + - class: satellite + shown: Satellite + description: Satellite-based station + + - class: service + shown: Service + description: Software running as a web service + +# +# mic-e device identifier index for new-style 2-character device +# suffixes. The first prefix byte indicates messaging capability. +# +mice: + - suffix: "_ " + vendor: Yaesu + model: VX-8 + class: ht + + - suffix: "_\"" + vendor: Yaesu + model: FTM-350 + class: rig + + - suffix: "_#" + vendor: Yaesu + model: VX-8G + class: ht + + - suffix: "_$" + vendor: Yaesu + model: FT1D + class: ht + + - suffix: "_(" + vendor: Yaesu + model: FT2D + class: ht + + - suffix: "_0" + vendor: Yaesu + model: FT3D + class: ht + + - suffix: "_3" + vendor: Yaesu + model: FT5D + class: ht + + - suffix: "_1" + vendor: Yaesu + model: FTM-300D + class: rig + + - suffix: "_2" + vendor: Yaesu + model: FTM-200D + class: rig + + - suffix: "_4" + vendor: Yaesu + model: FTM-500D + class: rig + + - suffix: "_)" + vendor: Yaesu + model: FTM-100D + class: rig + + - suffix: "_%" + vendor: Yaesu + model: FTM-400DR + class: rig + + - suffix: "(5" + vendor: Anytone + model: D578UV + class: ht + + - suffix: "(8" + vendor: Anytone + model: D878UV + class: ht + + - suffix: "|3" + vendor: Byonics + model: TinyTrak3 + class: tracker + + - suffix: "|4" + vendor: Byonics + model: TinyTrak4 + class: tracker + + - suffix: "^v" + vendor: HinzTec + model: anyfrog + + - suffix: "*v" + vendor: KissOZ + model: Tracker + class: tracker + + - suffix: ":2" + vendor: SQ8L + model: VP-Tracker + class: tracker + +# +# mic-e legacy devices, with an unique comment suffix and prefix +# + +micelegacy: + - prefix: ">" + vendor: Kenwood + model: TH-D7A + class: ht + features: + - messaging + + - prefix: ">" + suffix: "=" + vendor: Kenwood + model: TH-D72 + class: ht + features: + - messaging + + - prefix: ">" + suffix: "^" + vendor: Kenwood + model: TH-D74 + class: ht + features: + - messaging + + - prefix: ">" + suffix: "&" + vendor: Kenwood + model: TH-D75 + class: ht + features: + - messaging + + - prefix: "]" + vendor: Kenwood + model: TM-D700 + class: rig + features: + - messaging + + - prefix: "]" + suffix: "=" + vendor: Kenwood + model: TM-D710 + class: rig + features: + - messaging + +# +# TOCALL index +# +tocalls: + - tocall: AP1WWX + vendor: TAPR + model: T-238+ + class: wx + + - tocall: AP4R?? + vendor: Open Source + model: APRS4R + class: software + + - tocall: APAEP1 + vendor: Paraguay Space Agency (AEP) + model: "EIRUAPRSDIGIS&FV1" + class: satellite + + - tocall: APAF?? + model: AFilter + + - tocall: APAG?? + model: AGate + + - tocall: APAGW + vendor: SV2AGW + model: AGWtracker + class: software + os: Windows + + - tocall: APAGW? + vendor: SV2AGW + model: AGWtracker + class: software + os: Windows + + - tocall: APAH?? + model: AHub + + - tocall: APAIOR + vendor: J. Angelo Racoma DU2XXR/N2RAC + model: APRSPH net bot based on Ioreth + class: service + os: linux + contact: info@aprsph.net + features: + - messaging + + - tocall: APAM?? + vendor: Altus Metrum + model: AltOS + class: tracker + + - tocall: APAND? + vendor: Open Source + model: APRSdroid + os: Android + class: app + + - tocall: APAR?? + vendor: Øyvind, LA7ECA + model: Arctic Tracker + class: tracker + os: embedded + + - tocall: APAT51 + vendor: Anytone + model: AT-D578 + class: rig + + - tocall: APAT81 + vendor: Anytone + model: AT-D878 + class: ht + + - tocall: APAT?? + vendor: Anytone + + - tocall: APATAR + vendor: TA7W/OH2UDS Baris Dinc and TA6AEU + model: ATA-R APRS Digipeater + class: digi + + - tocall: APAVT5 + vendor: SainSonic + model: AP510 + class: tracker + + - tocall: APAW?? + vendor: SV2AGW + model: AGWPE + class: software + os: Windows + + - tocall: APAX?? + model: AFilterX + + - tocall: APB2MF + vendor: Mike, DL2MF + model: MF2APRS Radiosonde tracking tool + class: software + os: Windows + + - tocall: APBK?? + vendor: PY5BK + model: Bravo Tracker + class: tracker + + - tocall: APBL?? + vendor: BigRedBee + model: BeeLine GPS + class: tracker + + - tocall: APBM?? + vendor: R3ABM + model: BrandMeister DMR + + - tocall: APBPQ? + vendor: John Wiseman, G8BPQ + model: BPQ32 + class: software + os: Windows + + - tocall: APBSD? + vendor: hambsd.org + model: HamBSD + + - tocall: APBT62 + vendor: BTech + model: DMR 6x2 + + - tocall: APC??? + vendor: Rob Wittner, KZ5RW + model: APRS/CE + class: app + + - tocall: APCDS0 + vendor: ZS6LMG + model: cell tracker + class: tracker + + - tocall: APCLEY + vendor: ZS6EY + model: EYTraker + class: tracker + + - tocall: APCLEZ + vendor: ZS6EY + model: Telit EZ10 GSM application + class: tracker + + - tocall: APCLUB + model: Brazil APRS network + + - tocall: APCLWX + vendor: ZS6EY + model: EYWeather + class: wx + + - tocall: APCN?? + vendor: DG5OAW + model: carNET + + - tocall: APCSMS + vendor: USNA + model: Cosmos + + - tocall: APCSS + vendor: AMSAT + model: CubeSatSim CubeSat Simulator + + - tocall: APCTLK + vendor: Open Source + model: Codec2Talkie + class: app + + - tocall: APCWP8 + vendor: GM7HHB + model: WinphoneAPRS + class: app + + - tocall: APD5T? + vendor: Geoffrey, F4FXL + model: Open Source DStarGateway + class: dstar + contact: f4fxl@dstargateway.digital + + - tocall: APDF?? + model: Automatic DF units + + - tocall: APDG?? + vendor: Jonathan, G4KLX + model: ircDDB Gateway + class: dstar + + - tocall: APDI?? + vendor: Bela, HA5DI + model: DIXPRS + class: software + + - tocall: APDNO? + vendor: DO3SWW + model: APRSduino + class: tracker + os: embedded + + - tocall: APDPRS + vendor: unknown + model: D-Star APDPRS + class: dstar + + - tocall: APDR?? + vendor: Open Source + model: APRSdroid + os: Android + class: app + + - tocall: APDS?? + vendor: SP9UOB + model: dsDIGI + os: embedded + + - tocall: APDST? + vendor: SP9UOB + model: dsTracker + os: embedded + + - tocall: APDT?? + vendor: unknown + model: APRStouch Tone (DTMF) + + - tocall: APDU?? + vendor: JA7UDE + model: U2APRS + class: app + os: Android + + - tocall: APDV?? + vendor: OE6PLD + model: SSTV with APRS + class: software + + - tocall: APDW?? + vendor: WB2OSZ + model: DireWolf + + - tocall: APDnnn + vendor: Open Source + model: aprsd + class: software + os: Linux/Unix + + - tocall: APE2A? + vendor: NoseyNick, VA3NNW + model: Email-2-APRS gateway + class: software + os: Linux/Unix + + - tocall: APE??? + model: Telemetry devices + + - tocall: APECAN + vendor: KT5TK/DL7AD + model: Pecan Pico APRS Balloon Tracker + class: tracker + + - tocall: APELK? + vendor: WB8ELK + model: Balloon tracker + class: tracker + + - tocall: APEML? + vendor: Leszek, SP9MLI + model: SP9MLI for WX, Telemetry + class: software + contact: sp9mli@gmail.com + + - tocall: APEP?? + vendor: Patrick EGLOFF, TK5EP + model: LoRa WX station + class: wx + os: embedded + contact: pegloff@gmail.com + + - tocall: APERS? + vendor: Jason, KG7YKZ + model: Runner tracking + class: tracker + + - tocall: APERXQ + vendor: PE1RXQ + model: PE1RXQ APRS Tracker + class: tracker + + - tocall: APESP? + vendor: LY3PH + model: APRS-ESP + os: embedded + + - tocall: APFG?? + vendor: KP4DJT + model: Flood Gage + class: software + + - tocall: APFI?? + vendor: aprs.fi + class: app + + - tocall: APFII? + model: iPhone/iPad app + vendor: aprs.fi + os: ios + class: app + + - tocall: APGBLN + vendor: NW5W + model: GoBalloon + class: tracker + + - tocall: APGO?? + vendor: AA3NJ + model: APRS-Go + class: app + + - tocall: APHAX? + vendor: PY2UEP + model: SM2APRS SondeMonitor + class: software + os: Windows + + - tocall: APHBL? + vendor: KF7EEL + model: HBLink D-APRS Gateway + class: software + + - tocall: APHH? + vendor: Steven D. Bragg, KA9MVA + model: HamHud + class: tracker + + - tocall: APHK?? + vendor: LA1BR + model: Digipeater/tracker + + - tocall: APHMEY + vendor: Tapio Heiskanen, OH2TH + model: APRS-IS Client for Athom Homey + contact: oh2th@iki.fi + + - tocall: APHPIA + vendor: HP3ICC + model: Arduino APRS + + - tocall: APHPIB + vendor: HP3ICC + model: Python APRS Beacon + + - tocall: APHPIW + vendor: HP3ICC + model: Python APRS WX + + - tocall: APHRM? + vendor: Giovanni, IW1CGW + model: Meteo + class: wx + contact: iw1cgw@libero.it + + - tocall: APHRT? + vendor: Giovanni, IW1CGW + model: Telemetry + contact: iw1cgw@libero.it + + - tocall: APHT?? + vendor: IU0AAC + model: HMTracker + class: tracker + + - tocall: APHW?? + vendor: HamWAN + + - tocall: API282 + vendor: Icom + model: IC-2820 + class: dstar + + - tocall: API31 + vendor: Icom + model: IC-31 + class: dstar + + - tocall: API410 + vendor: Icom + model: IC-4100 + class: dstar + + - tocall: API51 + vendor: Icom + model: IC-51 + class: dstar + + - tocall: API510 + vendor: Icom + model: IC-5100 + class: dstar + + - tocall: API710 + vendor: Icom + model: IC-7100 + class: dstar + + - tocall: API80 + vendor: Icom + model: IC-80 + class: dstar + + - tocall: API880 + vendor: Icom + model: IC-880 + class: dstar + + - tocall: API910 + vendor: Icom + model: IC-9100 + class: dstar + + - tocall: API92 + vendor: Icom + model: IC-92 + class: dstar + + - tocall: API970 + vendor: Icom + model: IC-9700 + class: dstar + + - tocall: API??? + vendor: Icom + model: unknown + class: dstar + + - tocall: APIC?? + vendor: HA9MCQ + model: PICiGATE + + - tocall: APIE?? + vendor: W7KMV + model: PiAPRS + + - tocall: APIN?? + vendor: AB0WV + model: PinPoint + + - tocall: APIZCI + vendor: TA7W/OH2UDS and TA6AEU + model: hymTR IZCI Tracker + class: tracker + os: embedded + + - tocall: APJ8?? + vendor: KN4CRD + model: JS8Call + class: software + + - tocall: APJA?? + vendor: K4HG & AE5PL + model: JavAPRS + + - tocall: APJE?? + vendor: Gregg Wonderly, W5GGW + model: JeAPRS + + - tocall: APJI?? + vendor: Peter Loveall, AE5PL + model: jAPRSIgate + class: software + + - tocall: APJID2 + vendor: Peter Loveall, AE5PL + model: D-Star APJID2 + class: dstar + + - tocall: APJS?? + vendor: Peter Loveall, AE5PL + model: javAPRSSrvr + + - tocall: APJY?? + vendor: KA2DDO + model: YAAC + class: software + + - tocall: APK003 + vendor: Kenwood + model: TH-D72 + class: ht + + - tocall: APK004 + vendor: Kenwood + model: TH-D74 + class: ht + + - tocall: APK005 + vendor: Kenwood + model: TH-D75 + class: ht + + - tocall: APK0?? + vendor: Kenwood + model: TH-D7 + class: ht + + - tocall: APK1?? + vendor: Kenwood + model: TM-D700 + class: rig + + - tocall: APKHTW + vendor: Kip, W3SN + model: Tempest Weather Bridge + class: wx + os: embedded + contact: w3sn@moxracing.33mail.com + + - tocall: APKRAM + vendor: kramstuff.com + model: Ham Tracker + class: app + os: ios + + - tocall: APLC?? + vendor: DL3DCW + model: APRScube + + - tocall: APLDG? + vendor: Eddie, 9W2LWK + model: LoRAIGate + class: igate + os: embedded + contact: 9w2lwk@gmail.com + + - tocall: APLDH? + vendor: Eddie, 9W2LWK + model: LoraTracker + class: tracker + os: embedded + contact: 9w2lwk@gmail.com + + - tocall: APLDI? + vendor: David, OK2DDS + model: LoRa IGate/Digipeater + class: digi + + - tocall: APLDM? + vendor: David, OK2DDS + model: LoRa Meteostation + class: wx + + - tocall: APLETK + vendor: DL5TKL + model: T-Echo + class: tracker + os: embedded + contact: cfr34k-git@tkolb.de + + - tocall: APLFG? + vendor: Gabor, HG3FUG + model: LoRa WX station + class: wx + os: embedded + contact: hg3fug@fazi.hu + + - tocall: APLFM? + vendor: DO1MA + model: FemtoAPRS + class: tracker + os: embedded + + - tocall: APLG?? + vendor: OE5BPA + model: LoRa Gateway/Digipeater + class: digi + + - tocall: APLHI? + vendor: Giovanni, IW1CGW + model: LoRa IGate/Digipeater/Telemetry + class: digi + contact: iw1cgw@libero.it + + - tocall: APLHM? + vendor: Giovanni, IW1CGW + model: LoRa Meteostation + class: wx + contact: iw1cgw@libero.it + + - tocall: APLIG? + vendor: TA2MUN/TA9OHC + model: LightAPRS Tracker + class: tracker + + - tocall: APLM?? + vendor: WA0TQG + class: software + + - tocall: APLO?? + vendor: SQ9MDD + model: LoRa KISS TNC/Tracker + class: tracker + + - tocall: APLP0? + vendor: SQ9P + model: fajne digi + class: digi + os: embedded + contact: sq9p.peter@gmail.com + + - tocall: APLP1? + vendor: SQ9P + model: LORA/FSK/AFSK fajny tracker + class: tracker + os: embedded + contact: sq9p.peter@gmail.com + + - tocall: APLRG? + vendor: Ricardo, CA2RXU + model: ESP32 LoRa iGate + class: igate + os: embedded + contact: richonguzman@gmail.com + + - tocall: APLRT? + vendor: Ricardo, CA2RXU + model: ESP32 LoRa Tracker + class: tracker + os: embedded + contact: richonguzman@gmail.com + + - tocall: APLS?? + vendor: SARIMESH + model: SARIMESH + class: software + + - tocall: APLT?? + vendor: OE5BPA + model: LoRa Tracker + class: tracker + + - tocall: APLU0? + vendor: SP9UP + model: ESP32/SX12xx LoRa iGate / Digi + class: digi + os: embedded + contact: wajdzik.m@gmail.com + + - tocall: APLU1? + vendor: SP9UP + model: ESP32/SX12xx LoRa Tracker + class: tracker + os: embedded + contact: wajdzik.m@gmail.com + + - tocall: APMAIL + vendor: Mike, NA7Q + model: APRS Mailbox + class: service + contact: mike.ph4@gmail.com + + - tocall: APMG?? + vendor: Alex, AB0TJ + model: PiCrumbs and MiniGate + class: software + + - tocall: APMI01 + vendor: Microsat + os: embedded + model: WX3in1 + + - tocall: APMI02 + vendor: Microsat + os: embedded + model: WXEth + + - tocall: APMI03 + vendor: Microsat + os: embedded + model: PLXDigi + + - tocall: APMI04 + vendor: Microsat + os: embedded + model: WX3in1 Mini + + - tocall: APMI05 + vendor: Microsat + os: embedded + model: PLXTracker + + - tocall: APMI06 + vendor: Microsat + os: embedded + model: WX3in1 Plus 2.0 + + - tocall: APMI?? + vendor: Microsat + os: embedded + + - tocall: APMON? + vendor: Amon Schumann, DL9AS + model: APRS Balloon Tracker + class: tracker + os: embedded + + - tocall: APMPAD + vendor: DF1JSL + model: Multi-Purpose APRS Daemon + class: service + contact: joerg.schultze.lutter@gmail.com + features: + - messaging + + - tocall: APMQ?? + vendor: WB2OSZ + model: Ham Radio of Things + + - tocall: APMT?? + vendor: LZ1PPL + model: Micro APRS Tracker + class: tracker + + - tocall: APN102 + vendor: Gregg Wonderly, W5GGW + model: APRSNow + class: app + os: ipad + + - tocall: APN2?? + vendor: VE4KLM + model: NOSaprs for JNOS 2.0 + + - tocall: APN3?? + vendor: Kantronics + model: KPC-3 + + - tocall: APN9?? + vendor: Kantronics + model: KPC-9612 + + - tocall: APNCM + vendor: Keith Kaiser, WA0TJT + model: Net Control Manager + class: software + os: browser + contact: wa0tjt@gmail.com + + - tocall: APND?? + vendor: PE1MEW + model: DIGI_NED + + - tocall: APNIC4 + vendor: SQ5EKU + model: BidaTrak + class: tracker + os: embedded + + - tocall: APNJS? + vendor: Julien Sansonnens, HB9HRD + model: Web messaging service + class: service + contact: julien.owls@gmail.com + features: + - messaging + + - tocall: APNK01 + vendor: Kenwood + model: TM-D700 + class: rig + features: + - messaging + + - tocall: APNK80 + vendor: Kantronics + model: KAM + + - tocall: APNKMP + vendor: Kantronics + model: KAM+ + + - tocall: APNKMX + vendor: Kantronics + model: KAM-XL + + - tocall: APNM?? + vendor: MFJ + model: TNC + + - tocall: APNP?? + vendor: PacComm + model: TNC + + - tocall: APNT?? + vendor: SV2AGW + model: TNT TNC as a digipeater + class: digi + + - tocall: APNU?? + vendor: IW3FQG + model: UIdigi + class: digi + + - tocall: APNV0? + vendor: SQ8L + model: VP-Digi + os: embedded + class: digi + + - tocall: APNV1? + vendor: SQ8L + model: VP-Node + os: embedded + + - tocall: APNV2? + vendor: SQ8L + model: VP-Tracker + class: tracker + + - tocall: APNV?? + vendor: SQ8L + + - tocall: APNW?? + vendor: SQ3FYK + model: WX3in1 + os: embedded + + - tocall: APNX?? + vendor: K6DBG + model: TNC-X + + - tocall: APOA?? + vendor: OpenAPRS + model: app + class: app + os: ios + + - tocall: APOCSG + vendor: N0AGI + model: POCSAG + + - tocall: APODOT + vendor: Mike, NA7Q + model: Oregon Department of Transportion Traffic Alerts + class: service + + - tocall: APOG7? + vendor: OpenGD77 + model: OpenGD77 + os: embedded + contact: Roger VK3KYY/G4KYF + + - tocall: APOLU? + vendor: AMSAT-LU + model: Oscar + class: satellite + + - tocall: APOPYT + vendor: Mike, NA7Q + model: NA7Q Messenger + class: software + contact: mike.ph4@gmail.com + + - tocall: APOSAT + vendor: Mike, NA7Q + model: Open Source Satellite Gateway + class: service + contact: mike.ph4@gmail.com + + - tocall: APOSMS + vendor: Mike, NA7Q + model: Open Source SMS Gateway + class: service + contact: mike.ph4@gmail.com + features: + - messaging + + - tocall: APOT?? + vendor: Argent Data Systems + model: OpenTracker + class: tracker + + - tocall: APOVU? + vendor: K J Somaiya Institute + model: BeliefSat + + - tocall: APOZ?? + vendor: OZ1EKD, OZ7HVO + model: KissOZ + class: tracker + + - tocall: APP6?? + model: APRSlib + + - tocall: APPCO? + vendor: RadCommSoft, LLC + model: PicoAPRSTracker + class: tracker + os: embedded + contact: ab4mw@radcommsoft.com + + - tocall: APPIC? + vendor: DB1NTO + model: PicoAPRS + class: tracker + + - tocall: APPM?? + vendor: DL1MX + model: rtl-sdr Python iGate + class: software + + - tocall: APPRIS + vendor: DF1JSL + model: Apprise APRS plugin + class: service + contact: joerg.schultze.lutter@gmail.com + features: + - messaging + + - tocall: APPS?? + vendor: Øyvind, LA7ECA (for the Norwegian Radio Relay League) + model: Polaric Server + class: software + os: Linux + + - tocall: APPT?? + vendor: JF6LZE + model: KetaiTracker + class: tracker + + - tocall: APQTH? + vendor: Weston Bustraan, W8WJB + model: QTH.app + class: software + os: macOS + features: + - messaging + + - tocall: APR2MF + vendor: Mike, DL2MF + model: MF2wxAPRS Tinkerforge gateway + class: wx + os: Windows + + - tocall: APR8?? + vendor: Bob Bruninga, WB4APR + model: APRSdos + class: software + + - tocall: APRARX + vendor: Open Source + model: radiosonde_auto_rx + class: software + os: Linux/Unix + + - tocall: APRFG? + vendor: RF.Guru + contact: info@rf.guru + + - tocall: APRFGB + vendor: RF.Guru + model: APRS LoRa Pager + os: embedded + contact: info@rf.guru + + - tocall: APRFGD + vendor: RF.Guru + model: APRS Digipeater + class: digi + os: embedded + contact: info@rf.guru + + - tocall: APRFGH + vendor: RF.Guru + model: Hotspot + class: rig + os: embedded + contact: info@rf.guru + + - tocall: APRFGI + vendor: RF.Guru + model: LoRa APRS iGate + class: igate + os: embedded + contact: info@rf.guru + + - tocall: APRFGL + vendor: RF.Guru + model: Lora APRS Digipeater + class: digi + os: embedded + contact: info@rf.guru + + - tocall: APRFGM + vendor: RF.Guru + model: Mobile Radio + class: rig + os: embedded + contact: info@rf.guru + + - tocall: APRFGP + vendor: RF.Guru + model: Portable Radio + class: ht + os: embedded + contact: info@rf.guru + + - tocall: APRFGR + vendor: RF.Guru + model: Repeater + class: rig + os: embedded + contact: info@rf.guru + + - tocall: APRFGT + vendor: RF.Guru + model: LoRa APRS Tracker + class: tracker + os: embedded + contact: info@rf.guru + + - tocall: APRFGW + vendor: RF.Guru + model: LoRa APRS Weather Station + class: wx + os: embedded + contact: info@rf.guru + + - tocall: APRG?? + vendor: OH2GVE + model: aprsg + class: software + os: Linux/Unix + + - tocall: APRHH? + vendor: Steven D. Bragg, KA9MVA + model: HamHud + class: tracker + + - tocall: APRNOW + vendor: Gregg Wonderly, W5GGW + model: APRSNow + class: app + os: ipad + + - tocall: APRPR? + vendor: Robert DM4RW, Peter DL6MAA + model: Teensy RPR TNC + class: tracker + os: embedded + contact: dm4rw@skywaves.de + + - tocall: APRRDZ + model: rdzTTGOsonde + vendor: DL9RDZ + class: tracker + + - tocall: APRRF? + vendor: Jean-Francois Huet F1EVM + model: Tracker for RRF + class: tracker + os: embedded + contact: f1evm@f1evm.fr + features: + - messaging + + - tocall: APRRT? + vendor: RPC Electronics + model: RTrak + class: tracker + + - tocall: APRS + vendor: Unknown + model: Unknown + + - tocall: APRX?? + vendor: Kenneth W. Finnegan, W6KWF + model: Aprx + class: igate + os: Linux/Unix + + - tocall: APS??? + vendor: Brent Hildebrand, KH2Z + model: APRS+SA + class: software + + - tocall: APSAR + vendor: ZL4FOX + model: SARTrack + class: software + os: Windows + + - tocall: APSC?? + vendor: OH2MQK, OH7LZB + model: aprsc + class: software + + - tocall: APSF?? + vendor: F5OPV, SFCP_LABS + model: embedded APRS devices + os: embedded + + - tocall: APSFLG + vendor: F5OPV, SFCP_LABS + model: LoRa/APRS Gateway + class: digi + os: embedded + + - tocall: APSFRP + vendor: F5OPV, SFCP_LABS + model: VHF/UHF Repeater + os: embedded + + - tocall: APSFTL + vendor: F5OPV, SFCP_LABS + model: LoRa/APRS Telemetry Reporter + os: embedded + + - tocall: APSFWX + vendor: F5OPV, SFCP_LABS + model: embedded Weather Station + class: wx + os: embedded + + - tocall: APSK63 + vendor: Chris Moulding, G4HYG + model: APRS Messenger + class: software + os: Windows + + - tocall: APSMS? + vendor: Paul Dufresne + model: SMS gateway + class: software + + - tocall: APSRF? + vendor: SoftRF + model: Ham Edition + class: tracker + os: embedded + + - tocall: APSTM? + vendor: W7QO + model: Balloon tracker + class: tracker + + - tocall: APSTPO + vendor: N0AGI + model: Satellite Tracking and Operations + class: software + + - tocall: APT2?? + vendor: Byonics + model: TinyTrak2 + class: tracker + + - tocall: APT3?? + vendor: Byonics + model: TinyTrak3 + class: tracker + + - tocall: APT4?? + vendor: Byonics + model: TinyTrak4 + class: tracker + + - tocall: APTB?? + vendor: BG5HHP + model: TinyAPRS + + - tocall: APTCHE + vendor: PU3IKE + model: TcheTracker, Tcheduino + class: tracker + + - tocall: APTCMA + vendor: Cleber, PU1CMA + model: CAPI Tracker + class: tracker + + - tocall: APTEMP + vendor: KL7AF + model: APRS-Tempest Weather Gateway + class: wx + os: Linux/Unix + contact: kl7af@foghaven.net + + - tocall: APTHUR + model: APRSThursday weekly event mapbot daemon + contact: harihend1973@gmail.com + vendor: YD0BCX + class: service + os: linux/unix + features: + - messaging + + - tocall: APTKJ? + vendor: W9JAJ + model: ATTiny APRS Tracker + os: embedded + + - tocall: APTLVC + vendor: TA5LVC + model: TR80 APRS Tracker + class: tracker + + - tocall: APTNG? + vendor: Filip YU1TTN + model: Tango Tracker + class: tracker + + - tocall: APTPN? + vendor: KN4ORB + model: TARPN Packet Node Tracker + class: tracker + + - tocall: APTR?? + vendor: Motorola + model: MotoTRBO + + - tocall: APTT* + vendor: Byonics + model: TinyTrak + class: tracker + + - tocall: APTW?? + vendor: Byonics + model: WXTrak + class: wx + + - tocall: APU1?? + vendor: Roger Barker, G4IDE + model: UI-View16 + class: software + os: Windows + + - tocall: APU2* + vendor: Roger Barker, G4IDE + model: UI-View32 + class: software + os: Windows + + - tocall: APUDR? + vendor: NW Digital Radio + model: UDR + + - tocall: APVE?? + vendor: unknown + model: EchoLink + + - tocall: APVM?? + vendor: Digital Radio China Club + model: DRCC-DVM + class: igate + + - tocall: APVR?? + vendor: unknown + model: IRLP + + - tocall: APW9?? + vendor: Mile Strk, 9A9Y + model: WX Katarina + class: wx + os: embedded + features: + - messaging + + - tocall: APWA?? + vendor: KJ4ERJ + model: APRSISCE + class: software + os: Android + + - tocall: APWEE? + vendor: Tom Keffer and Matthew Wall + model: WeeWX Weather Software + class: software + os: Linux/Unix + + - tocall: APWM?? + vendor: KJ4ERJ + model: APRSISCE + class: software + os: Windows Mobile + features: + - messaging + - item-in-msg + + - tocall: APWW?? + vendor: KJ4ERJ + model: APRSIS32 + class: software + os: Windows + features: + - messaging + - item-in-msg + + - tocall: APWnnn + vendor: Sproul Brothers + model: WinAPRS + class: software + os: Windows + + - tocall: APX??? + vendor: Open Source + model: Xastir + class: software + os: Linux/Unix + + - tocall: APXR?? + vendor: G8PZT + model: Xrouter + + - tocall: APY01D + vendor: Yaesu + model: FT1D + class: ht + + - tocall: APY02D + vendor: Yaesu + model: FT2D + class: ht + + - tocall: APY05D + vendor: Yaesu + model: FT5D + class: ht + + - tocall: APY200 + vendor: Yaesu + model: FTM-200D + class: rig + + - tocall: APY300 + vendor: Yaesu + model: FTM-300D + class: rig + + - tocall: APY400 + vendor: Yaesu + model: FTM-400 + class: rig + + - tocall: APY500 + vendor: Yaesu + model: FTM-500D + class: rig + + - tocall: APYS?? + vendor: W2GMD + model: Python APRS + class: software + + - tocall: "APZ*" + vendor: Unknown + model: Experimental + + - tocall: APZ18 + vendor: IW3FQG + model: UIdigi + class: digi + + - tocall: APZ186 + vendor: IW3FQG + model: UIdigi + class: digi + + - tocall: APZ19 + vendor: IW3FQG + model: UIdigi + class: digi + + - tocall: APZ247 + model: UPRS + vendor: NR0Q + + - tocall: APZG?? + vendor: OH2GVE + model: aprsg + class: software + os: Linux/Unix + + - tocall: APZMAJ + vendor: M1MAJ + model: DeLorme inReach Tracker + + - tocall: APZMDR + vendor: Open Source + model: HaMDR + class: tracker + os: embedded + + - tocall: APZTKP + vendor: Nick Hanks, N0LP + model: TrackPoint + class: tracker + os: embedded + + - tocall: APZWKR + vendor: GM1WKR + model: NetSked + class: software + + - tocall: APnnnD + vendor: Painter Engineering + model: uSmartDigi D-Gate + class: dstar + + - tocall: APnnnU + vendor: Painter Engineering + model: uSmartDigi Digipeater + class: digi + + - tocall: PSKAPR + vendor: Open Source + model: PSKmail + class: software + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7a05ecc..d159e3f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,6 +10,7 @@ include_directories( ${PORTAUDIO_INCLUDE_DIRS} ${SNDIO_INCLUDE_DIRS} ${CUSTOM_GEOTRANZ_DIR} + ${GPIOD_INCLUDE_DIRS} ) if(WIN32 OR CYGWIN) @@ -38,6 +39,7 @@ list(APPEND direwolf_SOURCES beacon.c config.c decode_aprs.c + deviceid.c dedupe.c demod_9600.c demod_afsk.c @@ -162,6 +164,7 @@ target_link_libraries(direwolf ${ALSA_LIBRARIES} ${UDEV_LIBRARIES} ${PORTAUDIO_LIBRARIES} + ${GPIOD_LIBRARIES} ${SNDIO_LIBRARIES} ${AVAHI_LIBRARIES} ) @@ -176,6 +179,7 @@ endif() # decode_aprs list(APPEND decode_aprs_SOURCES decode_aprs.c + deviceid.c ais.c kiss_frame.c ax25_pad.c @@ -360,6 +364,7 @@ list(APPEND atest_SOURCES ax25_pad.c ax25_pad2.c decode_aprs.c + deviceid.c dwgpsnmea.c dwgps.c dwgpsd.c diff --git a/src/audio.h b/src/audio.h index cb5ca94..4fc0570 100644 --- a/src/audio.h +++ b/src/audio.h @@ -28,7 +28,8 @@ enum ptt_method_e { PTT_METHOD_NONE, /* VOX or no transmit. */ 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_HAMLIB, /* HAMLib, 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. */ - int defined; /* Was device defined? */ - /* First one defaults to yes. */ + int defined; /* Was device defined? 0=no. >0 for 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?). */ - /* 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?). */ 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). */ } adev[MAX_ADEVS]; @@ -304,6 +312,7 @@ struct audio_s { /* the case for CubieBoard where it was longer. */ /* This is filled in by ptt_init so we don't have to */ /* 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. */ diff --git a/src/ax25_link.c b/src/ax25_link.c index 3043a04..98d6c45 100644 --- a/src/ax25_link.c +++ b/src/ax25_link.c @@ -4642,6 +4642,8 @@ static void dm_frame (ax25_dlsm_t *S, int f) if (f == 1) { 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 ("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; @@ -4930,6 +4932,8 @@ static void frmr_frame (ax25_dlsm_t *S) 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 ("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; diff --git a/src/ax25_pad.c b/src/ax25_pad.c index 0f07580..4a52638 100644 --- a/src/ax25_pad.c +++ b/src/ax25_pad.c @@ -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 diff --git a/src/ax25_pad.h b/src/ax25_pad.h index cdb84c6..6d3d5cb 100644 --- a/src/ax25_pad.h +++ b/src/ax25_pad.h @@ -66,6 +66,7 @@ #define AX25_UI_FRAME 3 /* Control field value. */ #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_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_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_frame_len (packet_t this_p); diff --git a/src/config.c b/src/config.c index 1ad6c08..de8d74d 100644 --- a/src/config.c +++ b/src/config.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 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 // 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)); 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].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[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; channelmaxframe_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->v20_addrs = NULL; /* Go directly to v2.0 for stations listed. */ + 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 */ + /* without trying v2.2 first. */ p_misc_config->v20_count = 0; 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; /* @@ -1012,7 +1016,11 @@ void config_init (char *fname, struct audio_s *p_audio_config, * ADEVICE plughw:1,0 -- same for in and out. * 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. - * + * + * 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 */ @@ -1040,17 +1048,42 @@ void config_init (char *fname, struct audio_s *p_audio_config, 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; - - /* First channel of device is valid. */ - 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)); + // New case for release 1.8. - t = split(NULL,0); - if (t != NULL) { + if (strcmp(t, "=") == 0) { + 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)); + + 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; #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) { @@ -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. + * + * Why did I do this? Just add more to TXDELAY. + * Now undocumented in User Guide. Might disappear someday. */ else if (strcasecmp(t, "DWAIT") == 0) { @@ -2167,14 +2242,20 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } 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; } else { p_audio_config->achan[channel].slottime = DEFAULT_SLOTTIME; 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); + 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; } n = atoi(t); - if (n >= 0 && n <= 255) { + if (n >= 5 && n <= 250) { p_audio_config->achan[channel].persist = n; } else { p_audio_config->achan[channel].persist = DEFAULT_PERSIST; 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); + 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); 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; } else { @@ -2240,12 +2341,28 @@ void config_init (char *fname, struct audio_s *p_audio_config, } n = atoi(t); 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; } else { p_audio_config->achan[channel].txtail = DEFAULT_TXTAIL; text_color_set(DW_COLOR_ERROR); - dw_printf ("Line %d: Invalid time for transmit timing. Using %d.\n", + dw_printf ("Line %d: Invalid time for transmit timing. Using %d.\n", line, p_audio_config->achan[channel].txtail); } } @@ -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 ("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! 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"); } 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! 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! 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 { 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) { 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"); } } diff --git a/src/decode_aprs.c b/src/decode_aprs.c index ab93327..08534f7 100644 --- a/src/decode_aprs.c +++ b/src/decode_aprs.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2017, 2022 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 // it under the terms of the GNU General Public License as published by @@ -56,7 +56,7 @@ #include "decode_aprs.h" #include "telemetry.h" #include "ais.h" - +#include "deviceid.h" #define TRUE 1 #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 int get_maidenhead (decode_aprs_t *A, char *p); 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 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. */ @@ -303,7 +302,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr break; default: - decode_tocall (A, A->g_dest); + deviceid_decode_dest (A->g_dest, A->g_mfr, sizeof(A->g_mfr)); 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; 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" }; - unsigned char *pfirst, *plast; 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; +// 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. */ /* The telemetry field, in the original spec, is no longer used. */ - - strlcpy (A->g_mfr, "Unknown manufacturer", 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? */ + char trimmed[256]; // Comment with vendor/model removed. + deviceid_decode_mice (mcomment, trimmed, sizeof(trimmed), A->g_mfr, sizeof(A->g_mfr)); - 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 -// http://www.aprs.org/aprs12/mic-e-types.txt similar to how we use tocalls.txt. + if (strlen(trimmed) >=4 && + 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)) { - -// "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+4, strlen(trimmed) - 4); + return; } -/* - * 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 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 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 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 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. - */ + process_comment (A, trimmed, strlen(trimmed)); - if (plast > pfirst && pfirst[3] == '}') { - - 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); - -} +} // end aprs_mic_e /*------------------------------------------------------------------ @@ -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 '%s'\n", n, tocalls[n].len, tocalls[n].prefix, tocalls[n].description); - //} - } - - - for (n=0; ng_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. * * 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_lon - Might be adjusted from !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. * * /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? * @@ -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); } - 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) { regerror (e, &alt_re, emsg, sizeof(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) @@ -5186,7 +4890,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen) * * 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. * 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. text_color_init(1); text_color_set(DW_COLOR_INFO); + deviceid_init(); while (fgets(stuff, sizeof(stuff), stdin) != NULL) { diff --git a/src/deviceid.c b/src/deviceid.c new file mode 100644 index 0000000..de910e5 --- /dev/null +++ b/src/deviceid.c @@ -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 . +// + + +/*------------------------------------------------------------------ + * + * 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 +#include +#include +#include + +#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 diff --git a/src/deviceid.h b/src/deviceid.h new file mode 100644 index 0000000..d7a1b30 --- /dev/null +++ b/src/deviceid.h @@ -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); diff --git a/src/direwolf.c b/src/direwolf.c index e23aecb..2dfa58d 100644 --- a/src/direwolf.c +++ b/src/direwolf.c @@ -129,6 +129,7 @@ #include "dwsock.h" #include "dns_sd_dw.h" #include "dlq.h" // for fec_type_t definition. +#include "deviceid.h" //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 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." */ @@ -302,24 +303,27 @@ int main (int argc, char *argv[]) text_color_init(t_opt); text_color_set(DW_COLOR_INFO); //dw_printf ("Dire Wolf version %d.%d (%s) BETA TEST 7\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); - //dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "G", __DATE__); - dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION); + 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); -#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: "); -#if defined(ENABLE_GPSD) + #if defined(ENABLE_GPSD) dw_printf (" gpsd"); -#endif -#if defined(USE_HAMLIB) + #endif + #if defined(USE_HAMLIB) dw_printf (" hamlib"); -#endif -#if defined(USE_CM108) + #endif + #if defined(USE_CM108) dw_printf (" cm108-ptt"); -#endif -#if (USE_AVAHI_CLIENT|USE_MACOS_DNSSD) + #endif + #if defined(USE_GPIOD) + dw_printf (" libgpiod"); + #endif + #if (USE_AVAHI_CLIENT|USE_MACOS_DNSSD) dw_printf (" dns-sd"); -#endif + #endif dw_printf ("\n"); #endif @@ -982,6 +986,7 @@ int main (int argc, char *argv[]) * Files not supported at this time. * Can always "cat" the file and pipe it into stdin. */ + deviceid_init(); err = audio_open (&audio_config); 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 (" -q Quiet (suppress output) options:\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 (" -t n Text colors. 0=disabled. 1=default. 2,3,4,... alternatives.\n"); dw_printf (" Use 9 to test compatibility with your terminal.\n"); diff --git a/src/ptt.c b/src/ptt.c index 5187f1d..af74662 100644 --- a/src/ptt.c +++ b/src/ptt.c @@ -162,6 +162,10 @@ #include #endif +#ifdef USE_GPIOD +#include +#endif + /* So we can have more common code for fd. */ typedef int HANDLE; #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); dw_printf ("Error writing \"%s\" to %s, errno=%d\n", stemp, gpio_export_path, 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); } } @@ -634,6 +652,31 @@ void export_gpio(int ch, int ot, int invert, int direction) 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__ */ @@ -650,7 +693,8 @@ void export_gpio(int ch, int ot, int invert, int direction) * ptt_method Method for PTT signal. * PTT_METHOD_NONE - not configured. Could be using VOX. * 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_HAMLIB - HAMLib rig control. * 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) { 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, otnames[ot], 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_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].ptt_lpt_bit, 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) { 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 * 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->achan[ch].octrl[OCTYPE_PTT].ptt_method == PTT_METHOD_NONE) { 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); } + +#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 /* diff --git a/src/server.c b/src/server.c index 6b41af2..2cc108b 100644 --- a/src/server.c +++ b/src/server.c @@ -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 'D': strlcpy (datakind, "Connected AX.25 Data", 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 'U': strlcpy (datakind, "Monitored Unproto 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. */ - 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); if (debug_client) { @@ -1559,6 +1559,7 @@ static THREAD_F cmd_listen_thread (void *arg) case MEDIUM_RADIO: { + // Misleading if using stdin or udp. char stemp[100]; int a = ACHAN2ADEV(j); // If I was really ambitious, some description could be provided. @@ -1593,12 +1594,7 @@ static THREAD_F cmd_listen_thread (void *arg) break; default: - { - // 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)); - } + ; // Only list valid channels. break; } // switch @@ -1721,6 +1717,7 @@ static THREAD_F cmd_listen_thread (void *arg) packet_t pp; + int pid = cmd.hdr.pid; strlcpy (stemp, cmd.hdr.call_from, sizeof(stemp)); strlcat (stemp, ">", 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)); 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, p, sizeof(stemp)); + strlcat (stemp, " ", sizeof(stemp)); //text_color_set(DW_COLOR_DEBUG); //dw_printf ("Transmit '%s'\n", stemp); pp = ax25_from_text (stemp, 1); - if (pp == NULL) { text_color_set(DW_COLOR_ERROR); 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" */ - /* bits in the digipeater fields. */ + /* This goes into the low priority queue because it is an original. */ - /* 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. */ + /* Note that the protocol has no way to set the "has been used" */ + /* bits in the digipeater fields. */ - 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; @@ -1784,11 +1789,17 @@ static THREAD_F cmd_listen_thread (void *arg) // 00=Port 1 // 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 // first byte of the frame. Unfortunately, it did not subtract one from // 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)); 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? */ char dcall[7][10]; } -#if 1 + // October 2017. gcc ??? complained: // warning: dereferencing pointer 'v' does break strict-aliasing rules // 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. __attribute__((__may_alias__)) -#endif *v = (struct via_info *)cmd.data; 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; - (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]; - packet_t pp; strlcpy (stemp, cmd.hdr.call_from, sizeof(stemp)); strlcat (stemp, ">", sizeof(stemp)); @@ -2025,21 +2023,29 @@ static THREAD_F cmd_listen_thread (void *arg) 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, cmd.data, sizeof(stemp)); + strlcat (stemp, " ", sizeof(stemp)); //text_color_set(DW_COLOR_DEBUG); //dw_printf ("Transmit '%s'\n", stemp); - pp = ax25_from_text (stemp, 1); + packet_t pp = ax25_from_text (stemp, 1); if (pp == NULL) { text_color_set(DW_COLOR_ERROR); 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; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 91e06a2..da732ac 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -147,6 +147,7 @@ list(APPEND dtest_SOURCES ${CUSTOM_SRC_DIR}/tq.c ${CUSTOM_SRC_DIR}/textcolor.c ${CUSTOM_SRC_DIR}/decode_aprs.c + ${CUSTOM_SRC_DIR}/deviceid.c ${CUSTOM_SRC_DIR}/dwgpsnmea.c ${CUSTOM_SRC_DIR}/dwgps.c ${CUSTOM_SRC_DIR}/dwgpsd.c @@ -232,6 +233,7 @@ list(APPEND pftest_SOURCES ${CUSTOM_SRC_DIR}/textcolor.c ${CUSTOM_SRC_DIR}/fcs_calc.c ${CUSTOM_SRC_DIR}/decode_aprs.c + ${CUSTOM_SRC_DIR}/deviceid.c ${CUSTOM_SRC_DIR}/dwgpsnmea.c ${CUSTOM_SRC_DIR}/dwgps.c ${CUSTOM_SRC_DIR}/dwgpsd.c @@ -522,6 +524,7 @@ if(OPTIONAL_TEST) ${CUSTOM_SRC_DIR}/pfilter.c ${CUSTOM_SRC_DIR}/telemetry.c ${CUSTOM_SRC_DIR}/decode_aprs.c + ${CUSTOM_SRC_DIR}/deviceid.c.c ${CUSTOM_SRC_DIR}/dwgpsnmea.c ${CUSTOM_SRC_DIR}/dwgps.c ${CUSTOM_SRC_DIR}/dwgpsd.c @@ -574,6 +577,7 @@ if(OPTIONAL_TEST) ${CUSTOM_SRC_DIR}/fcs_calc.c ${CUSTOM_SRC_DIR}/ax25_pad.c ${CUSTOM_SRC_DIR}/decode_aprs.c + ${CUSTOM_SRC_DIR}/deviceid.c ${CUSTOM_SRC_DIR}/dwgpsnmea.c ${CUSTOM_SRC_DIR}/dwgps.c ${CUSTOM_SRC_DIR}/dwgpsd.c