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/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/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..adf26a1 --- /dev/null +++ b/data/tocalls.yaml @@ -0,0 +1,1481 @@ +# +# 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: "_)" + 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 + +# +# 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: APAM?? + vendor: Altus Metrum + model: AltOS + class: tracker + + - tocall: APAND? + vendor: Open Source + model: APRSdroid + os: Android + class: app + + - 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: 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: 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: 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: 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: APLG?? + vendor: OE5BPA + model: LoRa Gateway/Digipeater + class: digi + + - 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, CD2RXU + model: ESP32 LoRa iGate + class: igate + os: embedded + contact: richonguzman@gmail.com + + - tocall: APLRT? + vendor: Ricardo, CD2RXU + 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: 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 + + - tocall: APNV1? + vendor: SQ8L + model: VP-Node + os: embedded + + - 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: APOG7? + vendor: OpenGD77 + model: OpenGD77 + os: embedded + contact: Roger VK3KYY/G4KYF + + - tocall: APOLU? + vendor: AMSAT-LU + model: Oscar + class: satellite + + - 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: 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: APTKJ? + vendor: W9JAJ + model: ATTiny APRS Tracker + os: embedded + + - 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: APY300 + vendor: Yaesu + model: FTM-300D + class: rig + + - tocall: APY400 + vendor: Yaesu + model: FTM-400 + class: rig + + - tocall: APYS?? + vendor: W2GMD + model: Python APRS + class: software + + - 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 5320a16..19dada4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,6 +33,7 @@ list(APPEND direwolf_SOURCES beacon.c config.c decode_aprs.c + deviceid.c dedupe.c demod_9600.c demod_afsk.c @@ -171,6 +172,7 @@ endif() # decode_aprs list(APPEND decode_aprs_SOURCES decode_aprs.c + deviceid.c ais.c kiss_frame.c ax25_pad.c @@ -355,6 +357,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/decode_aprs.c b/src/decode_aprs.c index ab93327..54c2839 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,111 +1620,32 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int A->g_course = n; -/* 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)); +// 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 "_ " - pfirst = info + sizeof(struct aprs_mic_e_s); - plast = info + ilen - 1; - -/* Carriage return character at the end is not mentioned in spec. */ -/* Remove if found because it messes up extraction of manufacturer. */ -/* Don't drop trailing space because that is used for Yaesu VX-8. */ -/* As I recall, the IGate function trims trailing spaces. */ -/* That would be bad for this particular model. Maybe I'm mistaken? */ - - - if (*plast == '\r') plast--; - -#define isT(c) ((c) == ' ' || (c) == '>' || (c) == ']' || (c) == '`' || (c) == '\'') - -// 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. - -// TODO: Use https://github.com/aprsorg/aprs-deviceid rather than hardcoding. - - 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; } + 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'; } -/* - * 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. - */ +/* Now try to pick out manufacturer and other optional items. */ +/* The telemetry field, in the original spec, is no longer used. */ - if (plast > pfirst && pfirst[3] == '}') { + char trimmed[256]; // Comment with vendor/model removed. + deviceid_decode_mice (mcomment, trimmed, sizeof(trimmed), A->g_mfr, sizeof(A->g_mfr)); - 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])) + +// Possible altitude. 3 characters followed by } + + + if (strlen(trimmed) >=4 && trimmed[3] == '}') { + + A->g_altitude_ft = DW_METERS_TO_FEET((trimmed[0]-33)*91*91 + (trimmed[1]-33)*91 + (trimmed[2]-33) - 10000); + + if ( ! isdigit91(trimmed[0]) || ! isdigit91(trimmed[1]) || ! isdigit91(trimmed[2])) { if ( ! A->g_quiet) { text_color_set(DW_COLOR_ERROR); @@ -1736,12 +1655,13 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int A->g_altitude_ft = G_UNKNOWN; } - pfirst += 4; + process_comment (A, mcomment+4, strlen(mcomment) - 4); + return; } - process_comment (A, (char*)pfirst, (int)(plast - pfirst) + 1); + process_comment (A, mcomment, strlen(mcomment)); -} +} // end aprs_mic_e /*------------------------------------------------------------------ @@ -4251,221 +4171,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 +4218,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 +4248,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 +4417,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 +4777,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 +4895,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 +5041,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..594b20e --- /dev/null +++ b/src/deviceid.c @@ -0,0 +1,660 @@ +// +// 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); + + +// 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++) { + dw_printf ("Trying %s\n", search_locations[n]); + 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), //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 c8bb3a1..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; @@ -985,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) { 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