mirror of https://github.com/wb2osz/direwolf.git
Release 1.5.
This commit is contained in:
commit
c0abb4b216
|
@ -105,3 +105,5 @@ $RECYCLE.BIN/
|
|||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
/use_this_sdk
|
||||
*.dSYM
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# Normally, all of /dev/hidraw* are accessible only by root.
|
||||
#
|
||||
# $ ls -l /dev/hidraw*
|
||||
# crw------- 1 root root 247, 0 Sep 24 09:40 /dev/hidraw0
|
||||
#
|
||||
# An ordinary user, trying to acccess it will be denied.
|
||||
#
|
||||
# Unnecessarily running applications as root is generally a bad idea because it makes it too easy
|
||||
# to accidentally trash your system. We need to relax the restrictions so ordinary users can use these devices.
|
||||
#
|
||||
# If all went well with installation, the /etc/udev/rules.d directory should contain a file called
|
||||
# 99-direwolf-cmedia.rules containing:
|
||||
#
|
||||
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0d8c", GROUP="audio", MODE="0660"
|
||||
|
||||
#
|
||||
# I used the "audio" group, mimicking the permissions on the sound side of the device.
|
||||
#
|
||||
# $ ls -l /dev/snd/pcm*
|
||||
# crw-rw----+ 1 root audio 116, 16 Sep 24 09:40 /dev/snd/pcmC0D0p
|
||||
# crw-rw----+ 1 root audio 116, 17 Sep 24 09:40 /dev/snd/pcmC0D1p
|
||||
#
|
||||
# You should see something similar to this where someone in the "audio" group has read-write access.
|
||||
#
|
||||
# $ ls -l /dev/hidraw*
|
||||
# crw-rw---- 1 root audio 247, 0 Oct 6 19:24 /dev/hidraw0
|
||||
#
|
||||
# Read the User Guide and run the "cm108" application for more information.
|
||||
#
|
58
CHANGES.md
58
CHANGES.md
|
@ -2,6 +2,64 @@
|
|||
# Revision History #
|
||||
|
||||
|
||||
## Version 1.5 -- September 2018 ##
|
||||
|
||||
|
||||
### New Features: ###
|
||||
|
||||
- PTT using GPIO pin of CM108/CM119 (e.g. DMK URI, RB-USB RIM), Linux only.
|
||||
|
||||
- More efficient error recovery for AX.25 connected mode. Better generation and processing of REJ and SREJ to reduce unnecessary duplicate "**I**" frames.
|
||||
|
||||
- New configuration option, "**V20**", for listing stations known to not understand AX.25 v2.2. This will speed up connection by going right to SABM and not trying SABME first and failing.
|
||||
|
||||
- New "**NOXID**" configuration file option to avoid sending XID command to listed station(s). If other end is a partial v2.2 implementation, which recognizes SABME, but not XID, we would waste a lot of time resending XID many times before giving up. This is less drastic than the "**V20**" option which doesn't even attempt to use v2.2 with listed station(s).
|
||||
|
||||
- New application "**kissutil**" for troubleshooting a KISS TNC or interfacing to an application via files.
|
||||
|
||||
- KISS "Set Hardware" command to report transmit queue length.
|
||||
|
||||
- TCP KISS can now handle multiple concurrent applications.
|
||||
|
||||
- Linux can use serial port for KISS in addition to a pseudo terminal.
|
||||
|
||||
- decode_aprs utility can now accept KISS frames and AX.25 frames as series of two digit hexadecimal numbers.
|
||||
|
||||
- Full Duplex operation. (Put "FULLDUP ON" in channel section of configuration file.)
|
||||
|
||||
- Time slots for beaconing.
|
||||
|
||||
- Allow single log file with fixed name rather than starting a new one each day.
|
||||
|
||||
|
||||
|
||||
### Bugs Fixed: ###
|
||||
|
||||
- Possible crash when CDIGIPEAT did not include the optional alias.
|
||||
|
||||
- PACLEN configuration item no longer restricts length of received frames.
|
||||
|
||||
- Strange failures when trying to use multiple KISS client applications over TCP. Only Linux was affected.
|
||||
|
||||
- Under certain conditions, outgoing connected mode data would get stuck in a queue and not be transmitted. This could happen if client application sends a burst of data larger than the "window" size (MAXFRAME or EMAXFRAME option).
|
||||
|
||||
|
||||
- Little typographical / spelling errors in messages.
|
||||
|
||||
|
||||
### Documentation: ###
|
||||
|
||||
|
||||
- New document ***Bluetooth-KISS-TNC.pdf*** explaining how to use KISS over Bluetooth.
|
||||
|
||||
- Updates describing cheap SDR frequency inaccuracy and how to compensate for it.
|
||||
|
||||
### Notes: ###
|
||||
|
||||
Windows binary distribution now uses gcc (MinGW) version 6.3.0.
|
||||
|
||||
----------
|
||||
|
||||
## Version 1.4 -- April 2017 ##
|
||||
|
||||
|
||||
|
|
265
Makefile.linux
265
Makefile.linux
|
@ -2,7 +2,9 @@
|
|||
# Makefile for Linux version of Dire Wolf.
|
||||
#
|
||||
|
||||
APPS := direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc
|
||||
|
||||
|
||||
APPS := direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc kissutil cm108
|
||||
|
||||
all : $(APPS) direwolf.desktop direwolf.conf
|
||||
@echo " "
|
||||
|
@ -208,7 +210,7 @@ endif
|
|||
# If you compile with the RPi 2 specific options above and try to run it on the RPi
|
||||
# model B (pre version 2), it will die with "illegal instruction."
|
||||
#
|
||||
# Dire Wolf is known to work on the BeagleBone, CubieBoard2, etc.
|
||||
# Dire Wolf is known to work on the BeagleBone, CubieBoard2, CHIP, etc.
|
||||
# The best compiler options will depend on the specific type of processor
|
||||
# and the compiler target defaults.
|
||||
#
|
||||
|
@ -252,12 +254,22 @@ endif
|
|||
# but the initial test doesn't look good. About 2.3 times as slow.
|
||||
|
||||
# If you want to use OSS (for FreeBSD, OpenBSD) instead of
|
||||
# ALSA (for Linux), comment out (or remove) the two lines below.
|
||||
|
||||
# ALSA (for Linux), comment out (or remove) the line below.
|
||||
# TODO: Can we automate this somehow?
|
||||
|
||||
alsa = 1
|
||||
|
||||
ifeq ($(wildcard /usr/include/pthread.h),)
|
||||
$(error /usr/include/pthread.h does not exist. Install it with "sudo apt-get install libc6-dev" or "sudo yum install glibc-headers" )
|
||||
endif
|
||||
|
||||
ifneq ($(alsa),)
|
||||
CFLAGS += -DUSE_ALSA
|
||||
LDFLAGS += -lasound
|
||||
ifeq ($(wildcard /usr/include/alsa/asoundlib.h),)
|
||||
$(error /usr/include/alsa/asoundlib.h does not exist. Install it with "sudo apt-get install libasound2-dev" or "sudo yum install alsa-lib-devel" )
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
# Enable GPS if header file is present.
|
||||
|
@ -271,11 +283,32 @@ LDFLAGS += -lgps
|
|||
endif
|
||||
|
||||
|
||||
# Uncomment following lines to enable hamlib support.
|
||||
# TODO: automate this too. See if hamlib has been installed.
|
||||
# Enable hamlib support if header file is present.
|
||||
|
||||
#CFLAGS += -DUSE_HAMLIB
|
||||
#LDFLAGS += -lhamlib
|
||||
enable_hamlib := $(wildcard /usr/include/hamlib/rig.h /usr/local/include/hamlib/rig.h)
|
||||
ifneq ($(enable_hamlib),)
|
||||
CFLAGS += -DUSE_HAMLIB
|
||||
LDFLAGS += -lhamlib
|
||||
endif
|
||||
|
||||
|
||||
# Should enabling of this feature be strongly encouraged or
|
||||
# is it quite specialized and of interest to a small audience?
|
||||
# If, for some reason, can obtain the libudev-dev package, or
|
||||
# don't want to install it, comment out the next 3 lines.
|
||||
|
||||
#ifeq ($(wildcard /usr/include/libudev.h),)
|
||||
#$(error /usr/include/libudev.h does not exist. Install it with "sudo apt-get install libudev-dev" or "sudo yum install libudev-devel" )
|
||||
#endif
|
||||
|
||||
|
||||
# Enable cm108 PTT support if libudev header file is present.
|
||||
|
||||
enable_cm108 := $(wildcard /usr/include/libudev.h)
|
||||
ifneq ($(enable_cm108),)
|
||||
CFLAGS += -DUSE_CM108
|
||||
LDFLAGS += -ludev
|
||||
endif
|
||||
|
||||
|
||||
# Name of current directory.
|
||||
|
@ -292,20 +325,30 @@ z := $(notdir ${CURDIR})
|
|||
direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_psk.o demod_9600.o hdlc_rec.o \
|
||||
hdlc_rec2.o multi_modem.o rdq.o rrbb.o dlq.o \
|
||||
fcs_calc.o ax25_pad.o ax25_pad2.o xid.o \
|
||||
decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
|
||||
decode_aprs.o symbols.o server.o kiss.o kissserial.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
|
||||
gen_tone.o audio.o audio_stats.o digipeater.o cdigipeater.o pfilter.o dedupe.o tq.o xmit.o morse.o \
|
||||
ptt.o beacon.o encode_aprs.o latlong.o encode_aprs.o latlong.o textcolor.o \
|
||||
dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o waypoint.o serial_port.o log.o telemetry.o \
|
||||
dwgps.o dwgpsnmea.o dwgpsd.o dtime_now.o mheard.o ax25_link.o \
|
||||
dwgps.o dwgpsnmea.o dwgpsd.o dtime_now.o mheard.o ax25_link.o cm108.o \
|
||||
misc.a geotranz.a
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
@echo " "
|
||||
ifneq ($(enable_gpsd),)
|
||||
@echo " "
|
||||
@echo "This includes support for gpsd."
|
||||
@echo "\t>\tThis includes support for gpsd."
|
||||
else
|
||||
@echo " "
|
||||
@echo "This does NOT include support for gpsd."
|
||||
@echo "\t>\tThis does NOT include support for gpsd."
|
||||
endif
|
||||
ifneq ($(enable_hamlib),)
|
||||
@echo "\t>\tThis includes support for hamlib."
|
||||
else
|
||||
@echo "\t>\tThis does NOT include support for hamlib."
|
||||
endif
|
||||
ifneq ($(enable_cm108),)
|
||||
@echo "\t>\tThis includes support for CM108/CM119 PTT."
|
||||
else
|
||||
@echo "\t>\tThis does NOT include support for CM108/CM119 PTT."
|
||||
endif
|
||||
@echo " "
|
||||
|
||||
# Optimization for slow processors.
|
||||
|
||||
|
@ -355,7 +398,9 @@ tocalls-symbols :
|
|||
|
||||
# Separate application to decode raw data.
|
||||
|
||||
decode_aprs : decode_aprs.c dwgpsnmea.o dwgps.o dwgpsd.o serial_port.o symbols.o ax25_pad.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o misc.a
|
||||
# First three use .c rather than .o because they depend on DECAMAIN definition.
|
||||
|
||||
decode_aprs : decode_aprs.c kiss_frame.c ax25_pad.c dwgpsnmea.o dwgps.o dwgpsd.o serial_port.o symbols.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o misc.a
|
||||
$(CC) $(CFLAGS) -DDECAMAIN -o $@ $^ $(LDFLAGS)
|
||||
|
||||
|
||||
|
@ -405,6 +450,19 @@ aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.o misc.a
|
|||
$(CC) $(CFLAGS) -g -o $@ $^
|
||||
|
||||
|
||||
# Talk to a KISS TNC.
|
||||
# Note: kiss_frame.c has conditional compilation on KISSUTIL.
|
||||
|
||||
kissutil : kissutil.c kiss_frame.c ax25_pad.o fcs_calc.o textcolor.o serial_port.o dtime_now.o sock.o misc.a
|
||||
$(CC) $(CFLAGS) -g -DKISSUTIL -o $@ $^ $(LDFLAGS)
|
||||
|
||||
|
||||
# List USB audio adapters than can use GPIO for PTT.
|
||||
|
||||
cm108 : cm108.c textcolor.o misc.a
|
||||
$(CC) $(CFLAGS) -g -DCM108_MAIN -o $@ $^ $(LDFLAGS)
|
||||
|
||||
|
||||
# Touch Tone to Speech sample application.
|
||||
|
||||
ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a
|
||||
|
@ -474,17 +532,17 @@ direwolf.conf : generic.conf
|
|||
|
||||
# Where should we install it?
|
||||
|
||||
# My understanding, of the convention, is that something you compile
|
||||
# from source, that is not a standard part of the operating system,
|
||||
# should go in /usr/local/bin.
|
||||
# Something built from source and installed locally would normally go in /usr/local/...
|
||||
# If not specified on the make command line, this is our default.
|
||||
|
||||
# However, if you are preparing a "binary" DEB or RPM package, the
|
||||
# installation location should be /usr/bin.
|
||||
DESTDIR ?= /usr/local
|
||||
|
||||
# However, if you are preparing a "binary" DEB or RPM package, the installation location
|
||||
# would normally be /usr/... instead. In this case, use a command line like this:
|
||||
#
|
||||
# make DESTDIR=/usr install
|
||||
|
||||
# This is a step in the right direction but not sufficient to use /usr instead.
|
||||
# Eventually I'd like to have targets here to build the .DEB and .RPM packages.
|
||||
|
||||
INSTALLDIR := /usr/local
|
||||
|
||||
# Command to "install" to system directories. Use "ginstall" for Mac.
|
||||
|
||||
|
@ -502,22 +560,22 @@ direwolf.desktop :
|
|||
@echo '[Desktop Entry]' > $@
|
||||
@echo 'Type=Application' >> $@
|
||||
ifneq ($(wildcard /usr/bin/lxterminal),)
|
||||
@echo "Exec=lxterminal -t \"Dire Wolf\" -e \"$(INSTALLDIR)/bin/direwolf\"" >> $@
|
||||
@echo "Exec=lxterminal -t \"Dire Wolf\" -e \"$(DESTDIR)/bin/direwolf\"" >> $@
|
||||
else ifneq ($(wildcard /usr/bin/lxterm),)
|
||||
@echo "Exec=lxterm -hold -title \"Dire Wolf\" -bg white -e \"$(INSTALLDIR)/bin/direwolf\"" >> $@
|
||||
@echo "Exec=lxterm -hold -title \"Dire Wolf\" -bg white -e \"$(DESTDIR)/bin/direwolf\"" >> $@
|
||||
else
|
||||
@echo "Exec=xterm -hold -title \"Dire Wolf\" -bg white -e \"$(INSTALLDIR)/bin/direwolf\"" >> $@
|
||||
@echo "Exec=xterm -hold -title \"Dire Wolf\" -bg white -e \"$(DESTDIR)/bin/direwolf\"" >> $@
|
||||
endif
|
||||
@echo 'Name=Dire Wolf' >> $@
|
||||
@echo 'Comment=APRS Soundcard TNC' >> $@
|
||||
@echo 'Icon=/usr/share/direwolf/dw-icon.png' >> $@
|
||||
@echo 'Icon=$(DESTDIR)/share/direwolf/pixmaps/dw-icon.png' >> $@
|
||||
@echo "Path=$(HOME)" >> $@
|
||||
@echo '#Terminal=true' >> $@
|
||||
@echo 'Categories=HamRadio' >> $@
|
||||
@echo 'Keywords=Ham Radio;APRS;Soundcard TNC;KISS;AGWPE;AX.25' >> $@
|
||||
|
||||
|
||||
# Installation into /usr/local/...
|
||||
# Installation into $(DESTDIR), usually /usr/local/... or /usr/...
|
||||
# Needs to be run as root or with sudo.
|
||||
|
||||
|
||||
|
@ -527,83 +585,102 @@ install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon
|
|||
# Applications, not installed with package manager, normally go in /usr/local/bin.
|
||||
# /usr/bin is used instead when installing from .DEB or .RPM package.
|
||||
#
|
||||
$(INSTALL) direwolf $(INSTALLDIR)/bin
|
||||
$(INSTALL) decode_aprs $(INSTALLDIR)/bin
|
||||
$(INSTALL) text2tt $(INSTALLDIR)/bin
|
||||
$(INSTALL) tt2text $(INSTALLDIR)/bin
|
||||
$(INSTALL) ll2utm $(INSTALLDIR)/bin
|
||||
$(INSTALL) utm2ll $(INSTALLDIR)/bin
|
||||
$(INSTALL) aclients $(INSTALLDIR)/bin
|
||||
$(INSTALL) log2gpx $(INSTALLDIR)/bin
|
||||
$(INSTALL) gen_packets $(INSTALLDIR)/bin
|
||||
$(INSTALL) atest $(INSTALLDIR)/bin
|
||||
$(INSTALL) ttcalc $(INSTALLDIR)/bin
|
||||
$(INSTALL) dwespeak.sh $(INSTALLDIR)/bin
|
||||
$(INSTALL) -D --mode=755 direwolf $(DESTDIR)/bin/direwolf
|
||||
$(INSTALL) -D --mode=755 decode_aprs $(DESTDIR)/bin/decode_aprs
|
||||
$(INSTALL) -D --mode=755 text2tt $(DESTDIR)/bin/text2tt
|
||||
$(INSTALL) -D --mode=755 tt2text $(DESTDIR)/bin/tt2text
|
||||
$(INSTALL) -D --mode=755 ll2utm $(DESTDIR)/bin/ll2utm
|
||||
$(INSTALL) -D --mode=755 utm2ll $(DESTDIR)/bin/utm2ll
|
||||
$(INSTALL) -D --mode=755 aclients $(DESTDIR)/bin/aclients
|
||||
$(INSTALL) -D --mode=755 log2gpx $(DESTDIR)/bin/log2gpx
|
||||
$(INSTALL) -D --mode=755 gen_packets $(DESTDIR)/bin/gen_packets
|
||||
$(INSTALL) -D --mode=755 atest $(DESTDIR)/bin/atest
|
||||
$(INSTALL) -D --mode=755 ttcalc $(DESTDIR)/bin/ttcalc
|
||||
$(INSTALL) -D --mode=755 kissutil $(DESTDIR)/bin/kissutil
|
||||
$(INSTALL) -D --mode=755 cm108 $(DESTDIR)/bin/cm108
|
||||
$(INSTALL) -D --mode=755 dwespeak.sh $(DESTDIR)/bin/dwspeak.sh
|
||||
#
|
||||
# Telemetry Toolkit executables. Other .conf and .txt files will go into doc directory.
|
||||
#
|
||||
$(INSTALL) telemetry-toolkit/telem-balloon.pl $(INSTALLDIR)/bin
|
||||
$(INSTALL) telemetry-toolkit/telem-bits.pl $(INSTALLDIR)/bin
|
||||
$(INSTALL) telemetry-toolkit/telem-data.pl $(INSTALLDIR)/bin
|
||||
$(INSTALL) telemetry-toolkit/telem-data91.pl $(INSTALLDIR)/bin
|
||||
$(INSTALL) telemetry-toolkit/telem-eqns.pl $(INSTALLDIR)/bin
|
||||
$(INSTALL) telemetry-toolkit/telem-parm.pl $(INSTALLDIR)/bin
|
||||
$(INSTALL) telemetry-toolkit/telem-seq.sh $(INSTALLDIR)/bin
|
||||
$(INSTALL) telemetry-toolkit/telem-unit.pl $(INSTALLDIR)/bin
|
||||
$(INSTALL) telemetry-toolkit/telem-volts.py $(INSTALLDIR)/bin
|
||||
$(INSTALL) -D --mode=755 telemetry-toolkit/telem-balloon.pl $(DESTDIR)/bin/telem-balloon.pl
|
||||
$(INSTALL) -D --mode=755 telemetry-toolkit/telem-bits.pl $(DESTDIR)/bin/telem-bits.pl
|
||||
$(INSTALL) -D --mode=755 telemetry-toolkit/telem-data.pl $(DESTDIR)/bin/telem-data.pl
|
||||
$(INSTALL) -D --mode=755 telemetry-toolkit/telem-data91.pl $(DESTDIR)/bin/telem-data91.pl
|
||||
$(INSTALL) -D --mode=755 telemetry-toolkit/telem-eqns.pl $(DESTDIR)/bin/telem-eqns.pl
|
||||
$(INSTALL) -D --mode=755 telemetry-toolkit/telem-parm.pl $(DESTDIR)/bin/telem-parm.pl
|
||||
$(INSTALL) -D --mode=755 telemetry-toolkit/telem-seq.sh $(DESTDIR)/bin/telem-seq.sh
|
||||
$(INSTALL) -D --mode=755 telemetry-toolkit/telem-unit.pl $(DESTDIR)/bin/telem-unit.pl
|
||||
$(INSTALL) -D --mode=755 telemetry-toolkit/telem-volts.py $(DESTDIR)/bin/telem-volts.py
|
||||
#
|
||||
# Misc. data such as "tocall" to system mapping.
|
||||
#
|
||||
$(INSTALL) -D --mode=644 tocalls.txt /usr/share/direwolf/tocalls.txt
|
||||
$(INSTALL) -D --mode=644 symbols-new.txt /usr/share/direwolf/symbols-new.txt
|
||||
$(INSTALL) -D --mode=644 symbolsX.txt /usr/share/direwolf/symbolsX.txt
|
||||
$(INSTALL) -D --mode=644 dw-icon.png /usr/share/direwolf/dw-icon.png
|
||||
$(INSTALL) -D --mode=644 direwolf.desktop /usr/share/applications/direwolf.desktop
|
||||
$(INSTALL) -D --mode=644 tocalls.txt $(DESTDIR)/share/direwolf/tocalls.txt
|
||||
$(INSTALL) -D --mode=644 symbols-new.txt $(DESTDIR)/share/direwolf/symbols-new.txt
|
||||
$(INSTALL) -D --mode=644 symbolsX.txt $(DESTDIR)/share/direwolf/symbolsX.txt
|
||||
#
|
||||
# For desktop icon.
|
||||
#
|
||||
$(INSTALL) -D --mode=644 dw-icon.png $(DESTDIR)/share/direwolf/pixmaps/dw-icon.png
|
||||
$(INSTALL) -D --mode=644 direwolf.desktop $(DESTDIR)/share/applications/direwolf.desktop
|
||||
#
|
||||
# Documentation. Various plain text files and PDF.
|
||||
#
|
||||
$(INSTALL) -D --mode=644 CHANGES.md $(INSTALLDIR)/share/doc/direwolf/CHANGES.md
|
||||
$(INSTALL) -D --mode=644 LICENSE-dire-wolf.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-dire-wolf.txt
|
||||
$(INSTALL) -D --mode=644 LICENSE-other.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-other.txt
|
||||
$(INSTALL) -D --mode=644 CHANGES.md $(DESTDIR)/share/doc/direwolf/CHANGES.md
|
||||
$(INSTALL) -D --mode=644 LICENSE-dire-wolf.txt $(DESTDIR)/share/doc/direwolf/LICENSE-dire-wolf.txt
|
||||
$(INSTALL) -D --mode=644 LICENSE-other.txt $(DESTDIR)/share/doc/direwolf/LICENSE-other.txt
|
||||
#
|
||||
# ./README.md is an overview for the project main page.
|
||||
# Maybe we could stick it in some other place.
|
||||
# doc/README.md contains an overview of the PDF file contents and is more useful here.
|
||||
#
|
||||
$(INSTALL) -D --mode=644 doc/README.md $(INSTALLDIR)/share/doc/direwolf/README.md
|
||||
$(INSTALL) -D --mode=644 doc/User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf
|
||||
$(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf
|
||||
$(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS-Tracker.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf
|
||||
$(INSTALL) -D --mode=644 doc/Raspberry-Pi-SDR-IGate.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-SDR-IGate.pdf
|
||||
$(INSTALL) -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf
|
||||
$(INSTALL) -D --mode=644 doc/APRStt-interface-for-SARTrack.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-interface-for-SARTrack.pdf
|
||||
$(INSTALL) -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf
|
||||
$(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf
|
||||
$(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf
|
||||
$(INSTALL) -D --mode=644 doc/README.md $(DESTDIR)/share/doc/direwolf/README.md
|
||||
$(INSTALL) -D --mode=644 doc/2400-4800-PSK-for-APRS-Packet-Radio.pdf $(DESTDIR)/share/doc/direwolf/2400-4800-PSK-for-APRS-Packet-Radio.pdf
|
||||
$(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf $(DESTDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf
|
||||
$(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf $(DESTDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf
|
||||
$(INSTALL) -D --mode=644 doc/A-Closer-Look-at-the-WA8LMF-TNC-Test-CD.pdf $(DESTDIR)/share/doc/direwolf/A-Closer-Look-at-the-WA8LMF-TNC-Test-CD.pdf
|
||||
$(INSTALL) -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(DESTDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf
|
||||
$(INSTALL) -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(DESTDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf
|
||||
$(INSTALL) -D --mode=644 doc/APRStt-interface-for-SARTrack.pdf $(DESTDIR)/share/doc/direwolf/APRStt-interface-for-SARTrack.pdf
|
||||
$(INSTALL) -D --mode=644 doc/APRStt-Listening-Example.pdf $(DESTDIR)/share/doc/direwolf/APRStt-Listening-Example.pdf
|
||||
$(INSTALL) -D --mode=644 doc/Bluetooth-KISS-TNC.pdf $(DESTDIR)/share/doc/direwolf/Bluetooth-KISS-TNC.pdf
|
||||
$(INSTALL) -D --mode=644 doc/Going-beyond-9600-baud.pdf $(DESTDIR)/share/doc/direwolf/Going-beyond-9600-baud.pdf
|
||||
$(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS.pdf $(DESTDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf
|
||||
$(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS-Tracker.pdf $(DESTDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf
|
||||
$(INSTALL) -D --mode=644 doc/Raspberry-Pi-SDR-IGate.pdf $(DESTDIR)/share/doc/direwolf/Raspberry-Pi-SDR-IGate.pdf
|
||||
$(INSTALL) -D --mode=644 doc/Successful-APRS-IGate-Operation.pdf $(DESTDIR)/share/doc/direwolf/Successful-APRS-IGate-Operation.pdf
|
||||
$(INSTALL) -D --mode=644 doc/User-Guide.pdf $(DESTDIR)/share/doc/direwolf/User-Guide.pdf
|
||||
$(INSTALL) -D --mode=644 doc/WA8LMF-TNC-Test-CD-Results.pdf $(DESTDIR)/share/doc/direwolf/WA8LMF-TNC-Test-CD-Results.pdf
|
||||
#
|
||||
# Various sample config and other files go into examples under the doc directory.
|
||||
# When building from source, these can be put in home directory with "make install-conf".
|
||||
# When installed from .DEB or .RPM package, the user will need to copy these to
|
||||
# the home directory or other desired location.
|
||||
#
|
||||
$(INSTALL) -D --mode=644 direwolf.conf $(INSTALLDIR)/share/doc/direwolf/examples/direwolf.conf
|
||||
$(INSTALL) -D --mode=755 dw-start.sh $(INSTALLDIR)/share/doc/direwolf/examples/dw-start.sh
|
||||
$(INSTALL) -D --mode=644 sdr.conf $(INSTALLDIR)/share/doc/direwolf/examples/sdr.conf
|
||||
$(INSTALL) -D --mode=644 telemetry-toolkit/telem-m0xer-3.txt $(INSTALLDIR)/share/doc/direwolf/examples/telem-m0xer-3.txt
|
||||
$(INSTALL) -D --mode=644 telemetry-toolkit/telem-balloon.conf $(INSTALLDIR)/share/doc/direwolf/examples/telem-balloon.conf
|
||||
$(INSTALL) -D --mode=644 telemetry-toolkit/telem-volts.conf $(INSTALLDIR)/share/doc/direwolf/examples/telem-volts.conf
|
||||
$(INSTALL) -D --mode=644 direwolf.conf $(DESTDIR)/share/doc/direwolf/examples/direwolf.conf
|
||||
$(INSTALL) -D --mode=755 dw-start.sh $(DESTDIR)/share/doc/direwolf/examples/dw-start.sh
|
||||
$(INSTALL) -D --mode=644 sdr.conf $(DESTDIR)/share/doc/direwolf/examples/sdr.conf
|
||||
$(INSTALL) -D --mode=644 telemetry-toolkit/telem-m0xer-3.txt $(DESTDIR)/share/doc/direwolf/examples/telem-m0xer-3.txt
|
||||
$(INSTALL) -D --mode=644 telemetry-toolkit/telem-balloon.conf $(DESTDIR)/share/doc/direwolf/examples/telem-balloon.conf
|
||||
$(INSTALL) -D --mode=644 telemetry-toolkit/telem-volts.conf $(DESTDIR)/share/doc/direwolf/examples/telem-volts.conf
|
||||
#
|
||||
# "man" pages
|
||||
#
|
||||
$(INSTALL) -D --mode=644 man1/aclients.1 $(INSTALLDIR)/man/man1/aclients.1
|
||||
$(INSTALL) -D --mode=644 man1/atest.1 $(INSTALLDIR)/man/man1/atest.1
|
||||
$(INSTALL) -D --mode=644 man1/decode_aprs.1 $(INSTALLDIR)/man/man1/decode_aprs.1
|
||||
$(INSTALL) -D --mode=644 man1/direwolf.1 $(INSTALLDIR)/man/man1/direwolf.1
|
||||
$(INSTALL) -D --mode=644 man1/gen_packets.1 $(INSTALLDIR)/man/man1/gen_packets.1
|
||||
$(INSTALL) -D --mode=644 man1/ll2utm.1 $(INSTALLDIR)/man/man1/ll2utm.1
|
||||
$(INSTALL) -D --mode=644 man1/log2gpx.1 $(INSTALLDIR)/man/man1/log2gpx.1
|
||||
$(INSTALL) -D --mode=644 man1/text2tt.1 $(INSTALLDIR)/man/man1/text2tt.1
|
||||
$(INSTALL) -D --mode=644 man1/tt2text.1 $(INSTALLDIR)/man/man1/tt2text.1
|
||||
$(INSTALL) -D --mode=644 man1/utm2ll.1 $(INSTALLDIR)/man/man1/utm2ll.1
|
||||
$(INSTALL) -D --mode=644 man1/aclients.1 $(DESTDIR)/share/man/man1/aclients.1
|
||||
$(INSTALL) -D --mode=644 man1/atest.1 $(DESTDIR)/share/man/man1/atest.1
|
||||
$(INSTALL) -D --mode=644 man1/decode_aprs.1 $(DESTDIR)/share/man/man1/decode_aprs.1
|
||||
$(INSTALL) -D --mode=644 man1/direwolf.1 $(DESTDIR)/share/man/man1/direwolf.1
|
||||
$(INSTALL) -D --mode=644 man1/gen_packets.1 $(DESTDIR)/share/man/man1/gen_packets.1
|
||||
$(INSTALL) -D --mode=644 man1/kissutil.1 $(DESTDIR)/share/man/man1/kissutil.1
|
||||
$(INSTALL) -D --mode=644 man1/ll2utm.1 $(DESTDIR)/share/man/man1/ll2utm.1
|
||||
$(INSTALL) -D --mode=644 man1/log2gpx.1 $(DESTDIR)/share/man/man1/log2gpx.1
|
||||
$(INSTALL) -D --mode=644 man1/text2tt.1 $(DESTDIR)/share/man/man1/text2tt.1
|
||||
$(INSTALL) -D --mode=644 man1/tt2text.1 $(DESTDIR)/share/man/man1/tt2text.1
|
||||
$(INSTALL) -D --mode=644 man1/utm2ll.1 $(DESTDIR)/share/man/man1/utm2ll.1
|
||||
#
|
||||
# Set group and mode of HID devices corresponding to C-Media USB Audio adapters.
|
||||
# This will allow us to use the CM108/CM119 GPIO pins for PTT.
|
||||
#
|
||||
$(INSTALL) -D --mode=644 99-direwolf-cmedia.rules /etc/udev/rules.d/99-direwolf-cmedia.rules
|
||||
#
|
||||
@echo " "
|
||||
@echo "If this is your first install, not an upgrade, type this to put a copy"
|
||||
|
@ -613,8 +690,10 @@ install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon
|
|||
@echo " "
|
||||
|
||||
|
||||
# Put sample configuration files in home directory.
|
||||
# These would be done as ordinary user.
|
||||
# Put sample configuration & startup files in home directory.
|
||||
# This step would be done as ordinary user.
|
||||
# Some people like to put the direwolf config file in /etc/ax25.
|
||||
# Note that all of these are also in $(DESTDIR)/share/doc/direwolf/examples/.
|
||||
|
||||
# The Raspberry Pi has ~/Desktop but Ubuntu does not.
|
||||
|
||||
|
@ -622,6 +701,8 @@ install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon
|
|||
|
||||
# Version 1.4 - Add "-n" option to avoid clobbering existing, probably customized, config files.
|
||||
|
||||
# dw-start.sh is greatly improved in version 1.4.
|
||||
# It was moved from isntall-rpi to install-conf because it is not just for the RPi.
|
||||
|
||||
.PHONY: install-conf
|
||||
install-conf : direwolf.conf
|
||||
|
@ -629,23 +710,21 @@ install-conf : direwolf.conf
|
|||
cp -n sdr.conf ~
|
||||
cp -n telemetry-toolkit/telem-m0xer-3.txt ~
|
||||
cp -n telemetry-toolkit/telem-*.conf ~
|
||||
chmod +x dw-start.sh
|
||||
cp -n dw-start.sh ~
|
||||
ifneq ($(wildcard $(HOME)/Desktop),)
|
||||
@echo " "
|
||||
@echo "This will add a desktop icon on some systems:"
|
||||
@echo "This will add a desktop icon on some systems."
|
||||
@echo "This is known to work on Raspberry Pi but might not be compatible with other desktops."
|
||||
@echo " "
|
||||
@echo " make install-rpi"
|
||||
@echo " "
|
||||
endif
|
||||
|
||||
|
||||
# dw-start.sh is greatly improved in version 1.4.
|
||||
# It should probably be part of install-conf because it is not just for the RPi.
|
||||
|
||||
.PHONY: install-rpi
|
||||
install-rpi : dw-start.sh
|
||||
chmod +x dw-start.sh
|
||||
cp -n dw-start.sh ~
|
||||
ln -f -s /usr/share/applications/direwolf.desktop ~/Desktop/direwolf.desktop
|
||||
install-rpi :
|
||||
ln -f -s $(DESTDIR)/share/applications/direwolf.desktop ~/Desktop/direwolf.desktop
|
||||
|
||||
|
||||
|
||||
|
|
194
Makefile.macosx
194
Makefile.macosx
|
@ -24,9 +24,10 @@
|
|||
# 3. Removed fsk_fast_filter.h from atest receipe, clang compiler was having
|
||||
# a hissy fit. Not check with GCC.
|
||||
|
||||
APPS := direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc
|
||||
APPS := direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc kissutil
|
||||
|
||||
all : $(APPS) direwolf.desktop direwolf.conf @echo " "
|
||||
all : $(APPS) direwolf.conf
|
||||
@echo " "
|
||||
@echo "Next step install with: "
|
||||
@echo " "
|
||||
@echo " sudo make install"
|
||||
|
@ -35,27 +36,6 @@ all : $(APPS) direwolf.desktop direwolf.conf @echo " "
|
|||
|
||||
SYS_LIBS :=
|
||||
SYS_MIN :=
|
||||
#SDK := $(shell find /Developer -maxdepth 1 -type d -name "SDKs")
|
||||
#$(info $$SDK = ${SDK})
|
||||
#ifeq (${SDK},/Developer/SDKs)
|
||||
# SDK := $(shell find /Developer/SDKs -maxdepth 1 -type d -name "MacOSX10.8.sdk")
|
||||
# ifeq (${SDK},/Developer/SDKs/MacOSX10.8.sdk)
|
||||
# SYS_LIBS := -isystem /Developer/SDKs/MacOSX10.8.sdk
|
||||
# SYS_MIN := -mmacosx-version-min=10.8
|
||||
# else
|
||||
# SDK := $(shell find /Developer/SDKs -maxdepth 1 -type d -name "MacOSX10.9.sdk")
|
||||
# ifeq (${SDK},/Developer/SDKs/MacOSX10.9.sdk)
|
||||
# SYS_LIBS := -isystem /Developer/SDKs/MacOSX10.9.sdk
|
||||
# SYS_MIN := -mmacosx-version-min=10.9
|
||||
# else
|
||||
# SDK := $(shell find /Developer/SDKs -maxdepth 1 -type d -name "MacOSX10.10.sdk")
|
||||
# ifeq (${SDK},/Developer/SDKs/MacOSX10.10.sdk)
|
||||
# SYS_LIBS := -isystem /Developer/SDKs/MacOSX10.10.sdk
|
||||
# SYS_MIN := -mmacosx-version-min=10.10
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
SYS_LIBS := $(shell ./search_sdks.sh)
|
||||
EXTRA_CFLAGS :=
|
||||
|
@ -69,8 +49,12 @@ endif
|
|||
|
||||
# Change as required in support of the available libraries
|
||||
|
||||
#CC := $(DARWIN_CC) -m64 $(SYS_LIBS) $(SYS_MIN)
|
||||
UNAME_M := $(shell uname -m)
|
||||
ifeq (${UNAME_M},x86_64)
|
||||
CC := $(DARWIN_CC) -m64 $(SYS_LIBS) $(SYS_MIN)
|
||||
else
|
||||
CC := $(DARWIN_CC) -m32 $(SYS_LIBS) $(SYS_MIN)
|
||||
endif
|
||||
|
||||
# _XOPEN_SOURCE=600 and _DEFAULT_SOURCE=1 are needed for glibc >= 2.24.
|
||||
# Explanation here: https://github.com/wb2osz/direwolf/issues/62
|
||||
|
@ -85,86 +69,14 @@ CFLAGS += -D_BSD_SOURCE
|
|||
|
||||
# $(info $$CC is [${CC}])
|
||||
|
||||
#
|
||||
# The DSP filters spend a lot of time spinning around in little
|
||||
# loops multiplying and adding arrays of numbers. The Intel "SSE"
|
||||
# instructions, introduced in 1999 with the Pentium III series,
|
||||
# can speed this up considerably.
|
||||
#
|
||||
# SSE2 instructions, added in 2000, don't seem to offer any advantage.
|
||||
#
|
||||
#
|
||||
# Let's take a look at the effect of the compile options.
|
||||
#
|
||||
#
|
||||
# Times are elapsed time to process Track 2 of the TNC test CD.
|
||||
#
|
||||
# i.e. "./atest 02_Track_2.wav"
|
||||
# Default demodulator type is new "E" added for version 1.2.
|
||||
#
|
||||
|
||||
#
|
||||
# ---------- x86 (32 bit) ----------
|
||||
#
|
||||
|
||||
#
|
||||
# gcc 4.6.3 running on Ubuntu 12.04.05.
|
||||
# Intel(R) Celeron(R) CPU 2.53GHz. Appears to have only 32 bit instructions.
|
||||
# Probably from around 2004 or 2005.
|
||||
#
|
||||
# When gcc is generating code for a 32 bit x86 target, it assumes the ancient
|
||||
# i386 processor. This is good for portability but bad for performance.
|
||||
#
|
||||
# The code can run considerably faster by taking advantage of the SSE instructions
|
||||
# available in the Pentium 3 or later.
|
||||
#
|
||||
# seconds options comments
|
||||
# ------ ------- --------
|
||||
# 524
|
||||
# 183 -O2
|
||||
# 182 -O3
|
||||
# 183 -O3 -ffast-math (should be same as -Ofast)
|
||||
# 184 -Ofast
|
||||
# 189 -O3 -ffast-math -march=pentium
|
||||
# 122 -O3 -ffast-math -msse
|
||||
# 122 -O3 -ffast-math -march=pentium -msse
|
||||
# 121 -O3 -ffast-math -march=pentium3 (this implies -msse)
|
||||
# 120 -O3 -ffast-math -march=native
|
||||
#
|
||||
# Note that "-march=native" is essentially the same as "-march=pentium3."
|
||||
#
|
||||
|
||||
# If the compiler is generating code for the i386 target, we can
|
||||
# get much better results by telling it we have at least a Pentium 3.
|
||||
# If the compiler is generating code for a 32 bit target (-m32), we can
|
||||
# get much better results by telling it we have at least a Pentium 3
|
||||
# which hass the SSE instructions.
|
||||
|
||||
CFLAGS += -march=core2 -msse4.1 -std=gnu99
|
||||
#CFLAGS += -march=pentium3 -sse
|
||||
|
||||
#
|
||||
# gcc 4.8.2 running on Ubuntu 14.04.1.
|
||||
# Intel Core 2 Duo from around 2007 or 2008.
|
||||
#
|
||||
# 64 bit target implies that we have SSE and probably even better vector instructions.
|
||||
#
|
||||
# seconds options comments
|
||||
# ------ ------- --------
|
||||
# 245
|
||||
# 75 -01
|
||||
# 72 -02
|
||||
# 71 -03
|
||||
# 73 -O3 -march=native
|
||||
# 42 -O3 -ffast-math
|
||||
# 42 -Ofast (note below)
|
||||
# 40 -O3 -ffast-math -march=native
|
||||
#
|
||||
#
|
||||
# Note that "-Ofast" is a newer option roughly equivalent to "-O3 -ffast-math".
|
||||
# I use the longer form because it is compatible with older compilers.
|
||||
#
|
||||
# Why don't I don't have "-march=native?"
|
||||
# Older compilers don't recognize "native" as one of the valid options.
|
||||
# One article said it was added with gcc 4.2 but I haven't verified that.
|
||||
#
|
||||
|
||||
# Add -ffastmath in only if compiler version recognizes it.
|
||||
|
||||
|
@ -173,22 +85,6 @@ ifneq ($(useffast),)
|
|||
CFLAGS += -ffast-math
|
||||
endif
|
||||
|
||||
#
|
||||
# You would expect "-march=native" to produce the fastest code.
|
||||
# Why don't I use it here?
|
||||
#
|
||||
# 1. In my benchmarks, above, it has a negligible impact if any at all.
|
||||
# 2. Some older versions of gcc don't recognize "native" as a valid choice.
|
||||
# 3. Results are less portable. Not a consideration if you are
|
||||
# building only for your own use but very important for anyone
|
||||
# redistributing a "binary" version.
|
||||
#
|
||||
# If you are planning to distribute the binary version to other
|
||||
# people (in some ham radio software collection, RPM, or DEB package),
|
||||
# avoid # fine tuning it for your particular computer. It could
|
||||
# cause compatibility issues for those with older computers.
|
||||
#
|
||||
|
||||
#CFLAGS += -D_FORTIFY_SOURCE
|
||||
|
||||
# Use PortAudio Library
|
||||
|
@ -237,7 +133,7 @@ direwolf : direwolf.o aprs_tt.o audio_portaudio.o audio_stats.o ax25_link.o ax25
|
|||
demod.o digipeater.o cdigipeater.o dlq.o dsp.o dtime_now.o dtmf.o dwgps.o \
|
||||
encode_aprs.o encode_aprs.o fcs_calc.o fcs_calc.o gen_tone.o \
|
||||
geotranz.a hdlc_rec.o hdlc_rec2.o hdlc_send.o igate.o kiss_frame.o \
|
||||
kiss.o kissnet.o latlong.o latlong.o log.o morse.o multi_modem.o \
|
||||
kiss.o kissserial.o kissnet.o latlong.o latlong.o log.o morse.o multi_modem.o \
|
||||
waypoint.o serial_port.o pfilter.o ptt.o rdq.o recv.o rrbb.o server.o \
|
||||
symbols.o telemetry.o textcolor.o tq.o tt_text.o tt_user.o xid.o xmit.o \
|
||||
dwgps.o dwgpsnmea.o mheard.o
|
||||
|
@ -295,30 +191,24 @@ direwolf.conf : generic.conf
|
|||
|
||||
|
||||
# Where should we install it?
|
||||
|
||||
# My understanding, of the convention, is that something you compile
|
||||
# from source, that is not a standard part of the operating system,
|
||||
# should go in /usr/local/bin.
|
||||
|
||||
# This is a step in the right direction but not sufficient to use /usr instead.
|
||||
# Macports typically installs in /opt/local so maybe you want to use that instead.
|
||||
|
||||
INSTALLDIR := /usr/local
|
||||
#INSTALLDIR := /opt/local
|
||||
|
||||
# TODO: Test this better.
|
||||
|
||||
# Optional installation into /usr/local/...
|
||||
# Optional installation into INSTALLDIR.
|
||||
# Needs to be run as root or with sudo.
|
||||
# TODO: Review file locations.
|
||||
|
||||
# Command to "install" to system directories. "install" for Linux. "ginstall" for Mac.
|
||||
|
||||
INSTALL=ginstall
|
||||
|
||||
.PHONY: install
|
||||
install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png direwolf.desktop
|
||||
install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png
|
||||
#
|
||||
# Applications, not installed with package manager, normally go in /usr/local/bin.
|
||||
# /usr/bin is used instead when installing from .DEB or .RPM package.
|
||||
# Applications.
|
||||
#
|
||||
$(INSTALL) direwolf $(INSTALLDIR)/bin
|
||||
$(INSTALL) decode_aprs $(INSTALLDIR)/bin
|
||||
|
@ -331,6 +221,7 @@ install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon
|
|||
$(INSTALL) gen_packets $(INSTALLDIR)/bin
|
||||
$(INSTALL) atest $(INSTALLDIR)/bin
|
||||
$(INSTALL) ttcalc $(INSTALLDIR)/bin
|
||||
$(INSTALL) kissutil $(INSTALLDIR)/bin
|
||||
$(INSTALL) dwespeak.sh $(INSTALLDIR)/bin
|
||||
#
|
||||
# Telemetry Toolkit executables. Other .conf and .txt files will go into doc directory.
|
||||
|
@ -346,11 +237,11 @@ install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon
|
|||
#
|
||||
# Misc. data such as "tocall" to system mapping.
|
||||
#
|
||||
$(INSTALL) -D --mode=644 tocalls.txt /usr/share/direwolf/tocalls.txt
|
||||
$(INSTALL) -D --mode=644 symbols-new.txt /usr/share/direwolf/symbols-new.txt
|
||||
$(INSTALL) -D --mode=644 symbolsX.txt /usr/share/direwolf/symbolsX.txt
|
||||
$(INSTALL) -D --mode=644 dw-icon.png /usr/share/direwolf/dw-icon.png
|
||||
$(INSTALL) -D --mode=644 direwolf.desktop /usr/share/applications/direwolf.desktop
|
||||
$(INSTALL) -D --mode=644 tocalls.txt $(INSTALLDIR)/share/direwolf/tocalls.txt
|
||||
$(INSTALL) -D --mode=644 symbols-new.txt $(INSTALLDIR)/share/direwolf/symbols-new.txt
|
||||
$(INSTALL) -D --mode=644 symbolsX.txt $(INSTALLDIR)/share/direwolf/symbolsX.txt
|
||||
$(INSTALL) -D --mode=644 dw-icon.png $(INSTALLDIR)/share/direwolf/dw-icon.png
|
||||
|
||||
#
|
||||
# Documentation. Various plain text files and PDF.
|
||||
#
|
||||
|
@ -359,15 +250,26 @@ install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon
|
|||
$(INSTALL) -D --mode=644 LICENSE-dire-wolf.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-dire-wolf.txt
|
||||
$(INSTALL) -D --mode=644 LICENSE-other.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-other.txt
|
||||
#
|
||||
$(INSTALL) -D --mode=644 doc/User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf
|
||||
# ./README.md is an overview for the project main page.
|
||||
# doc/README.md contains an overview of the PDF file contents and is more useful here.
|
||||
#
|
||||
$(INSTALL) -D --mode=644 doc/README.md $(INSTALLDIR)/share/doc/direwolf/README.md
|
||||
$(INSTALL) -D --mode=644 doc/2400-4800-PSK-for-APRS-Packet-Radio.pdf $(INSTALLDIR)/share/doc/direwolf/2400-4800-PSK-for-APRS-Packet-Radio.pdf
|
||||
$(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf
|
||||
$(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf
|
||||
$(INSTALL) -D --mode=644 doc/A-Closer-Look-at-the-WA8LMF-TNC-Test-CD.pdf $(INSTALLDIR)/share/doc/direwolf/A-Closer-Look-at-the-WA8LMF-TNC-Test-CD.pdf
|
||||
$(INSTALL) -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf
|
||||
$(INSTALL) -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf
|
||||
$(INSTALL) -D --mode=644 doc/APRStt-interface-for-SARTrack.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-interface-for-SARTrack.pdf
|
||||
$(INSTALL) -D --mode=644 doc/APRStt-Listening-Example.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Listening-Example.pdf
|
||||
$(INSTALL) -D --mode=644 doc/Bluetooth-KISS-TNC.pdf $(INSTALLDIR)/share/doc/direwolf/Bluetooth-KISS-TNC.pdf
|
||||
$(INSTALL) -D --mode=644 doc/Going-beyond-9600-baud.pdf $(INSTALLDIR)/share/doc/direwolf/Going-beyond-9600-baud.pdf
|
||||
$(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf
|
||||
$(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS-Tracker.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf
|
||||
$(INSTALL) -D --mode=644 doc/Raspberry-Pi-SDR-IGate.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-SDR-IGate.pdf
|
||||
$(INSTALL) -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf
|
||||
$(INSTALL) -D --mode=644 doc/APRStt-interface-for-SARTrack.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-interface-for-SARTrack.pdf
|
||||
$(INSTALL) -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf
|
||||
$(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf
|
||||
$(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf
|
||||
$(INSTALL) -D --mode=644 doc/Successful-APRS-IGate-Operation.pdf $(INSTALLDIR)/share/doc/direwolf/Successful-APRS-IGate-Operation.pdf
|
||||
$(INSTALL) -D --mode=644 doc/User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf
|
||||
$(INSTALL) -D --mode=644 doc/WA8LMF-TNC-Test-CD-Results.pdf $(INSTALLDIR)/share/doc/direwolf/WA8LMF-TNC-Test-CD-Results.pdf
|
||||
#
|
||||
# Sample config files also go into the doc directory.
|
||||
# When building from source, these can be put in home directory with "make install-conf".
|
||||
|
@ -419,7 +321,9 @@ install-conf : direwolf.conf
|
|||
|
||||
# Separate application to decode raw data.
|
||||
|
||||
decode_aprs : decode_aprs.c dwgpsnmea.o dwgps.o dwgpsd.o serial_port.o symbols.o ax25_pad.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o
|
||||
# First three use .c rather than .o because they depend on DECAMAIN definition.
|
||||
|
||||
decode_aprs : decode_aprs.c kiss_frame.c ax25_pad.c dwgpsnmea.o dwgps.o dwgpsd.o serial_port.o symbols.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o
|
||||
$(CC) $(CFLAGS) -DDECAMAIN -o $@ $^ -lm
|
||||
|
||||
# Convert between text and touch tone representation.
|
||||
|
@ -472,7 +376,7 @@ testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o
|
|||
# Unit test for demodulators
|
||||
|
||||
atest : atest.c demod.c dsp.c demod_afsk.c demod_psk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \
|
||||
fcs_calc.c ax25_pad.c decode_aprs.c dwgpsnmea.o dwgps.o serial_port.o telemetry.c dtest_now.o latlong.c symbols.c textcolor.c tt_text.c
|
||||
fcs_calc.c ax25_pad.c decode_aprs.c dwgpsnmea.o dwgps.o serial_port.o telemetry.c dtime_now.o latlong.c symbols.c textcolor.c tt_text.c
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lm
|
||||
#atest : atest.c fsk_fast_filter.h demod.c dsp.c demod_afsk.c demod_psk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \
|
||||
# fcs_calc.c ax25_pad.c decode_aprs.c dwgpsnmea.o dwgps.o serial_port.o telemetry.c latlong.c symbols.c textcolor.c tt_text.c
|
||||
|
@ -522,6 +426,14 @@ aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.c
|
|||
$(CC) $(CFLAGS) -g -o $@ $^
|
||||
|
||||
|
||||
# Talk to a KISS TNC.
|
||||
# Note: kiss_frame.c has conditional compilation on KISSUTIL.
|
||||
|
||||
kissutil : kissutil.c kiss_frame.c ax25_pad.o fcs_calc.o textcolor.o serial_port.o dtime_now.o sock.o
|
||||
$(CC) $(CFLAGS) -g -DKISSUTIL -o $@ $^
|
||||
|
||||
|
||||
|
||||
# Touch Tone to Speech sample application.
|
||||
|
||||
ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o
|
||||
|
@ -555,6 +467,7 @@ dist-mac: direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx ge
|
|||
$(INSTALLDIR)/bin/gen_packets \
|
||||
$(INSTALLDIR)/bin/atest \
|
||||
$(INSTALLDIR)/bin/ttcalc \
|
||||
$(INSTALLDIR)/bin/kissutil \
|
||||
$(INSTALLDIR)/bin/dwespeak.sh \
|
||||
$(INSTALLDIR)/share/direwolf/tocalls.txt \
|
||||
$(INSTALLDIR)/share/direwolf/config/direwolf.conf \
|
||||
|
@ -575,6 +488,7 @@ dist-mac: direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx ge
|
|||
$(INSTALLDIR)/man/man1/decode_aprs.1 \
|
||||
$(INSTALLDIR)/man/man1/direwolf.1 \
|
||||
$(INSTALLDIR)/man/man1/gen_packets.1 \
|
||||
$(INSTALLDIR)/man/man1/kissutil.1 \
|
||||
$(INSTALLDIR)/man/man1/ll2utm.1 \
|
||||
$(INSTALLDIR)/man/man1/log2gpx.1 \
|
||||
$(INSTALLDIR)/man/man1/text2tt.1 \
|
||||
|
|
61
Makefile.win
61
Makefile.win
|
@ -16,7 +16,7 @@
|
|||
#
|
||||
|
||||
|
||||
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets atest ttcalc tnctest
|
||||
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets atest ttcalc tnctest kissutil
|
||||
|
||||
|
||||
# People say we need -mthreads option for threads to work properly.
|
||||
|
@ -30,6 +30,8 @@ CFLAGS := -Ofast -march=pentium3 -msse -Iregex -Iutm -Igeotranz -mthreads -DUSE_
|
|||
AR := ar
|
||||
|
||||
CFLAGS += -g
|
||||
# TEMP EXPERIMENT - DO NOT RELEASE
|
||||
#CFLAGS += -fsanitize=undefined
|
||||
|
||||
# For version 1.4, we upgrade from gcc 4.6.2 to 4.9.3.
|
||||
|
||||
|
@ -98,11 +100,11 @@ demod_psk.o : fsk_demod_state.h
|
|||
direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_psk.o demod_9600.o hdlc_rec.o \
|
||||
hdlc_rec2.o multi_modem.o rdq.o rrbb.o dlq.o \
|
||||
fcs_calc.o ax25_pad.o ax25_pad2.o xid.o \
|
||||
decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
|
||||
decode_aprs.o symbols.o server.o kiss.o kissserial.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
|
||||
gen_tone.o morse.o audio_win.o audio_stats.o digipeater.o cdigipeater.o pfilter.o dedupe.o tq.o xmit.o \
|
||||
ptt.o beacon.o dwgps.o encode_aprs.o latlong.o textcolor.o \
|
||||
dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o waypoint.o serial_port.o log.o telemetry.o \
|
||||
dwgps.o dwgpsnmea.o dtime_now.o mheard.o ax25_link.o \
|
||||
dwgps.o dwgpsnmea.o dtime_now.o mheard.o ax25_link.o cm108.c \
|
||||
dw-icon.o regex.a misc.a geotranz.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
|
||||
|
||||
|
@ -159,7 +161,9 @@ tocalls-symbols :
|
|||
|
||||
# Separate application to decode raw data.
|
||||
|
||||
decode_aprs : decode_aprs.c dwgpsnmea.o dwgps.o serial_port.o symbols.o ax25_pad.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.c regex.a misc.a geotranz.a
|
||||
# First three use .c rather than .o because they depend on DECAMAIN definition.
|
||||
|
||||
decode_aprs : decode_aprs.c kiss_frame.c ax25_pad.c dwgpsnmea.o dwgps.o serial_port.o symbols.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o regex.a misc.a geotranz.a
|
||||
$(CC) $(CFLAGS) -DDECAMAIN -o decode_aprs $^
|
||||
|
||||
|
||||
|
@ -193,6 +197,12 @@ gen_packets : gen_packets.o ax25_pad.o hdlc_send.o fcs_calc.o gen_tone.o morse.
|
|||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
|
||||
# Connected mode packet application server.
|
||||
|
||||
appserver : appserver.o textcolor.o ax25_pad.o fcs_calc.o misc.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
|
||||
|
||||
|
||||
|
||||
# ------------------------------------------- Libraries --------------------------------------------
|
||||
|
||||
|
@ -267,7 +277,6 @@ strlcat.o : misc/strlcat.c
|
|||
|
||||
# Combine some unit tests into a single regression sanity check.
|
||||
|
||||
|
||||
check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest pad2test xidtest dtmftest check-modem1200 check-modem300 check-modem9600 check-modem19200 check-modem2400 check-modem4800
|
||||
|
||||
# Can we encode and decode at popular data rates?
|
||||
|
@ -548,6 +557,14 @@ aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.c misc.a regex.a
|
|||
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
|
||||
|
||||
|
||||
# Talk to a KISS TNC.
|
||||
|
||||
# Note: kiss_frame.c has conditional compilation on KISSUTIL.
|
||||
|
||||
kissutil : kissutil.c kiss_frame.c ax25_pad.o fcs_calc.o textcolor.o serial_port.o sock.o dtime_now.o misc.a regex.a
|
||||
$(CC) $(CFLAGS) -DKISSUTIL -o $@ $^ -lwinmm -lws2_32
|
||||
|
||||
|
||||
# Touch Tone to Speech sample application.
|
||||
|
||||
ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a regex.a
|
||||
|
@ -591,7 +608,7 @@ z := $(notdir ${CURDIR})
|
|||
|
||||
.PHONY: dist-win
|
||||
dist-win : direwolf.exe decode_aprs.exe text2tt.exe tt2text.exe ll2utm.exe utm2ll.exe \
|
||||
aclients.exe log2gpx.exe gen_packets.exe atest.exe ttcalc.exe \
|
||||
aclients.exe log2gpx.exe gen_packets.exe atest.exe ttcalc.exe kissutil.exe \
|
||||
generic.conf dwespeak.bat \
|
||||
README.md CHANGES.md \
|
||||
doc/User-Guide.pdf \
|
||||
|
@ -600,20 +617,25 @@ dist-win : direwolf.exe decode_aprs.exe text2tt.exe tt2text.exe ll2utm.exe utm2l
|
|||
rm -f ../$z-win.zip
|
||||
egrep '^C|^W' generic.conf | cut -c2-999 > direwolf.conf
|
||||
unix2dos direwolf.conf
|
||||
cp doc/README.md README-doc.md
|
||||
zip --junk-paths ../$z-win.zip \
|
||||
README.md \
|
||||
CHANGES.md \
|
||||
doc/User-Guide.pdf \
|
||||
doc/Raspberry-Pi-APRS.pdf \
|
||||
README-doc.md \
|
||||
doc/2400-4800-PSK-for-APRS-Packet-Radio.pdf \
|
||||
doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf \
|
||||
doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf \
|
||||
doc/A-Closer-Look-at-the-WA8LMF-TNC-Test-CD.pdf \
|
||||
doc/APRS-Telemetry-Toolkit.pdf \
|
||||
doc/APRStt-Implementation-Notes.pdf \
|
||||
doc/APRStt-interface-for-SARTrack.pdf \
|
||||
doc/APRStt-Listening-Example.pdf \
|
||||
doc/Bluetooth-KISS-TNC.pdf \
|
||||
doc/Going-beyond-9600-baud.pdf \
|
||||
doc/Raspberry-Pi-APRS.pdf \
|
||||
doc/Raspberry-Pi-APRS-Tracker.pdf \
|
||||
doc/Raspberry-Pi-SDR-IGate.pdf \
|
||||
doc/Successful-APRS-IGate-Operation.pdf \
|
||||
doc/User-Guide.pdf \
|
||||
doc/WA8LMF-TNC-Test-CD-Results.pdf \
|
||||
LICENSE* \
|
||||
|
@ -628,12 +650,34 @@ dist-win : direwolf.exe decode_aprs.exe text2tt.exe tt2text.exe ll2utm.exe utm2l
|
|||
gen_packets.exe \
|
||||
atest.exe \
|
||||
ttcalc.exe \
|
||||
kissutil.exe \
|
||||
dwespeak.bat \
|
||||
telemetry-toolkit/*
|
||||
rm README-doc.md
|
||||
|
||||
|
||||
# Reminders if pdf files are not up to date.
|
||||
|
||||
doc/User-Guide.pdf : doc/User-Guide.docx
|
||||
echo "***** User-Guide.pdf is out of date *****"
|
||||
|
||||
doc/Raspberry-Pi-APRS.pdf : doc/Raspberry-Pi-APRS.docx
|
||||
echo "***** Raspberry-Pi-APRS.pdf is out of date *****"
|
||||
|
||||
doc/Raspberry-Pi-APRS-Tracker.pdf : doc/Raspberry-Pi-APRS-Tracker.docx
|
||||
echo "***** Raspberry-Pi-APRS-Tracker.pdf is out of date *****"
|
||||
|
||||
doc/APRStt-Implementation-Notes.pdf : doc/APRStt-Implementation-Notes.docx
|
||||
echo "***** APRStt-Implementation-Notes.pdf is out of date *****"
|
||||
|
||||
doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf : doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.docx
|
||||
echo "***** A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf is out of date *****"
|
||||
|
||||
doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf : doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.docx
|
||||
echo "***** A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf is out of date *****"
|
||||
|
||||
doc/APRS-Telemetry-Toolkit.pdf : doc/APRS-Telemetry-Toolkit.docx
|
||||
echo "***** APRS-Telemetry-Toolkit.pdf is out of date *****"
|
||||
|
||||
|
||||
|
||||
|
@ -648,3 +692,4 @@ backup :
|
|||
#
|
||||
# DO NOT DELETE
|
||||
|
||||
|
||||
|
|
14
README.md
14
README.md
|
@ -1,4 +1,4 @@
|
|||
|
||||
|
||||
# Dire Wolf #
|
||||
|
||||
### Decoded Information from Radio Emissions for Windows Or Linux Fans ###
|
||||
|
@ -20,7 +20,7 @@ Without any additional software, it can perform as:
|
|||
- [APRStt](http://www.aprs.org/aprstt.html) gateway
|
||||
|
||||
|
||||
It can also be used as a virtual TNC for other applications such as [APRSIS32](http://aprsisce.wikidot.com/), [UI-View32](http://www.ui-view.net/), [Xastir](http://xastir.org/index.php/Main_Page), [APRS-TW](http://aprstw.blandranch.net/), [YAAC](http://www.ka2ddo.org/ka2ddo/YAAC.html), [UISS](http://users.belgacom.net/hamradio/uiss.htm), [Linux AX25](http://www.linux-ax25.org/wiki/Main_Page), [SARTrack](http://www.sartrack.co.nz/index.html), [RMS Express](http://www.winlink.org/RMSExpress), [BPQ32](http://www.cantab.net/users/john.wiseman/Documents/BPQ32.html), [Outpost PM](http://www.outpostpm.org/), and many others.
|
||||
It can also be used as a virtual TNC for other applications such as [APRSIS32](http://aprsisce.wikidot.com/), [UI-View32](http://www.ui-view.net/), [Xastir](http://xastir.org/index.php/Main_Page), [APRS-TW](http://aprstw.blandranch.net/), [YAAC](http://www.ka2ddo.org/ka2ddo/YAAC.html), [UISS](http://users.belgacom.net/hamradio/uiss.htm), [Linux AX25](http://www.linux-ax25.org/wiki/Main_Page), [SARTrack](http://www.sartrack.co.nz/index.html), [Winlink Express (formerly known as RMS Express, formerly known as Winlink 2000 or WL2K)](http://www.winlink.org/RMSExpress), [BPQ32](http://www.cantab.net/users/john.wiseman/Documents/BPQ32.html), [Outpost PM](http://www.outpostpm.org/), and many others.
|
||||
|
||||
|
||||
## Features & Benefits ##
|
||||
|
@ -36,6 +36,7 @@ It can also be used as a virtual TNC for other applications such as [APRSIS32](h
|
|||
Send periodic beacons to provide information to others. For tracking the location is provided by a GPS receiver.
|
||||
Build your own telemetry applications with the toolkit.
|
||||
|
||||
|
||||
- **APRStt Gateway.**
|
||||
|
||||
Very few hams have portable equipment for APRS but nearly everyone has a handheld radio that can send DTMF tones. APRStt allows a user, equipped with only DTMF (commonly known as Touch Tone) generation capability, to enter information into the global APRS data network. Responses can be sent by Morse Code or synthesized speech.
|
||||
|
@ -145,6 +146,15 @@ Go to the [releases page](https://github.com/wb2osz/direwolf/releases). Chose d
|
|||
|
||||
For more details see the **User Guide** in the [**doc** directory](https://github.com/wb2osz/direwolf/tree/master/doc). Special considerations for the Raspberry Pi are found in **Raspberry-Pi-APRS.pdf**
|
||||
|
||||
|
||||
### Macintosh OS X ###
|
||||
|
||||
Read the **User Guide** in the [**doc** directory](https://github.com/wb2osz/direwolf/tree/master/doc). It is a lot more complicated than Linux.
|
||||
|
||||
If you have problems, post them to the [Dire Wolf packet TNC](https://groups.yahoo.com/neo/groups/direwolf_packet/info) discussion group. I don't have a Mac and probably won't be able to help you. I rely on others, in the user community, for the Mac version.
|
||||
|
||||
|
||||
|
||||
## Join the conversation ##
|
||||
|
||||
Here are some good places to ask questions and share your experience:
|
||||
|
|
21
aclients.c
21
aclients.c
|
@ -549,12 +549,7 @@ static void * client_thread_net (void *arg)
|
|||
|
||||
mon_cmd.kind_lo = 'k';
|
||||
|
||||
#if __WIN32__
|
||||
send (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd), 0);
|
||||
#else
|
||||
err = write (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd));
|
||||
#endif
|
||||
|
||||
SOCK_SEND (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd));
|
||||
|
||||
/*
|
||||
* Print all of the monitored packets.
|
||||
|
@ -563,14 +558,10 @@ static void * client_thread_net (void *arg)
|
|||
while (1) {
|
||||
int n;
|
||||
|
||||
#if __WIN32__
|
||||
n = recv (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd), 0);
|
||||
#else
|
||||
n = read (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd));
|
||||
#endif
|
||||
n = SOCK_RECV (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd));
|
||||
|
||||
if (n != sizeof(mon_cmd)) {
|
||||
printf ("Read error, client %d received %d command bytes.\n", my_index, n);
|
||||
printf ("Read error, client %d received %d command bytes. Terminating.\n", my_index, n);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
|
@ -581,11 +572,7 @@ static void * client_thread_net (void *arg)
|
|||
assert (mon_cmd.data_len >= 0 && mon_cmd.data_len < (int)(sizeof(data)));
|
||||
|
||||
if (mon_cmd.data_len > 0) {
|
||||
#if __WIN32__
|
||||
n = recv (server_sock, data, mon_cmd.data_len, 0);
|
||||
#else
|
||||
n = read (server_sock, data, mon_cmd.data_len);
|
||||
#endif
|
||||
n = SOCK_RECV (server_sock, data, mon_cmd.data_len);
|
||||
|
||||
if (n != mon_cmd.data_len) {
|
||||
printf ("Read error, client %d received %d data bytes.\n", my_index, n);
|
||||
|
|
8
atest.c
8
atest.c
|
@ -312,7 +312,13 @@ int main (int argc, char *argv[])
|
|||
/* We have similar logic in direwolf.c, config.c, gen_packets.c, and atest.c, */
|
||||
/* that need to be kept in sync. Maybe it could be a common function someday. */
|
||||
|
||||
if (my_audio_config.achan[0].baud < 600) {
|
||||
if (my_audio_config.achan[0].baud == 100) {
|
||||
my_audio_config.achan[0].modem_type = MODEM_AFSK;
|
||||
my_audio_config.achan[0].mark_freq = 1615;
|
||||
my_audio_config.achan[0].space_freq = 1785;
|
||||
strlcpy (my_audio_config.achan[0].profiles, "D", sizeof(my_audio_config.achan[0].profiles));
|
||||
}
|
||||
else if (my_audio_config.achan[0].baud < 600) {
|
||||
my_audio_config.achan[0].modem_type = MODEM_AFSK;
|
||||
my_audio_config.achan[0].mark_freq = 1600;
|
||||
my_audio_config.achan[0].space_freq = 1800;
|
||||
|
|
17
audio.c
17
audio.c
|
@ -921,11 +921,22 @@ int audio_get (int a)
|
|||
/* Error */
|
||||
// TODO: Needs more study and testing.
|
||||
|
||||
// TODO: print n. should snd_strerror use n or errno?
|
||||
// Audio input device error: Unknown error
|
||||
// Only expected error conditions:
|
||||
// -EBADFD PCM is not in the right state (SND_PCM_STATE_PREPARED or SND_PCM_STATE_RUNNING)
|
||||
// -EPIPE an overrun occurred
|
||||
// -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery)
|
||||
|
||||
// Data overrun is displayed as "broken pipe" which seems a little misleading.
|
||||
// Add our own message which says something about CPU being too slow.
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Audio input device %d error: %s\n", a, snd_strerror(n));
|
||||
dw_printf ("Audio input device %d error code %d: %s\n", a, n, snd_strerror(n));
|
||||
|
||||
if (n == (-EPIPE)) {
|
||||
dw_printf ("This is most likely caused by the CPU being too slow to keep up with the audio stream.\n");
|
||||
dw_printf ("Use the \"top\" command, in another command window, to look at CPU usage.\n");
|
||||
dw_printf ("This might be a temporary condition so we will attempt to recover a few times before giving up.\n");
|
||||
}
|
||||
|
||||
audio_stats (a,
|
||||
save_audio_config_p->adev[a].num_channels,
|
||||
|
|
30
audio.h
30
audio.h
|
@ -30,7 +30,8 @@ enum ptt_method_e {
|
|||
PTT_METHOD_SERIAL, /* Serial port RTS or DTR. */
|
||||
PTT_METHOD_GPIO, /* General purpose I/O, Linux only. */
|
||||
PTT_METHOD_LPT, /* Parallel printer port, Linux only. */
|
||||
PTT_METHOD_HAMLIB }; /* HAMLib, Linux only. */
|
||||
PTT_METHOD_HAMLIB, /* HAMLib, Linux only. */
|
||||
PTT_METHOD_CM108 }; /* GPIO pin of CM108/CM119/etc. Linux only. */
|
||||
|
||||
typedef enum ptt_method_e ptt_method_t;
|
||||
|
||||
|
@ -96,6 +97,10 @@ struct audio_s {
|
|||
|
||||
int recv_error_rate; /* Similar but the % probablity of dropping a received frame. */
|
||||
|
||||
char timestamp_format[40]; /* -T option */
|
||||
/* Precede received & transmitted frames with timestamp. */
|
||||
/* Command line option uses "strftime" format string. */
|
||||
|
||||
|
||||
/* Properties for each audio channel, common to receive and transmit. */
|
||||
/* Can be different for each radio channel. */
|
||||
|
@ -191,10 +196,17 @@ struct audio_s {
|
|||
|
||||
struct {
|
||||
|
||||
ptt_method_t ptt_method; /* none, serial port, GPIO, LPT, HAMLIB. */
|
||||
ptt_method_t ptt_method; /* none, serial port, GPIO, LPT, HAMLIB, CM108. */
|
||||
|
||||
char ptt_device[20]; /* Serial device name for PTT. e.g. COM1 or /dev/ttyS0 */
|
||||
char ptt_device[100]; /* Serial device name for PTT. e.g. COM1 or /dev/ttyS0 */
|
||||
/* Also used for HAMLIB. Could be host:port when model is 1. */
|
||||
/* For years, 20 characters was plenty then we start getting extreme names like this: */
|
||||
/* /dev/serial/by-id/usb-FTDI_Navigator__CAT___2nd_PTT__00000000-if00-port0 */
|
||||
/* /dev/serial/by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller_D-if00-port0 */
|
||||
/* Issue 104, changed to 100 bytes in version 1.5. */
|
||||
|
||||
/* This same field is also used for CM108 GPIO PTT which will */
|
||||
/* have a name like /dev/hidraw1. */
|
||||
|
||||
ptt_line_t ptt_line; /* Control line when using serial port. PTT_LINE_RTS, PTT_LINE_DTR. */
|
||||
ptt_line_t ptt_line2; /* Optional second one: PTT_LINE_NONE when not used. */
|
||||
|
@ -202,8 +214,9 @@ struct audio_s {
|
|||
int out_gpio_num; /* GPIO number. Originally this was only for PTT. */
|
||||
/* It is now more general. */
|
||||
/* octrl array is indexed by PTT, DCD, or CONnected indicator. */
|
||||
/* For CM108, this should be in range of 1-8. */
|
||||
|
||||
#define MAX_GPIO_NAME_LEN 16 // 12 would cover any case I've seen so this should be safe
|
||||
#define MAX_GPIO_NAME_LEN 20 // 12 would cover any case I've seen so this should be safe
|
||||
|
||||
char out_gpio_name[MAX_GPIO_NAME_LEN];
|
||||
/* orginally, gpio number NN was assumed to simply */
|
||||
|
@ -212,6 +225,8 @@ struct audio_s {
|
|||
/* This is filled in by ptt_init so we don't have to */
|
||||
/* recalculate it each time we access it. */
|
||||
|
||||
/* This could probably be collapsed into ptt_device instead of being separate. */
|
||||
|
||||
int ptt_lpt_bit; /* Bit number for parallel printer port. */
|
||||
/* Bit 0 = pin 2, ..., bit 7 = pin 9. */
|
||||
|
||||
|
@ -270,7 +285,10 @@ struct audio_s {
|
|||
/* are done sending the data. This is to avoid */
|
||||
/* dropping PTT too soon and chopping off the end */
|
||||
/* of the frame. Again 10 mS units. */
|
||||
/* At this point, I'm thinking of 10 as the default. */
|
||||
/* At this point, I'm thinking of 10 (= 100 mS) as the default */
|
||||
/* because we're not quite sure when the soundcard audio stops. */
|
||||
|
||||
int fulldup; /* Full Duplex. */
|
||||
|
||||
} achan[MAX_CHANS];
|
||||
|
||||
|
@ -357,7 +375,7 @@ struct audio_s {
|
|||
#define DEFAULT_PERSIST 63
|
||||
#define DEFAULT_TXDELAY 30
|
||||
#define DEFAULT_TXTAIL 10
|
||||
|
||||
#define DEFAULT_FULLDUP 0
|
||||
|
||||
/*
|
||||
* Note that we have two versions of these in audio.c and audio_win.c.
|
||||
|
|
18
audio_win.c
18
audio_win.c
|
@ -313,10 +313,15 @@ int audio_open (struct audio_s *pa)
|
|||
|
||||
/* Does config file have a number? */
|
||||
/* If so, it is an index into list of devices. */
|
||||
/* Originally only a single digit was recognized. */
|
||||
/* v 1.5 also recognizes two digits. (Issue 116) */
|
||||
|
||||
if (strlen(pa->adev[a].adevice_in) == 1 && isdigit(pa->adev[a].adevice_in[0])) {
|
||||
in_dev_no[a] = atoi(pa->adev[a].adevice_in);
|
||||
}
|
||||
else if (strlen(pa->adev[a].adevice_in) == 2 && isdigit(pa->adev[a].adevice_in[0]) && isdigit(pa->adev[a].adevice_in[1])) {
|
||||
in_dev_no[a] = atoi(pa->adev[a].adevice_in);
|
||||
}
|
||||
|
||||
/* Otherwise, does it have search string? */
|
||||
|
||||
|
@ -344,6 +349,9 @@ int audio_open (struct audio_s *pa)
|
|||
if (strlen(pa->adev[a].adevice_out) == 1 && isdigit(pa->adev[a].adevice_out[0])) {
|
||||
out_dev_no[a] = atoi(pa->adev[a].adevice_out);
|
||||
}
|
||||
else if (strlen(pa->adev[a].adevice_out) == 2 && isdigit(pa->adev[a].adevice_out[0]) && isdigit(pa->adev[a].adevice_out[1])) {
|
||||
out_dev_no[a] = atoi(pa->adev[a].adevice_out);
|
||||
}
|
||||
|
||||
if ((UINT)(out_dev_no[a]) == WAVE_MAPPER && strlen(pa->adev[a].adevice_out) >= 1) {
|
||||
num_devices = waveOutGetNumDevs();
|
||||
|
@ -842,7 +850,7 @@ int audio_get (int a)
|
|||
|
||||
assert (A->udp_sock > 0);
|
||||
|
||||
res = recv (A->udp_sock, A->stream_data, SDR_UDP_BUF_MAXLEN, 0);
|
||||
res = SOCK_RECV (A->udp_sock, A->stream_data, SDR_UDP_BUF_MAXLEN);
|
||||
if (res <= 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Can't read from udp socket, errno %d", WSAGetLastError());
|
||||
|
@ -943,7 +951,15 @@ int audio_put (int a, int c)
|
|||
timeout--;
|
||||
if (timeout <= 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
|
||||
// TODO: open issues 78 & 165. How can we avoid/improve this?
|
||||
|
||||
dw_printf ("Audio output failure waiting for buffer.\n");
|
||||
dw_printf ("This can occur when we are producing audio output for\n");
|
||||
dw_printf ("transmit and the operating system doesn't provide buffer\n");
|
||||
dw_printf ("space after waiting and retrying many times.\n");
|
||||
//dw_printf ("In recent years, this has been reported only when running the\n");
|
||||
//dw_printf ("Windows version with VMWare on a Macintosh.\n");
|
||||
ptt_term ();
|
||||
return (-1);
|
||||
}
|
||||
|
|
773
ax25_link.c
773
ax25_link.c
File diff suppressed because it is too large
Load Diff
|
@ -52,6 +52,8 @@ void ax25_link_init (struct misc_config_s *pconfig);
|
|||
// These functions must be called on a single thread, one at a time.
|
||||
// The Data Link Queue (DLQ) is used to serialize events from multiple sources.
|
||||
|
||||
// Maybe the dispatch switch should be moved to ax25_link.c so they can all
|
||||
// be made static and they can't be called from the wrong place accidentally.
|
||||
|
||||
void dl_connect_request (dlq_item_t *E);
|
||||
|
||||
|
@ -68,6 +70,8 @@ void dl_client_cleanup (dlq_item_t *E);
|
|||
|
||||
void lm_data_indication (dlq_item_t *E);
|
||||
|
||||
void lm_seize_confirm (dlq_item_t *E);
|
||||
|
||||
void lm_channel_busy (dlq_item_t *E);
|
||||
|
||||
|
||||
|
|
119
ax25_pad.c
119
ax25_pad.c
|
@ -707,7 +707,7 @@ packet_t ax25_dup (packet_t copy_from)
|
|||
*
|
||||
* in_addr - Input such as "WB2OSZ-15*"
|
||||
*
|
||||
* strict - TRUE for strict checking (6 characters, no lower case,
|
||||
* strict - 1 (true) for strict checking (6 characters, no lower case,
|
||||
* SSID must be in range of 0 to 15).
|
||||
* Strict is appropriate for packets sent
|
||||
* over the radio. Communication with IGate
|
||||
|
@ -716,6 +716,8 @@ packet_t ax25_dup (packet_t copy_from)
|
|||
* We also get messages like this from a server.
|
||||
* KB1POR>APU25N,TCPIP*,qAC,T2NUENGLD:...
|
||||
*
|
||||
* 2 (extra true) will complain if * is found at end.
|
||||
*
|
||||
* Outputs: out_addr - Address without any SSID.
|
||||
* Must be at least AX25_MAX_ADDR_LEN bytes.
|
||||
*
|
||||
|
@ -724,6 +726,7 @@ packet_t ax25_dup (packet_t copy_from)
|
|||
* out_heard - True if "*" found.
|
||||
*
|
||||
* Returns: True (1) if OK, false (0) if any error.
|
||||
* When 0, out_addr, out_ssid, and out_heard are unpredictable.
|
||||
*
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
@ -744,35 +747,53 @@ int ax25_parse_addr (int position, char *in_addr, int strict, char *out_addr, in
|
|||
*out_ssid = 0;
|
||||
*out_heard = 0;
|
||||
|
||||
if (strict && strlen(in_addr) >= 2 && strncmp(in_addr, "qA", 2) == 0) {
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("%sAddress \"%s\" is a \"q-construct\" used for communicating\n", position_name[position], in_addr);
|
||||
dw_printf ("with APRS Internet Servers. It was not expected here.\n");
|
||||
}
|
||||
|
||||
//dw_printf ("ax25_parse_addr in: %s\n", in_addr);
|
||||
|
||||
if (position < -1) position = -1;
|
||||
if (position > AX25_REPEATER_8) position = AX25_REPEATER_8;
|
||||
position++; /* Adjust for position_name above. */
|
||||
|
||||
|
||||
if (strict && strlen(in_addr) >= 2 && strncmp(in_addr, "qA", 2) == 0) {
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("%sAddress \"%s\" is a \"q-construct\" used for communicating with\n", position_name[position], in_addr);
|
||||
dw_printf ("APRS Internet Servers. It should never appear when going over the radio.\n");
|
||||
}
|
||||
|
||||
//dw_printf ("ax25_parse_addr in: %s\n", in_addr);
|
||||
|
||||
|
||||
maxlen = strict ? 6 : (AX25_MAX_ADDR_LEN-1);
|
||||
p = in_addr;
|
||||
i = 0;
|
||||
for (p = in_addr; isalnum(*p); p++) {
|
||||
for (p = in_addr; *p != '\0' && *p != '-' && *p != '*'; p++) {
|
||||
if (i >= maxlen) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("%sAddress is too long. \"%s\" has more than %d characters.\n", position_name[position], in_addr, maxlen);
|
||||
return 0;
|
||||
}
|
||||
if ( ! isalnum(*p)) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("%sAddress, \"%s\" contains character other than letter or digit in character position %d.\n", position_name[position], in_addr, (int)(long)(p-in_addr)+1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
out_addr[i++] = *p;
|
||||
out_addr[i] = '\0';
|
||||
|
||||
#if DECAMAIN // Hack when running in decode_aprs utility.
|
||||
// Exempt the "qA..." case because it was already mentioned.
|
||||
|
||||
if (strict && islower(*p) && strncmp(in_addr, "qA", 2) != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("%sAddress has lower case letters. \"%s\" must be all upper case.\n", position_name[position], in_addr);
|
||||
}
|
||||
#else
|
||||
if (strict && islower(*p)) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("%sAddress has lower case letters. \"%s\" must be all upper case.\n", position_name[position], in_addr);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
j = 0;
|
||||
|
@ -804,6 +825,11 @@ int ax25_parse_addr (int position, char *in_addr, int strict, char *out_addr, in
|
|||
if (*p == '*') {
|
||||
*out_heard = 1;
|
||||
p++;
|
||||
if (strict == 2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\"*\" is not allowed at end of address \"%s\" here.\n", in_addr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (*p != '\0') {
|
||||
|
@ -1257,13 +1283,22 @@ void ax25_get_addr_with_ssid (packet_t this_p, int n, char *station)
|
|||
return;
|
||||
}
|
||||
|
||||
memset (station, 0, 7);
|
||||
for (i=0; i<6; i++) {
|
||||
unsigned char ch;
|
||||
// At one time this would stop at the first space, on the assumption we would have only trailing spaces.
|
||||
// Then there was a forum discussion where someone encountered the address " WIDE2" with a leading space.
|
||||
// In that case, we would have returned a zero length string here.
|
||||
// Now we return exactly what is in the address field and trim trailing spaces.
|
||||
// This will provide better information for troubleshooting.
|
||||
|
||||
ch = (this_p->frame_data[n*7+i] >> 1) & 0x7f;
|
||||
if (ch <= ' ') break;
|
||||
station[i] = ch;
|
||||
for (i=0; i<6; i++) {
|
||||
station[i] = (this_p->frame_data[n*7+i] >> 1) & 0x7f;
|
||||
}
|
||||
station[6] = '\0';
|
||||
|
||||
for (i=5; i>=0; i--) {
|
||||
if (station[i] == ' ')
|
||||
station[i] = '\0';
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
ssid = ax25_get_ssid (this_p, n);
|
||||
|
@ -1322,13 +1357,22 @@ void ax25_get_addr_no_ssid (packet_t this_p, int n, char *station)
|
|||
return;
|
||||
}
|
||||
|
||||
memset (station, 0, 7);
|
||||
for (i=0; i<6; i++) {
|
||||
unsigned char ch;
|
||||
// At one time this would stop at the first space, on the assumption we would have only trailing spaces.
|
||||
// Then there was a forum discussion where someone encountered the address " WIDE2" with a leading space.
|
||||
// In that case, we would have returned a zero length string here.
|
||||
// Now we return exactly what is in the address field and trim trailing spaces.
|
||||
// This will provide better information for troubleshooting.
|
||||
|
||||
ch = (this_p->frame_data[n*7+i] >> 1) & 0x7f;
|
||||
if (ch <= ' ') break;
|
||||
station[i] = ch;
|
||||
for (i=0; i<6; i++) {
|
||||
station[i] = (this_p->frame_data[n*7+i] >> 1) & 0x7f;
|
||||
}
|
||||
station[6] = '\0';
|
||||
|
||||
for (i=5; i>=0; i--) {
|
||||
if (station[i] == ' ')
|
||||
station[i] = '\0';
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
} /* end ax25_get_addr_no_ssid */
|
||||
|
@ -1956,7 +2000,7 @@ int ax25_pack (packet_t this_p, unsigned char result[AX25_MAX_PACKET_LEN])
|
|||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
assert (this_p->frame_len > 0 && this_p->frame_len <= AX25_MAX_PACKET_LEN);
|
||||
assert (this_p->frame_len >= 0 && this_p->frame_len <= AX25_MAX_PACKET_LEN);
|
||||
|
||||
memcpy (result, this_p->frame_data, this_p->frame_len);
|
||||
|
||||
|
@ -2459,6 +2503,33 @@ int ax25_get_pid (packet_t this_p)
|
|||
}
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: ax25_get_frame_len
|
||||
*
|
||||
* Purpose: Get length of frame.
|
||||
*
|
||||
* Inputs: this_p - pointer to packet object.
|
||||
*
|
||||
* Returns: Number of octets in the frame buffer.
|
||||
* Does NOT include the extra 2 for FCS.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
int ax25_get_frame_len (packet_t this_p)
|
||||
{
|
||||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
assert (this_p->frame_len >= 0 && this_p->frame_len <= AX25_MAX_PACKET_LEN);
|
||||
|
||||
return (this_p->frame_len);
|
||||
|
||||
} /* end ax25_get_frame_len */
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: ax25_dedupe_crc
|
||||
|
|
|
@ -427,6 +427,8 @@ extern int ax25_get_c2 (packet_t this_p);
|
|||
|
||||
extern int ax25_get_pid (packet_t this_p);
|
||||
|
||||
extern int ax25_get_frame_len (packet_t this_p);
|
||||
|
||||
extern unsigned short ax25_dedupe_crc (packet_t pp);
|
||||
|
||||
extern unsigned short ax25_m_m_crc (packet_t pp);
|
||||
|
|
60
ax25_pad2.c
60
ax25_pad2.c
|
@ -355,15 +355,19 @@ packet_t ax25_u_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_ad
|
|||
*
|
||||
* pf - Poll/Final flag.
|
||||
*
|
||||
* pinfo - Pointer to data for Info field. Allowed only for SREJ.
|
||||
*
|
||||
* info_len - Length for Info field.
|
||||
*
|
||||
*
|
||||
* Returns: Pointer to new packet object.
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
#if AX25MEMDEBUG
|
||||
packet_t ax25_s_frame_debug (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int modulo, int nr, int pf, char *src_file, int src_line)
|
||||
packet_t ax25_s_frame_debug (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int modulo, int nr, int pf, unsigned char *pinfo, int info_len, char *src_file, int src_line)
|
||||
#else
|
||||
packet_t ax25_s_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int modulo, int nr, int pf)
|
||||
packet_t ax25_s_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int modulo, int nr, int pf, unsigned char *pinfo, int info_len)
|
||||
#endif
|
||||
{
|
||||
packet_t this_p;
|
||||
|
@ -383,7 +387,7 @@ packet_t ax25_s_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_ad
|
|||
|
||||
if ( ! set_addrs (this_p, addrs, num_addr, cr)) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: Could not set addresses for U frame.\n", __func__);
|
||||
dw_printf ("Internal error in %s: Could not set addresses for S frame.\n", __func__);
|
||||
ax25_delete (this_p);
|
||||
return (NULL);
|
||||
}
|
||||
|
@ -401,6 +405,14 @@ packet_t ax25_s_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_ad
|
|||
nr &= (modulo - 1);
|
||||
}
|
||||
|
||||
// Erratum: The AX.25 spec is not clear about whether SREJ should be command, response, or both.
|
||||
// The underlying X.25 spec clearly says it is reponse only. Let's go with that.
|
||||
|
||||
if (ftype == frame_type_S_SREJ && cr != cr_res) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: SREJ must be response.\n", __func__);
|
||||
}
|
||||
|
||||
switch (ftype) {
|
||||
|
||||
case frame_type_S_RR: ctrl = 0x01; break;
|
||||
|
@ -434,6 +446,24 @@ packet_t ax25_s_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_ad
|
|||
this_p->frame_len++;
|
||||
}
|
||||
|
||||
if (ftype == frame_type_S_SREJ) {
|
||||
if (pinfo != NULL && info_len > 0) {
|
||||
if (info_len > AX25_MAX_INFO_LEN) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: SREJ frame, Invalid information field length %d.\n", __func__, info_len);
|
||||
info_len = AX25_MAX_INFO_LEN;
|
||||
}
|
||||
memcpy (p, pinfo, info_len);
|
||||
p += info_len;
|
||||
this_p->frame_len += info_len;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (pinfo != NULL || info_len != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: Info part not allowed for RR, RNR, REJ frame.\n", __func__);
|
||||
}
|
||||
}
|
||||
*p = '\0';
|
||||
|
||||
assert (p == this_p->frame_data + this_p->frame_len);
|
||||
|
@ -813,7 +843,7 @@ int main ()
|
|||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("\nConstruct S frame, cmd=%d, ftype=%d, pid=0x%02x\n", cr, ftype, pid);
|
||||
|
||||
pp = ax25_s_frame (addrs, num_addr, cr, ftype, modulo, nr, pf);
|
||||
pp = ax25_s_frame (addrs, num_addr, cr, ftype, modulo, nr, pf, NULL, 0);
|
||||
|
||||
ax25_hex_dump (pp);
|
||||
ax25_delete (pp);
|
||||
|
@ -827,7 +857,7 @@ int main ()
|
|||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("\nConstruct S frame, cmd=%d, ftype=%d, pid=0x%02x\n", cr, ftype, pid);
|
||||
|
||||
pp = ax25_s_frame (addrs, num_addr, cr, ftype, modulo, nr, pf);
|
||||
pp = ax25_s_frame (addrs, num_addr, cr, ftype, modulo, nr, pf, NULL, 0);
|
||||
|
||||
ax25_hex_dump (pp);
|
||||
ax25_delete (pp);
|
||||
|
@ -835,6 +865,26 @@ int main ()
|
|||
}
|
||||
}
|
||||
|
||||
/* SREJ is only S frame which can have information part. */
|
||||
|
||||
static char srej_info[] = { 1<<1, 2<<1, 3<<1, 4<<1 };
|
||||
|
||||
ftype = frame_type_S_SREJ;
|
||||
for (pf = 0; pf <= 1; pf++) {
|
||||
|
||||
modulo = 128;
|
||||
nr = 127;
|
||||
cr = cr_res;
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("\nConstruct Multi-SREJ S frame, cmd=%d, ftype=%d, pid=0x%02x\n", cr, ftype, pid);
|
||||
|
||||
pp = ax25_s_frame (addrs, num_addr, cr, ftype, modulo, nr, pf, srej_info, (int)(sizeof(srej_info)));
|
||||
|
||||
ax25_hex_dump (pp);
|
||||
ax25_delete (pp);
|
||||
}
|
||||
|
||||
dw_printf ("\n----------\n\n");
|
||||
|
||||
/* I frame */
|
||||
|
|
|
@ -22,14 +22,14 @@
|
|||
|
||||
packet_t ax25_u_frame_debug (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int pf, int pid, unsigned char *pinfo, int info_len, char *src_file, int src_line);
|
||||
|
||||
packet_t ax25_s_frame_debug (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int modulo, int nr, int pf, char *src_file, int src_line);
|
||||
packet_t ax25_s_frame_debug (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int modulo, int nr, int pf, unsigned char *pinfo, int info_len, char *src_file, int src_line);
|
||||
|
||||
packet_t ax25_i_frame_debug (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, int modulo, int nr, int ns, int pf, int pid, unsigned char *pinfo, int info_len, char *src_file, int src_line);
|
||||
|
||||
|
||||
#define ax25_u_frame(a,n,c,f,p,q,i,l) ax25_u_frame_debug(a,n,c,f,p,q,i,l,__FILE__,__LINE__)
|
||||
|
||||
#define ax25_s_frame(a,n,c,f,m,r,p) ax25_s_frame_debug(a,n,c,f,m,r,p,__FILE__,__LINE__)
|
||||
#define ax25_s_frame(a,n,c,f,m,r,p,i,l) ax25_s_frame_debug(a,n,c,f,m,r,p,i,l,__FILE__,__LINE__)
|
||||
|
||||
#define ax25_i_frame(a,n,c,m,r,s,p,q,i,l) ax25_i_frame_debug(a,n,c,m,r,s,p,q,i,l,__FILE__,__LINE__)
|
||||
|
||||
|
@ -38,7 +38,7 @@ packet_t ax25_i_frame_debug (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int
|
|||
|
||||
packet_t ax25_u_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int pf, int pid, unsigned char *pinfo, int info_len);
|
||||
|
||||
packet_t ax25_s_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int modulo, int nr, int pf);
|
||||
packet_t ax25_s_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int modulo, int nr, int pf, unsigned char *pinfo, int info_len);
|
||||
|
||||
packet_t ax25_i_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, int modulo, int nr, int ns, int pf, int pid, unsigned char *pinfo, int info_len);
|
||||
|
||||
|
|
174
beacon.c
174
beacon.c
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2011, 2013, 2014, 2015, 2016 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2013, 2014, 2015, 2016, 2017 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
|
||||
|
@ -141,6 +141,7 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo);
|
|||
void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct igate_config_s *pigate)
|
||||
{
|
||||
time_t now;
|
||||
struct tm tm;
|
||||
int j;
|
||||
int count;
|
||||
#if __WIN32__
|
||||
|
@ -270,21 +271,71 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct
|
|||
}
|
||||
|
||||
/*
|
||||
* Calculate first time for each beacon from the 'delay' value.
|
||||
* Calculate first time for each beacon from the 'slot' or 'delay' value.
|
||||
*/
|
||||
|
||||
now = time(NULL);
|
||||
localtime_r (&now, &tm);
|
||||
|
||||
for (j=0; j<g_misc_config_p->num_beacons; j++) {
|
||||
struct beacon_s *bp = & (g_misc_config_p->beacon[j]);
|
||||
#if DEBUG
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("beacon[%d] chan=%d, delay=%d, every=%d\n",
|
||||
dw_printf ("beacon[%d] chan=%d, delay=%d, slot=%d, every=%d\n",
|
||||
j,
|
||||
g_misc_config_p->beacon[j].sendto_chan,
|
||||
g_misc_config_p->beacon[j].delay,
|
||||
g_misc_config_p->beacon[j].every);
|
||||
bp->sendto_chan,
|
||||
bp->delay,
|
||||
bp->slot,
|
||||
bp->every);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If timeslots, there must be a full number of beacon intervals per hour.
|
||||
*/
|
||||
#define IS_GOOD(x) ((3600/(x))*(x) == 3600)
|
||||
|
||||
if (bp->slot != G_UNKNOWN) {
|
||||
|
||||
if ( ! IS_GOOD(bp->every)) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file, line %d: When using timeslots, there must be a whole number of beacon intervals per hour.\n", bp->lineno);
|
||||
|
||||
// Try to make it valid by adjusting up or down.
|
||||
|
||||
int n;
|
||||
for (n=1; ; n++) {
|
||||
int e;
|
||||
e = bp->every + n;
|
||||
if (e > 3600) {
|
||||
bp->every = 3600;
|
||||
break;
|
||||
}
|
||||
if (IS_GOOD(e)) {
|
||||
bp->every = e;
|
||||
break;
|
||||
}
|
||||
e = bp->every - n;
|
||||
if (e < 1) {
|
||||
bp->every = 1; // Impose a larger minimum?
|
||||
break;
|
||||
}
|
||||
if (IS_GOOD(e)) {
|
||||
bp->every = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file, line %d: Time between slotted beacons has been adjusted to %d seconds.\n", bp->lineno, bp->every);
|
||||
}
|
||||
/*
|
||||
* Determine when next slot time will arrive.
|
||||
*/
|
||||
bp->delay = bp->slot - (tm.tm_min * 60 + tm.tm_sec);
|
||||
while (bp->delay > bp->every) bp->delay -= bp->every;
|
||||
while (bp->delay < 5) bp->delay += bp->every;
|
||||
}
|
||||
|
||||
g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].delay;
|
||||
}
|
||||
|
||||
|
@ -491,10 +542,12 @@ static void * beacon_thread (void *arg)
|
|||
*/
|
||||
for (j=0; j<g_misc_config_p->num_beacons; j++) {
|
||||
|
||||
if (g_misc_config_p->beacon[j].btype == BEACON_IGNORE)
|
||||
struct beacon_s *bp = & (g_misc_config_p->beacon[j]);
|
||||
|
||||
if (bp->btype == BEACON_IGNORE)
|
||||
continue;
|
||||
|
||||
if (g_misc_config_p->beacon[j].next <= now) {
|
||||
if (bp->next <= now) {
|
||||
|
||||
/* Send the beacon. */
|
||||
|
||||
|
@ -503,13 +556,20 @@ static void * beacon_thread (void *arg)
|
|||
/* Calculate when the next one should be sent. */
|
||||
/* Easy for fixed interval. SmartBeaconing takes more effort. */
|
||||
|
||||
if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) {
|
||||
if (bp->btype == BEACON_TRACKER) {
|
||||
|
||||
if (gpsinfo.fix < DWFIX_2D) {
|
||||
/* Fix not available so beacon was not sent. */
|
||||
/* Try again in a couple seconds. */
|
||||
|
||||
g_misc_config_p->beacon[j].next = now + 2;
|
||||
if (g_misc_config_p->sb_configured) {
|
||||
/* Try again in a couple seconds. */
|
||||
bp->next = now + 2;
|
||||
}
|
||||
else {
|
||||
/* Stay with the schedule. */
|
||||
/* Important for slotted. Might reconsider otherwise. */
|
||||
bp->next += bp->every;
|
||||
}
|
||||
}
|
||||
else if (g_misc_config_p->sb_configured) {
|
||||
|
||||
|
@ -519,18 +579,21 @@ static void * beacon_thread (void *arg)
|
|||
sb_prev_time = now;
|
||||
sb_prev_course = gpsinfo.track;
|
||||
|
||||
g_misc_config_p->beacon[j].next = sb_calculate_next_time (now,
|
||||
bp->next = sb_calculate_next_time (now,
|
||||
DW_KNOTS_TO_MPH(gpsinfo.speed_knots), gpsinfo.track,
|
||||
sb_prev_time, sb_prev_course);
|
||||
}
|
||||
else {
|
||||
g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
|
||||
/* Tracker beacon, fixed spacing. */
|
||||
bp->next += bp->every;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* non-tracker beacons are at fixed spacing. */
|
||||
/* Non-tracker beacon, fixed spacing. */
|
||||
/* Increment by 'every' so slotted times come out right. */
|
||||
/* i.e. Don't take relative to now in case there was some delay. */
|
||||
|
||||
g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
|
||||
bp->next += bp->every;
|
||||
}
|
||||
|
||||
} /* if time to send it */
|
||||
|
@ -682,6 +745,7 @@ static time_t sb_calculate_next_time (time_t now,
|
|||
static void beacon_send (int j, dwgps_info_t *gpsinfo)
|
||||
{
|
||||
|
||||
struct beacon_s *bp = & (g_misc_config_p->beacon[j]);
|
||||
|
||||
int strict = 1; /* Strict packet checking because they will go over air. */
|
||||
char stemp[20];
|
||||
|
@ -704,13 +768,13 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
|
|||
*/
|
||||
strlcpy (mycall, "NOCALL", sizeof(mycall));
|
||||
|
||||
assert (g_misc_config_p->beacon[j].sendto_chan >= 0);
|
||||
assert (bp->sendto_chan >= 0);
|
||||
|
||||
strlcpy (mycall, g_modem_config_p->achan[g_misc_config_p->beacon[j].sendto_chan].mycall, sizeof(mycall));
|
||||
strlcpy (mycall, g_modem_config_p->achan[bp->sendto_chan].mycall, sizeof(mycall));
|
||||
|
||||
if (strlen(mycall) == 0 || strcmp(mycall, "NOCALL") == 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("MYCALL not set for beacon in config file line %d.\n", g_misc_config_p->beacon[j].lineno);
|
||||
dw_printf ("MYCALL not set for beacon in config file line %d.\n", bp->lineno);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -723,17 +787,17 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
|
|||
strlcpy (beacon_text, mycall, sizeof(beacon_text));
|
||||
strlcat (beacon_text, ">", sizeof(beacon_text));
|
||||
|
||||
if (g_misc_config_p->beacon[j].dest != NULL) {
|
||||
strlcat (beacon_text, g_misc_config_p->beacon[j].dest, sizeof(beacon_text));
|
||||
if (bp->dest != NULL) {
|
||||
strlcat (beacon_text, bp->dest, sizeof(beacon_text));
|
||||
}
|
||||
else {
|
||||
snprintf (stemp, sizeof(stemp), "%s%1d%1d", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION);
|
||||
strlcat (beacon_text, stemp, sizeof(beacon_text));
|
||||
}
|
||||
|
||||
if (g_misc_config_p->beacon[j].via != NULL) {
|
||||
if (bp->via != NULL) {
|
||||
strlcat (beacon_text, ",", sizeof(beacon_text));
|
||||
strlcat (beacon_text, g_misc_config_p->beacon[j].via, sizeof(beacon_text));
|
||||
strlcat (beacon_text, bp->via, sizeof(beacon_text));
|
||||
}
|
||||
strlcat (beacon_text, ":", sizeof(beacon_text));
|
||||
|
||||
|
@ -746,23 +810,23 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
|
|||
// TODO: test & document.
|
||||
|
||||
strlcpy (super_comment, "", sizeof(super_comment));
|
||||
if (g_misc_config_p->beacon[j].comment != NULL) {
|
||||
strlcpy (super_comment, g_misc_config_p->beacon[j].comment, sizeof(super_comment));
|
||||
if (bp->comment != NULL) {
|
||||
strlcpy (super_comment, bp->comment, sizeof(super_comment));
|
||||
}
|
||||
|
||||
if (g_misc_config_p->beacon[j].commentcmd != NULL) {
|
||||
if (bp->commentcmd != NULL) {
|
||||
char var_comment[AX25_MAX_INFO_LEN];
|
||||
int k;
|
||||
|
||||
/* Run given command to get variable part of comment. */
|
||||
|
||||
k = dw_run_cmd (g_misc_config_p->beacon[j].commentcmd, 2, var_comment, sizeof(var_comment));
|
||||
k = dw_run_cmd (bp->commentcmd, 2, var_comment, sizeof(var_comment));
|
||||
if (k > 0) {
|
||||
strlcat (super_comment, var_comment, sizeof(super_comment));
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("xBEACON, config file line %d, COMMENTCMD failure.\n", g_misc_config_p->beacon[j].lineno);
|
||||
dw_printf ("xBEACON, config file line %d, COMMENTCMD failure.\n", bp->lineno);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -770,17 +834,17 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
|
|||
/*
|
||||
* Add the info part depending on beacon type.
|
||||
*/
|
||||
switch (g_misc_config_p->beacon[j].btype) {
|
||||
switch (bp->btype) {
|
||||
|
||||
case BEACON_POSITION:
|
||||
|
||||
encode_position (g_misc_config_p->beacon[j].messaging, g_misc_config_p->beacon[j].compress,
|
||||
g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon, 0,
|
||||
(int)roundf(DW_METERS_TO_FEET(g_misc_config_p->beacon[j].alt_m)),
|
||||
g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol,
|
||||
g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
|
||||
encode_position (bp->messaging, bp->compress,
|
||||
bp->lat, bp->lon, bp->ambiguity,
|
||||
(int)roundf(DW_METERS_TO_FEET(bp->alt_m)),
|
||||
bp->symtab, bp->symbol,
|
||||
bp->power, bp->height, bp->gain, bp->dir,
|
||||
G_UNKNOWN, G_UNKNOWN, /* course, speed */
|
||||
g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
|
||||
bp->freq, bp->tone, bp->offset,
|
||||
super_comment,
|
||||
info, sizeof(info));
|
||||
strlcat (beacon_text, info, sizeof(beacon_text));
|
||||
|
@ -788,11 +852,11 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
|
|||
|
||||
case BEACON_OBJECT:
|
||||
|
||||
encode_object (g_misc_config_p->beacon[j].objname, g_misc_config_p->beacon[j].compress, 0, g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon, 0,
|
||||
g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol,
|
||||
g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
|
||||
encode_object (bp->objname, bp->compress, 0, bp->lat, bp->lon, bp->ambiguity,
|
||||
bp->symtab, bp->symbol,
|
||||
bp->power, bp->height, bp->gain, bp->dir,
|
||||
G_UNKNOWN, G_UNKNOWN, /* course, speed */
|
||||
g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset, super_comment,
|
||||
bp->freq, bp->tone, bp->offset, super_comment,
|
||||
info, sizeof(info));
|
||||
strlcat (beacon_text, info, sizeof(beacon_text));
|
||||
break;
|
||||
|
@ -809,7 +873,7 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
|
|||
/* transmission of altitude from GPS. */
|
||||
|
||||
my_alt_ft = G_UNKNOWN;
|
||||
if (gpsinfo->fix >= 3 && gpsinfo->altitude != G_UNKNOWN && g_misc_config_p->beacon[j].alt_m > 0) {
|
||||
if (gpsinfo->fix >= 3 && gpsinfo->altitude != G_UNKNOWN && bp->alt_m > 0) {
|
||||
my_alt_ft = (int)roundf(DW_METERS_TO_FEET(gpsinfo->altitude));
|
||||
}
|
||||
|
||||
|
@ -818,12 +882,12 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
|
|||
coarse = (int)roundf(gpsinfo->track);
|
||||
}
|
||||
|
||||
encode_position (g_misc_config_p->beacon[j].messaging, g_misc_config_p->beacon[j].compress,
|
||||
gpsinfo->dlat, gpsinfo->dlon, 0, my_alt_ft,
|
||||
g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol,
|
||||
g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
|
||||
encode_position (bp->messaging, bp->compress,
|
||||
gpsinfo->dlat, gpsinfo->dlon, bp->ambiguity, my_alt_ft,
|
||||
bp->symtab, bp->symbol,
|
||||
bp->power, bp->height, bp->gain, bp->dir,
|
||||
coarse, (int)roundf(gpsinfo->speed_knots),
|
||||
g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
|
||||
bp->freq, bp->tone, bp->offset,
|
||||
super_comment,
|
||||
info, sizeof(info));
|
||||
strlcat (beacon_text, info, sizeof(beacon_text));
|
||||
|
@ -845,8 +909,8 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
|
|||
A.g_dcs = G_UNKNOWN;
|
||||
|
||||
strlcpy (A.g_src, mycall, sizeof(A.g_src));
|
||||
A.g_symbol_table = g_misc_config_p->beacon[j].symtab;
|
||||
A.g_symbol_code = g_misc_config_p->beacon[j].symbol;
|
||||
A.g_symbol_table = bp->symtab;
|
||||
A.g_symbol_code = bp->symbol;
|
||||
A.g_lat = gpsinfo->dlat;
|
||||
A.g_lon = gpsinfo->dlon;
|
||||
A.g_speed_mph = DW_KNOTS_TO_MPH(gpsinfo->speed_knots);
|
||||
|
@ -865,25 +929,25 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
|
|||
|
||||
case BEACON_CUSTOM:
|
||||
|
||||
if (g_misc_config_p->beacon[j].custom_info != NULL) {
|
||||
if (bp->custom_info != NULL) {
|
||||
|
||||
/* Fixed handcrafted text. */
|
||||
|
||||
strlcat (beacon_text, g_misc_config_p->beacon[j].custom_info, sizeof(beacon_text));
|
||||
strlcat (beacon_text, bp->custom_info, sizeof(beacon_text));
|
||||
}
|
||||
else if (g_misc_config_p->beacon[j].custom_infocmd != NULL) {
|
||||
else if (bp->custom_infocmd != NULL) {
|
||||
char info_part[AX25_MAX_INFO_LEN];
|
||||
int k;
|
||||
|
||||
/* Run given command to obtain the info part for packet. */
|
||||
|
||||
k = dw_run_cmd (g_misc_config_p->beacon[j].custom_infocmd, 2, info_part, sizeof(info_part));
|
||||
k = dw_run_cmd (bp->custom_infocmd, 2, info_part, sizeof(info_part));
|
||||
if (k > 0) {
|
||||
strlcat (beacon_text, info_part, sizeof(beacon_text));
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("CBEACON, config file line %d, INFOCMD failure.\n", g_misc_config_p->beacon[j].lineno);
|
||||
dw_printf ("CBEACON, config file line %d, INFOCMD failure.\n", bp->lineno);
|
||||
strlcpy (beacon_text, "", sizeof(beacon_text)); // abort!
|
||||
}
|
||||
}
|
||||
|
@ -935,7 +999,7 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
|
|||
alevel_t alevel;
|
||||
|
||||
|
||||
switch (g_misc_config_p->beacon[j].sendto_type) {
|
||||
switch (bp->sendto_type) {
|
||||
|
||||
case SENDTO_IGATE:
|
||||
|
||||
|
@ -949,7 +1013,7 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
|
|||
case SENDTO_XMIT:
|
||||
default:
|
||||
|
||||
tq_append (g_misc_config_p->beacon[j].sendto_chan, TQ_PRIO_1_LO, pp);
|
||||
tq_append (bp->sendto_chan, TQ_PRIO_1_LO, pp);
|
||||
break;
|
||||
|
||||
case SENDTO_RECV:
|
||||
|
@ -957,13 +1021,13 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
|
|||
/* Simulated reception from radio. */
|
||||
|
||||
memset (&alevel, 0xff, sizeof(alevel));
|
||||
dlq_rec_frame (g_misc_config_p->beacon[j].sendto_chan, 0, 0, pp, alevel, 0, "");
|
||||
dlq_rec_frame (bp->sendto_chan, 0, 0, pp, alevel, 0, "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Failed to parse packet constructed from line %d.\n", g_misc_config_p->beacon[j].lineno);
|
||||
dw_printf ("Config file: Failed to parse packet constructed from line %d.\n", bp->lineno);
|
||||
dw_printf ("%s\n", beacon_text);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2016 John Langner, WB2OSZ
|
||||
// Copyright (C) 2016, 2017 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
|
||||
|
@ -59,7 +59,7 @@
|
|||
|
||||
|
||||
static packet_t cdigipeat_match (int from_chan, packet_t pp, char *mycall_rec, char *mycall_xmit,
|
||||
regex_t *alias, int to_chan, char *filter_str);
|
||||
int has_alias, regex_t *alias, int to_chan, char *cfilter_str);
|
||||
|
||||
|
||||
/*
|
||||
|
@ -150,8 +150,9 @@ void cdigipeater (int from_chan, packet_t pp)
|
|||
|
||||
result = cdigipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall,
|
||||
save_audio_config_p->achan[to_chan].mycall,
|
||||
&save_cdigi_config_p->alias[from_chan][to_chan], to_chan,
|
||||
save_cdigi_config_p->filter_str[from_chan][to_chan]);
|
||||
save_cdigi_config_p->has_alias[from_chan][to_chan],
|
||||
&(save_cdigi_config_p->alias[from_chan][to_chan]), to_chan,
|
||||
save_cdigi_config_p->cfilter_str[from_chan][to_chan]);
|
||||
if (result != NULL) {
|
||||
tq_append (to_chan, TQ_PRIO_0_HI, result);
|
||||
cdigi_count[from_chan][to_chan]++;
|
||||
|
@ -172,8 +173,9 @@ void cdigipeater (int from_chan, packet_t pp)
|
|||
|
||||
result = cdigipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall,
|
||||
save_audio_config_p->achan[to_chan].mycall,
|
||||
&save_cdigi_config_p->alias[from_chan][to_chan], to_chan,
|
||||
save_cdigi_config_p->filter_str[from_chan][to_chan]);
|
||||
save_cdigi_config_p->has_alias[from_chan][to_chan],
|
||||
&(save_cdigi_config_p->alias[from_chan][to_chan]), to_chan,
|
||||
save_cdigi_config_p->cfilter_str[from_chan][to_chan]);
|
||||
if (result != NULL) {
|
||||
tq_append (to_chan, TQ_PRIO_0_HI, result);
|
||||
cdigi_count[from_chan][to_chan]++;
|
||||
|
@ -203,12 +205,15 @@ void cdigipeater (int from_chan, packet_t pp)
|
|||
* packet is to be transmitted. Could be the same as
|
||||
* mycall_rec or different.
|
||||
*
|
||||
* alias - Compiled pattern for my station aliases.
|
||||
* Could be NULL if no aliases.
|
||||
* has_alias - True if we have an alias.
|
||||
*
|
||||
* alias - Optional compiled pattern for my station aliases.
|
||||
* Do NOT attempt to use this if 'has_alias' is false.
|
||||
*
|
||||
* to_chan - Channel number that we are transmitting to.
|
||||
*
|
||||
* filter_str - Filter expression string or NULL.
|
||||
* cfilter_str - Filter expression string for the from/to channel pair or NULL.
|
||||
* Note that only a subset of the APRS filters are applicable here.
|
||||
*
|
||||
* Returns: Packet object for transmission or NULL.
|
||||
* The original packet is not modified. The caller is responsible for freeing it.
|
||||
|
@ -227,20 +232,38 @@ void cdigipeater (int from_chan, packet_t pp)
|
|||
|
||||
|
||||
static packet_t cdigipeat_match (int from_chan, packet_t pp, char *mycall_rec, char *mycall_xmit,
|
||||
regex_t *alias, int to_chan, char *filter_str)
|
||||
int has_alias, regex_t *alias, int to_chan, char *cfilter_str)
|
||||
{
|
||||
int r;
|
||||
char repeater[AX25_MAX_ADDR_LEN];
|
||||
int err;
|
||||
char err_msg[100];
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("cdigipeat_match (from_chan=%d, pp=%p, mycall_rec=%s, mycall_xmit=%s, has_alias=%d, alias=%p, to_chan=%d, cfilter_str=%s\n",
|
||||
from_chan, pp, mycall_rec, mycall_xmit, has_alias, alias, to_chan, cfilter_str);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* First check if filtering has been configured.
|
||||
* Note that we have three different config file filter commands:
|
||||
*
|
||||
* FILTER - APRS digipeating and IGate client side.
|
||||
* Originally this was the only one.
|
||||
* Should we change it to AFILTER to make it clearer?
|
||||
* CFILTER - Similar for connected moded digipeater.
|
||||
* IGFILTER - APRS-IS (IGate) server side - completely diffeent.
|
||||
* Confusing with similar name but much different idea.
|
||||
* Maybe this should be renamed to SUBSCRIBE or something like that.
|
||||
*
|
||||
* Logically this should come later, after an address/alias match.
|
||||
* But here we only have to do it once.
|
||||
*/
|
||||
|
||||
if (filter_str != NULL) {
|
||||
if (cfilter_str != NULL) {
|
||||
|
||||
if (pfilter(from_chan, to_chan, filter_str, pp, 0) != 1) {
|
||||
if (pfilter(from_chan, to_chan, cfilter_str, pp, 0) != 1) {
|
||||
return(NULL);
|
||||
}
|
||||
}
|
||||
|
@ -286,7 +309,11 @@ static packet_t cdigipeat_match (int from_chan, packet_t pp, char *mycall_rec, c
|
|||
/*
|
||||
* If we have an alias match, substitute MYCALL.
|
||||
*/
|
||||
if (alias != NULL) {
|
||||
if (has_alias) {
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Checking %s for alias match.\n", repeater);
|
||||
#endif
|
||||
err = regexec(alias,repeater,0,NULL,0);
|
||||
if (err == 0) {
|
||||
packet_t result;
|
||||
|
@ -304,6 +331,12 @@ static packet_t cdigipeat_match (int from_chan, packet_t pp, char *mycall_rec, c
|
|||
dw_printf ("%s\n", err_msg);
|
||||
}
|
||||
}
|
||||
else {
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("No alias was specified.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't repeat it if we get here.
|
||||
|
|
|
@ -23,15 +23,18 @@ struct cdigi_config_s {
|
|||
/*
|
||||
* Rules for each of the [from_chan][to_chan] combinations.
|
||||
*/
|
||||
int enabled[MAX_CHANS][MAX_CHANS]; // Is it enabled for from/to pair?
|
||||
|
||||
int has_alias[MAX_CHANS][MAX_CHANS]; // If there was no alias in the config file,
|
||||
// the structure below will not be set up
|
||||
// properly and an attempt to use it could
|
||||
// result in a crash. (fixed v1.5)
|
||||
// Not needed for [APRS] DIGIPEAT because
|
||||
// the alias is mandatory there.
|
||||
regex_t alias[MAX_CHANS][MAX_CHANS];
|
||||
|
||||
int enabled[MAX_CHANS][MAX_CHANS];
|
||||
|
||||
char *filter_str[MAX_CHANS+1][MAX_CHANS+1];
|
||||
char *cfilter_str[MAX_CHANS][MAX_CHANS];
|
||||
// NULL or optional Packet Filter strings such as "t/m".
|
||||
// Notice the size of arrays is one larger than normal.
|
||||
// That extra position is for the IGate.
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -0,0 +1,712 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2017 John Langner, WB2OSZ
|
||||
//
|
||||
// Parts of this were adapted from "hamlib" which contains the notice:
|
||||
//
|
||||
// * Copyright (c) 2000-2012 by Stephane Fillod
|
||||
// * Copyright (c) 2011 by Andrew Errington
|
||||
// * CM108 detection code Copyright (c) Thomas Sailer used with permission
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Module: cm108.c
|
||||
*
|
||||
* Purpose: Use the CM108/CM119 (or compatible) GPIO pins for the Push To Talk (PTT) Control.
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* There is an incresing demand for using the GPIO pins of USB audio devices for PTT.
|
||||
* We have a couple commercial products:
|
||||
*
|
||||
* DMK URI http://www.dmkeng.com/URI_Order_Page.htm
|
||||
* RB-USB RIM http://www.repeater-builder.com/products/usb-rim-lite.html
|
||||
*
|
||||
* and homebrew projects which are all very similar.
|
||||
*
|
||||
* http://www.qsl.net/kb9mwr/projects/voip/usbfob-119.pdf
|
||||
* http://rtpdir.weebly.com/uploads/1/6/8/7/1687703/usbfob.pdf
|
||||
* http://www.repeater-builder.com/projects/fob/USB-Fob-Construction.pdf
|
||||
* https://irongarment.wordpress.com/2011/03/29/cm108-compatible-chips-with-gpio/
|
||||
*
|
||||
* Usually GPIO 3 is used because it is easier to tack solder a wire to a pin on the end.
|
||||
*
|
||||
* Soundmodem and hamlib paved the way but didn't get too far.
|
||||
* Dire Wolf 1.3 added HAMLIB support (Linux only) which theoretically allows this in a
|
||||
* roundabout way. This is documented in the User Guide, section called,
|
||||
* "Hamlib PTT Example 2: Use GPIO of USB audio adapter. (e.g. DMK URI)"
|
||||
*
|
||||
* It's rather involved and the explantion doesn't cover the case of multiple
|
||||
* USB-Audio adapters. It is not as straightforward as you might expect. Here we have
|
||||
* an example of 3 C-Media USB adapters, a SignaLink USB, a keyboard, and a mouse.
|
||||
*
|
||||
*
|
||||
* VID PID Product Sound ADEVICE HID [ptt]
|
||||
* --- --- ------- ----- ------- ---------
|
||||
* ** 0d8c 000c C-Media USB Headphone Set /dev/snd/pcmC1D0c plughw:1,0 /dev/hidraw0
|
||||
* ** 0d8c 000c C-Media USB Headphone Set /dev/snd/pcmC1D0p plughw:1,0 /dev/hidraw0
|
||||
* ** 0d8c 000c C-Media USB Headphone Set /dev/snd/controlC1 /dev/hidraw0
|
||||
* 08bb 2904 USB Audio CODEC /dev/snd/pcmC2D0c plughw:2,0 /dev/hidraw2
|
||||
* 08bb 2904 USB Audio CODEC /dev/snd/pcmC2D0p plughw:2,0 /dev/hidraw2
|
||||
* 08bb 2904 USB Audio CODEC /dev/snd/controlC2 /dev/hidraw2
|
||||
* ** 0d8c 000c C-Media USB Headphone Set /dev/snd/pcmC0D0c plughw:0,0 /dev/hidraw1
|
||||
* ** 0d8c 000c C-Media USB Headphone Set /dev/snd/pcmC0D0p plughw:0,0 /dev/hidraw1
|
||||
* ** 0d8c 000c C-Media USB Headphone Set /dev/snd/controlC0 /dev/hidraw1
|
||||
* ** 0d8c 0008 C-Media USB Audio Device /dev/snd/pcmC4D0c plughw:4,0 /dev/hidraw6
|
||||
* ** 0d8c 0008 C-Media USB Audio Device /dev/snd/pcmC4D0p plughw:4,0 /dev/hidraw6
|
||||
* ** 0d8c 0008 C-Media USB Audio Device /dev/snd/controlC4 /dev/hidraw6
|
||||
* 413c 2010 Dell USB Keyboard /dev/hidraw4
|
||||
* 0461 4d15 USB Optical Mouse /dev/hidraw5
|
||||
*
|
||||
*
|
||||
* The USB soundcards (/dev/snd/pcm...) have an associated Human Interface Device (HID)
|
||||
* corresponding to the GPIO pins which are sometimes connected to pushbuttons.
|
||||
* The mapping has no obvious pattern.
|
||||
*
|
||||
* Sound Card 0 HID 1
|
||||
* Sound Card 1 HID 0
|
||||
* Sound Card 2 HID 2
|
||||
* Sound Card 4 HID 6
|
||||
*
|
||||
* That would be a real challenge if you had to figure that all out and configure manually.
|
||||
* Dire Wolf version 1.5 makes this much more flexible and easier to use by supporting multiple
|
||||
* sound devices and automatically determining the corresponding HID for the PTT signal.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#ifndef USE_CM108
|
||||
|
||||
#ifdef CM108_MAIN
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "textcolor.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
text_color_init (0); // Turn off text color.
|
||||
dw_printf ("CM108 PTT support was disabled in Makefile.linux.\n");
|
||||
dw_printf ("It was excluded because /usr/include/libudev.h was missing.\n");
|
||||
dw_printf ("Install it with \"sudo apt-get install libudev-dev\" or\n");
|
||||
dw_printf ("\"sudo yum install libudev-devel\" then rebuild.\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#else // USE_CM108 is defined.
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <libudev.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <locale.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h> // ioctl, _IOR
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <linux/hidraw.h> // for HIDIOCGRAWINFO
|
||||
|
||||
#include "textcolor.h"
|
||||
#include "cm108.h"
|
||||
|
||||
static int cm108_write (char *name, int iomask, int iodata);
|
||||
|
||||
|
||||
// The CM108, CM109, and CM119 datasheets all say that idProduct can be in the range
|
||||
// of 0008 to 000f programmable by MSEL and MODE pin. How can we tell the difference?
|
||||
|
||||
// CM108B is 0012.
|
||||
// CM119B is 0013.
|
||||
// CM108AH is 0139 programmable by MSEL and MODE pin.
|
||||
// CM119A is 013A programmable by MSEL and MODE pin.
|
||||
|
||||
// To make matters even more confusing, these can be overridden
|
||||
// with an external EEPROM. Some have 8, rather than 4 GPIO.
|
||||
|
||||
#define CMEDIA_VID 0xd8c // Vendor ID
|
||||
#define CMEDIA_PID1_MIN 0x0008 // range for CM108, CM109, CM119 (no following letters)
|
||||
#define CMEDIA_PID1_MAX 0x000f
|
||||
|
||||
#define CMEDIA_PID_CM108AH 0x0139 // CM108AH
|
||||
#define CMEDIA_PID_CM108B 0x0012 // CM108B
|
||||
#define CMEDIA_PID_CM119A 0x013a // CM119A
|
||||
#define CMEDIA_PID_CM119B 0x0013 // CM119B
|
||||
#define CMEDIA_PID_HS100 0x013c // HS100
|
||||
|
||||
// The SSS chips seem to be pretty much compatible but they have only two GPIO.
|
||||
// https://irongarment.wordpress.com/2011/03/29/cm108-compatible-chips-with-gpio/
|
||||
// Data sheet says VID/PID is from an EEPROM but mentions no default.
|
||||
|
||||
#define SSS_VID 0x0c76 // SSS1621, SSS1623
|
||||
#define SSS_PID1 0x1605
|
||||
#define SSS_PID2 0x1607
|
||||
#define SSS_PID3 0x160b
|
||||
|
||||
|
||||
// Device VID PID Number of GPIO
|
||||
// ------ --- --- --------------
|
||||
// CM108 0d8c 0008-000f * 4
|
||||
// CM108AH 0d8c 0139 * 3 Has GPIO 1,3,4 but not 2
|
||||
// CM108B 0d8c 0012 3 Has GPIO 1,3,4 but not 2
|
||||
// CM109 0d8c 0008-000f * 8
|
||||
// CM119 0d8c 0008-000f * 8
|
||||
// CM119A 0d8c 013a * 8
|
||||
// CM119B 0d8c 0013 8
|
||||
// HS100 0d8c 013c 0
|
||||
//
|
||||
// SSS1621 0c76 1605 2 per ZL3AME, Can't find data sheet
|
||||
// SSS1623 0c76 1607,160b 2 per ZL3AME, Not in data sheet.
|
||||
//
|
||||
// * idProduct programmable by MSEL and MODE pin.
|
||||
//
|
||||
|
||||
// CMedia pin GPIO Notes
|
||||
// ---------- ---- -----
|
||||
// 43 1
|
||||
// 11 2 N.C. for CM108AH, CM108B
|
||||
// 13 3 Most popular for PTT because it is on the end.
|
||||
// 15 4
|
||||
// 16 5 CM109, CM119, CM119A, CM119B only
|
||||
// 17 6 "
|
||||
// 20 7 "
|
||||
// 22 8 "
|
||||
|
||||
// Test for supported devices.
|
||||
|
||||
#define GOOD_DEVICE(v,p) ( (v == CMEDIA_VID && ((p >= CMEDIA_PID1_MIN && p <= CMEDIA_PID1_MAX) \
|
||||
|| p == CMEDIA_PID_CM108AH \
|
||||
|| p == CMEDIA_PID_CM108B \
|
||||
|| p == CMEDIA_PID_CM119A \
|
||||
|| p == CMEDIA_PID_CM119B )) \
|
||||
|| \
|
||||
(v == SSS_VID && (p == SSS_PID1 || p == SSS_PID2 || p == SSS_PID3)) )
|
||||
|
||||
// Look out for null source pointer, and avoid buffer overflow on destination.
|
||||
|
||||
#define SAFE_STRCPY(to,from) { if (from != NULL) { strncpy(to,from,sizeof(to)); to[sizeof(to)-1] = '\0'; } }
|
||||
|
||||
|
||||
// Used to process regular expression matching results.
|
||||
|
||||
static void inline substr_se (char *dest, const char *src, int start, int endp1)
|
||||
{
|
||||
int len = endp1 - start;
|
||||
|
||||
if (start < 0 || endp1 < 0 || len <= 0) {
|
||||
dest[0] = '\0';
|
||||
return;
|
||||
}
|
||||
memcpy (dest, src + start, len);
|
||||
dest[len] = '\0';
|
||||
|
||||
} /* end substr_se */
|
||||
|
||||
|
||||
/*
|
||||
* Result of taking inventory of USB soundcards and USB HIDs.
|
||||
*/
|
||||
|
||||
struct thing_s {
|
||||
int vid; // vendor id
|
||||
int pid; // product id
|
||||
char product[32]; // product name (e.g. manufacturer, model)
|
||||
char devnode_sound[22]; // e.g. /dev/snd/pcmC0D0p
|
||||
char plughw[15]; // Above in more familiar format e.g. plughw:0,0
|
||||
char devnode_hidraw[17]; // e.g. /dev/hidraw3
|
||||
char devnode_usb[25]; // e.g. /dev/bus/usb/001/012
|
||||
};
|
||||
|
||||
int cm108_inventory (struct thing_s *things, int max_things);
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: main
|
||||
*
|
||||
* Purpose: Test program to list USB audio and HID devices.
|
||||
*
|
||||
* sudo apt-get install libudev-dev
|
||||
* gcc -DCM108_MAIN textcolor.c -l udev
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
#define MAXX_THINGS 60
|
||||
|
||||
#ifdef CM108_MAIN
|
||||
|
||||
int main (void)
|
||||
{
|
||||
struct thing_s things[MAXX_THINGS];
|
||||
int num_things;
|
||||
int i;
|
||||
|
||||
text_color_init (0); // Turn off text color.
|
||||
|
||||
num_things = cm108_inventory (things, MAXX_THINGS);
|
||||
|
||||
dw_printf (" VID PID %-*s %-*s %-*s %-*s"
|
||||
#if EXTRA
|
||||
" %-*s"
|
||||
#endif
|
||||
"\n", (int)sizeof(things[0].product), "Product",
|
||||
(int)sizeof(things[0].devnode_sound), "Sound",
|
||||
(int)sizeof(things[0].plughw), "ADEVICE",
|
||||
(int)sizeof(things[0].devnode_hidraw), "HID [ptt]"
|
||||
#if EXTRA
|
||||
, (int)sizeof(things[0].devnode_usb), "USB"
|
||||
#endif
|
||||
);
|
||||
|
||||
dw_printf (" --- --- %-*s %-*s %-*s %-*s"
|
||||
#if EXTRA
|
||||
" %-*s"
|
||||
#endif
|
||||
"\n", (int)sizeof(things[0].product), "-------",
|
||||
(int)sizeof(things[0].devnode_sound), "-----",
|
||||
(int)sizeof(things[0].plughw), "-------",
|
||||
(int)sizeof(things[0].devnode_hidraw), "---------"
|
||||
#if EXTRA
|
||||
, (int)sizeof(things[0].devnode_usb), "---"
|
||||
#endif
|
||||
);
|
||||
for (i = 0; i < num_things; i++) {
|
||||
|
||||
dw_printf ("%2s %04x %04x %-*s %-*s %-*s %-*s"
|
||||
#if EXTRA
|
||||
" %-*s"
|
||||
#endif
|
||||
"\n",
|
||||
GOOD_DEVICE(things[i].vid,things[i].pid) ? "**" : " ",
|
||||
things[i].vid, things[i].pid,
|
||||
(int)sizeof(things[i].product), things[i].product,
|
||||
(int)sizeof(things[i].devnode_sound), things[i].devnode_sound,
|
||||
(int)sizeof(things[0].plughw), things[i].plughw,
|
||||
(int)sizeof(things[i].devnode_hidraw), things[i].devnode_hidraw
|
||||
#if EXTRA
|
||||
, (int)sizeof(things[i].devnode_usb), things[i].devnode_usb
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
#endif // CM108_MAIN
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: cm108_inventory
|
||||
*
|
||||
* Purpose: Take inventory of USB audio and HID.
|
||||
*
|
||||
* Inputs: max_things - Maximum number of items to collect.
|
||||
*
|
||||
* Outputs: things - Array of items collected.
|
||||
* Corresponding sound device and HID are merged into one item.
|
||||
*
|
||||
* Returns: Number of items placed in things array.
|
||||
* Should be in the range of 0 thru max_things.
|
||||
* -1 for a bad unexpected error.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
|
||||
int cm108_inventory (struct thing_s *things, int max_things)
|
||||
{
|
||||
struct udev *udev;
|
||||
struct udev_enumerate *enumerate;
|
||||
struct udev_list_entry *devices, *dev_list_entry;
|
||||
struct udev_device *dev;
|
||||
struct udev_device *parentdev;
|
||||
|
||||
int num_things = 0;
|
||||
memset (things, 0, sizeof(struct thing_s) * max_things);
|
||||
|
||||
/*
|
||||
* First get a list of the USB audio devices.
|
||||
* This is based on the example in http://www.signal11.us/oss/udev/
|
||||
*/
|
||||
udev = udev_new();
|
||||
if (!udev) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("INTERNAL ERROR: Can't create udev.\n");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
enumerate = udev_enumerate_new(udev);
|
||||
udev_enumerate_add_match_subsystem(enumerate, "sound");
|
||||
udev_enumerate_scan_devices(enumerate);
|
||||
devices = udev_enumerate_get_list_entry(enumerate);
|
||||
udev_list_entry_foreach(dev_list_entry, devices) {
|
||||
const char *path;
|
||||
path = udev_list_entry_get_name(dev_list_entry);
|
||||
dev = udev_device_new_from_syspath(udev, path);
|
||||
char const *devnode = udev_device_get_devnode(dev);
|
||||
if (devnode != NULL) {
|
||||
parentdev = udev_device_get_parent_with_subsystem_devtype( dev, "usb", "usb_device");
|
||||
if (parentdev != NULL) {
|
||||
char const *p;
|
||||
int vid = 0;
|
||||
int pid = 0;
|
||||
|
||||
p = udev_device_get_sysattr_value(parentdev,"idVendor");
|
||||
if (p != NULL) vid = strtol(p, NULL, 16);
|
||||
p = udev_device_get_sysattr_value(parentdev,"idProduct");
|
||||
if (p != NULL) pid = strtol(p, NULL, 16);
|
||||
|
||||
if (num_things < max_things) {
|
||||
things[num_things].vid = vid;
|
||||
things[num_things].pid = pid;
|
||||
SAFE_STRCPY (things[num_things].product, udev_device_get_sysattr_value(parentdev,"product"));
|
||||
SAFE_STRCPY (things[num_things].devnode_sound, devnode);
|
||||
SAFE_STRCPY (things[num_things].devnode_usb, udev_device_get_devnode(parentdev));
|
||||
num_things++;
|
||||
}
|
||||
udev_device_unref(parentdev);
|
||||
}
|
||||
}
|
||||
}
|
||||
udev_enumerate_unref(enumerate);
|
||||
udev_unref(udev);
|
||||
|
||||
/*
|
||||
* Now merge in all of the USB HID.
|
||||
*/
|
||||
udev = udev_new();
|
||||
if (!udev) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("INTERNAL ERROR: Can't create udev.\n");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
enumerate = udev_enumerate_new(udev);
|
||||
udev_enumerate_add_match_subsystem(enumerate, "hidraw");
|
||||
udev_enumerate_scan_devices(enumerate);
|
||||
devices = udev_enumerate_get_list_entry(enumerate);
|
||||
udev_list_entry_foreach(dev_list_entry, devices) {
|
||||
const char *path;
|
||||
path = udev_list_entry_get_name(dev_list_entry);
|
||||
dev = udev_device_new_from_syspath(udev, path);
|
||||
char const *devnode = udev_device_get_devnode(dev);
|
||||
if (devnode != NULL) {
|
||||
parentdev = udev_device_get_parent_with_subsystem_devtype( dev, "usb", "usb_device");
|
||||
if (parentdev != NULL) {
|
||||
char const *p;
|
||||
int vid = 0;
|
||||
int pid = 0;
|
||||
|
||||
p = udev_device_get_sysattr_value(parentdev,"idVendor");
|
||||
if (p != NULL) vid = strtol(p, NULL, 16);
|
||||
p = udev_device_get_sysattr_value(parentdev,"idProduct");
|
||||
if (p != NULL) pid = strtol(p, NULL, 16);
|
||||
|
||||
int j, matched = 0;
|
||||
char const *usb = udev_device_get_devnode(parentdev);
|
||||
|
||||
// Add hidraw name to any matching existing.
|
||||
for (j = 0; j < num_things; j++) {
|
||||
if (things[j].vid == vid && things[j].pid == pid && usb != NULL && strcmp(things[j].devnode_usb,usb) == 0) {
|
||||
matched = 1;
|
||||
SAFE_STRCPY (things[j].devnode_hidraw, devnode);
|
||||
}
|
||||
}
|
||||
|
||||
// If it did not match to existing, add new entry.
|
||||
if (matched == 0 && num_things < max_things) {
|
||||
things[num_things].vid = vid;
|
||||
things[num_things].pid = pid;
|
||||
SAFE_STRCPY (things[num_things].product, udev_device_get_sysattr_value(parentdev,"product"));
|
||||
SAFE_STRCPY (things[num_things].devnode_hidraw, devnode);
|
||||
SAFE_STRCPY (things[num_things].devnode_usb, usb);
|
||||
num_things++;
|
||||
}
|
||||
udev_device_unref(parentdev);
|
||||
}
|
||||
}
|
||||
}
|
||||
udev_enumerate_unref(enumerate);
|
||||
udev_unref(udev);
|
||||
|
||||
/*
|
||||
* Seeing the form /dev/snd/pcmC4D0p will be confusing to many because we
|
||||
* would generally something like plughw:4,0 for in the direwolf configuration file.
|
||||
* Construct the more familiar form.
|
||||
*/
|
||||
int i;
|
||||
regex_t pcm_re;
|
||||
char emsg[100];
|
||||
int e = regcomp (&pcm_re, "pcmC([0-9]+)D([0-9]+)[cp]", REG_EXTENDED);
|
||||
if (e) {
|
||||
regerror (e, &pcm_re, emsg, sizeof(emsg));
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("INTERNAL ERROR: %s:%d: %s\n", __FILE__, __LINE__, emsg);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
for (i = 0; i < num_things; i++) {
|
||||
regmatch_t match[3];
|
||||
|
||||
if (regexec (&pcm_re, things[i].devnode_sound, 3, match, 0) == 0) {
|
||||
char c[32], d[32];
|
||||
substr_se (c, things[i].devnode_sound, match[1].rm_so, match[1].rm_eo);
|
||||
substr_se (d, things[i].devnode_sound, match[2].rm_so, match[2].rm_eo);
|
||||
snprintf (things[i].plughw, sizeof(things[i].plughw), "plughw:%s,%s", c, d);
|
||||
}
|
||||
}
|
||||
|
||||
return (num_things);
|
||||
|
||||
} /* end cm108_inventory */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: cm108_find_ptt
|
||||
*
|
||||
* Purpose: Try to find /dev/hidraw corresponding to plughw:n,n
|
||||
*
|
||||
* Inputs: output_audio_device
|
||||
* - Device name used in the ADEVICE configuration.
|
||||
* This would generally be something like plughw:1,0
|
||||
*
|
||||
* ptt_device_size - Size of result area to avoid buffer overflow.
|
||||
*
|
||||
* Outputs: ptt_device - Device name, something like /dev/hidraw2.
|
||||
* Will be emptry string if no match found.
|
||||
*
|
||||
* Returns: none
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
void cm108_find_ptt (char *output_audio_device, char *ptt_device, int ptt_device_size)
|
||||
{
|
||||
struct thing_s things[MAXX_THINGS];
|
||||
int num_things;
|
||||
int i;
|
||||
|
||||
strlcpy (ptt_device, "", ptt_device_size);
|
||||
num_things = cm108_inventory (things, MAXX_THINGS);
|
||||
|
||||
for (i = 0; i < num_things; i++) {
|
||||
|
||||
if (GOOD_DEVICE(things[i].vid,things[i].pid) ) {
|
||||
if (strcmp(output_audio_device, things[i].plughw) == 0) {
|
||||
strlcpy (ptt_device, things[i].devnode_hidraw, ptt_device_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* end cm108_find_ptt */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: cm108_set_gpio_pin
|
||||
*
|
||||
* Purpose: Set one GPIO pin of the CM108 or similar.
|
||||
*
|
||||
* Inputs: name - Name of device such as /dev/hidraw2.
|
||||
*
|
||||
* num - GPIO number, range 1 thru 8.
|
||||
*
|
||||
* state - 1 for on, 0 for off.
|
||||
*
|
||||
* Returns: 0 for success. -1 for error.
|
||||
*
|
||||
* Errors: A descriptive error message will be printed for any problem.
|
||||
*
|
||||
* Future: For our initial implementation we are making the simplifying
|
||||
* restriction of using only one GPIO pin per device and limit
|
||||
* configuratin to PTT only.
|
||||
* Longer term, we might want to have DCD, and maybe other
|
||||
* controls thru the same chip.
|
||||
* In this case, we would need to retain bit masks for each
|
||||
* device so new data can be merged with old before sending it out.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
int cm108_set_gpio_pin (char *name, int num, int state)
|
||||
{
|
||||
int iomask;
|
||||
int iodata;
|
||||
|
||||
if (num < 1 || num > 8) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("%s CM108 GPIO number %d must be in range of 1 thru 8.\n", name, num);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (state != 0 && state != 1) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("%s CM108 GPIO state %d must be 0 or 1.\n", name, state);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
iomask = 1 << (num - 1);
|
||||
iodata = state << (num - 1);
|
||||
|
||||
return (cm108_write (name, iomask, iodata));
|
||||
|
||||
} /* end cm108_set_gpio_pin */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: cm108_write
|
||||
*
|
||||
* Purpose: Set the GPIO pins of the CM108 or similar.
|
||||
*
|
||||
* Inputs: name - Name of device such as /dev/hidraw2.
|
||||
*
|
||||
* iomask - Bit mask for I/O direction.
|
||||
* LSB is GPIO1, bit 1 is GPIO2, etc.
|
||||
* 1 for output, 0 for input.
|
||||
*
|
||||
* iodata - Output data, same bit order as iomask.
|
||||
*
|
||||
* Returns: 0 for success. -1 for error.
|
||||
*
|
||||
* Errors: A descriptive error message will be printed for any problem.
|
||||
*
|
||||
* Description: This is the lowest level function.
|
||||
* An application probably wants to use cm108_set_gpio_pin.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
static int cm108_write (char *name, int iomask, int iodata)
|
||||
{
|
||||
int fd;
|
||||
struct hidraw_devinfo info;
|
||||
char io[5];
|
||||
int n;
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("TEMP DEBUG cm108_write: %s %d %d\n", name, iomask, iodata);
|
||||
|
||||
/*
|
||||
* By default, the USB HID are accessible only by root:
|
||||
*
|
||||
* crw------- 1 root root 249, 1 ... /dev/hidraw1
|
||||
*
|
||||
* How should we handle this?
|
||||
* Manually changing it will revert back on the next reboot or
|
||||
* when the device is removed and reinserted.
|
||||
*
|
||||
* According to various articles on the Internet, we should be able to
|
||||
* add a file to /etc/udev/rules.d. "99-direwolf-cmedia.rules" would be a
|
||||
* suitable name. The leading number is the order. We want this to be
|
||||
* near the end. I think the file extension must be ".rules."
|
||||
*
|
||||
* We could completely open it up to everyone like this:
|
||||
*
|
||||
* # Allow ordinary user to access CMedia GPIO for PTT.
|
||||
* SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0d8c", MODE="0666"
|
||||
*
|
||||
* Whenever we have CMedia USB audio adapter, it should be accessible by everyone.
|
||||
* This would not apply to other /dev/hidraw* corresponding to keyboard, mouse, etc.
|
||||
*
|
||||
* Notice the == (double =) for testing and := for setting a property.
|
||||
*
|
||||
* If you are concerned about security, you could restrict access to
|
||||
* a particular group, something like this:
|
||||
*
|
||||
* SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0d8c", GROUP="audio", MODE="0660"
|
||||
*
|
||||
* I figure "audio" makes more sense than "gpio" because we need to be part of
|
||||
* audio group to use the USB Audio adapter for sound.
|
||||
*/
|
||||
|
||||
fd = open (name, O_WRONLY);
|
||||
if (fd == -1) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Could not open %s for write, errno=%d\n", name, errno);
|
||||
if (errno == EACCES) { // 13
|
||||
dw_printf ("Type \"ls -l %s\" and verify that it has audio group rw similar to this:\n", name);
|
||||
dw_printf (" crw-rw---- 1 root audio 247, 0 Oct 6 19:24 %s\n", name);
|
||||
dw_printf ("rather than root-only access like this:\n");
|
||||
dw_printf (" crw------- 1 root root 247, 0 Sep 24 09:40 %s\n", name);
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
||||
// Just for fun, let's get the device information.
|
||||
|
||||
#if 1
|
||||
n = ioctl(fd, HIDIOCGRAWINFO, &info);
|
||||
if (n == 0) {
|
||||
if ( ! GOOD_DEVICE(info.vendor, info.product)) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ioctl HIDIOCGRAWINFO failed for %s. errno = %d.\n", name, errno);
|
||||
}
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("%s is not a supported device type. Proceed at your own risk. vid=%04x pid=%04x\n", name, info.vendor, info.product);
|
||||
}
|
||||
#endif
|
||||
// To make a long story short, I think we need 0 for the first two bytes.
|
||||
|
||||
io[0] = 0;
|
||||
io[1] = 0;
|
||||
io[2] = iomask;
|
||||
io[3] = iodata;
|
||||
io[4] = 0;
|
||||
|
||||
// Writing 4 bytes fails with errno 32, EPIPE, "broken pipe."
|
||||
// Hamlib writes 5 bytes which I don't understand.
|
||||
// Writing 5 bytes works.
|
||||
// I have no idea why. From the CMedia datasheet it looks like we need 4.
|
||||
|
||||
n = write (fd, io, sizeof(io));
|
||||
if (n != sizeof(io)) {
|
||||
// Errors observed during development.
|
||||
// as pi EACCES 13 /* Permission denied */
|
||||
// as root EPIPE 32 /* Broken pipe - Happens if we send 4 bytes */
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Write to %s failed, n=%d, errno=%d\n", name, n, errno);
|
||||
|
||||
if (errno == EACCES) {
|
||||
dw_printf ("Type \"ls -l %s\" and verify that it has audio group rw similar to this:\n", name);
|
||||
dw_printf (" crw-rw---- 1 root audio 247, 0 Oct 6 19:24 %s\n", name);
|
||||
dw_printf ("rather than root-only access like this:\n");
|
||||
dw_printf (" crw------- 1 root root 247, 0 Sep 24 09:40 %s\n", name);
|
||||
}
|
||||
|
||||
close (fd);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
close (fd);
|
||||
return (0);
|
||||
|
||||
} /* end cm108_write */
|
||||
|
||||
#endif // ifdef USE_CM108
|
||||
|
||||
/* end cm108.c */
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
/* Dire Wolf cm108.h */
|
||||
|
||||
extern void cm108_find_ptt (char *output_audio_device, char *ptt_device, int ptt_device_size);
|
||||
|
||||
extern int cm108_set_gpio_pin (char *name, int num, int state);
|
422
config.c
422
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 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 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
|
||||
|
@ -63,6 +63,10 @@
|
|||
#include "tt_text.h"
|
||||
#include "ax25_link.h"
|
||||
|
||||
#ifdef USE_CM108 // Linux only
|
||||
#include "cm108.h"
|
||||
#endif
|
||||
|
||||
// geotranz
|
||||
|
||||
#include "utm.h"
|
||||
|
@ -515,7 +519,7 @@ static int check_via_path (char *via_path)
|
|||
|
||||
r = stemp;
|
||||
while (( a = strsep(&r,",")) != NULL) {
|
||||
int strict = 1;
|
||||
int strict = 2;
|
||||
int ok;
|
||||
char addr[AX25_MAX_ADDR_LEN];
|
||||
int ssid;
|
||||
|
@ -590,7 +594,10 @@ static char *split (char *string, int rest_of_line)
|
|||
{
|
||||
static char cmd[MAXCMDLEN];
|
||||
static char token[MAXCMDLEN];
|
||||
static char *c; // current position in cmd.
|
||||
static char shutup[] = " "; // Shut up static analysis which gets upset
|
||||
// over the case where this could be called with
|
||||
// string NULL and c was not yet initialized.
|
||||
static char *c = shutup; // Current position in command line.
|
||||
char *s, *t;
|
||||
int in_quotes;
|
||||
|
||||
|
@ -790,6 +797,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
p_audio_config->achan[channel].persist = DEFAULT_PERSIST;
|
||||
p_audio_config->achan[channel].txdelay = DEFAULT_TXDELAY;
|
||||
p_audio_config->achan[channel].txtail = DEFAULT_TXTAIL;
|
||||
p_audio_config->achan[channel].fulldup = DEFAULT_FULLDUP;
|
||||
}
|
||||
|
||||
/* First channel should always be valid. */
|
||||
|
@ -798,9 +806,9 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
p_audio_config->achan[0].valid = 1;
|
||||
|
||||
|
||||
memset (p_digi_config, 0, sizeof(struct digi_config_s));
|
||||
memset (p_digi_config, 0, sizeof(struct digi_config_s)); // APRS digipeater
|
||||
p_digi_config->dedupe_time = DEFAULT_DEDUPE;
|
||||
memset (p_cdigi_config, 0, sizeof(struct cdigi_config_s));
|
||||
memset (p_cdigi_config, 0, sizeof(struct cdigi_config_s)); // Connected mode digipeater
|
||||
|
||||
memset (p_tt_config, 0, sizeof(struct tt_config_s));
|
||||
p_tt_config->gateway_enabled = 0;
|
||||
|
@ -864,19 +872,22 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
p_igate_config->tx_limit_1 = IGATE_TX_LIMIT_1_DEFAULT;
|
||||
p_igate_config->tx_limit_5 = IGATE_TX_LIMIT_5_DEFAULT;
|
||||
p_igate_config->igmsp = 1;
|
||||
p_igate_config->rx2ig_dedupe_time = IGATE_RX2IG_DEDUPE_TIME;
|
||||
|
||||
|
||||
/* People find this confusing. */
|
||||
/* Ideally we'd like to figure out if com0com is installed */
|
||||
/* and automatically enable this. */
|
||||
|
||||
//strlcpy (p_misc_config->nullmodem, DEFAULT_NULLMODEM, sizeof(p_misc_config->nullmodem));
|
||||
strlcpy (p_misc_config->nullmodem, "", sizeof(p_misc_config->nullmodem));
|
||||
strlcpy (p_misc_config->kiss_serial_port, "", sizeof(p_misc_config->kiss_serial_port));
|
||||
p_misc_config->kiss_serial_speed = 0;
|
||||
p_misc_config->kiss_serial_poll = 0;
|
||||
|
||||
strlcpy (p_misc_config->gpsnmea_port, "", sizeof(p_misc_config->gpsnmea_port));
|
||||
strlcpy (p_misc_config->waypoint_port, "", sizeof(p_misc_config->waypoint_port));
|
||||
|
||||
strlcpy (p_misc_config->logdir, "", sizeof(p_misc_config->logdir));
|
||||
p_misc_config->log_daily_names = 0;
|
||||
strlcpy (p_misc_config->log_path, "", sizeof(p_misc_config->log_path));
|
||||
|
||||
/* connected mode. */
|
||||
|
||||
|
@ -891,6 +902,10 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
p_misc_config->maxframe_extended = AX25_K_MAXFRAME_EXTENDED_DEFAULT; /* Max frames to send before ACK. mod 128 "Window" size. */
|
||||
|
||||
p_misc_config->maxv22 = AX25_N2_RETRY_DEFAULT / 2; /* Max SABME before falling back to SABM. */
|
||||
p_misc_config->v20_addrs = NULL; /* Go directly to v2.0 for stations listed. */
|
||||
p_misc_config->v20_count = 0;
|
||||
p_misc_config->noxid_addrs = NULL; /* Don't send XID to these stations. */
|
||||
p_misc_config->noxid_count = 0;
|
||||
|
||||
/*
|
||||
* Try to extract options from a file.
|
||||
|
@ -971,20 +986,23 @@ 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Note that ALSA name can contain comma such as hw:1,0 */
|
||||
|
||||
if (strncasecmp(t, "ADEVICE", 7) == 0) {
|
||||
/* "ADEVICE" is equivalent to "ADEVICE0". */
|
||||
adevice = 0;
|
||||
if (isdigit(t[7])) {
|
||||
adevice = t[7] - '0';
|
||||
if (strlen(t) >= 8) {
|
||||
adevice = atoi(t+7);
|
||||
}
|
||||
|
||||
if (adevice < 0 || adevice >= MAX_ADEVS) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Device number %d out of range for ADEVICE command on line %d.\n", adevice, line);
|
||||
dw_printf ("If you really need more than %d audio devices, increase MAX_ADEVS and recompile.\n", MAX_ADEVS);
|
||||
adevice = 0;
|
||||
continue;
|
||||
}
|
||||
|
@ -1192,6 +1210,24 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
}
|
||||
else {
|
||||
|
||||
char *p;
|
||||
int const strict = 2;
|
||||
char call_no_ssid[AX25_MAX_ADDR_LEN];
|
||||
int ssid, heard;
|
||||
|
||||
for (p = t; *p != '\0'; p++) {
|
||||
if (islower(*p)) {
|
||||
*p = toupper(*p); /* Silently force upper case. */
|
||||
/* Might change to warning someday. */
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! ax25_parse_addr (-1, t, strict, call_no_ssid, &ssid, &heard)) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Invalid value for MYCALL command on line %d.\n", line);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Definitely set for current channel.
|
||||
// Set for other channels which have not been set yet.
|
||||
|
||||
|
@ -1204,17 +1240,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
strcasecmp(p_audio_config->achan[c].mycall, "NOCALL") == 0 ||
|
||||
strcasecmp(p_audio_config->achan[c].mycall, "N0CALL") == 0) {
|
||||
|
||||
char *p;
|
||||
|
||||
strlcpy (p_audio_config->achan[c].mycall, t, sizeof(p_audio_config->achan[c].mycall));
|
||||
|
||||
for (p = p_audio_config->achan[c].mycall; *p != '\0'; p++) {
|
||||
if (islower(*p)) {
|
||||
*p = toupper(*p); /* silently force upper case. */
|
||||
}
|
||||
}
|
||||
// TODO: additional checks if valid.
|
||||
// Should have a function to check for valid callsign[-ssid]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1601,6 +1627,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
* xxx LPT [-]bit-num
|
||||
* PTT RIG model port
|
||||
* PTT RIG AUTO port
|
||||
* PTT CM108 [ [-]bit-num ] [ hid-device ]
|
||||
*
|
||||
* When model is 2, port would host:port like 127.0.0.1:4532
|
||||
* Otherwise, port would be a serial port like /dev/ttyS0
|
||||
|
@ -1629,7 +1656,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file line %d: Missing serial port name for %s command.\n",
|
||||
dw_printf ("Config file line %d: Missing output control device for %s command.\n",
|
||||
line, otname);
|
||||
continue;
|
||||
}
|
||||
|
@ -1725,9 +1752,97 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
|
||||
p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_HAMLIB;
|
||||
|
||||
#else
|
||||
#if __WIN32__
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file line %d: Windows version of direwolf does not support HAMLIB.\n", line);
|
||||
exit (EXIT_FAILURE);
|
||||
#else
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file line %d: %s with RIG is only available when hamlib support is enabled.\n", line, otname);
|
||||
dw_printf ("You must rebuild direwolf with hamlib support.\n");
|
||||
dw_printf ("See User Guide for details.\n");
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
else if (strcasecmp(t, "CM108") == 0) {
|
||||
|
||||
/* CM108 - GPIO of USB sound card. case, Linux only. */
|
||||
|
||||
#ifdef USE_CM108
|
||||
|
||||
if (ot != OCTYPE_PTT) {
|
||||
// Future project: Allow DCD and CON via the same device.
|
||||
// This gets more complicated because we can't selectively change a single GPIO bit.
|
||||
// We would need to keep track of what is currently there, change one bit, in our local
|
||||
// copy of the status and then write out the byte for all of the pins.
|
||||
// Let's keep it simple with just PTT for the first stab at this.
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file line %d: PTT CM108 option is only valid for PTT, not %s.\n", line, otname);
|
||||
continue;
|
||||
}
|
||||
|
||||
p_audio_config->achan[channel].octrl[ot].out_gpio_num = 3; // All known designs use GPIO 3.
|
||||
// User can override for special cases.
|
||||
p_audio_config->achan[channel].octrl[ot].ptt_invert = 0; // High for transmit.
|
||||
strcpy (p_audio_config->achan[channel].octrl[ot].ptt_device, "");
|
||||
|
||||
// Try to find PTT device for audio output device.
|
||||
// Simplifiying assumption is that we have one radio per USB Audio Adapter.
|
||||
// Failure at this point is not an error.
|
||||
// See if config file sets it explicitly before complaining.
|
||||
|
||||
cm108_find_ptt (p_audio_config->adev[ACHAN2ADEV(channel)].adevice_out,
|
||||
p_audio_config->achan[channel].octrl[ot].ptt_device,
|
||||
(int)sizeof(p_audio_config->achan[channel].octrl[ot].ptt_device));
|
||||
|
||||
while ((t = split(NULL,0)) != NULL) {
|
||||
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 if (isdigit(*t)) {
|
||||
p_audio_config->achan[channel].octrl[ot].out_gpio_num = atoi(t);
|
||||
p_audio_config->achan[channel].octrl[ot].ptt_invert = 0;
|
||||
}
|
||||
else if (*t == '/') {
|
||||
strlcpy (p_audio_config->achan[channel].octrl[ot].ptt_device, t, sizeof(p_audio_config->achan[channel].octrl[ot].ptt_device));
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file line %d: Found \"%s\" when expecting GPIO number or device name like /dev/hidraw1.\n", line, t);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (p_audio_config->achan[channel].octrl[ot].out_gpio_num < 1 || p_audio_config->achan[channel].octrl[ot].out_gpio_num > 8) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file line %d: CM108 GPIO number %d is not in range of 1 thru 8.\n", line,
|
||||
p_audio_config->achan[channel].octrl[ot].out_gpio_num);
|
||||
continue;
|
||||
}
|
||||
if (strlen(p_audio_config->achan[channel].octrl[ot].ptt_device) == 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file line %d: Could not determine USB Audio GPIO PTT device for audio output %s.\n", line,
|
||||
p_audio_config->adev[ACHAN2ADEV(channel)].adevice_out);
|
||||
dw_printf ("You must explicitly mention a device name such as /dev/hidraw1.\n");
|
||||
dw_printf ("See User Guide for details.\n");
|
||||
continue;
|
||||
}
|
||||
p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_CM108;
|
||||
|
||||
#else
|
||||
#if __WIN32__
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file line %d: CM108 USB Audio GPIO PTT is not available for Windows.\n", line);
|
||||
#else
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file line %d: %s with CM108 is only available when USB Audio GPIO support is enabled.\n", line, otname);
|
||||
dw_printf ("You must rebuild direwolf with CM108 Audio Adapter GPIO PTT support.\n");
|
||||
dw_printf ("See User Guide for details.\n");
|
||||
#endif
|
||||
exit (EXIT_FAILURE);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
|
@ -1818,7 +1933,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
*
|
||||
* TXINH - TX holdoff input
|
||||
*
|
||||
* xxx GPIO [-]gpio-num (only type supported yet)
|
||||
* TXINH GPIO [-]gpio-num (only type supported so far)
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "TXINH") == 0) {
|
||||
|
@ -1861,7 +1976,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
|
||||
|
||||
/*
|
||||
* DWAIT - Extra delay for receiver squelch.
|
||||
* DWAIT n - Extra delay for receiver squelch. n = 10 mS units.
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "DWAIT") == 0) {
|
||||
|
@ -1885,7 +2000,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
}
|
||||
|
||||
/*
|
||||
* SLOTTIME - For non-digipeat transmit delay timing.
|
||||
* SLOTTIME n - For non-digipeat transmit delay timing. n = 10 mS units.
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "SLOTTIME") == 0) {
|
||||
|
@ -1933,7 +2048,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
}
|
||||
|
||||
/*
|
||||
* TXDELAY - For transmit delay timing.
|
||||
* TXDELAY n - For transmit delay timing. n = 10 mS units.
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "TXDELAY") == 0) {
|
||||
|
@ -1957,7 +2072,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
}
|
||||
|
||||
/*
|
||||
* TXTAIL - For transmit timing.
|
||||
* TXTAIL n - For transmit timing. n = 10 mS units.
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "TXTAIL") == 0) {
|
||||
|
@ -1980,6 +2095,30 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* FULLDUP {on|off} - Full Duplex
|
||||
*/
|
||||
else if (strcasecmp(t, "FULLDUP") == 0) {
|
||||
|
||||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Missing parameter for FULLDUP command. Expecting ON or OFF.\n", line);
|
||||
continue;
|
||||
}
|
||||
if (strcasecmp(t, "ON") == 0) {
|
||||
p_audio_config->achan[channel].fulldup = 1;
|
||||
}
|
||||
else if (strcasecmp(t, "OFF") == 0) {
|
||||
p_audio_config->achan[channel].fulldup = 0;
|
||||
}
|
||||
else {
|
||||
p_audio_config->achan[channel].fulldup = 0;
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Expected ON or OFF for FULLDUP.\n", line);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* SPEECH script
|
||||
*
|
||||
|
@ -2104,10 +2243,18 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
t = split(NULL,0);
|
||||
}
|
||||
else if (strcasecmp(t, "DROP") == 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file, line %d: Preemptive digipeating DROP option is discouraged.\n", line);
|
||||
dw_printf ("It can create a via path which is misleading about the actual path taken.\n");
|
||||
dw_printf ("TRACE is the best choice for this feature.\n");
|
||||
p_digi_config->preempt[from_chan][to_chan] = PREEMPT_DROP;
|
||||
t = split(NULL,0);
|
||||
}
|
||||
else if (strcasecmp(t, "MARK") == 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file, line %d: Preemptive digipeating MARK option is discouraged.\n", line);
|
||||
dw_printf ("It can create a via path which is misleading about the actual path taken.\n");
|
||||
dw_printf ("TRACE is the best choice for this feature.\n");
|
||||
p_digi_config->preempt[from_chan][to_chan] = PREEMPT_MARK;
|
||||
t = split(NULL,0);
|
||||
}
|
||||
|
@ -2257,7 +2404,10 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
t = split(NULL,0);
|
||||
if (t != NULL) {
|
||||
e = regcomp (&(p_cdigi_config->alias[from_chan][to_chan]), t, REG_EXTENDED|REG_NOSUB);
|
||||
if (e != 0) {
|
||||
if (e == 0) {
|
||||
p_cdigi_config->has_alias[from_chan][to_chan] = 1;
|
||||
}
|
||||
else {
|
||||
regerror (e, &(p_cdigi_config->alias[from_chan][to_chan]), message, sizeof(message));
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Invalid alias matching pattern on line %d:\n%s\n",
|
||||
|
@ -2284,6 +2434,19 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
* FILTER from-chan to-chan filter_specification_expression
|
||||
* FILTER from-chan IG filter_specification_expression
|
||||
* FILTER IG to-chan filter_specification_expression
|
||||
*
|
||||
*
|
||||
* Note that we have three different config file filter commands:
|
||||
*
|
||||
* FILTER - Originally for APRS digipeating but later enhanced
|
||||
* to include IGate client side. Maybe it should be
|
||||
* renamed AFILTER to make it clearer after adding CFILTER.
|
||||
*
|
||||
* CFILTER - Similar for connected moded digipeater.
|
||||
*
|
||||
* IGFILTER - APRS-IS (IGate) server side - completely diffeent.
|
||||
* I'm not happy with this name because IG sounds like IGate
|
||||
* which is really the client side. More comments later.
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "FILTER") == 0) {
|
||||
|
@ -2423,7 +2586,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
t = " "; /* Empty means permit nothing. */
|
||||
}
|
||||
|
||||
p_cdigi_config->filter_str[from_chan][to_chan] = strdup(t);
|
||||
p_cdigi_config->cfilter_str[from_chan][to_chan] = strdup(t);
|
||||
|
||||
//TODO1.2: Do a test run to see errors now instead of waiting.
|
||||
|
||||
|
@ -3919,6 +4082,9 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
|
||||
/*
|
||||
* IGFILTER - IGate Server side filters.
|
||||
* Is this name too confusing. Too similar to FILTER IG 0 ...
|
||||
* Maybe SSFILTER suggesting Server Side.
|
||||
* SUBSCRIBE might be better because it's not a filter that limits.
|
||||
*
|
||||
* IGFILTER filter-spec ...
|
||||
*/
|
||||
|
@ -3927,6 +4093,12 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
|
||||
t = split(NULL,1); /* Take rest of line as one string. */
|
||||
|
||||
if (p_igate_config->t2_filter != NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Warning - Earlier IGFILTER value will be replaced by this one.\n", line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t != NULL && strlen(t) > 0) {
|
||||
p_igate_config->t2_filter = strdup (t);
|
||||
}
|
||||
|
@ -4005,13 +4177,13 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
p_igate_config->igmsp = n;
|
||||
}
|
||||
else {
|
||||
p_igate_config->satgate_delay = 1;
|
||||
p_igate_config->igmsp = 1;
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Unreasonable number of times for message sender position. Using default 1.\n", line);
|
||||
}
|
||||
}
|
||||
else {
|
||||
p_igate_config->satgate_delay = 1;
|
||||
p_igate_config->igmsp = 1;
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Missing number of times for message sender position. Using default 1.\n", line);
|
||||
}
|
||||
|
@ -4027,6 +4199,9 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
|
||||
else if (strcasecmp(t, "SATGATE") == 0) {
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Line %d: SATGATE is pretty useless and will be removed in a future version.\n", line);
|
||||
|
||||
t = split(NULL,0);
|
||||
if (t != NULL) {
|
||||
|
||||
|
@ -4102,17 +4277,58 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
}
|
||||
|
||||
/*
|
||||
* NULLMODEM - Device name for our end of the virtual "null modem"
|
||||
* NULLMODEM name [ speed ] - Device name for serial port or our end of the virtual "null modem"
|
||||
* SERIALKISS name [ speed ]
|
||||
*
|
||||
* Version 1.5: Added SERIALKISS which is equivalent to NULLMODEM.
|
||||
* The original name sort of made sense when it was used only for one end of a virtual
|
||||
* null modem cable on Windows only. Now it is also available for Linux.
|
||||
* TODO1.5: In retrospect, this doesn't seem like such a good name.
|
||||
*/
|
||||
else if (strcasecmp(t, "nullmodem") == 0) {
|
||||
|
||||
else if (strcasecmp(t, "NULLMODEM") == 0 || strcasecmp(t, "SERIALKISS") == 0) {
|
||||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Missing device name for my end of the 'null modem' on line %d.\n", line);
|
||||
dw_printf ("Config file: Missing serial port name on line %d.\n", line);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
strlcpy (p_misc_config->nullmodem, t, sizeof(p_misc_config->nullmodem));
|
||||
if (strlen(p_misc_config->kiss_serial_port) > 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Warning serial port name on line %d replaces earlier value.\n", line);
|
||||
}
|
||||
strlcpy (p_misc_config->kiss_serial_port, t, sizeof(p_misc_config->kiss_serial_port));
|
||||
p_misc_config->kiss_serial_speed = 0;
|
||||
p_misc_config->kiss_serial_poll = 0;
|
||||
}
|
||||
|
||||
t = split(NULL,0);
|
||||
if (t != NULL) {
|
||||
p_misc_config->kiss_serial_speed = atoi(t);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* SERIALKISSPOLL name - Poll for serial port name that might come and go.
|
||||
* e.g. /dev/rfcomm0 for bluetooth.
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "SERIALKISSPOLL") == 0) {
|
||||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Missing serial port name on line %d.\n", line);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
if (strlen(p_misc_config->kiss_serial_port) > 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Warning serial port name on line %d replaces earlier value.\n", line);
|
||||
}
|
||||
strlcpy (p_misc_config->kiss_serial_port, t, sizeof(p_misc_config->kiss_serial_port));
|
||||
p_misc_config->kiss_serial_speed = 0;
|
||||
p_misc_config->kiss_serial_poll = 1; // set polling.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4222,7 +4438,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
}
|
||||
|
||||
/*
|
||||
* LOGDIR - Directory name for storing log files. Use "." for current working directory.
|
||||
* LOGDIR - Directory name for automatically named daily log files. Use "." for current working directory.
|
||||
*/
|
||||
else if (strcasecmp(t, "logdir") == 0) {
|
||||
t = split(NULL,0);
|
||||
|
@ -4232,7 +4448,12 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
continue;
|
||||
}
|
||||
else {
|
||||
strlcpy (p_misc_config->logdir, t, sizeof(p_misc_config->logdir));
|
||||
if (strlen(p_misc_config->log_path) > 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: LOGDIR on line %d is replacing an earlier LOGDIR or LOGFILE.\n", line);
|
||||
}
|
||||
p_misc_config->log_daily_names = 1;
|
||||
strlcpy (p_misc_config->log_path, t, sizeof(p_misc_config->log_path));
|
||||
}
|
||||
t = split(NULL,0);
|
||||
if (t != NULL) {
|
||||
|
@ -4241,6 +4462,31 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* LOGFILE - Log file name, including any directory part.
|
||||
*/
|
||||
else if (strcasecmp(t, "logfile") == 0) {
|
||||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Missing file name for LOGFILE on line %d.\n", line);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
if (strlen(p_misc_config->log_path) > 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: LOGFILE on line %d is replacing an earlier LOGDIR or LOGFILE.\n", line);
|
||||
}
|
||||
p_misc_config->log_daily_names = 0;
|
||||
strlcpy (p_misc_config->log_path, t, sizeof(p_misc_config->log_path));
|
||||
}
|
||||
t = split(NULL,0);
|
||||
if (t != NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: LOGFILE on line %d should have file name and nothing more.\n", line);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* BEACON channel delay every message
|
||||
*
|
||||
|
@ -4520,6 +4766,77 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* V20 address [ address ... ] - Stations known to support only AX.25 v2.0.
|
||||
* When connecting to these, skip SABME and go right to SABM.
|
||||
* Possible to have multiple and they are cummulative.
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "V20") == 0) {
|
||||
|
||||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Missing address(es) for V20.\n", line);
|
||||
continue;
|
||||
}
|
||||
|
||||
while (t != NULL) {
|
||||
int const strict = 2;
|
||||
char call_no_ssid[AX25_MAX_ADDR_LEN];
|
||||
int ssid, heard;
|
||||
|
||||
if (ax25_parse_addr (AX25_DESTINATION, t, strict, call_no_ssid, &ssid, &heard)) {
|
||||
p_misc_config->v20_addrs = (char**)realloc (p_misc_config->v20_addrs, sizeof(char*) * (p_misc_config->v20_count + 1));
|
||||
p_misc_config->v20_addrs[p_misc_config->v20_count++] = strdup(t);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Invalid station address for V20 command.\n", line);
|
||||
|
||||
// continue processing any others following.
|
||||
}
|
||||
t = split(NULL,0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* NOXID address [ address ... ] - Stations known not to understand XID.
|
||||
* After connecting to these (with v2.2 obviously), don't try using XID commmand.
|
||||
* AX.25 for Linux is the one known case so far.
|
||||
* Possible to have multiple and they are cummulative.
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "NOXID") == 0) {
|
||||
|
||||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Missing address(es) for NOXID.\n", line);
|
||||
continue;
|
||||
}
|
||||
|
||||
while (t != NULL) {
|
||||
int const strict = 2;
|
||||
char call_no_ssid[AX25_MAX_ADDR_LEN];
|
||||
int ssid, heard;
|
||||
|
||||
if (ax25_parse_addr (AX25_DESTINATION, t, strict, call_no_ssid, &ssid, &heard)) {
|
||||
p_misc_config->noxid_addrs = (char**)realloc (p_misc_config->noxid_addrs, sizeof(char*) * (p_misc_config->noxid_count + 1));
|
||||
p_misc_config->noxid_addrs[p_misc_config->noxid_count++] = strdup(t);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Invalid station address for NOXID command.\n", line);
|
||||
|
||||
// continue processing any others following.
|
||||
}
|
||||
t = split(NULL,0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Invalid command.
|
||||
*/
|
||||
|
@ -4676,11 +4993,13 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_
|
|||
b->sendto_type = SENDTO_XMIT;
|
||||
b->sendto_chan = 0;
|
||||
b->delay = 60;
|
||||
b->slot = G_UNKNOWN;
|
||||
b->every = 600;
|
||||
//b->delay = 6; // temp test.
|
||||
//b->every = 3600;
|
||||
b->lat = G_UNKNOWN;
|
||||
b->lon = G_UNKNOWN;
|
||||
b->ambiguity = 0;
|
||||
b->alt_m = G_UNKNOWN;
|
||||
b->symtab = '/';
|
||||
b->symbol = '-'; /* house */
|
||||
|
@ -4709,6 +5028,15 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_
|
|||
if (strcasecmp(keyword, "DELAY") == 0) {
|
||||
b->delay = parse_interval(value,line);
|
||||
}
|
||||
else if (strcasecmp(keyword, "SLOT") == 0) {
|
||||
int n = parse_interval(value,line);
|
||||
if ( n < 1 || n > 3600) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file, line %d: Beacon time slot, %d, must be in range of 1 to 3600 seconds.\n", line, n);
|
||||
continue;
|
||||
}
|
||||
b->slot = n;
|
||||
}
|
||||
else if (strcasecmp(keyword, "EVERY") == 0) {
|
||||
b->every = parse_interval(value,line);
|
||||
}
|
||||
|
@ -4721,7 +5049,7 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_
|
|||
int n = atoi(value+1);
|
||||
if ( n < 0 || n >= MAX_CHANS || ! p_audio_config->achan[n].valid) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file, line %d: Send to channel %d is not valid.\n", line, n);
|
||||
dw_printf ("Config file, line %d: Simulated receive on channel %d is not valid.\n", line, n);
|
||||
continue;
|
||||
}
|
||||
b->sendto_type = SENDTO_RECV;
|
||||
|
@ -4798,6 +5126,16 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_
|
|||
else if (strcasecmp(keyword, "LONG") == 0 || strcasecmp(keyword, "LON") == 0) {
|
||||
b->lon = parse_ll (value, LON, line);
|
||||
}
|
||||
else if (strcasecmp(keyword, "AMBIGUITY") == 0 || strcasecmp(keyword, "AMBIG") == 0) {
|
||||
int n = atoi(value);
|
||||
if (n >= 0 && n <= 4) {
|
||||
b->ambiguity = n;
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Location ambiguity, on line %d, must be in range of 0 to 4.\n", line);
|
||||
}
|
||||
}
|
||||
else if (strcasecmp(keyword, "ALT") == 0 || strcasecmp(keyword, "ALTITUDE") == 0) {
|
||||
b->alt_m = atof(value);
|
||||
}
|
||||
|
@ -4865,7 +5203,13 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_
|
|||
|
||||
if (b->custom_info != NULL && b->custom_infocmd != NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file, line %d: Can't use both INFO and INFOCMD at the same time..\n", line);
|
||||
dw_printf ("Config file, line %d: Can't use both INFO and INFOCMD at the same time.\n", line);
|
||||
}
|
||||
|
||||
if (b->compress && b->ambiguity != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file, line %d: Position ambiguity can't be used with compressed location format.\n", line);
|
||||
b->ambiguity = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -4898,7 +5242,7 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_
|
|||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file, line %d: When any of ZONE, EASTING, NORTHING specifed, they must all be specified.\n", line);
|
||||
dw_printf ("Config file, line %d: When any of ZONE, EASTING, NORTHING specified, they must all be specified.\n", line);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
34
config.h
34
config.h
|
@ -34,13 +34,24 @@ enum sendto_type_e { SENDTO_XMIT, SENDTO_IGATE, SENDTO_RECV };
|
|||
struct misc_config_s {
|
||||
|
||||
int agwpe_port; /* Port number for the "AGW TCPIP Socket Interface" */
|
||||
int kiss_port; /* Port number for the "KISS" protocol. */
|
||||
int kiss_port; /* Port number for the "TCP KISS" protocol. */
|
||||
int enable_kiss_pt; /* Enable pseudo terminal for KISS. */
|
||||
/* Want this to be off by default because it hangs */
|
||||
/* after a while if nothing is reading from other end. */
|
||||
|
||||
char nullmodem[20]; /* Serial port name for our end of the */
|
||||
char kiss_serial_port[20];
|
||||
/* Serial port name for our end of the */
|
||||
/* virtual null modem for native Windows apps. */
|
||||
/* Version 1.5 add same capability for Linux. */
|
||||
|
||||
int kiss_serial_speed; /* Speed, in bps, for the KISS serial port. */
|
||||
/* If 0, just leave what was already there. */
|
||||
|
||||
int kiss_serial_poll; /* When using Bluetooth KISS, the /dev/rfcomm0 device */
|
||||
/* will appear and disappear as the remote application */
|
||||
/* opens and closes the virtual COM port. */
|
||||
/* When this is non-zero, we will check periodically to */
|
||||
/* see if the device has appeared and we will open it. */
|
||||
|
||||
char gpsnmea_port[20]; /* Serial port name for reading NMEA sentences from GPS. */
|
||||
/* e.g. COM22, /dev/ttyACM0 */
|
||||
|
@ -66,7 +77,9 @@ struct misc_config_s {
|
|||
#define WPT_FORMAT_KENWOOD 0x08 /* K $PKWDWPL */
|
||||
|
||||
|
||||
char logdir[80]; /* Directory for saving activity logs. */
|
||||
int log_daily_names; /* True to generate new log file each day. */
|
||||
|
||||
char log_path[80]; /* Either directory or full file name depending on above. */
|
||||
|
||||
int sb_configured; /* TRUE if SmartBeaconing is configured. */
|
||||
int sb_fast_speed; /* MPH */
|
||||
|
@ -93,6 +106,17 @@ struct misc_config_s {
|
|||
/* switching to SABM. This is to handle the case of an old */
|
||||
/* TNC which simply ignores SABME rather than replying with FRMR. */
|
||||
|
||||
char **v20_addrs; /* Stations known to understand only AX.25 v2.0 so we don't */
|
||||
/* waste time trying v2.2 first. */
|
||||
|
||||
int v20_count; /* Number of station addresses in array above. */
|
||||
|
||||
char **noxid_addrs; /* Stations known not to understand XID command so don't */
|
||||
/* waste time sending it and eventually giving up. */
|
||||
/* AX.25 for Linux is the one known case, so far, where */
|
||||
/* SABME is implemented but XID is not. */
|
||||
|
||||
int noxid_count; /* Number of station addresses in array above. */
|
||||
|
||||
|
||||
// Beacons.
|
||||
|
@ -120,6 +144,9 @@ struct misc_config_s {
|
|||
|
||||
int delay; /* Seconds to delay before first transmission. */
|
||||
|
||||
int slot; /* Seconds after hour for slotted time beacons. */
|
||||
/* If specified, it overrides any 'delay' value. */
|
||||
|
||||
int every; /* Time between transmissions, seconds. */
|
||||
/* Remains fixed for PBEACON and OBEACON. */
|
||||
/* Dynamically adjusted for TBEACON. */
|
||||
|
@ -146,6 +173,7 @@ struct misc_config_s {
|
|||
|
||||
double lat; /* Latitude and longitude. */
|
||||
double lon;
|
||||
int ambiguity; /* Number of lower digits to trim from location. 0 (default), 1, 2, 3, 4. */
|
||||
float alt_m; /* Altitude in meters. */
|
||||
|
||||
char symtab; /* Symbol table: / or \ or overlay character. */
|
||||
|
|
251
decode_aprs.c
251
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 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2017 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
|
||||
|
@ -973,6 +973,8 @@ static void aprs_raw_nmea (decode_aprs_t *A, unsigned char *info, int ilen)
|
|||
*
|
||||
* References: Mic-E TYPE CODES -- http://www.aprs.org/aprs12/mic-e-types.txt
|
||||
*
|
||||
* This is up to date with the 24 Aug 16 version mentioning the TH-D74.
|
||||
*
|
||||
* Mic-E TEST EXAMPLES -- http://www.aprs.org/aprs12/mic-e-examples.txt
|
||||
*
|
||||
* Examples: `b9Z!4y>/>"4N}Paul's_TH-D7
|
||||
|
@ -1461,7 +1463,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
|
|||
*
|
||||
* A->g_message_number Message number if any. Required for ack/rej.
|
||||
*
|
||||
* Description: An APRS message is a text string with a specifed addressee.
|
||||
* Description: An APRS message is a text string with a specified addressee.
|
||||
*
|
||||
* It's a lot more complicated with different types of addressees
|
||||
* and replies with acknowledgement or rejection.
|
||||
|
@ -3183,7 +3185,7 @@ double get_latitude_8 (char *p, int quiet)
|
|||
else {
|
||||
if ( ! quiet) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Error: '%c' found for latitude hemisphere. Specification requires upper case N or s.\n", plat->ns);
|
||||
dw_printf("Error: '%c' found for latitude hemisphere. Specification requires upper case N or S.\n", plat->ns);
|
||||
}
|
||||
return (G_UNKNOWN);
|
||||
}
|
||||
|
@ -3704,8 +3706,12 @@ static int data_extension_comment (decode_aprs_t *A, char *pdext)
|
|||
*
|
||||
* Windows version: File must be in current working directory.
|
||||
*
|
||||
* Linux version: Search order is current working directory
|
||||
* then /usr/share/direwolf 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
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
|
@ -3728,11 +3734,20 @@ static struct tocalls_s {
|
|||
static int num_tocalls = 0;
|
||||
|
||||
// Make sure the array is null terminated.
|
||||
// If search order is changed, do the same in symbols.c
|
||||
|
||||
static const char *search_locations[] = {
|
||||
(const char *) "tocalls.txt",
|
||||
#ifndef __WIN32__
|
||||
(const char *) "/usr/share/direwolf/tocalls.txt",
|
||||
(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
|
||||
};
|
||||
|
@ -4437,6 +4452,26 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen)
|
|||
* to stretch the numeric range to be 0 to 99.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Here is an interesting case.
|
||||
*
|
||||
* W8SAT-1>T2UV0P:`qC<0x1f>l!Xu\'"69}WMNI EDS Response Unit #1|+/%0'n|!w:X!|3
|
||||
*
|
||||
* Let's break that down into pieces.
|
||||
*
|
||||
* W8SAT-1>T2UV0P:`qC<0x1f>l!Xu\'"69} MIC-E format
|
||||
* N 42 56.0000, W 085 39.0300,
|
||||
* 0 MPH, course 160, alt 709 ft
|
||||
* WMNI EDS Response Unit #1 comment
|
||||
* |+/%0'n| base 91 telemetry
|
||||
* !w:X! DAO
|
||||
* |3 Tiny Track 3
|
||||
*
|
||||
* Comment earlier points out that MIC-E format has resolution of 0.01 minute,
|
||||
* same as non-compressed format, so the DAO does work out, after thinking
|
||||
* about it for a while.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The spec appears to be wrong. It says '}' is the maximum value when it should be '{'.
|
||||
*/
|
||||
|
@ -4583,6 +4618,14 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen)
|
|||
*
|
||||
* WB2OSZ-1>APN383,qAR,N1EDU-2:!4237.14NS07120.83W#PHG7130Chelmsford, MA
|
||||
*
|
||||
* New for 1.5:
|
||||
*
|
||||
* Also allow hexadecimal bytes for raw AX.25 or KISS. e.g.
|
||||
*
|
||||
* 00 82 a0 ae ae 62 60 e0 82 96 68 84 40 40 60 9c 68 b0 ae 86 40 e0 40 ae 92 88 8a 64 63 03 f0 3e 45 4d 36 34 6e 65 2f 23 20 45 63 68 6f 6c 69 6e 6b 20 31 34 35 2e 33 31 30 2f 31 30 30 68 7a 20 54 6f 6e 65
|
||||
*
|
||||
* If it begins with 00 or C0 (which would be impossible for AX.25 address) process as KISS.
|
||||
* Also print these formats.
|
||||
*
|
||||
* Outputs: stdout
|
||||
*
|
||||
|
@ -4614,11 +4657,16 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen)
|
|||
* TODO: To make it more useful,
|
||||
* - Remove any leading timestamp.
|
||||
* - Remove any "qA*" and following from the path.
|
||||
* - Handle non-APRS frames properly.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
#if DECAMAIN
|
||||
|
||||
#include "kiss_frame.h"
|
||||
|
||||
|
||||
|
||||
/* Stub for stand-alone decoder. */
|
||||
|
||||
void nmea_send_waypoint (char *wname_in, double dlat, double dlong, char symtab, char symbol,
|
||||
|
@ -4627,11 +4675,48 @@ void nmea_send_waypoint (char *wname_in, double dlat, double dlong, char symtab,
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO: hex_dump is currently in server.c and we don't want to drag that in.
|
||||
// Someday put it in a more reasonable place, with other general utilities, and remove the private copy here.
|
||||
|
||||
|
||||
static void hex_dump (unsigned char *p, int len)
|
||||
{
|
||||
int n, i, offset;
|
||||
|
||||
offset = 0;
|
||||
while (len > 0) {
|
||||
n = len < 16 ? len : 16;
|
||||
dw_printf (" %03x: ", offset);
|
||||
for (i=0; i<n; i++) {
|
||||
dw_printf (" %02x", p[i]);
|
||||
}
|
||||
for (i=n; i<16; i++) {
|
||||
dw_printf (" ");
|
||||
}
|
||||
dw_printf (" ");
|
||||
for (i=0; i<n; i++) {
|
||||
dw_printf ("%c", isprint(p[i]) ? p[i] : '.');
|
||||
}
|
||||
dw_printf ("\n");
|
||||
p += 16;
|
||||
offset += 16;
|
||||
len -= 16;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Do we have two hexadecimal digits followed by whitespace or end of line?
|
||||
|
||||
#define ISHEX2(x) (isxdigit(x[0]) && isxdigit(x[1]) && (x[2] == '\0' || isspace(x[2])))
|
||||
|
||||
#define MAXLINE 9000
|
||||
#define MAXBYTES 3000
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
char stuff[300];
|
||||
char stuff[MAXLINE];
|
||||
unsigned char bytes[MAXBYTES];
|
||||
int num_bytes;
|
||||
char *p;
|
||||
packet_t pp;
|
||||
|
||||
|
@ -4687,36 +4772,150 @@ int main (int argc, char *argv[])
|
|||
text_color_set(DW_COLOR_REC);
|
||||
dw_printf("\n%s\n", stuff);
|
||||
|
||||
pp = ax25_from_text(stuff, 1);
|
||||
if (pp != NULL)
|
||||
{
|
||||
// Do we have monitor format, KISS, or AX.25 frame?
|
||||
|
||||
p = stuff;
|
||||
while (isspace(*p)) p++;
|
||||
|
||||
if (ISHEX2(p)) {
|
||||
|
||||
// Collect a bunch of hexadecimal numbers.
|
||||
|
||||
num_bytes = 0;
|
||||
|
||||
while (ISHEX2(p) && num_bytes < MAXBYTES) {
|
||||
|
||||
bytes[num_bytes++] = strtoul(p, NULL, 16);
|
||||
p += 2;
|
||||
while (isspace(*p)) p++;
|
||||
}
|
||||
|
||||
if (num_bytes == 0 || *p != '\0') {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Parse error around column %d.\n", (int)(long)(p - stuff) + 1);
|
||||
dw_printf("Was expecting only space separated 2 digit hexadecimal numbers.\n\n");
|
||||
continue; // next line
|
||||
}
|
||||
|
||||
// If we have 0xC0 at start, remove it and expect same at end.
|
||||
|
||||
if (bytes[0] == FEND) {
|
||||
|
||||
if (num_bytes < 2 || bytes[1] != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Was expecting to find 00 after the initial C0.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bytes[num_bytes-1] == FEND) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf("Removing KISS FEND characters at beginning and end.\n");
|
||||
int n;
|
||||
for (n = 0; n < num_bytes-1; n++) {
|
||||
bytes[n] = bytes[n+1];
|
||||
}
|
||||
num_bytes -= 2;
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf("Removing KISS FEND character at beginning. Was expecting another at end.\n");
|
||||
int n;
|
||||
for (n = 0; n < num_bytes-1; n++) {
|
||||
bytes[n] = bytes[n+1];
|
||||
}
|
||||
num_bytes -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (bytes[0] == 0) {
|
||||
|
||||
// Treat as KISS. Undo any KISS encoding.
|
||||
|
||||
unsigned char kiss_frame[MAXBYTES];
|
||||
int kiss_len = num_bytes;
|
||||
|
||||
memcpy (kiss_frame, bytes, num_bytes);
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("--- KISS frame ---\n");
|
||||
hex_dump (kiss_frame, kiss_len);
|
||||
|
||||
// Put FEND at end to keep kiss_unwrap happy.
|
||||
// Having one at the begining is optional.
|
||||
|
||||
kiss_frame[kiss_len++] = FEND;
|
||||
|
||||
// In the more general case, we would need to include
|
||||
// the command byte because it could be escaped.
|
||||
// Here we know it is 0, so we take a short cut and
|
||||
// remove it before, rather than after, the conversion.
|
||||
|
||||
num_bytes = kiss_unwrap (kiss_frame + 1, kiss_len - 1, bytes);
|
||||
}
|
||||
|
||||
// Treat as AX.25.
|
||||
|
||||
alevel_t alevel;
|
||||
memset (&alevel, 0, sizeof(alevel));
|
||||
|
||||
pp = ax25_from_frame(bytes, num_bytes, alevel);
|
||||
if (pp != NULL) {
|
||||
char addrs[120];
|
||||
unsigned char *pinfo;
|
||||
int info_len;
|
||||
decode_aprs_t A;
|
||||
|
||||
// log directory option someday?
|
||||
decode_aprs (&A, pp, 0);
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("--- AX.25 frame ---\n");
|
||||
ax25_hex_dump (pp);
|
||||
dw_printf ("-------------------\n");
|
||||
|
||||
//Print it all out in human readable format.
|
||||
ax25_format_addrs (pp, addrs);
|
||||
text_color_set(DW_COLOR_DECODED);
|
||||
dw_printf ("%s", addrs);
|
||||
|
||||
decode_aprs_print (&A);
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
ax25_safe_print ((char *)pinfo, info_len, 1); // Display non-ASCII to hexadecimal.
|
||||
dw_printf ("\n");
|
||||
|
||||
/*
|
||||
* Perform validity check on each address.
|
||||
* This should print an error message if any issues.
|
||||
*/
|
||||
(void)ax25_check_addresses(pp);
|
||||
decode_aprs (&A, pp, 0); // Extract information into structure.
|
||||
|
||||
// Send to log file?
|
||||
decode_aprs_print (&A); // Now print it in human readable format.
|
||||
|
||||
// if (logdir != NULL && *logdir != '\0') {
|
||||
// log_write (&A, pp, logdir);
|
||||
// }
|
||||
(void)ax25_check_addresses(pp); // Errors for invalid addresses.
|
||||
|
||||
ax25_delete (pp);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("\n%s\n", "ERROR - Could not parse input!\n");
|
||||
dw_printf("Could not construct AX.25 frame from bytes supplied!\n\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// Normal monitoring format.
|
||||
|
||||
pp = ax25_from_text(stuff, 1);
|
||||
if (pp != NULL) {
|
||||
decode_aprs_t A;
|
||||
|
||||
decode_aprs (&A, pp, 0); // Extract information into structure.
|
||||
|
||||
decode_aprs_print (&A); // Now print it in human readable format.
|
||||
|
||||
// This seems to be redundant because we used strict option
|
||||
// when parsing the monitoring format text.
|
||||
//(void)ax25_check_addresses(pp); // Errors for invalid addresses.
|
||||
|
||||
// Future? Add -d option to include hex dump and maybe KISS?
|
||||
|
||||
ax25_delete (pp);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("ERROR - Could not parse monitoring format input!\n\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
8
demod.c
8
demod.c
|
@ -639,9 +639,13 @@ int demod_init (struct audio_s *pa)
|
|||
/* Not sure if it should be on for ARM too. */
|
||||
/* Need to take a look at CPU usage and performance difference. */
|
||||
|
||||
#ifndef __arm__
|
||||
/* Version 1.5: Remove special case for ARM. */
|
||||
/* We want higher performance to be the default. */
|
||||
/* "MODEM 9600 -" can be used on very slow CPU if necessary. */
|
||||
|
||||
//#ifndef __arm__
|
||||
strlcpy (save_audio_config_p->achan[chan].profiles, "+", sizeof(save_audio_config_p->achan[chan].profiles));
|
||||
#endif
|
||||
//#endif
|
||||
}
|
||||
|
||||
#ifdef TUNE_UPSAMPLE
|
||||
|
|
|
@ -491,7 +491,9 @@ inline static void nudge_pll (int chan, int subchan, int slice, float demod_out_
|
|||
*/
|
||||
|
||||
D->slicer[slice].prev_d_c_pll = D->slicer[slice].data_clock_pll;
|
||||
D->slicer[slice].data_clock_pll += D->pll_step_per_sample;
|
||||
|
||||
// Perform the add as unsigned to avoid signed overflow error.
|
||||
D->slicer[slice].data_clock_pll = (signed)((unsigned)(D->slicer[slice].data_clock_pll) + (unsigned)(D->pll_step_per_sample));
|
||||
|
||||
if ( D->slicer[slice].prev_d_c_pll > 1000000000 && D->slicer[slice].data_clock_pll < -1000000000) {
|
||||
|
||||
|
|
|
@ -1122,7 +1122,9 @@ inline static void nudge_pll (int chan, int subchan, int slice, int demod_data,
|
|||
*/
|
||||
|
||||
D->slicer[slice].prev_d_c_pll = D->slicer[slice].data_clock_pll;
|
||||
D->slicer[slice].data_clock_pll += D->pll_step_per_sample;
|
||||
|
||||
// Perform the add as unsigned to avoid signed overflow error.
|
||||
D->slicer[slice].data_clock_pll = (signed)((unsigned)(D->slicer[slice].data_clock_pll) + (unsigned)(D->pll_step_per_sample));
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
// dw_printf ("prev = %lx, new data clock pll = %lx\n" D->prev_d_c_pll, D->data_clock_pll);
|
||||
|
|
|
@ -794,7 +794,8 @@ inline static void nudge_pll (int chan, int subchan, int slice, int demod_bits,
|
|||
|
||||
D->slicer[slice].prev_d_c_pll = D->slicer[slice].data_clock_pll;
|
||||
|
||||
D->slicer[slice].data_clock_pll += D->pll_step_per_sample;
|
||||
// Perform the add as unsigned to avoid signed overflow error.
|
||||
D->slicer[slice].data_clock_pll = (signed)((unsigned)(D->slicer[slice].data_clock_pll) + (unsigned)(D->pll_step_per_sample));
|
||||
|
||||
if (D->slicer[slice].data_clock_pll < 0 && D->slicer[slice].prev_d_c_pll >= 0) {
|
||||
|
||||
|
|
10
digipeater.c
10
digipeater.c
|
@ -484,6 +484,16 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
break;
|
||||
}
|
||||
|
||||
// Idea: Here is an interesting idea for a new option. REORDER?
|
||||
// The preemptive digipeater could move its call after the (formerly) last used digi field
|
||||
// and preserve all the unused fields after that. The list of used addresses would
|
||||
// accurately record the journey taken by the packet.
|
||||
|
||||
// https://groups.yahoo.com/neo/groups/aprsisce/conversations/topics/31935
|
||||
|
||||
// > I was wishing for a non-marking preemptive digipeat so that the original packet would be left intact
|
||||
// > or maybe something like WIDE1-1,WIDE2-1,KJ4OVQ-9 becoming KJ4OVQ-9*,WIDE1-1,WIDE2-1.
|
||||
|
||||
return (result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,8 +38,6 @@ struct digi_config_s {
|
|||
|
||||
enum preempt_e { PREEMPT_OFF, PREEMPT_DROP, PREEMPT_MARK, PREEMPT_TRACE } preempt[MAX_CHANS][MAX_CHANS];
|
||||
|
||||
//char type_filter[MAX_CHANS][MAX_CHANS][20]; // TODO1.2: remove this
|
||||
|
||||
char *filter_str[MAX_CHANS+1][MAX_CHANS+1];
|
||||
// NULL or optional Packet Filter strings such as "t/m".
|
||||
// Notice the size of arrays is one larger than normal.
|
||||
|
|
112
direwolf.c
112
direwolf.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 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017 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
|
||||
|
@ -94,6 +94,7 @@
|
|||
#include "server.h"
|
||||
#include "kiss.h"
|
||||
#include "kissnet.h"
|
||||
#include "kissserial.h"
|
||||
#include "kiss_frame.h"
|
||||
#include "waypoint.h"
|
||||
#include "gen_tone.h"
|
||||
|
@ -116,6 +117,7 @@
|
|||
#include "morse.h"
|
||||
#include "mheard.h"
|
||||
#include "ax25_link.h"
|
||||
#include "dtime_now.h"
|
||||
|
||||
|
||||
//static int idx_decoded = 0;
|
||||
|
@ -162,8 +164,8 @@ static void __cpuid(int cpuinfo[4], int infotype){
|
|||
|
||||
static struct audio_s audio_config;
|
||||
static struct tt_config_s tt_config;
|
||||
//struct digi_config_s digi_config;
|
||||
//struct cdigi_config_s cdigi_config;
|
||||
static struct misc_config_s misc_config;
|
||||
|
||||
|
||||
static const int audio_amplitude = 100; /* % of audio sample range. */
|
||||
/* This translates to +-32k for 16 bit samples. */
|
||||
|
@ -177,9 +179,6 @@ static int q_d_opt = 0; /* "-q d" Quiet, suppress the printing of decoded of A
|
|||
|
||||
|
||||
|
||||
static struct misc_config_s misc_config;
|
||||
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
int err;
|
||||
|
@ -193,8 +192,10 @@ int main (int argc, char *argv[])
|
|||
struct igate_config_s igate_config;
|
||||
int r_opt = 0, n_opt = 0, b_opt = 0, B_opt = 0, D_opt = 0; /* Command line options. */
|
||||
char P_opt[16];
|
||||
char l_opt[80];
|
||||
char l_opt_logdir[80];
|
||||
char L_opt_logfile[80];
|
||||
char input_file[80];
|
||||
char T_opt_timestamp[40];
|
||||
|
||||
int t_opt = 1; /* Text color option. */
|
||||
int a_opt = 0; /* "-a n" interval, in seconds, for audio statistics report. 0 for none. */
|
||||
|
@ -213,8 +214,10 @@ int main (int argc, char *argv[])
|
|||
int E_tx_opt = 0; /* "-E n" Error rate % for clobbering trasmit frames. */
|
||||
int E_rx_opt = 0; /* "-E Rn" Error rate % for clobbering receive frames. */
|
||||
|
||||
strlcpy(l_opt, "", sizeof(l_opt));
|
||||
strlcpy(l_opt_logdir, "", sizeof(l_opt_logdir));
|
||||
strlcpy(L_opt_logfile, "", sizeof(L_opt_logfile));
|
||||
strlcpy(P_opt, "", sizeof(P_opt));
|
||||
strlcpy(T_opt_timestamp, "", sizeof(T_opt_timestamp));
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
|
@ -259,17 +262,21 @@ 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\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
|
||||
//dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "H", __DATE__);
|
||||
//dw_printf ("Dire Wolf version %d.%d (%s) Beta Test 4\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
|
||||
//dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "C", __DATE__);
|
||||
dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
|
||||
|
||||
#if defined(ENABLE_GPSD) || defined(USE_HAMLIB)
|
||||
|
||||
#if defined(ENABLE_GPSD) || defined(USE_HAMLIB) || defined(USE_CM108)
|
||||
dw_printf ("Includes optional support for: ");
|
||||
#if defined(ENABLE_GPSD)
|
||||
dw_printf (" gpsd");
|
||||
#endif
|
||||
#if defined(USE_HAMLIB)
|
||||
dw_printf (" hamlib");
|
||||
#endif
|
||||
#if defined(USE_CM108)
|
||||
dw_printf (" cm108-ptt");
|
||||
#endif
|
||||
dw_printf ("\n");
|
||||
#endif
|
||||
|
@ -345,7 +352,7 @@ int main (int argc, char *argv[])
|
|||
|
||||
/* ':' following option character means arg is required. */
|
||||
|
||||
c = getopt_long(argc, argv, "P:B:D:c:pxr:b:n:d:q:t:Ul:Sa:E:",
|
||||
c = getopt_long(argc, argv, "P:B:D:c:pxr:b:n:d:q:t:Ul:L:Sa:E:T:",
|
||||
long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
@ -466,7 +473,7 @@ int main (int argc, char *argv[])
|
|||
|
||||
case 'a': server_set_debug(1); break;
|
||||
|
||||
case 'k': d_k_opt++; kiss_serial_set_debug (d_k_opt); break;
|
||||
case 'k': d_k_opt++; kissserial_set_debug (d_k_opt); kisspt_set_debug (d_k_opt); break;
|
||||
case 'n': d_n_opt++; kiss_net_set_debug (d_n_opt); break;
|
||||
|
||||
case 'u': d_u_opt = 1; break;
|
||||
|
@ -521,11 +528,17 @@ int main (int argc, char *argv[])
|
|||
exit (0);
|
||||
break;
|
||||
|
||||
case 'l': /* -l for log file directory name */
|
||||
case 'l': /* -l for log directory with daily files */
|
||||
|
||||
strlcpy (l_opt, optarg, sizeof(l_opt));
|
||||
strlcpy (l_opt_logdir, optarg, sizeof(l_opt_logdir));
|
||||
break;
|
||||
|
||||
case 'L': /* -L for log file name with full path */
|
||||
|
||||
strlcpy (L_opt_logfile, optarg, sizeof(L_opt_logfile));
|
||||
break;
|
||||
|
||||
|
||||
case 'S': /* Print symbol tables and exit. */
|
||||
|
||||
symbols_init ();
|
||||
|
@ -554,6 +567,10 @@ int main (int argc, char *argv[])
|
|||
}
|
||||
break;
|
||||
|
||||
case 'T': /* -T for receive timestamp. */
|
||||
strlcpy (T_opt_timestamp, optarg, sizeof(T_opt_timestamp));
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
/* Should not be here. */
|
||||
|
@ -657,14 +674,27 @@ int main (int argc, char *argv[])
|
|||
audio_config.achan[0].decimate = D_opt;
|
||||
}
|
||||
|
||||
strlcpy(audio_config.timestamp_format, T_opt_timestamp, sizeof(audio_config.timestamp_format));
|
||||
|
||||
// temp - only xmit errors.
|
||||
|
||||
audio_config.xmit_error_rate = E_tx_opt;
|
||||
audio_config.recv_error_rate = E_rx_opt;
|
||||
|
||||
|
||||
if (strlen(l_opt) > 0) {
|
||||
strlcpy (misc_config.logdir, l_opt, sizeof(misc_config.logdir));
|
||||
if (strlen(l_opt_logdir) > 0 && strlen(L_opt_logfile) > 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Logging options -l and -L can't be used together. Pick one or the other.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (strlen(L_opt_logfile) > 0) {
|
||||
misc_config.log_daily_names = 0;
|
||||
strlcpy (misc_config.log_path, L_opt_logfile, sizeof(misc_config.log_path));
|
||||
}
|
||||
else if (strlen(l_opt_logdir) > 0) {
|
||||
misc_config.log_daily_names = 1;
|
||||
strlcpy (misc_config.log_path, l_opt_logdir, sizeof(misc_config.log_path));
|
||||
}
|
||||
|
||||
misc_config.enable_kiss_pt = enable_pseudo_terminal;
|
||||
|
@ -766,7 +796,8 @@ int main (int argc, char *argv[])
|
|||
/*
|
||||
* Create a pseudo terminal and KISS TNC emulator.
|
||||
*/
|
||||
kiss_init (&misc_config);
|
||||
kisspt_init (&misc_config);
|
||||
kissserial_init (&misc_config);
|
||||
kiss_frame_init (&audio_config);
|
||||
|
||||
/*
|
||||
|
@ -782,7 +813,7 @@ int main (int argc, char *argv[])
|
|||
* log the tracker beacon transmissions with fake channel 999.
|
||||
*/
|
||||
|
||||
log_init(misc_config.logdir);
|
||||
log_init(misc_config.log_daily_names, misc_config.log_path);
|
||||
mheard_init (d_m_opt);
|
||||
beacon_init (&audio_config, &misc_config, &igate_config);
|
||||
|
||||
|
@ -879,6 +910,14 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
|
||||
ax25_alevel_to_text (alevel, alevel_text);
|
||||
|
||||
// Experiment: try displaying the DC bias.
|
||||
// Should be 0 for soundcard but could show mistuning with SDR.
|
||||
|
||||
#if 0
|
||||
char bias[16];
|
||||
snprintf (bias, sizeof(bias), " DC%+d", multi_modem_get_dc_average (chan));
|
||||
strlcat (alevel_text, bias, sizeof(alevel_text));
|
||||
#endif
|
||||
|
||||
/* As suggested by KJ4ERJ, if we are receiving from */
|
||||
/* WIDEn-0, it is quite likely (but not guaranteed), that */
|
||||
|
@ -926,9 +965,21 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
|
||||
// -1 for APRStt DTMF decoder.
|
||||
|
||||
char ts[100]; // optional time stamp
|
||||
|
||||
if (strlen(audio_config.timestamp_format) > 0) {
|
||||
char tstmp[100];
|
||||
timestamp_user_format (tstmp, sizeof(tstmp), audio_config.timestamp_format);
|
||||
strlcpy (ts, " ", sizeof(ts)); // space after channel.
|
||||
strlcat (ts, tstmp, sizeof(ts));
|
||||
}
|
||||
else {
|
||||
strlcpy (ts, "", sizeof(ts));
|
||||
}
|
||||
|
||||
if (subchan == -1) {
|
||||
text_color_set(DW_COLOR_REC);
|
||||
dw_printf ("[%d.dtmf] ", chan);
|
||||
dw_printf ("[%d.dtmf%s] ", chan, ts);
|
||||
}
|
||||
else {
|
||||
if (ax25_is_aprs(pp)) {
|
||||
|
@ -939,16 +990,16 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
}
|
||||
|
||||
if (audio_config.achan[chan].num_subchan > 1 && audio_config.achan[chan].num_slicers == 1) {
|
||||
dw_printf ("[%d.%d] ", chan, subchan);
|
||||
dw_printf ("[%d.%d%s] ", chan, subchan, ts);
|
||||
}
|
||||
else if (audio_config.achan[chan].num_subchan == 1 && audio_config.achan[chan].num_slicers > 1) {
|
||||
dw_printf ("[%d.%d] ", chan, slice);
|
||||
dw_printf ("[%d.%d%s] ", chan, slice, ts);
|
||||
}
|
||||
else if (audio_config.achan[chan].num_subchan > 1 && audio_config.achan[chan].num_slicers > 1) {
|
||||
dw_printf ("[%d.%d.%d] ", chan, subchan, slice);
|
||||
dw_printf ("[%d.%d.%d%s] ", chan, subchan, slice, ts);
|
||||
}
|
||||
else {
|
||||
dw_printf ("[%d] ", chan);
|
||||
dw_printf ("[%d%s] ", chan, ts);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -972,7 +1023,7 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
dw_printf ("(%s)", desc);
|
||||
if (ftype == frame_type_U_XID) {
|
||||
struct xid_param_s param;
|
||||
char info2text[100];
|
||||
char info2text[150];
|
||||
|
||||
xid_parse (pinfo, info_len, ¶m, info2text, sizeof(info2text));
|
||||
dw_printf (" %s\n", info2text);
|
||||
|
@ -1078,15 +1129,17 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
|
||||
/* Send to another application if connected. */
|
||||
// TODO: Put a wrapper around this so we only call one function to send by all methods.
|
||||
// We see the same sequence in tt_user.c.
|
||||
|
||||
int flen;
|
||||
unsigned char fbuf[AX25_MAX_PACKET_LEN];
|
||||
|
||||
flen = ax25_pack(pp, fbuf);
|
||||
|
||||
server_send_rec_packet (chan, pp, fbuf, flen);
|
||||
kissnet_send_rec_packet (chan, fbuf, flen);
|
||||
kiss_send_rec_packet (chan, fbuf, flen);
|
||||
server_send_rec_packet (chan, pp, fbuf, flen); // AGW net protocol
|
||||
kissnet_send_rec_packet (chan, KISS_CMD_DATA_FRAME, fbuf, flen, -1); // KISS TCP
|
||||
kissserial_send_rec_packet (chan, KISS_CMD_DATA_FRAME, fbuf, flen, -1); // KISS serial port
|
||||
kisspt_send_rec_packet (chan, KISS_CMD_DATA_FRAME, fbuf, flen, -1); // KISS pseudo terminal
|
||||
|
||||
/*
|
||||
* If it came from DTMF decoder, send it to APRStt gateway.
|
||||
|
@ -1204,7 +1257,7 @@ static void usage (char **argv)
|
|||
dw_printf (" -D n Divide audio sample rate by n for channel 0.\n");
|
||||
dw_printf (" -d Debug options:\n");
|
||||
dw_printf (" a a = AGWPE network protocol client.\n");
|
||||
dw_printf (" k k = KISS serial port client.\n");
|
||||
dw_printf (" k k = KISS serial port or pseudo terminal client.\n");
|
||||
dw_printf (" n n = KISS network client.\n");
|
||||
dw_printf (" u u = Display non-ASCII text in hexadecimal.\n");
|
||||
dw_printf (" p p = dump Packets in hexadecimal.\n");
|
||||
|
@ -1230,6 +1283,7 @@ static void usage (char **argv)
|
|||
dw_printf (" -x Send Xmit level calibration tones.\n");
|
||||
dw_printf (" -U Print UTF-8 test string and exit.\n");
|
||||
dw_printf (" -S Print symbol tables and exit.\n");
|
||||
dw_printf (" -T fmt Time stamp format for sent and received frames.\n");
|
||||
dw_printf ("\n");
|
||||
|
||||
dw_printf ("After any options, there can be a single command line argument for the source of\n");
|
||||
|
|
50
direwolf.h
50
direwolf.h
|
@ -32,7 +32,6 @@
|
|||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Previously, we could handle only a single audio device.
|
||||
* This meant we could have only two radio channels.
|
||||
|
@ -120,6 +119,43 @@
|
|||
#endif
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
// https://groups.yahoo.com/neo/groups/direwolf_packet/conversations/messages/2072
|
||||
|
||||
// The original suggestion was to add this to only ptt.c.
|
||||
// I thought it would make sense to put it here, so it will apply to all files,
|
||||
// consistently, rather than only one file ptt.c.
|
||||
|
||||
// The placement of this is critical. Putting it earlier was a problem.
|
||||
// https://github.com/wb2osz/direwolf/issues/113
|
||||
|
||||
// It needs to be after the include pthread.h because
|
||||
// pthread.h pulls in <sys/cdefs.h>, which redefines __DARWIN_C_LEVEL back to ansi,
|
||||
// which breaks things.
|
||||
// Maybe it should just go in ptt.c as originally suggested.
|
||||
|
||||
// #define __DARWIN_C_LEVEL __DARWIN_C_FULL
|
||||
|
||||
// There is a more involved patch here:
|
||||
// https://groups.yahoo.com/neo/groups/direwolf_packet/conversations/messages/2458
|
||||
|
||||
#ifndef _DARWIN_C_SOURCE
|
||||
#define _DARWIN_C_SOURCE
|
||||
#endif
|
||||
|
||||
// Defining _DARWIN_C_SOURCE ensures that the definition for the cfmakeraw function (or similar)
|
||||
// are pulled in through the include file <sys/termios.h>.
|
||||
|
||||
#ifdef __DARWIN_C_LEVEL
|
||||
#undef __DARWIN_C_LEVEL
|
||||
#endif
|
||||
|
||||
#define __DARWIN_C_LEVEL __DARWIN_C_FULL
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* Not sure where to put these. */
|
||||
|
||||
/* Prefix with DW_ because /usr/include/gps.h uses a couple of these names. */
|
||||
|
@ -213,6 +249,18 @@ typedef pthread_mutex_t dw_mutex_t;
|
|||
|
||||
|
||||
|
||||
// Formerly used write/read on Linux, for some forgotten reason,
|
||||
// but always using send/recv makes more sense.
|
||||
// Need option to prevent a SIGPIPE signal on Linux. (added for 1.5 beta 2)
|
||||
|
||||
#if __WIN32__ || __APPLE__
|
||||
#define SOCK_SEND(s,data,size) send(s,data,size,0)
|
||||
#else
|
||||
#define SOCK_SEND(s,data,size) send(s,data,size, MSG_NOSIGNAL)
|
||||
#endif
|
||||
#define SOCK_RECV(s,data,size) recv(s,data,size,0)
|
||||
|
||||
|
||||
/* Platform differences for string functions. */
|
||||
|
||||
|
||||
|
|
|
@ -1,20 +1,31 @@
|
|||
#%global git_commit b2548ec58f44f4b651626757a166b9f4f18d8000
|
||||
%global git_commit 37179479caf0bf36adf8c9bc0fde641884edaeac
|
||||
%global git_date 20171216
|
||||
|
||||
%global git_short_commit %(echo %{git_commit} | cut -c -8)
|
||||
%global git_suffix %{git_date}git%{git_short_commit}
|
||||
|
||||
Name: direwolf
|
||||
Version: 1.1b1
|
||||
Release: 1%{?dist}
|
||||
Version: 1.5Beta
|
||||
Release: 1.%{git_suffix}%{?dist}
|
||||
Summary: Soundcard based AX.25 TNC
|
||||
|
||||
Group: Applications/Communications
|
||||
License: GPLv2
|
||||
URL: http://home.comcast.net/~wb2osz
|
||||
Source0: http://home.comcast.net/~wb2osz/Version%201.1/direwolf-%{version}.tgz
|
||||
URL: https://github.com/wb2osz/direwolf
|
||||
#Source0: https://github.com/wb2osz/direwolf/archive/%{name}-%{version}.tar.gz
|
||||
Source: %{name}-%{version}-%{git_suffix}.tgz
|
||||
Packager: David Ranch (KI6ZHD) <dranch@trinnet.net>
|
||||
Distribution: RedHat Linux
|
||||
|
||||
Patch0: direwolf-makefile7.patch
|
||||
Patch0: direwolf-1.5-makefile.patch
|
||||
|
||||
BuildRequires: automake
|
||||
BuildRequires: alsa-lib-devel
|
||||
|
||||
#If the gpsd and gpsd-devel packages are installed, Direwolf will add gps support
|
||||
|
||||
|
||||
|
||||
%description
|
||||
Dire Wolf is a software "soundcard" modem/TNC and APRS encoder/decoder. It can
|
||||
|
@ -29,34 +40,64 @@ Linux AX25, SARTrack, RMS Express, and many others.
|
|||
%patch0 -p0
|
||||
|
||||
%build
|
||||
make -f Makefile.linux tocalls-symbols
|
||||
make %{?_smp_mflags} -f Makefile.linux
|
||||
|
||||
make -f Makefile.linux
|
||||
#make -f Makefile.linux tocalls-symbols
|
||||
make %{?_smp_mflags}
|
||||
|
||||
|
||||
%install
|
||||
make -f Makefile.linux install DESTDIR=$RPM_BUILD_ROOT
|
||||
make -f Makefile.linux install-conf DESTDIR=$RPM_BUILD_ROOT
|
||||
make install INSTALLDIR=$RPM_BUILD_ROOT/usr
|
||||
make install-conf INSTALLDIR=$RPM_BUILD_ROOT/usr
|
||||
|
||||
# Install icon
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/pixmaps/
|
||||
cp dw-icon.png ${RPM_BUILD_ROOT}%{_datadir}/pixmaps/
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/pixmaps/direwolf/
|
||||
cp dw-icon.png ${RPM_BUILD_ROOT}%{_datadir}/pixmaps/direwolf/
|
||||
mv symbols-new.txt ${RPM_BUILD_ROOT}%{_docdir}/%{name}/
|
||||
mv symbolsX.txt ${RPM_BUILD_ROOT}%{_docdir}/%{name}/
|
||||
mv tocalls.txt ${RPM_BUILD_ROOT}%{_docdir}/%{name}/
|
||||
desktop-file-install \
|
||||
--dir=${RPM_BUILD_ROOT}%{_datadir}/applications direwolf.desktop
|
||||
#temp bug
|
||||
#non echo "fixing $RPM_BUILD_ROOT/%{_bindir}/bin"
|
||||
#non rm -f $RPM_BUILD_ROOT/usr/bin
|
||||
|
||||
|
||||
%files
|
||||
%{_sysconfdir}/ax25/direwolf.conf
|
||||
%{_sysconfdir}/ax25/sdr.conf
|
||||
%{_sysconfdir}/ax25/telemetry-toolkit/telem-balloon.conf
|
||||
%{_sysconfdir}/ax25/telemetry-toolkit/telem-m0xer-3.txt
|
||||
%{_sysconfdir}/ax25/telemetry-toolkit/telem-volts.conf
|
||||
%{_sysconfdir}/udev/rules.d/99-direwolf-cmedia.rules
|
||||
%{_bindir}/*
|
||||
%{_datadir}/pixmaps/dw-icon.png
|
||||
%{_datadir}/pixmaps/direwolf/dw-icon.png
|
||||
%{_datadir}/applications/%{name}.desktop
|
||||
%{_datadir}/direwolf/*
|
||||
%{_docdir}/%{name}/*
|
||||
%{_docdir}/*
|
||||
%{_mandir}/man1/*
|
||||
|
||||
|
||||
|
||||
%changelog
|
||||
* Sat Dec 16 2017 David Ranch <dranch@trinnet.net> - 1.5-1
|
||||
- New 1.5-Beta version from Git
|
||||
* Sun Apr 2 2017 David Ranch <dranch@trinnet.net> - 1.4-1
|
||||
- New 1.4-Beta1 version from Git
|
||||
* Sun Mar 5 2017 David Ranch <dranch@trinnet.net> - 1.4-1
|
||||
- New 1.4-H Alpha version from Git version
|
||||
* Fri Aug 26 2016 David Ranch <dranch@trinnet.net> - 1.4-1
|
||||
- New version
|
||||
* Fri May 06 2016 David Ranch <dranch@trinnet.net> - 1.3-1
|
||||
- New version
|
||||
* Sat Sep 12 2015 David Ranch <dranch@trinnet.net> - 1.3F-1
|
||||
- New version with new features
|
||||
* Sun May 10 2015 David Ranch <dranch@trinnet.net> - 1.2E-1
|
||||
- New version that supports a PASSALL function
|
||||
- Updated the Makefile.linux patch
|
||||
* Sat Mar 21 2015 David Ranch <dranch@trinnet.net> - 1.2C-1
|
||||
- changed to support different make installation variable
|
||||
* Sat Feb 14 2015 David Ranch <dranch@trinnet.net> - 1.2b-1
|
||||
- new spec file
|
||||
* Sat Dec 20 2014 David Ranch <dranch@trinnet.net> - 1.1b1-1
|
||||
- new spec file
|
||||
|
|
22
direwolf.txt
22
direwolf.txt
|
@ -312,7 +312,7 @@ W# COM3 and the client application will use COM4.
|
|||
W#
|
||||
W# Uncomment following line to use this feature.
|
||||
W
|
||||
W#NULLMODEM COM3
|
||||
W#SERIALKISS COM3
|
||||
W
|
||||
W
|
||||
C#
|
||||
|
@ -434,22 +434,26 @@ C#PBEACON sendto=IG delay=0:30 every=60:00 symbol="igate" overlay=T lat=42^37.14
|
|||
C
|
||||
C
|
||||
C# To relay messages from the Internet to radio, you need to add
|
||||
C# one more option with the transmit channel number and a VIA path.
|
||||
C# one more options with the transmit channel number and a VIA path.
|
||||
C
|
||||
C#IGTXVIA 0 WIDE1-1
|
||||
C
|
||||
C# You might want to apply a filter for what packets will be obtained from the server.
|
||||
C# Read about filters here: http://www.aprs-is.net/javaprsfilter.aspx
|
||||
C# The APRS Internet Server (APRS-IS) has its own idea about what you
|
||||
C# should be transmitting. This includes "messages" addressed to stations
|
||||
C# recently heard in your area. For special situations, you can subscribe
|
||||
C# to
|
||||
C# decrease what you are already subscribed to. This is known as a server
|
||||
C# side filter. Read here: http://www.aprs-is.net/javaprsfilter.aspx
|
||||
C# Example, positions and objects within 50 km of my location:
|
||||
C
|
||||
C#IGFILTER m/50
|
||||
C
|
||||
C# That is known as a server-side filter. It is processed by the IGate server.
|
||||
C# You can also apply local filtering to limit what will be transmitted on the
|
||||
C# RF side. For example, transmit only "messages" on channel 0 and weather
|
||||
C# reports on channel 1.
|
||||
C# Sometimes the server will send you more than you want. You can also apply
|
||||
C# local filtering to limit what will be transmitted on the RF side.
|
||||
C# For example, transmit only "messages" (which is the default) on channel 0
|
||||
C# and weather reports on channel 1.
|
||||
C
|
||||
C#FILTER IG 0 t/m
|
||||
C#FILTER IG 0 i/30
|
||||
C#FILTER IG 1 t/wn
|
||||
C
|
||||
C# Finally, we don't want to flood the radio channel.
|
||||
|
|
47
dlq.c
47
dlq.c
|
@ -767,6 +767,53 @@ void dlq_channel_busy (int chan, int activity, int status)
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: dlq_seize_confirm
|
||||
*
|
||||
* Purpose: Inform data link state machine that the transmitter is on.
|
||||
* This is in reponse to lm_seize_request.
|
||||
*
|
||||
* Inputs: chan - Radio channel number.
|
||||
*
|
||||
* Outputs: Request is appended to queue for processing by
|
||||
* the data link state machine.
|
||||
*
|
||||
* Description: When removed from the data link state machine queue, this
|
||||
* becomes lm_seize_confirm.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
void dlq_seize_confirm (int chan)
|
||||
{
|
||||
struct dlq_item_s *pnew;
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("dlq_seize_confirm (chan=%d)\n", chan);
|
||||
#endif
|
||||
|
||||
|
||||
/* Allocate a new queue item. */
|
||||
|
||||
pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
|
||||
s_new_count++;
|
||||
|
||||
pnew->type = DLQ_SEIZE_CONFIRM;
|
||||
pnew->chan = chan;
|
||||
|
||||
/* Put it into queue. */
|
||||
|
||||
append_to_queue (pnew);
|
||||
|
||||
|
||||
} /* end dlq_seize_confirm */
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: dlq_client_cleanup
|
||||
|
|
4
dlq.h
4
dlq.h
|
@ -35,7 +35,7 @@ typedef struct cdata_s {
|
|||
|
||||
/* Types of things that can be in queue. */
|
||||
|
||||
typedef enum dlq_type_e {DLQ_REC_FRAME, DLQ_CONNECT_REQUEST, DLQ_DISCONNECT_REQUEST, DLQ_XMIT_DATA_REQUEST, DLQ_REGISTER_CALLSIGN, DLQ_UNREGISTER_CALLSIGN, DLQ_CHANNEL_BUSY, DLQ_CLIENT_CLEANUP} dlq_type_t;
|
||||
typedef enum dlq_type_e {DLQ_REC_FRAME, DLQ_CONNECT_REQUEST, DLQ_DISCONNECT_REQUEST, DLQ_XMIT_DATA_REQUEST, DLQ_REGISTER_CALLSIGN, DLQ_UNREGISTER_CALLSIGN, DLQ_CHANNEL_BUSY, DLQ_SEIZE_CONFIRM, DLQ_CLIENT_CLEANUP} dlq_type_t;
|
||||
|
||||
|
||||
/* A queue item. */
|
||||
|
@ -116,6 +116,8 @@ void dlq_unregister_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client
|
|||
|
||||
void dlq_channel_busy (int chan, int activity, int status);
|
||||
|
||||
void dlq_seize_confirm (int chan);
|
||||
|
||||
void dlq_client_cleanup (int client);
|
||||
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,4 +1,4 @@
|
|||
# Documentation for Dire Wolf #
|
||||
# Documentation for Dire Wolf #
|
||||
|
||||
Click on the document name to view in your web browser or the link following to download the PDF file.
|
||||
|
||||
|
@ -25,6 +25,10 @@ These dive into more detail for specialized topics or typical usage scenarios.
|
|||
|
||||
This explains how it all works, proper configuration, and troubleshooting.
|
||||
|
||||
- [**Bluetooth KISS TNC**](Bluetooth-KISS-TNC.pdf) [ [*download*](../../../raw/master/doc/Bluetooth-KISS-TNC.pdf) ]
|
||||
|
||||
Eliminate the cable between your TNC and application. Use Bluetooth instead.
|
||||
|
||||
- [**APRStt Implementation Notes**](APRStt-Implementation-Notes.pdf) [ [*download*](../../../raw/master/doc/APRStt-Implementation-Notes.pdf) ]
|
||||
|
||||
Very few hams have portable equipment for APRS but nearly everyone has a handheld radio that can send DTMF tones. APRStt allows a user, equipped with only DTMF (commonly known as Touch Tone) generation capability, to enter information into the global APRS data network.
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
168
dtime_now.c
168
dtime_now.c
|
@ -1,5 +1,8 @@
|
|||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "textcolor.h"
|
||||
#include "dtime_now.h"
|
||||
|
||||
|
@ -17,8 +20,28 @@
|
|||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include <string.h> // needed for Mac.
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: dtime_now
|
||||
*
|
||||
* Purpose: Return current time as double precision.
|
||||
*
|
||||
* Input: none
|
||||
*
|
||||
* Returns: Unix time, as double precision, so we can get resolution
|
||||
* finer than one second.
|
||||
*
|
||||
* Description: Normal unix time is in seconds since 1/1/1970 00:00:00 UTC.
|
||||
* Sometimes we want resolution finer than a second.
|
||||
* Rather than having a separate variable for the fractional
|
||||
* part of a second, and having extra calculations everywhere,
|
||||
* simply use double precision floating point to make usage
|
||||
* easier.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
|
||||
double dtime_now (void)
|
||||
|
@ -59,3 +82,148 @@ double dtime_now (void)
|
|||
|
||||
return (result);
|
||||
}
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
/*
|
||||
* Windows doesn't have localtime_r.
|
||||
* It should have the equivalent localtime_s, with opposite parameter
|
||||
* order, but I get undefined reference when trying to use it.
|
||||
*/
|
||||
|
||||
static struct tm *localtime_r(time_t *clock, struct tm *res)
|
||||
{
|
||||
struct tm *tm;
|
||||
|
||||
tm = localtime (clock);
|
||||
memcpy (res, tm, sizeof(struct tm));
|
||||
return (res);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: timestamp_now
|
||||
*
|
||||
* Purpose: Convert local time to one of these formats for debug output.
|
||||
*
|
||||
* HH:MM:SS
|
||||
* HH:MM:SS.mmm
|
||||
*
|
||||
* Input: result_size - Size of result location.
|
||||
* Should be at least 9 or 13.
|
||||
*
|
||||
* show_ms - True to display milliseconds.
|
||||
*
|
||||
* Output: result - Result is placed here.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
void timestamp_now (char *result, int result_size, int show_ms)
|
||||
{
|
||||
double now = dtime_now();
|
||||
time_t t = (int)now;
|
||||
struct tm tm;
|
||||
|
||||
localtime_r (&t, &tm);
|
||||
strftime (result, result_size, "%H:%M:%S", &tm);
|
||||
|
||||
if (show_ms) {
|
||||
int ms = (now - (int)t) * 1000;
|
||||
char strms[16];
|
||||
|
||||
if (ms == 1000) ms = 999;
|
||||
sprintf (strms, ".%03d", ms);
|
||||
strlcat (result, strms, result_size);
|
||||
}
|
||||
|
||||
} /* end timestamp_now */
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: timestamp_user_format
|
||||
*
|
||||
* Purpose: Convert local time user-specified format. e.g.
|
||||
*
|
||||
* HH:MM:SS
|
||||
* mm/dd/YYYY HH:MM:SS
|
||||
* dd/mm/YYYY HH:MM:SS
|
||||
*
|
||||
* Input: result_size - Size of result location.
|
||||
*
|
||||
* user_format - See strftime documentation.
|
||||
*
|
||||
* https://linux.die.net/man/3/strftime
|
||||
* https://msdn.microsoft.com/en-us/library/aa272978(v=vs.60).aspx
|
||||
*
|
||||
* Note that Windows does not support all of the Linux formats.
|
||||
* For example, Linux has %T which is equivalent to %H:%M:%S
|
||||
*
|
||||
* Output: result - Result is placed here.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
void timestamp_user_format (char *result, int result_size, char *user_format)
|
||||
{
|
||||
double now = dtime_now();
|
||||
time_t t = (int)now;
|
||||
struct tm tm;
|
||||
|
||||
localtime_r (&t, &tm);
|
||||
strftime (result, result_size, user_format, &tm);
|
||||
|
||||
} /* end timestamp_user_format */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: timestamp_filename
|
||||
*
|
||||
* Purpose: Generate unique file name based on the current time.
|
||||
* The format will be:
|
||||
*
|
||||
* YYYYMMDD-HHMMSS-mmm
|
||||
*
|
||||
* Input: result_size - Size of result location.
|
||||
* Should be at least 20.
|
||||
*
|
||||
* Output: result - Result is placed here.
|
||||
*
|
||||
* Description: This is for the kissutil "-r" option which places
|
||||
* each received frame in a new file. It is possible to
|
||||
* have two packets arrive in less than a second so we
|
||||
* need more than one second resolution.
|
||||
*
|
||||
* What if someone wants UTC, rather than local time?
|
||||
* You can simply set an environment variable like this:
|
||||
*
|
||||
* TZ=UTC direwolf
|
||||
*
|
||||
* so it's probably not worth the effort to add another
|
||||
* option.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
void timestamp_filename (char *result, int result_size)
|
||||
{
|
||||
double now = dtime_now();
|
||||
time_t t = (int)now;
|
||||
struct tm tm;
|
||||
|
||||
localtime_r (&t, &tm);
|
||||
strftime (result, result_size, "%Y%m%d-%H%M%S", &tm);
|
||||
|
||||
int ms = (now - (int)t) * 1000;
|
||||
char strms[16];
|
||||
|
||||
if (ms == 1000) ms = 999;
|
||||
sprintf (strms, "-%03d", ms);
|
||||
strlcat (result, strms, result_size);
|
||||
|
||||
} /* end timestamp_filename */
|
||||
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
|
||||
|
||||
extern double dtime_now (void);
|
||||
|
||||
void timestamp_now (char *result, int result_size, int show_ms);
|
||||
|
||||
void timestamp_user_format (char *result, int result_size, char *user_format);
|
||||
|
||||
void timestamp_filename (char *result, int result_size);
|
16
fcs_calc.c
16
fcs_calc.c
|
@ -87,6 +87,22 @@ unsigned short fcs_calc (unsigned char *data, int len)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* CRC is also used for duplicate checking for the digipeater and IGate.
|
||||
* A packet is considered a duplicate if the source, destination, and
|
||||
* information parts match. In other words, we ignore the via path
|
||||
* which changes along the way.
|
||||
* Rather than keeping a variable length string we just keep a 16 bit
|
||||
* CRC which takes less memory and processing to compare.
|
||||
*
|
||||
* This can result in occasional false matches. If we had a random
|
||||
* 16 bit number, there is a 1/65536 ( = 0.0015 % ) chance that it will
|
||||
* match and we will drop something that should be passed along.
|
||||
*
|
||||
* Looking at it another way, there is a 0.9999847412109375 (out of 1)
|
||||
* probability of doing the right thing.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This can be used when we want to calculate a single CRC over disjoint data.
|
||||
*
|
||||
|
|
|
@ -84,7 +84,8 @@
|
|||
static int seed = 1;
|
||||
|
||||
static int my_rand (void) {
|
||||
seed = ((seed * 1103515245) + 12345) & MY_RAND_MAX;
|
||||
// Perform the calculation as unsigned to avoid signed overflow error.
|
||||
seed = (int)(((unsigned)seed * 1103515245) + 12345) & MY_RAND_MAX;
|
||||
return (seed);
|
||||
}
|
||||
|
||||
|
@ -254,7 +255,7 @@ int main(int argc, char **argv)
|
|||
modem.achan[0].baud = atoi(optarg);
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Data rate set to %d bits / second.\n", modem.achan[0].baud);
|
||||
if (modem.achan[0].baud < MIN_BAUD || modem.achan[0].baud > MAX_BAUD) {
|
||||
if (modem.achan[0].baud != 100 && (modem.achan[0].baud < MIN_BAUD || modem.achan[0].baud > MAX_BAUD)) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Use a more reasonable bit rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD);
|
||||
exit (EXIT_FAILURE);
|
||||
|
@ -263,7 +264,12 @@ int main(int argc, char **argv)
|
|||
/* We have similar logic in direwolf.c, config.c, gen_packets.c, and atest.c, */
|
||||
/* that need to be kept in sync. Maybe it could be a common function someday. */
|
||||
|
||||
if (modem.achan[0].baud < 600) {
|
||||
if (modem.achan[0].baud == 100) {
|
||||
modem.achan[0].modem_type = MODEM_AFSK;
|
||||
modem.achan[0].mark_freq = 1615;
|
||||
modem.achan[0].space_freq = 1785;
|
||||
}
|
||||
else if (modem.achan[0].baud < 600) {
|
||||
modem.achan[0].modem_type = MODEM_AFSK;
|
||||
modem.achan[0].mark_freq = 1600; // Typical for HF SSB
|
||||
modem.achan[0].space_freq = 1800;
|
||||
|
@ -446,7 +452,7 @@ int main(int argc, char **argv)
|
|||
|
||||
if (strlen(output_file) == 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR: The -o ouput file option must be specified.\n");
|
||||
dw_printf ("ERROR: The -o output file option must be specified.\n");
|
||||
usage (argv);
|
||||
exit (1);
|
||||
}
|
||||
|
@ -619,7 +625,7 @@ int main(int argc, char **argv)
|
|||
*/
|
||||
for (i = 1; i <= packet_count; i++) {
|
||||
|
||||
char stemp[80];
|
||||
char stemp[88];
|
||||
|
||||
if (modem.achan[0].baud < 600) {
|
||||
/* e.g. 300 bps AFSK - About 2/3 should be decoded properly. */
|
||||
|
|
|
@ -868,7 +868,7 @@ long Convert_MGRS_To_Geodetic (char* MGRS,
|
|||
*/
|
||||
{ /* Convert_MGRS_To_Geodetic */
|
||||
long zone;
|
||||
char hemisphere;
|
||||
char hemisphere = '?';
|
||||
double easting;
|
||||
double northing;
|
||||
long zone_exists;
|
||||
|
@ -1260,9 +1260,9 @@ long Convert_MGRS_To_UPS ( char *MGRS,
|
|||
double false_northing; /* False northing for 3rd letter */
|
||||
double grid_easting; /* easting for 100,000 meter grid square */
|
||||
double grid_northing; /* northing for 100,000 meter grid square */
|
||||
long zone;
|
||||
long zone = 0;
|
||||
long letters[MGRS_LETTERS];
|
||||
long in_precision;
|
||||
long in_precision = 0;
|
||||
int index = 0;
|
||||
long error_code = MGRS_NO_ERROR;
|
||||
|
||||
|
|
|
@ -782,7 +782,7 @@ long Convert_USNG_To_Geodetic (char* USNG,
|
|||
*/
|
||||
{ /* Convert_USNG_To_Geodetic */
|
||||
long zone;
|
||||
char hemisphere;
|
||||
char hemisphere = '?';
|
||||
double easting;
|
||||
double northing;
|
||||
long zone_exists;
|
||||
|
@ -1178,9 +1178,9 @@ long Convert_USNG_To_UPS ( char *USNG,
|
|||
double false_northing; /* False northing for 3rd letter */
|
||||
double grid_easting; /* easting for 100,000 meter grid square */
|
||||
double grid_northing; /* northing for 100,000 meter grid square */
|
||||
long zone;
|
||||
long zone = 0;
|
||||
long letters[USNG_LETTERS];
|
||||
long in_precision;
|
||||
long in_precision = 0;
|
||||
int index = 0;
|
||||
long error_code = USNG_NO_ERROR;
|
||||
|
||||
|
|
64
igate.c
64
igate.c
|
@ -33,6 +33,9 @@
|
|||
* APRS iGate properties
|
||||
* http://wiki.ham.fi/APRS_iGate_properties
|
||||
*
|
||||
* Notes to iGate developers
|
||||
* https://github.com/hessu/aprsc/blob/master/doc/IGATE-HINTS.md#igates-dropping-duplicate-packets-unnecessarily
|
||||
*
|
||||
* SATgate mode.
|
||||
* http://www.tapr.org/pipermail/aprssig/2016-January/045283.html
|
||||
*
|
||||
|
@ -1263,7 +1266,7 @@ static void send_msg_to_server (const char *imsg, int imsg_len)
|
|||
|
||||
|
||||
#if __WIN32__
|
||||
err = send (igate_sock, stemp, stemp_len, 0);
|
||||
err = SOCK_SEND (igate_sock, stemp, stemp_len);
|
||||
if (err == SOCKET_ERROR)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
|
@ -1274,7 +1277,7 @@ static void send_msg_to_server (const char *imsg, int imsg_len)
|
|||
WSACleanup();
|
||||
}
|
||||
#else
|
||||
err = write (igate_sock, stemp, stemp_len);
|
||||
err = SOCK_SEND (igate_sock, stemp, stemp_len);
|
||||
if (err <= 0)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
|
@ -1316,11 +1319,7 @@ static int get1ch (void)
|
|||
// TODO: might read complete packets and unpack from own buffer
|
||||
// rather than using a system call for each byte.
|
||||
|
||||
#if __WIN32__
|
||||
n = recv (igate_sock, (char*)(&ch), 1, 0);
|
||||
#else
|
||||
n = read (igate_sock, &ch, 1);
|
||||
#endif
|
||||
n = SOCK_RECV (igate_sock, (char*)(&ch), 1);
|
||||
|
||||
if (n == 1) {
|
||||
#if DEBUG9
|
||||
|
@ -1959,9 +1958,37 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan)
|
|||
* There is a 1 / 65536 chance of getting a false positive match
|
||||
* which is good enough for this application.
|
||||
*
|
||||
*
|
||||
* Original thinking:
|
||||
*
|
||||
* Occasionally someone will get on one of the discussion groups and say:
|
||||
* I don't think my IGate is working. I look at packets, from local stations,
|
||||
* on aprs.fi or findu.com, and they are always through some other IGate station,
|
||||
* never mine.
|
||||
* Then someone has to explain, this is not a valid strategy for analyzing
|
||||
* everything going thru the network. The APRS-IS servers drop duplicate
|
||||
* packets (ignoring the via path) within a 30 second period. If some
|
||||
* other IGate gets the same thing there a millisecond faster than you,
|
||||
* the one you send is discarded.
|
||||
* In this scenario, it would make sense to perform additional duplicate
|
||||
* suppression before forwarding RF packets to the Server.
|
||||
* I don't recall if I saw some specific recommendation to do this or if
|
||||
* it just seemed like the obvious thing to do to avoid sending useless
|
||||
* stuff that would just be discarded anyhow. It seems others came to the
|
||||
* same conclusion. http://www.tapr.org/pipermail/aprssig/2016-July/045907.html
|
||||
*
|
||||
* Version 1.5: Rethink strategy.
|
||||
*
|
||||
* Issue 85, https://github.com/wb2osz/direwolf/issues/85 ,
|
||||
* got me thinking about this some more. Sending more information will
|
||||
* allow the APRS-IS servers to perform future additional network analysis.
|
||||
* To make a long story short, the RF>IS direction duplicate checking
|
||||
* is now disabled. The code is still there in case I change my mind
|
||||
* and want to add a configuration option to allow it. The dedupe
|
||||
* time is set to 0 which means don't do the checking.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
#define RX2IG_DEDUPE_TIME 60 /* Do not send duplicate within 60 seconds. */
|
||||
#define RX2IG_HISTORY_MAX 30 /* Remember the last 30 sent to IGate server. */
|
||||
|
||||
static int rx2ig_insert_next;
|
||||
|
@ -1982,6 +2009,12 @@ static void rx_to_ig_init (void)
|
|||
static void rx_to_ig_remember (packet_t pp)
|
||||
{
|
||||
|
||||
// No need to save the information if we are not doing duplicate checking.
|
||||
|
||||
if (save_igate_config_p->rx2ig_dedupe_time == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
rx2ig_time_stamp[rx2ig_insert_next] = time(NULL);
|
||||
rx2ig_checksum[rx2ig_insert_next] = ax25_dedupe_crc(pp);
|
||||
|
||||
|
@ -2031,8 +2064,21 @@ static int rx_to_ig_allow (packet_t pp)
|
|||
dw_printf ("rx_to_ig_allow? %d \"%s>%s:%s\"\n", crc, src, dest, pinfo);
|
||||
}
|
||||
|
||||
|
||||
// Do we have duplicate checking at all in the RF>IS direction?
|
||||
|
||||
if (save_igate_config_p->rx2ig_dedupe_time == 0) {
|
||||
if (s_debug >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("rx_to_ig_allow? YES, no dedupe checking\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Yes, check for duplicates within certain time.
|
||||
|
||||
for (j=0; j<RX2IG_HISTORY_MAX; j++) {
|
||||
if (rx2ig_checksum[j] == crc && rx2ig_time_stamp[j] >= now - RX2IG_DEDUPE_TIME) {
|
||||
if (rx2ig_checksum[j] == crc && rx2ig_time_stamp[j] >= now - save_igate_config_p->rx2ig_dedupe_time) {
|
||||
if (s_debug >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
// could be multiple entries and this might not be the most recent.
|
||||
|
|
12
igate.h
12
igate.h
|
@ -39,6 +39,9 @@ struct igate_config_s {
|
|||
char t2_passcode[8]; /* Max. 5 digits. Could be "-1". */
|
||||
|
||||
char *t2_filter; /* Optional filter for IS -> RF direction. */
|
||||
/* This is the "server side" filter. */
|
||||
/* A better name would be subscription or something */
|
||||
/* like that because we can only ask for more. */
|
||||
|
||||
/*
|
||||
* For transmitting.
|
||||
|
@ -69,6 +72,11 @@ struct igate_config_s {
|
|||
/* We allow additional flexibility of 0 to disable feature */
|
||||
/* or a small number to allow more. */
|
||||
|
||||
/*
|
||||
* Receiver to IS data options.
|
||||
*/
|
||||
int rx2ig_dedupe_time; /* seconds. 0 to disable. */
|
||||
|
||||
/*
|
||||
* Special SATgate mode to delay packets heard directly.
|
||||
*/
|
||||
|
@ -82,6 +90,10 @@ struct igate_config_s {
|
|||
#define IGATE_TX_LIMIT_5_DEFAULT 20
|
||||
#define IGATE_TX_LIMIT_5_MAX 80
|
||||
|
||||
#define IGATE_RX2IG_DEDUPE_TIME 0 /* Issue 85. 0 means disable dupe checking in RF>IS direction. */
|
||||
/* See comments in rx_to_ig_remember & rx_to_ig_allow. */
|
||||
/* Currently there is no configuration setting to change this. */
|
||||
|
||||
#define DEFAULT_SATGATE_DELAY 10
|
||||
#define MIN_SATGATE_DELAY 5
|
||||
#define MAX_SATGATE_DELAY 30
|
||||
|
|
619
kiss.c
619
kiss.c
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2011, 2013, 2014, 2016 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2013, 2014, 2016, 2017 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
|
||||
|
@ -24,11 +24,7 @@
|
|||
* Module: kiss.c
|
||||
*
|
||||
* Purpose: Act as a virtual KISS TNC for use by other packet radio applications.
|
||||
* On Windows, it is a serial port. On Linux, a pseudo terminal.
|
||||
*
|
||||
* Input:
|
||||
*
|
||||
* Outputs:
|
||||
* This file implements it with a pseudo terminal for Linux only.
|
||||
*
|
||||
* Description: It implements the KISS TNC protocol as described in:
|
||||
* http://www.ka9q.net/papers/kiss.html
|
||||
|
@ -49,94 +45,82 @@
|
|||
*
|
||||
* Commands from application recognized:
|
||||
*
|
||||
* 0 Data Frame AX.25 frame in raw format.
|
||||
* _0 Data Frame AX.25 frame in raw format.
|
||||
*
|
||||
* 1 TXDELAY See explanation in xmit.c.
|
||||
* _1 TXDELAY See explanation in xmit.c.
|
||||
*
|
||||
* 2 Persistence " "
|
||||
* _2 Persistence " "
|
||||
*
|
||||
* 3 SlotTime " "
|
||||
* _3 SlotTime " "
|
||||
*
|
||||
* 4 TXtail " "
|
||||
* _4 TXtail " "
|
||||
* Spec says it is obsolete but Xastir
|
||||
* sends it and we respect it.
|
||||
*
|
||||
* 5 FullDuplex Ignored. Always full duplex.
|
||||
* _5 FullDuplex Ignored.
|
||||
*
|
||||
* 6 SetHardware TNC specific. Ignored.
|
||||
* _6 SetHardware TNC specific.
|
||||
*
|
||||
* FF Return Exit KISS mode. Ignored.
|
||||
*
|
||||
*
|
||||
* Messages sent to client application:
|
||||
*
|
||||
* 0 Data Frame Received AX.25 frame in raw format.
|
||||
*
|
||||
* _0 Data Frame Received AX.25 frame in raw format.
|
||||
*
|
||||
*
|
||||
* Platform differences:
|
||||
*
|
||||
* We can use a pseudo terminal for Linux or Cygwin applications.
|
||||
* However, Microsoft Windows doesn't seem to have similar functionality.
|
||||
* Native Windows applications expect to see a device named COM1,
|
||||
* COM2, COM3, or COM4. Some might offer more flexibility but others
|
||||
* might be limited to these four choices.
|
||||
*
|
||||
* The documentation instucts the user to install the com0com
|
||||
* "Null-modem emulator" from http://sourceforge.net/projects/com0com/
|
||||
* and configure it for COM3 & COM4.
|
||||
*
|
||||
* By default Dire Wolf will use COM3 (/dev/ttyS2 or /dev/com3 - lower case!)
|
||||
* and the client application will use COM4 (available as /dev/ttyS or
|
||||
* /dev/com4 for Cygwin applications).
|
||||
*
|
||||
*
|
||||
* This can get confusing.
|
||||
*
|
||||
* If __WIN32__ is defined,
|
||||
* We use the Windows interface to the specfied serial port.
|
||||
* This could be a real serial port or the nullmodem driver
|
||||
* connected to another application.
|
||||
*
|
||||
* If __CYGWIN__ is defined,
|
||||
* We connect to a serial port as in the previous case but
|
||||
* use the Linux I/O interface.
|
||||
* We also supply a pseudo terminal for any Cygwin applications
|
||||
* such as Xastir so the null modem is not needed.
|
||||
*
|
||||
* For the Linux case,
|
||||
* We supply a pseudo terminal for use by other applications.
|
||||
*
|
||||
*
|
||||
* Reference: http://www.robbayer.com/files/serial-win.pdf
|
||||
* Version 1.5: Split serial port version off into its own file.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
|
||||
#if __WIN32__ // Stub for Windows.
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "kiss.h"
|
||||
|
||||
void kisspt_init (struct misc_config_s *mc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void kisspt_set_debug (int n)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void kisspt_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, int client)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#else // Rest of file is for Linux only.
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if __WIN32__
|
||||
#include <stdlib.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
#include <errno.h>
|
||||
#else
|
||||
#include <sys/errno.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#include "tq.h"
|
||||
#include "ax25_pad.h"
|
||||
|
@ -146,71 +130,38 @@
|
|||
#include "xmit.h"
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
typedef HANDLE MYFDTYPE;
|
||||
#define MYFDERROR INVALID_HANDLE_VALUE
|
||||
#else
|
||||
typedef int MYFDTYPE;
|
||||
#define MYFDERROR (-1)
|
||||
#endif
|
||||
/*
|
||||
* Accumulated KISS frame and state of decoder.
|
||||
*/
|
||||
|
||||
|
||||
static kiss_frame_t kf; /* Accumulated KISS frame and state of decoder. */
|
||||
static kiss_frame_t kf;
|
||||
|
||||
|
||||
/*
|
||||
* These are for a Linux/Cygwin pseudo terminal.
|
||||
* These are for a Linux pseudo terminal.
|
||||
*/
|
||||
|
||||
#if ! __WIN32__
|
||||
|
||||
static MYFDTYPE pt_master_fd = MYFDERROR; /* File descriptor for my end. */
|
||||
static int pt_master_fd = -1; /* File descriptor for my end. */
|
||||
|
||||
static char pt_slave_name[32]; /* Pseudo terminal slave name */
|
||||
/* like /dev/pts/999 */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Symlink to pseudo terminal name which changes.
|
||||
*/
|
||||
|
||||
#define TMP_KISSTNC_SYMLINK "/tmp/kisstnc"
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is for native Windows applications and a virtual null modem.
|
||||
*/
|
||||
|
||||
#if __CYGWIN__ || __WIN32__
|
||||
|
||||
static MYFDTYPE nullmodem_fd = MYFDERROR;
|
||||
|
||||
#endif
|
||||
static void * kisspt_listen_thread (void *arg);
|
||||
|
||||
|
||||
// TODO: define in one place, use everywhere.
|
||||
#if __WIN32__
|
||||
#define THREAD_F unsigned __stdcall
|
||||
#else
|
||||
#define THREAD_F void *
|
||||
#endif
|
||||
static int kisspt_debug = 0; /* Print information flowing from and to client. */
|
||||
|
||||
static THREAD_F kiss_listen_thread (void *arg);
|
||||
|
||||
|
||||
|
||||
#if DEBUG9
|
||||
static FILE *log_fp;
|
||||
#endif
|
||||
|
||||
|
||||
static int kiss_debug = 0; /* Print information flowing from and to client. */
|
||||
|
||||
void kiss_serial_set_debug (int n)
|
||||
void kisspt_set_debug (int n)
|
||||
{
|
||||
kiss_debug = n;
|
||||
kisspt_debug = n;
|
||||
}
|
||||
|
||||
|
||||
|
@ -224,12 +175,12 @@ void hex_dump (unsigned char *p, int len);
|
|||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: kiss_init
|
||||
* Name: kisspt_init
|
||||
*
|
||||
* Purpose: Set up a pseudo terminal acting as a virtual KISS TNC.
|
||||
*
|
||||
*
|
||||
* Inputs: mc->nullmodem - name of device for our end of nullmodem.
|
||||
* Inputs:
|
||||
*
|
||||
* Outputs:
|
||||
*
|
||||
|
@ -240,43 +191,28 @@ void hex_dump (unsigned char *p, int len);
|
|||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
#if __WIN32__
|
||||
static MYFDTYPE kiss_open_nullmodem (char *device);
|
||||
#else
|
||||
static MYFDTYPE kiss_open_pt (void);
|
||||
#endif
|
||||
static int kisspt_open_pt (void);
|
||||
|
||||
|
||||
void kiss_init (struct misc_config_s *mc)
|
||||
void kisspt_init (struct misc_config_s *mc)
|
||||
{
|
||||
|
||||
#if __WIN32__
|
||||
HANDLE kiss_nullmodem_listen_th;
|
||||
#else
|
||||
pthread_t kiss_pterm_listen_tid;
|
||||
//pthread_t kiss_nullmodem_listen_tid;
|
||||
int e;
|
||||
#endif
|
||||
|
||||
memset (&kf, 0, sizeof(kf));
|
||||
|
||||
/*
|
||||
* This reads messages from client.
|
||||
*/
|
||||
|
||||
#if ! __WIN32__
|
||||
|
||||
/*
|
||||
* Pseudo terminal for Cygwin and Linux versions.
|
||||
*/
|
||||
pt_master_fd = MYFDERROR;
|
||||
pt_master_fd = -1;
|
||||
|
||||
if (mc->enable_kiss_pt) {
|
||||
|
||||
pt_master_fd = kiss_open_pt ();
|
||||
pt_master_fd = kisspt_open_pt ();
|
||||
|
||||
if (pt_master_fd != MYFDERROR) {
|
||||
e = pthread_create (&kiss_pterm_listen_tid, (pthread_attr_t*)NULL, kiss_listen_thread, NULL);
|
||||
if (pt_master_fd != -1) {
|
||||
e = pthread_create (&kiss_pterm_listen_tid, (pthread_attr_t*)NULL, kisspt_listen_thread, NULL);
|
||||
if (e != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
perror("Could not create kiss listening thread for Linux pseudo terminal");
|
||||
|
@ -284,75 +220,25 @@ void kiss_init (struct misc_config_s *mc)
|
|||
}
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Use -p command line option to enable KISS pseudo terminal.\n");
|
||||
//text_color_set(DW_COLOR_INFO);
|
||||
//dw_printf ("Use -p command line option to enable KISS pseudo terminal.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __CYGWIN__ || __WIN32
|
||||
|
||||
/*
|
||||
* Cygwin and native Windows versions have serial port connection.
|
||||
*/
|
||||
if (strlen(mc->nullmodem) > 0) {
|
||||
|
||||
#if ! __WIN32__
|
||||
|
||||
/* Translate Windows device name into Linux name. */
|
||||
/* COM1 -> /dev/ttyS0, etc. */
|
||||
|
||||
if (strncasecmp(mc->nullmodem, "COM", 3) == 0) {
|
||||
int n = atoi (mc->nullmodem + 3);
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Converted nullmodem device '%s'", mc->nullmodem);
|
||||
if (n < 1) n = 1;
|
||||
snprintf (mc->nullmodem, sizeof(mc->nullmodem), "/dev/ttyS%d", n-1);
|
||||
dw_printf (" to Linux equivalent '%s'\n", mc->nullmodem);
|
||||
}
|
||||
#endif
|
||||
nullmodem_fd = kiss_open_nullmodem (mc->nullmodem);
|
||||
|
||||
if (nullmodem_fd != MYFDERROR) {
|
||||
#if __WIN32__
|
||||
kiss_nullmodem_listen_th = (HANDLE)_beginthreadex (NULL, 0, kiss_listen_thread, NULL, 0, NULL);
|
||||
if (kiss_nullmodem_listen_th == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Could not create kiss nullmodem thread\n");
|
||||
return;
|
||||
}
|
||||
#else
|
||||
e = pthread_create (&kiss_nullmodem_listen_tid, NULL, kiss_listen_thread, NULL);
|
||||
if (e != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
perror("Could not create kiss listening thread for Windows virtual COM port.");
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if DEBUG
|
||||
text_color_set (DW_COLOR_DEBUG);
|
||||
#if ! __WIN32__
|
||||
dw_printf ("end of kiss_init: pt_master_fd = %d\n", pt_master_fd);
|
||||
#endif
|
||||
#if __CYGWIN__ || __WIN32__
|
||||
dw_printf ("end of kiss_init: nullmodem_fd = %d\n", nullmodem_fd);
|
||||
|
||||
dw_printf ("end of kisspt_init: pt_master_fd = %d\n", pt_master_fd);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns fd for master side of pseudo terminal or MYFDERROR for error.
|
||||
* Returns fd for master side of pseudo terminal or -1 for error.
|
||||
*/
|
||||
|
||||
#if ! __WIN32__
|
||||
|
||||
static MYFDTYPE kiss_open_pt (void)
|
||||
static int kisspt_open_pt (void)
|
||||
{
|
||||
int fd;
|
||||
char *pts;
|
||||
|
@ -362,19 +248,18 @@ static MYFDTYPE kiss_open_pt (void)
|
|||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("kiss_open_pt ( )\n");
|
||||
dw_printf ("kisspt_open_pt ( )\n");
|
||||
#endif
|
||||
|
||||
|
||||
fd = posix_openpt(O_RDWR|O_NOCTTY);
|
||||
|
||||
if (fd == MYFDERROR
|
||||
|| grantpt (fd) == MYFDERROR
|
||||
|| unlockpt (fd) == MYFDERROR
|
||||
if (fd == -1
|
||||
|| grantpt (fd) == -1
|
||||
|| unlockpt (fd) == -1
|
||||
|| (pts = ptsname (fd)) == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR - Could not create pseudo terminal for KISS TNC.\n");
|
||||
return (MYFDERROR);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
strlcpy (pt_slave_name, pts, sizeof(pt_slave_name));
|
||||
|
@ -430,11 +315,11 @@ static MYFDTYPE kiss_open_pt (void)
|
|||
#if 1
|
||||
// Sample code shows this. Why would we open it here?
|
||||
// On Ubuntu, the slave side disappears after a few
|
||||
// seconds if no one opens it. Same on Raspian which
|
||||
// seconds if no one opens it. Same on Raspbian which
|
||||
// is also based on Debian.
|
||||
// Need to revisit this.
|
||||
|
||||
MYFDTYPE pt_slave_fd;
|
||||
int pt_slave_fd;
|
||||
|
||||
pt_slave_fd = open(pt_slave_name, O_RDWR|O_NOCTTY);
|
||||
|
||||
|
@ -442,7 +327,7 @@ static MYFDTYPE kiss_open_pt (void)
|
|||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Can't open %s\n", pt_slave_name);
|
||||
perror ("");
|
||||
return MYFDERROR;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -471,155 +356,20 @@ static MYFDTYPE kiss_open_pt (void)
|
|||
return (fd);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Returns fd for our side of null modem or MYFDERROR for error.
|
||||
*/
|
||||
|
||||
|
||||
#if __CYGWIN__ || __WIN32__
|
||||
|
||||
static MYFDTYPE kiss_open_nullmodem (char *devicename)
|
||||
{
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
MYFDTYPE fd;
|
||||
DCB dcb;
|
||||
int ok;
|
||||
char bettername[50];
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("kiss_open_nullmodem ( '%s' )\n", devicename);
|
||||
#endif
|
||||
|
||||
#if DEBUG9
|
||||
log_fp = fopen ("kiss-debug.txt", "w");
|
||||
#endif
|
||||
|
||||
// Need to use FILE_FLAG_OVERLAPPED for full duplex operation.
|
||||
// Without it, write blocks when waiting on read.
|
||||
|
||||
// Read http://support.microsoft.com/kb/156932
|
||||
|
||||
// Bug fix in release 1.1 - Need to munge name for COM10 and up.
|
||||
// http://support.microsoft.com/kb/115831
|
||||
|
||||
strlcpy (bettername, devicename, sizeof(bettername));
|
||||
if (strncasecmp(devicename, "COM", 3) == 0) {
|
||||
int n;
|
||||
n = atoi(devicename+3);
|
||||
if (n >= 10) {
|
||||
strlcpy (bettername, "\\\\.\\", sizeof(bettername));
|
||||
strlcat (bettername, devicename, sizeof(bettername));
|
||||
}
|
||||
}
|
||||
|
||||
fd = CreateFile(bettername, GENERIC_READ | GENERIC_WRITE,
|
||||
0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||||
|
||||
if (fd == MYFDERROR) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR - Could not connect to %s side of null modem for Windows KISS TNC.\n", devicename);
|
||||
return (MYFDERROR);
|
||||
}
|
||||
|
||||
/* Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/aa363201(v=vs.85).aspx */
|
||||
|
||||
memset (&dcb, 0, sizeof(dcb));
|
||||
dcb.DCBlength = sizeof(DCB);
|
||||
|
||||
ok = GetCommState (fd, &dcb);
|
||||
if (! ok) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("kiss_open_nullmodem: GetCommState failed.\n");
|
||||
}
|
||||
|
||||
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx */
|
||||
|
||||
dcb.DCBlength = sizeof(DCB);
|
||||
dcb.BaudRate = CBR_9600; // shouldn't matter
|
||||
dcb.fBinary = 1;
|
||||
dcb.fParity = 0;
|
||||
dcb.fOutxCtsFlow = 0;
|
||||
dcb.fOutxDsrFlow = 0;
|
||||
dcb.fDtrControl = 0;
|
||||
dcb.fDsrSensitivity = 0;
|
||||
dcb.fOutX = 0;
|
||||
dcb.fInX = 0;
|
||||
dcb.fErrorChar = 0;
|
||||
dcb.fNull = 0; /* Don't drop nul characters! */
|
||||
dcb.fRtsControl = 0;
|
||||
dcb.ByteSize = 8;
|
||||
dcb.Parity = NOPARITY;
|
||||
dcb.StopBits = ONESTOPBIT;
|
||||
|
||||
ok = SetCommState (fd, &dcb);
|
||||
if (! ok) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("kiss_open_nullmodem: SetCommState failed.\n");
|
||||
}
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf("Virtual KISS TNC is connected to %s side of null modem.\n", devicename);
|
||||
|
||||
#else
|
||||
|
||||
/* Cygwin version. */
|
||||
|
||||
int fd;
|
||||
struct termios ts;
|
||||
int e;
|
||||
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("kiss_open_nullmodem ( '%s' )\n", devicename);
|
||||
#endif
|
||||
|
||||
fd = open (devicename, O_RDWR);
|
||||
|
||||
if (fd == MYFDERROR) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR - Could not connect to %s side of null modem for Windows KISS TNC.\n", devicename);
|
||||
return (MYFDERROR);
|
||||
}
|
||||
|
||||
e = tcgetattr (fd, &ts);
|
||||
if (e != 0) { perror ("nm tcgetattr"); }
|
||||
|
||||
cfmakeraw (&ts);
|
||||
|
||||
ts.c_cc[VMIN] = 1; /* wait for at least one character */
|
||||
ts.c_cc[VTIME] = 0; /* no fancy timing. */
|
||||
|
||||
e = tcsetattr (fd, TCSANOW, &ts);
|
||||
if (e != 0) { perror ("nm tcsetattr"); }
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf("Virtual KISS TNC is connected to %s side of null modem.\n", devicename);
|
||||
|
||||
#endif
|
||||
|
||||
return (fd);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: kiss_send_rec_packet
|
||||
* Name: kisspt_send_rec_packet
|
||||
*
|
||||
* Purpose: Send a received packet or text string to the client app.
|
||||
*
|
||||
* Inputs: chan - Channel number where packet was received.
|
||||
* 0 = first, 1 = second if any.
|
||||
*
|
||||
* kiss_cmd - Usually KISS_CMD_DATA_FRAME but we can also have
|
||||
* KISS_CMD_SET_HARDWARE when responding to a query.
|
||||
*
|
||||
* pp - Identifier for packet object.
|
||||
*
|
||||
* fbuf - Address of raw received frame buffer
|
||||
|
@ -628,6 +378,10 @@ static MYFDTYPE kiss_open_nullmodem (char *devicename)
|
|||
* flen - Length of raw received frame not including the FCS
|
||||
* or -1 for a text string.
|
||||
*
|
||||
* client - Not used for pseudo terminal.
|
||||
* Here so that 3 related functions all have
|
||||
* the same parameter list.
|
||||
*
|
||||
* Description: Send message to client.
|
||||
* We really don't care if anyone is listening or not.
|
||||
* I don't even know if we can find out.
|
||||
|
@ -635,28 +389,20 @@ static MYFDTYPE kiss_open_nullmodem (char *devicename)
|
|||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen)
|
||||
void kisspt_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, int client)
|
||||
{
|
||||
unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN + 2];
|
||||
int kiss_len;
|
||||
int err;
|
||||
|
||||
#if ! __WIN32__
|
||||
if (pt_master_fd == MYFDERROR) {
|
||||
|
||||
if (pt_master_fd == -1) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __CYGWIN__ || __WIN32__
|
||||
|
||||
if (nullmodem_fd == MYFDERROR) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (flen < 0) {
|
||||
flen = strlen((char*)fbuf);
|
||||
if (kiss_debug) {
|
||||
if (kisspt_debug) {
|
||||
kiss_debug_print (TO_CLIENT, "Fake command prompt", fbuf, flen);
|
||||
}
|
||||
strlcpy ((char *)kiss_buff, (char *)fbuf, sizeof(kiss_buff));
|
||||
|
@ -664,15 +410,18 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen)
|
|||
}
|
||||
else {
|
||||
|
||||
|
||||
unsigned char stemp[AX25_MAX_PACKET_LEN + 1];
|
||||
|
||||
assert (flen < (int)(sizeof(stemp)));
|
||||
if (flen > (int)(sizeof(stemp)) - 1) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nPseudo Terminal KISS buffer too small. Truncated.\n\n");
|
||||
flen = (int)(sizeof(stemp)) - 1;
|
||||
}
|
||||
|
||||
stemp[0] = (chan << 4) + 0;
|
||||
stemp[0] = (chan << 4) | kiss_cmd;
|
||||
memcpy (stemp+1, fbuf, flen);
|
||||
|
||||
if (kiss_debug >= 2) {
|
||||
if (kisspt_debug >= 2) {
|
||||
/* AX.25 frame with the CRC removed. */
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("\n");
|
||||
|
@ -684,16 +433,12 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen)
|
|||
|
||||
/* This has KISS framing and escapes for sending to client app. */
|
||||
|
||||
if (kiss_debug) {
|
||||
if (kisspt_debug) {
|
||||
kiss_debug_print (TO_CLIENT, NULL, kiss_buff, kiss_len);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if ! __WIN32__
|
||||
|
||||
/* Pseudo terminal for Cygwin and Linux. */
|
||||
|
||||
err = write (pt_master_fd, kiss_buff, (size_t)kiss_len);
|
||||
|
||||
if (err == -1 && errno == EWOULDBLOCK) {
|
||||
|
@ -709,84 +454,18 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen)
|
|||
perror ("pt write");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if __CYGWIN__ || __WIN32__
|
||||
|
||||
|
||||
/*
|
||||
* This write can block if nothing is connected to the other end.
|
||||
* The solution is found in the com0com ReadMe file:
|
||||
*
|
||||
* Q. My application hangs during its startup when it sends anything to one paired
|
||||
* COM port. The only way to unhang it is to start HyperTerminal, which is connected
|
||||
* to the other paired COM port. I didn't have this problem with physical serial
|
||||
* ports.
|
||||
* A. Your application can hang because receive buffer overrun is disabled by
|
||||
* default. You can fix the problem by enabling receive buffer overrun for the
|
||||
* receiving port. Also, to prevent some flow control issues you need to enable
|
||||
* baud rate emulation for the sending port. So, if your application use port CNCA0
|
||||
* and other paired port is CNCB0, then:
|
||||
*
|
||||
* 1. Launch the Setup Command Prompt shortcut.
|
||||
* 2. Enter the change commands, for example:
|
||||
*
|
||||
* command> change CNCB0 EmuOverrun=yes
|
||||
* command> change CNCA0 EmuBR=yes
|
||||
*/
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
DWORD nwritten;
|
||||
|
||||
/* Without this, write blocks while we are waiting on a read. */
|
||||
static OVERLAPPED ov_wr;
|
||||
memset (&ov_wr, 0, sizeof(ov_wr));
|
||||
|
||||
if ( ! WriteFile (nullmodem_fd, kiss_buff, kiss_len, &nwritten, &ov_wr))
|
||||
{
|
||||
err = GetLastError();
|
||||
if (err != ERROR_IO_PENDING)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nError sending KISS message to client application thru null modem. Error %d.\n\n", (int)GetLastError());
|
||||
//CloseHandle (nullmodem_fd);
|
||||
//nullmodem_fd = MYFDERROR;
|
||||
}
|
||||
}
|
||||
else if ((int)nwritten != kiss_len)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nError sending KISS message to client application thru null modem. Only %d of %d written.\n\n", (int)nwritten, kiss_len);
|
||||
//CloseHandle (nullmodem_fd);
|
||||
//nullmodem_fd = MYFDERROR;
|
||||
}
|
||||
|
||||
#else
|
||||
err = write (nullmodem_fd, kiss_buf, (size_t)kiss_len);
|
||||
if (err != len)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nError sending KISS message to client application thru null modem. err=%d\n\n", err);
|
||||
//close (nullmodem_fd);
|
||||
//nullmodem_fd = MYFDERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
} /* kiss_send_rec_packet */
|
||||
} /* kisspt_send_rec_packet */
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: kiss_get
|
||||
* Name: kisspt_get
|
||||
*
|
||||
* Purpose: Read one byte from the KISS client app.
|
||||
*
|
||||
* Global In: nullmodem_fd (Windows) or pt_master_fd (Linux)
|
||||
* Global In: pt_master_fd
|
||||
*
|
||||
* Returns: one byte (value 0 - 255) or terminate thread on error.
|
||||
*
|
||||
|
@ -799,78 +478,10 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen)
|
|||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static int kiss_get (/* MYFDTYPE fd*/ void )
|
||||
static int kisspt_get (void)
|
||||
{
|
||||
unsigned char ch;
|
||||
|
||||
#if __WIN32__ /* Native Windows version. */
|
||||
|
||||
DWORD n;
|
||||
static OVERLAPPED ov_rd;
|
||||
|
||||
memset (&ov_rd, 0, sizeof(ov_rd));
|
||||
ov_rd.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
|
||||
|
||||
|
||||
/* Overlapped I/O makes reading rather complicated. */
|
||||
/* See: http://msdn.microsoft.com/en-us/library/ms810467.aspx */
|
||||
|
||||
/* It seems that the read completes OK with a count */
|
||||
/* of 0 every time we send a message to the serial port. */
|
||||
|
||||
n = 0; /* Number of characters read. */
|
||||
|
||||
while (n == 0) {
|
||||
|
||||
if ( ! ReadFile (nullmodem_fd, &ch, 1, &n, &ov_rd))
|
||||
{
|
||||
int err1 = GetLastError();
|
||||
|
||||
if (err1 == ERROR_IO_PENDING)
|
||||
{
|
||||
/* Wait for completion. */
|
||||
|
||||
if (WaitForSingleObject (ov_rd.hEvent, INFINITE) == WAIT_OBJECT_0)
|
||||
{
|
||||
if ( ! GetOverlappedResult (nullmodem_fd, &ov_rd, &n, 1))
|
||||
{
|
||||
int err3 = GetLastError();
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nKISS GetOverlappedResult error %d.\n\n", err3);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Success! n should be 1 */
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nKISS ReadFile error %d. Closing connection.\n\n", err1);
|
||||
CloseHandle (nullmodem_fd);
|
||||
nullmodem_fd = MYFDERROR;
|
||||
//pthread_exit (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
} /* end while n==0 */
|
||||
|
||||
CloseHandle(ov_rd.hEvent);
|
||||
|
||||
if (n != 1) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nKISS failed to get one byte. n=%d.\n\n", (int)n);
|
||||
|
||||
#if DEBUG9
|
||||
fprintf (log_fp, "n=%d\n", n);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#else /* Linux/Cygwin version */
|
||||
|
||||
int n = 0;
|
||||
fd_set fd_in, fd_ex;
|
||||
int rc;
|
||||
|
@ -934,7 +545,7 @@ static int kiss_get (/* MYFDTYPE fd*/ void )
|
|||
continue; // When could we get a 0?
|
||||
}
|
||||
|
||||
if (rc == MYFDERROR
|
||||
if (rc == -1
|
||||
|| (n = read(pt_master_fd, &ch, (size_t)1)) != 1)
|
||||
{
|
||||
|
||||
|
@ -944,70 +555,52 @@ static int kiss_get (/* MYFDTYPE fd*/ void )
|
|||
|
||||
close (pt_master_fd);
|
||||
|
||||
pt_master_fd = MYFDERROR;
|
||||
pt_master_fd = -1;
|
||||
unlink (TMP_KISSTNC_SYMLINK);
|
||||
pthread_exit (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if DEBUGx
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("kiss_get(%d) returns 0x%02x\n", fd, ch);
|
||||
dw_printf ("kisspt_get(%d) returns 0x%02x\n", fd, ch);
|
||||
#endif
|
||||
|
||||
#if DEBUG9
|
||||
fprintf (log_fp, "%02x %c %c", ch,
|
||||
isprint(ch) ? ch : '.' ,
|
||||
(isupper(ch>>1) || isdigit(ch>>1) || (ch>>1) == ' ') ? (ch>>1) : '.');
|
||||
if (ch == FEND) fprintf (log_fp, " FEND");
|
||||
if (ch == FESC) fprintf (log_fp, " FESC");
|
||||
if (ch == TFEND) fprintf (log_fp, " TFEND");
|
||||
if (ch == TFESC) fprintf (log_fp, " TFESC");
|
||||
if (ch == '\r') fprintf (log_fp, " CR");
|
||||
if (ch == '\n') fprintf (log_fp, " LF");
|
||||
fprintf (log_fp, "\n");
|
||||
if (ch == FEND) fflush (log_fp);
|
||||
#endif
|
||||
return (ch);
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: kiss_listen_thread
|
||||
* Name: kisspt_listen_thread
|
||||
*
|
||||
* Purpose: Read messages from serial port KISS client application.
|
||||
*
|
||||
* Global In: nullmodem_fd (Windows) or pt_master_fd (Linux)
|
||||
* Global In:
|
||||
*
|
||||
* Description: Reads bytes from the KISS client app and
|
||||
* sends them to kiss_rec_byte for processing.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static THREAD_F kiss_listen_thread (void *arg)
|
||||
static void * kisspt_listen_thread (void *arg)
|
||||
{
|
||||
unsigned char ch;
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("kiss_listen_thread ( %d )\n", fd);
|
||||
dw_printf ("kisspt_listen_thread ( %d )\n", fd);
|
||||
#endif
|
||||
|
||||
|
||||
while (1) {
|
||||
ch = kiss_get();
|
||||
kiss_rec_byte (&kf, ch, kiss_debug, kiss_send_rec_packet);
|
||||
ch = kisspt_get();
|
||||
kiss_rec_byte (&kf, ch, kisspt_debug, -1, kisspt_send_rec_packet);
|
||||
}
|
||||
|
||||
#if __WIN32__
|
||||
return(0);
|
||||
#else
|
||||
return (THREAD_F) 0; /* Unreachable but avoids compiler warning. */
|
||||
#endif
|
||||
return (void *) 0; /* Unreachable but avoids compiler warning. */
|
||||
}
|
||||
|
||||
#endif // Linux version
|
||||
|
||||
/* end kiss.c */
|
||||
|
|
8
kiss.h
8
kiss.h
|
@ -1,6 +1,8 @@
|
|||
|
||||
/*
|
||||
* Name: kiss.h
|
||||
*
|
||||
* This is for the pseudo terminal KISS interface.
|
||||
*/
|
||||
|
||||
|
||||
|
@ -11,11 +13,11 @@
|
|||
|
||||
|
||||
|
||||
void kiss_init (struct misc_config_s *misc_config);
|
||||
void kisspt_init (struct misc_config_s *misc_config);
|
||||
|
||||
void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen);
|
||||
void kisspt_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, int client);
|
||||
|
||||
void kiss_serial_set_debug (int n);
|
||||
void kisspt_set_debug (int n);
|
||||
|
||||
|
||||
/* end kiss.h */
|
||||
|
|
354
kiss_frame.c
354
kiss_frame.c
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2013, 2014 John Langner, WB2OSZ
|
||||
// Copyright (C) 2013, 2014, 2017 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
|
||||
|
@ -25,7 +25,10 @@
|
|||
*
|
||||
* Purpose: Common code used by Serial port and network versions of KISS protocol.
|
||||
*
|
||||
* Description: The KISS TNS protocol is described in http://www.ka9q.net/papers/kiss.html
|
||||
* Description: The KISS TNC protocol is described in http://www.ka9q.net/papers/kiss.html
|
||||
*
|
||||
* ( An extended form, to handle multiple TNCs on a single serial port.
|
||||
* Not applicable for our situation. http://he.fi/pub/oh7lzb/bpq/multi-kiss.pdf )
|
||||
*
|
||||
* Briefly, a frame is composed of
|
||||
*
|
||||
|
@ -37,34 +40,41 @@
|
|||
*
|
||||
* The first byte of the frame contains:
|
||||
*
|
||||
* * port number in upper nybble.
|
||||
* * port number (radio channel) in upper nybble.
|
||||
* * command in lower nybble.
|
||||
*
|
||||
*
|
||||
* Commands from application recognized:
|
||||
* Commands from application tp TNC:
|
||||
*
|
||||
* 0 Data Frame AX.25 frame in raw format.
|
||||
* _0 Data Frame AX.25 frame in raw format.
|
||||
*
|
||||
* 1 TXDELAY See explanation in xmit.c.
|
||||
* _1 TXDELAY See explanation in xmit.c.
|
||||
*
|
||||
* 2 Persistence " "
|
||||
* _2 Persistence " "
|
||||
*
|
||||
* 3 SlotTime " "
|
||||
* _3 SlotTime " "
|
||||
*
|
||||
* 4 TXtail " "
|
||||
* _4 TXtail " "
|
||||
* Spec says it is obsolete but Xastir
|
||||
* sends it and we respect it.
|
||||
*
|
||||
* 5 FullDuplex Ignored. Always full duplex.
|
||||
* _5 FullDuplex Full Duplex. Transmit immediately without
|
||||
* waiting for channel to be clear.
|
||||
*
|
||||
* 6 SetHardware TNC specific. Ignored.
|
||||
* _6 SetHardware TNC specific.
|
||||
*
|
||||
* _C XKISS extension - not supported.
|
||||
* _E XKISS extention - not supported.
|
||||
*
|
||||
* FF Return Exit KISS mode. Ignored.
|
||||
*
|
||||
*
|
||||
* Messages sent to client application:
|
||||
*
|
||||
* 0 Data Frame Received AX.25 frame in raw format.
|
||||
* _0 Data Frame Received AX.25 frame in raw format.
|
||||
*
|
||||
* _6 SetHardware TNC specific.
|
||||
* Usually a response to a query.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
|
@ -82,13 +92,38 @@
|
|||
#include "kiss_frame.h"
|
||||
#include "tq.h"
|
||||
#include "xmit.h"
|
||||
#include "version.h"
|
||||
|
||||
|
||||
/* In server.c. Should probably move to some misc. function file. */
|
||||
void hex_dump (unsigned char *p, int len);
|
||||
|
||||
#ifdef KISSUTIL
|
||||
void hex_dump (unsigned char *p, int len)
|
||||
{
|
||||
int n, i, offset;
|
||||
|
||||
|
||||
|
||||
offset = 0;
|
||||
while (len > 0) {
|
||||
n = len < 16 ? len : 16;
|
||||
printf (" %03x: ", offset);
|
||||
for (i=0; i<n; i++) {
|
||||
printf (" %02x", p[i]);
|
||||
}
|
||||
for (i=n; i<16; i++) {
|
||||
printf (" ");
|
||||
}
|
||||
printf (" ");
|
||||
for (i=0; i<n; i++) {
|
||||
printf ("%c", isprint(p[i]) ? p[i] : '.');
|
||||
}
|
||||
printf ("\n");
|
||||
p += 16;
|
||||
offset += 16;
|
||||
len -= 16;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if KISSTEST
|
||||
|
||||
|
@ -101,10 +136,18 @@ void text_color_set (dw_color_t c)
|
|||
|
||||
#else
|
||||
|
||||
static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug);
|
||||
#ifndef DECAMAIN
|
||||
#ifndef KISSUTIL
|
||||
static void kiss_set_hardware (int chan, char *command, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//#if KISSUTIL
|
||||
//#define text_color_set(x) ;
|
||||
//#define dw_printf printf
|
||||
//#endif
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
|
@ -134,6 +177,7 @@ void kiss_frame_init (struct audio_s *pa)
|
|||
* Inputs: in - Address of input block.
|
||||
* First byte is the "type indicator" with type and
|
||||
* channel but we don't care about that here.
|
||||
* If it happens to be FEND or FESC, it is escaped, like any other byte.
|
||||
*
|
||||
* This seems cumbersome and confusing to have this
|
||||
* one byte offset when encapsulating an AX.25 frame.
|
||||
|
@ -153,7 +197,7 @@ void kiss_frame_init (struct audio_s *pa)
|
|||
* FEND - Magic frame separator.
|
||||
*
|
||||
* Returns: Number of bytes in the output.
|
||||
* Absolute max length will be twice input plus 2.
|
||||
* Absolute max length (extremely unlikely) will be twice input plus 2.
|
||||
*
|
||||
*-----------------------------------------------------------------*/
|
||||
|
||||
|
@ -205,6 +249,8 @@ int kiss_encapsulate (unsigned char *in, int ilen, unsigned char *out)
|
|||
* the escapes or FEND.
|
||||
* First byte is the "type indicator" with type and
|
||||
* channel but we don't care about that here.
|
||||
* We treat it like any other byte with special handling
|
||||
* if it happens to be FESC.
|
||||
* Note that this is "binary" data and can contain
|
||||
* nul (0x00) values. Don't treat it like a text string!
|
||||
*
|
||||
|
@ -212,7 +258,7 @@ int kiss_encapsulate (unsigned char *in, int ilen, unsigned char *out)
|
|||
*
|
||||
*-----------------------------------------------------------------*/
|
||||
|
||||
static int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out)
|
||||
int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out)
|
||||
{
|
||||
int olen;
|
||||
int j;
|
||||
|
@ -278,6 +324,8 @@ static int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out)
|
|||
} /* end kiss_unwrap */
|
||||
|
||||
|
||||
#ifndef DECAMAIN
|
||||
|
||||
#ifndef KISSTEST
|
||||
|
||||
|
||||
|
@ -291,6 +339,8 @@ static int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out)
|
|||
* Inputs: kf - Current state of building a frame.
|
||||
* ch - A byte from the input stream.
|
||||
* debug - Activates debug output.
|
||||
* client - Client app number for TCP KISS.
|
||||
* Ignored for pseudo termal and serial port.
|
||||
* sendfun - Function to send something to the client application.
|
||||
*
|
||||
* Outputs: kf - Current state is updated.
|
||||
|
@ -326,7 +376,7 @@ static int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out)
|
|||
|
||||
|
||||
|
||||
void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(int,unsigned char*,int))
|
||||
void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int))
|
||||
{
|
||||
|
||||
//dw_printf ("kiss_frame ( %c %02x ) \n", ch, ch);
|
||||
|
@ -364,14 +414,17 @@ void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfu
|
|||
kf->noise[kf->noise_len] = '\0';
|
||||
}
|
||||
|
||||
#ifndef KISSUTIL
|
||||
/* Try to appease client app by sending something back. */
|
||||
if (strcasecmp("restart\r", (char*)(kf->noise)) == 0 ||
|
||||
strcasecmp("reset\r", (char*)(kf->noise)) == 0) {
|
||||
(*sendfun) (0, (unsigned char *)"\xc0\xc0", -1);
|
||||
// first 2 parameters don't matter when length is -1 indicating text.
|
||||
(*sendfun) (0, 0, (unsigned char *)"\xc0\xc0", -1, client);
|
||||
}
|
||||
else {
|
||||
(*sendfun) (0, (unsigned char *)"\r\ncmd:", -1);
|
||||
(*sendfun) (0, 0, (unsigned char *)"\r\ncmd:", -1, client);
|
||||
}
|
||||
#endif
|
||||
kf->noise_len = 0;
|
||||
}
|
||||
return;
|
||||
|
@ -415,7 +468,7 @@ void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfu
|
|||
hex_dump (unwrapped+1, ulen-1);
|
||||
}
|
||||
|
||||
kiss_process_msg (unwrapped, ulen, debug);
|
||||
kiss_process_msg (unwrapped, ulen, debug, client, sendfun);
|
||||
|
||||
kf->state = KS_SEARCHING;
|
||||
return;
|
||||
|
@ -452,9 +505,22 @@ void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfu
|
|||
*
|
||||
* debug - Debug option is selected.
|
||||
*
|
||||
* client - Client app number for TCP KISS.
|
||||
* Ignored for pseudo termal and serial port.
|
||||
*
|
||||
* sendfun - Function to send something to the client application.
|
||||
* "Set Hardware" can send a response.
|
||||
*
|
||||
*-----------------------------------------------------------------*/
|
||||
|
||||
static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug)
|
||||
#ifndef KISSUTIL // All these ifdefs in here are a sign that this should be refactored.
|
||||
// Should split this into multiple files.
|
||||
// Some functions are only for the TNC end.
|
||||
// Other functions are suitble for both TNC and client app.
|
||||
|
||||
// This is used only by the TNC sided.
|
||||
|
||||
void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int))
|
||||
{
|
||||
int port;
|
||||
int cmd;
|
||||
|
@ -466,11 +532,21 @@ static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug)
|
|||
|
||||
switch (cmd)
|
||||
{
|
||||
case 0: /* Data Frame */
|
||||
case KISS_CMD_DATA_FRAME: /* 0 = Data Frame */
|
||||
|
||||
/* Special hack - Discard apparently bad data from Linux AX25. */
|
||||
|
||||
if ((port == 2 || port == 8) &&
|
||||
/* Note July 2017: There is a variant of of KISS, called SMACK, that assumes */
|
||||
/* a TNC can never have more than 8 ports. http://symek.de/g/smack.html */
|
||||
/* It uses the MSB to indicate that a checksum is added. I wonder if this */
|
||||
/* is why we sometimes hear about a request to transmit on channel 8. */
|
||||
/* Should we have a message that asks the user if SMACK is being used, */
|
||||
/* and if so, turn it off in the application configuration? */
|
||||
/* Our current default is a maximum of 6 channels but it is easily */
|
||||
/* increased by changing one number and recompiling. */
|
||||
|
||||
if (kiss_len > 16 &&
|
||||
(port == 2 || port == 8) &&
|
||||
kiss_msg[1] == 'Q' << 1 &&
|
||||
kiss_msg[2] == 'S' << 1 &&
|
||||
kiss_msg[3] == 'T' << 1 &&
|
||||
|
@ -519,50 +595,103 @@ static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug)
|
|||
}
|
||||
break;
|
||||
|
||||
case 1: /* TXDELAY */
|
||||
case KISS_CMD_TXDELAY: /* 1 = TXDELAY */
|
||||
|
||||
if (kiss_len < 2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("KISS ERROR: Missing value for TXDELAY command.\n");
|
||||
return;
|
||||
}
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("KISS protocol set TXDELAY = %d (*10mS units = %d mS), port %d\n", kiss_msg[1], kiss_msg[1] * 10, port);
|
||||
if (kiss_msg[1] < 4 || kiss_msg[1] > 100) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Are you sure you want such an extreme value for TXDELAY?\n");
|
||||
dw_printf ("See \"Radio Channel - Transmit Timing\" section of User Guide for explanation.\n");
|
||||
}
|
||||
xmit_set_txdelay (port, kiss_msg[1]);
|
||||
break;
|
||||
|
||||
case 2: /* Persistence */
|
||||
case KISS_CMD_PERSISTENCE: /* 2 = Persistence */
|
||||
|
||||
if (kiss_len < 2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("KISS ERROR: Missing value for PERSISTENCE command.\n");
|
||||
return;
|
||||
}
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("KISS protocol set Persistence = %d, port %d\n", kiss_msg[1], port);
|
||||
if (kiss_msg[1] < 5 || kiss_msg[1] > 250) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Are you sure you want such an extreme value for PERSIST?\n");
|
||||
dw_printf ("See \"Radio Channel - Transmit Timing\" section of User Guide for explanation.\n");
|
||||
}
|
||||
xmit_set_persist (port, kiss_msg[1]);
|
||||
break;
|
||||
|
||||
case 3: /* SlotTime */
|
||||
case KISS_CMD_SLOTTIME: /* 3 = SlotTime */
|
||||
|
||||
if (kiss_len < 2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("KISS ERROR: Missing value for SLOTTIME command.\n");
|
||||
return;
|
||||
}
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("KISS protocol set SlotTime = %d (*10mS units = %d mS), port %d\n", kiss_msg[1], kiss_msg[1] * 10, port);
|
||||
if (kiss_msg[1] < 2 || kiss_msg[1] > 50) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Are you sure you want such an extreme value for SLOTTIME?\n");
|
||||
dw_printf ("See \"Radio Channel - Transmit Timing\" section of User Guide for explanation.\n");
|
||||
}
|
||||
xmit_set_slottime (port, kiss_msg[1]);
|
||||
break;
|
||||
|
||||
case 4: /* TXtail */
|
||||
case KISS_CMD_TXTAIL: /* 4 = TXtail */
|
||||
|
||||
if (kiss_len < 2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("KISS ERROR: Missing value for TXTAIL command.\n");
|
||||
return;
|
||||
}
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("KISS protocol set TXtail = %d (*10mS units = %d mS), port %d\n", kiss_msg[1], kiss_msg[1] * 10, port);
|
||||
if (kiss_msg[1] < 2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Setting TXTAIL so low is asking for trouble. You probably don't want to do this.\n");
|
||||
dw_printf ("See \"Radio Channel - Transmit Timing\" section of User Guide for explanation.\n");
|
||||
}
|
||||
xmit_set_txtail (port, kiss_msg[1]);
|
||||
break;
|
||||
|
||||
case 5: /* FullDuplex */
|
||||
case KISS_CMD_FULLDUPLEX: /* 5 = FullDuplex */
|
||||
|
||||
if (kiss_len < 2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("KISS ERROR: Missing value for FULLDUPLEX command.\n");
|
||||
return;
|
||||
}
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("KISS protocol set FullDuplex = %d, port %d\n", kiss_msg[1], port);
|
||||
xmit_set_fulldup (port, kiss_msg[1]);
|
||||
break;
|
||||
|
||||
case 6: /* TNC specific */
|
||||
case KISS_CMD_SET_HARDWARE: /* 6 = TNC specific */
|
||||
|
||||
if (kiss_len < 2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("KISS ERROR: Missing value for SET HARDWARE command.\n");
|
||||
return;
|
||||
}
|
||||
kiss_msg[kiss_len] = '\0';
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("KISS protocol set hardware - ignored.\n");
|
||||
dw_printf ("KISS protocol set hardware \"%s\", port %d\n", (char*)(kiss_msg+1), port);
|
||||
kiss_set_hardware (port, (char*)(kiss_msg+1), debug, client, sendfun);
|
||||
break;
|
||||
|
||||
case 15: /* End KISS mode, port should be 15. */
|
||||
case KISS_CMD_END_KISS: /* 15 = End KISS mode, port should be 15. */
|
||||
/* Ignore it. */
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("KISS protocol end KISS mode\n");
|
||||
dw_printf ("KISS protocol end KISS mode - Ignored.\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -575,11 +704,158 @@ static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug)
|
|||
dw_printf ("Use \"-d kn\" option on direwolf command line to observe\n");
|
||||
dw_printf ("all communication with the client application.\n");
|
||||
|
||||
if (cmd == XKISS_CMD_DATA || cmd == XKISS_CMD_POLL) {
|
||||
dw_printf ("\n");
|
||||
dw_printf ("It looks like you are trying to use the \"XKISS\" protocol which is not supported.\n");
|
||||
dw_printf ("Change your application settings to use standard \"KISS\" rather than some other variant.\n");
|
||||
dw_printf ("If you are using Winlink Express, configure like this:\n");
|
||||
dw_printf (" Packet TNC Type: KISS\n");
|
||||
dw_printf (" Packet TNC Model: NORMAL -- Using ACKMODE will cause this error.\n");
|
||||
dw_printf ("\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
} /* end kiss_process_msg */
|
||||
|
||||
#endif // ifndef KISSUTIL
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: kiss_set_hardware
|
||||
*
|
||||
* Purpose: Process the "set hardware" command.
|
||||
*
|
||||
* Inputs: chan - channel, 0 - 15.
|
||||
*
|
||||
* command - All but the first byte. e.g. "TXBUF:99"
|
||||
* Case sensitive.
|
||||
* Will be modified so be sure caller doesn't care.
|
||||
*
|
||||
* debug - debug level.
|
||||
*
|
||||
* client - Client app number for TCP KISS.
|
||||
* Needed so we can send any response to the right client app.
|
||||
* Ignored for pseudo terminal and serial port.
|
||||
*
|
||||
* sendfun - Function to send something to the client application.
|
||||
*
|
||||
* This is the tricky part. We can have any combination of
|
||||
* serial port, pseudo terminal, and multiple TCP clients.
|
||||
* We need to send the response to same place where query came
|
||||
* from. The function is different for each class of device
|
||||
* and we need a client number for the TCP case because we
|
||||
* can have multiple TCP KISS clients at the same time.
|
||||
*
|
||||
*
|
||||
* Description: This is new in version 1.5. "Set hardware" was previously ignored.
|
||||
*
|
||||
* There are times when the client app might want to send configuration
|
||||
* commands, such as modem speed, to the KISS TNC or inquire about its
|
||||
* current state.
|
||||
*
|
||||
* The immediate motivation for adding this is that one application wants
|
||||
* to know how many frames are currently in the transmit queue. This can
|
||||
* be used for throttling of large transmissions and performing some action
|
||||
* after the last frame has been sent.
|
||||
*
|
||||
* The original KISS protocol spec offers no guidance on what "Set Hardware" might look
|
||||
* like. I'm aware of only two, drastically different, implementations:
|
||||
*
|
||||
* fldigi - http://www.w1hkj.com/FldigiHelp-3.22/kiss_command_page.html
|
||||
*
|
||||
* Everything is in human readable in both directions:
|
||||
*
|
||||
* COMMAND: [ parameter [ , parameter ... ] ]
|
||||
*
|
||||
* Lack of a parameter, in the client to TNC direction, is a query
|
||||
* which should generate a response in the same format.
|
||||
*
|
||||
* Used by applications, http://www.w1hkj.com/FldigiHelp/kiss_host_prgs_page.html
|
||||
* - BPQ32
|
||||
* - UIChar
|
||||
* - YAAC
|
||||
*
|
||||
* mobilinkd - https://raw.githubusercontent.com/mobilinkd/tnc1/tnc2/bertos/net/kiss.c
|
||||
*
|
||||
* Single byte with the command / response code, followed by
|
||||
* zero or more value bytes.
|
||||
*
|
||||
* Used by applications:
|
||||
* - APRSdroid
|
||||
*
|
||||
* It would be beneficial to adopt one of them rather than doing something
|
||||
* completely different. It might even be possible to recognize both.
|
||||
* This might allow leveraging of other existing applications.
|
||||
*
|
||||
* Let's start with the easy to understand human readable format.
|
||||
*
|
||||
* Commands: (Client to TNC, with parameter(s) to set something.)
|
||||
*
|
||||
* none yet
|
||||
*
|
||||
* Queries: (Client to TNC, no parameters, generate a response.)
|
||||
*
|
||||
* Query Response Comment
|
||||
* ----- -------- -------
|
||||
*
|
||||
* TNC: TNC:DIREWOLF 9.9 9.9 represents current version.
|
||||
*
|
||||
* TXBUF: TXBUF:999 Number of bytes (not frames) in transmit queue.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
#ifndef KISSUTIL
|
||||
|
||||
static void kiss_set_hardware (int chan, char *command, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int))
|
||||
{
|
||||
char *param;
|
||||
char response[100];
|
||||
|
||||
param = strchr (command, ':');
|
||||
if (param != NULL) {
|
||||
*param = '\0';
|
||||
param++;
|
||||
|
||||
if (strcmp(command, "TNC") == 0) { /* TNC - Identify software version. */
|
||||
|
||||
if (strlen(param) > 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("KISS Set Hardware TNC: Did not expect a parameter.\n");
|
||||
}
|
||||
|
||||
snprintf (response, sizeof(response), "DIREWOLF %d.%d", MAJOR_VERSION, MINOR_VERSION);
|
||||
(*sendfun) (chan, KISS_CMD_SET_HARDWARE, (unsigned char *)response, strlen(response), client);
|
||||
}
|
||||
|
||||
else if (strcmp(command, "TXBUF") == 0) { /* TXBUF - Number of bytes in transmit queue. */
|
||||
|
||||
if (strlen(param) > 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("KISS Set Hardware TXBUF: Did not expect a parameter.\n");
|
||||
}
|
||||
|
||||
int n = tq_count (chan, -1, "", "", 1);
|
||||
snprintf (response, sizeof(response), "TXBUF:%d", n);
|
||||
(*sendfun) (chan, KISS_CMD_SET_HARDWARE, (unsigned char *)response, strlen(response), client);
|
||||
}
|
||||
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("KISS Set Hardware unrecognized command: %s.\n", command);
|
||||
}
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("KISS Set Hardware \"%s\" expected the form COMMAND:[parameter[,parameter...]]\n", command);
|
||||
}
|
||||
return;
|
||||
|
||||
} /* end kiss_set_hardware */
|
||||
|
||||
#endif // ifndef KISSUTIL
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
|
@ -597,6 +873,7 @@ static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug)
|
|||
|
||||
void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int msg_len)
|
||||
{
|
||||
#ifndef KISSUTIL
|
||||
const char *direction [2] = { "from", "to" };
|
||||
const char *prefix [2] = { "<<<", ">>>" };
|
||||
const char *function[16] = {
|
||||
|
@ -604,11 +881,14 @@ void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int
|
|||
"TXtail", "FullDuplex", "SetHardware", "Invalid 7",
|
||||
"Invalid 8", "Invalid 9", "Invalid 10", "Invalid 11",
|
||||
"Invalid 12", "Invalid 13", "Invalid 14", "Return" };
|
||||
|
||||
#endif
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("\n");
|
||||
|
||||
#ifdef KISSUTIL
|
||||
dw_printf ("From KISS TNC:\n");
|
||||
#else
|
||||
dw_printf ("\n");
|
||||
if (special == NULL) {
|
||||
unsigned char *p; /* to skip over FEND if present. */
|
||||
|
||||
|
@ -624,6 +904,7 @@ void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int
|
|||
prefix[(int)fromto], special, direction[(int)fromto],
|
||||
msg_len);
|
||||
}
|
||||
#endif
|
||||
hex_dump (pmsg, msg_len);
|
||||
|
||||
} /* end kiss_debug_print */
|
||||
|
@ -631,6 +912,7 @@ void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int
|
|||
|
||||
#endif
|
||||
|
||||
#endif /* DECAMAIN */
|
||||
|
||||
/* Quick unit test for encapsulate & unwrap */
|
||||
|
||||
|
@ -674,7 +956,7 @@ int main ()
|
|||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* KISSTEST */
|
||||
|
||||
#endif /* WALK96 */
|
||||
|
||||
|
|
30
kiss_frame.h
30
kiss_frame.h
|
@ -4,6 +4,25 @@
|
|||
#include "audio.h" /* for struct audio_s */
|
||||
|
||||
|
||||
/*
|
||||
* The first byte of a KISS frame has:
|
||||
* channel in upper nybble.
|
||||
* command in lower nybble.
|
||||
*/
|
||||
|
||||
#define KISS_CMD_DATA_FRAME 0
|
||||
#define KISS_CMD_TXDELAY 1
|
||||
#define KISS_CMD_PERSISTENCE 2
|
||||
#define KISS_CMD_SLOTTIME 3
|
||||
#define KISS_CMD_TXTAIL 4
|
||||
#define KISS_CMD_FULLDUPLEX 5
|
||||
#define KISS_CMD_SET_HARDWARE 6
|
||||
#define XKISS_CMD_DATA 12 // Not supported. http://he.fi/pub/oh7lzb/bpq/multi-kiss.pdf
|
||||
#define XKISS_CMD_POLL 14 // Not supported.
|
||||
#define KISS_CMD_END_KISS 15
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Special characters used by SLIP protocol.
|
||||
*/
|
||||
|
@ -14,8 +33,10 @@
|
|||
#define TFESC 0xDD
|
||||
|
||||
|
||||
|
||||
enum kiss_state_e {
|
||||
KS_SEARCHING, /* Looking for FEND to start KISS frame. */
|
||||
KS_SEARCHING = 0, /* Looking for FEND to start KISS frame. */
|
||||
/* Must be 0 so we can simply zero whole structure to initialize. */
|
||||
KS_COLLECTING}; /* In process of collecting KISS frame. */
|
||||
|
||||
|
||||
|
@ -40,15 +61,20 @@ typedef struct kiss_frame_s {
|
|||
} kiss_frame_t;
|
||||
|
||||
|
||||
#ifndef KISSUTIL
|
||||
void kiss_frame_init (struct audio_s *pa);
|
||||
#endif
|
||||
|
||||
int kiss_encapsulate (unsigned char *in, int ilen, unsigned char *out);
|
||||
|
||||
void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(int,unsigned char*,int));
|
||||
int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out);
|
||||
|
||||
void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int));
|
||||
|
||||
typedef enum fromto_e { FROM_CLIENT=0, TO_CLIENT=1 } fromto_t;
|
||||
|
||||
void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int));
|
||||
|
||||
void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int msg_len);
|
||||
|
||||
/* end kiss_frame.h */
|
312
kissnet.c
312
kissnet.c
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2011-2014, 2015 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011-2014, 2015, 2017 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
|
||||
|
@ -49,28 +49,28 @@
|
|||
*
|
||||
* Commands from application recognized:
|
||||
*
|
||||
* 0 Data Frame AX.25 frame in raw format.
|
||||
* _0 Data Frame AX.25 frame in raw format.
|
||||
*
|
||||
* 1 TXDELAY See explanation in xmit.c.
|
||||
* _1 TXDELAY See explanation in xmit.c.
|
||||
*
|
||||
* 2 Persistence " "
|
||||
* _2 Persistence " "
|
||||
*
|
||||
* 3 SlotTime " "
|
||||
* _3 SlotTime " "
|
||||
*
|
||||
* 4 TXtail " "
|
||||
* _4 TXtail " "
|
||||
* Spec says it is obsolete but Xastir
|
||||
* sends it and we respect it.
|
||||
*
|
||||
* 5 FullDuplex Ignored. Always full duplex.
|
||||
* _5 FullDuplex Ignored.
|
||||
*
|
||||
* 6 SetHardware TNC specific. Ignored.
|
||||
* _6 SetHardware TNC specific.
|
||||
*
|
||||
* FF Return Exit KISS mode. Ignored.
|
||||
*
|
||||
*
|
||||
* Messages sent to client application:
|
||||
*
|
||||
* 0 Data Frame Received AX.25 frame in raw format.
|
||||
* _0 Data Frame Received AX.25 frame in raw format.
|
||||
*
|
||||
*
|
||||
*
|
||||
|
@ -91,7 +91,6 @@
|
|||
/*
|
||||
* Native Windows: Use the Winsock interface.
|
||||
* Linux: Use the BSD socket interface.
|
||||
* Cygwin: Can use either one.
|
||||
*/
|
||||
|
||||
|
||||
|
@ -131,15 +130,27 @@
|
|||
void hex_dump (unsigned char *p, int len); // This should be in a .h file.
|
||||
|
||||
|
||||
static kiss_frame_t kf; /* Accumulated KISS frame and state of decoder. */
|
||||
// TODO: multiple instances if multiple KISS network clients!
|
||||
/*
|
||||
* Early on we allowed one AGW connection and one KISS TCP connection at a time.
|
||||
* In version 1.1, we allowed multiple concurrent client apps to attach with the AGW network protocol.
|
||||
* In Version 1.5, we do essentially the same here to allow multiple concurrent KISS TCP clients.
|
||||
* The default is a limit of 3 client applications at the same time.
|
||||
* You can increase the limit by changing the line below.
|
||||
* A larger number consumes more resources so don't go crazy by making it larger than needed.
|
||||
*/
|
||||
|
||||
#define MAX_NET_CLIENTS 3
|
||||
|
||||
static int client_sock; /* File descriptor for socket for */
|
||||
static int client_sock[MAX_NET_CLIENTS];
|
||||
/* File descriptor for socket for */
|
||||
/* communication with client application. */
|
||||
/* Set to -1 if not connected. */
|
||||
/* (Don't use SOCKET type because it is unsigned.) */
|
||||
|
||||
static kiss_frame_t kf[MAX_NET_CLIENTS];
|
||||
/* Accumulated KISS frame and state of decoder. */
|
||||
|
||||
|
||||
|
||||
// TODO: define in one place, use everywhere.
|
||||
#if __WIN32__
|
||||
|
@ -187,15 +198,17 @@ void kiss_net_set_debug (int n)
|
|||
|
||||
void kissnet_init (struct misc_config_s *mc)
|
||||
{
|
||||
int client;
|
||||
|
||||
#if __WIN32__
|
||||
HANDLE connect_listen_th;
|
||||
HANDLE cmd_listen_th;
|
||||
HANDLE cmd_listen_th[MAX_NET_CLIENTS];
|
||||
#else
|
||||
pthread_t connect_listen_tid;
|
||||
pthread_t cmd_listen_tid;
|
||||
pthread_t cmd_listen_tid[MAX_NET_CLIENTS];
|
||||
int e;
|
||||
#endif
|
||||
int kiss_port = mc->kiss_port;
|
||||
int kiss_port = mc->kiss_port; /* default 8001 but easily changed. */
|
||||
|
||||
|
||||
#if DEBUG
|
||||
|
@ -203,9 +216,11 @@ void kissnet_init (struct misc_config_s *mc)
|
|||
dw_printf ("kissnet_init ( %d )\n", kiss_port);
|
||||
#endif
|
||||
|
||||
memset (&kf, 0, sizeof(kf));
|
||||
|
||||
client_sock = -1;
|
||||
for (client=0; client<MAX_NET_CLIENTS; client++) {
|
||||
client_sock[client] = -1;
|
||||
memset (&(kf[client]), 0, sizeof(kf[client]));
|
||||
}
|
||||
|
||||
if (kiss_port == 0) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
|
@ -214,7 +229,7 @@ void kissnet_init (struct misc_config_s *mc)
|
|||
}
|
||||
|
||||
/*
|
||||
* This waits for a client to connect and sets client_sock.
|
||||
* This waits for a client to connect and sets client_sock[n].
|
||||
*/
|
||||
#if __WIN32__
|
||||
connect_listen_th = (HANDLE)_beginthreadex (NULL, 0, connect_listen_thread, (void *)kiss_port, 0, NULL);
|
||||
|
@ -233,23 +248,30 @@ void kissnet_init (struct misc_config_s *mc)
|
|||
#endif
|
||||
|
||||
/*
|
||||
* This reads messages from client when client_sock is valid.
|
||||
* These read messages from client when client_sock[n] is valid.
|
||||
* Currently we start up a separate thread for each potential connection.
|
||||
* Possible later refinement. Start one now, others only as needed.
|
||||
*/
|
||||
for (client = 0; client < MAX_NET_CLIENTS; client++) {
|
||||
|
||||
#if __WIN32__
|
||||
cmd_listen_th = (HANDLE)_beginthreadex (NULL, 0, kissnet_listen_thread, NULL, 0, NULL);
|
||||
if (cmd_listen_th == NULL) {
|
||||
cmd_listen_th[client] = (HANDLE)_beginthreadex (NULL, 0, kissnet_listen_thread, (void*)client, 0, NULL);
|
||||
if (cmd_listen_th[client] == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Could not create KISS socket command listening thread\n");
|
||||
dw_printf ("Could not create KISS command listening thread for client %d\n", client);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
e = pthread_create (&cmd_listen_tid, NULL, kissnet_listen_thread, NULL);
|
||||
e = pthread_create (&(cmd_listen_tid[client]), NULL, kissnet_listen_thread, (void *)(long)client);
|
||||
if (e != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
perror("Could not create KISS socket command listening thread");
|
||||
dw_printf ("Could not create KISS command listening thread for client %d\n", client);
|
||||
// Replace add perror with better message handling.
|
||||
perror("");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -352,13 +374,22 @@ static THREAD_F connect_listen_thread (void *arg)
|
|||
|
||||
while (1) {
|
||||
|
||||
while (client_sock > 0) {
|
||||
SLEEP_SEC(1); /* Already connected. Try again later. */
|
||||
int client;
|
||||
int c;
|
||||
|
||||
client = -1;
|
||||
for (c = 0; c < MAX_NET_CLIENTS && client < 0; c++) {
|
||||
if (client_sock[c] <= 0) {
|
||||
client = c;
|
||||
}
|
||||
}
|
||||
|
||||
#define QUEUE_SIZE 5
|
||||
/*
|
||||
* Listen for connection if we have not reached maximum.
|
||||
*/
|
||||
if (client >= 0) {
|
||||
|
||||
if(listen(listen_sock,QUEUE_SIZE) == SOCKET_ERROR)
|
||||
if(listen(listen_sock, MAX_NET_CLIENTS) == SOCKET_ERROR)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Listen failed with error: %d\n", WSAGetLastError());
|
||||
|
@ -366,11 +397,11 @@ static THREAD_F connect_listen_thread (void *arg)
|
|||
}
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf("Ready to accept KISS client application on port %s ...\n", kiss_port_str);
|
||||
dw_printf("Ready to accept KISS TCP client application %d on port %s ...\n", client, kiss_port_str);
|
||||
|
||||
client_sock = accept(listen_sock, NULL, NULL);
|
||||
client_sock[client] = accept(listen_sock, NULL, NULL);
|
||||
|
||||
if (client_sock == -1) {
|
||||
if (client_sock[client] == -1) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Accept failed with error: %d\n", WSAGetLastError());
|
||||
closesocket(listen_sock);
|
||||
|
@ -379,8 +410,14 @@ static THREAD_F connect_listen_thread (void *arg)
|
|||
}
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf("\nConnected to KISS client application ...\n\n");
|
||||
dw_printf("\nAttached to KISS TCP client application %d ...\n\n", client);
|
||||
|
||||
// Reset the state and buffer.
|
||||
memset (&(kf[client]), 0, sizeof(kf[client]));
|
||||
}
|
||||
else {
|
||||
SLEEP_SEC(1); /* wait then check again if more clients allowed. */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -430,18 +467,24 @@ static THREAD_F connect_listen_thread (void *arg)
|
|||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf("opened KISS socket as fd (%d) on port (%d) for stream i/o\n", listen_sock, ntohs(sockaddr.sin_port) );
|
||||
dw_printf("opened KISS TCP socket as fd (%d) on port (%d) for stream i/o\n", listen_sock, ntohs(sockaddr.sin_port) );
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
|
||||
while (client_sock > 0) {
|
||||
SLEEP_SEC(1); /* Already connected. Try again later. */
|
||||
int client;
|
||||
int c;
|
||||
|
||||
client = -1;
|
||||
for (c = 0; c < MAX_NET_CLIENTS && client < 0; c++) {
|
||||
if (client_sock[c] <= 0) {
|
||||
client = c;
|
||||
}
|
||||
}
|
||||
|
||||
#define QUEUE_SIZE 5
|
||||
if (client >= 0) {
|
||||
|
||||
if(listen(listen_sock,QUEUE_SIZE) == -1)
|
||||
if(listen(listen_sock,MAX_NET_CLIENTS) == -1)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
perror ("connect_listen_thread: Listen failed");
|
||||
|
@ -449,13 +492,19 @@ static THREAD_F connect_listen_thread (void *arg)
|
|||
}
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf("Ready to accept KISS client application on port %d ...\n", kiss_port);
|
||||
dw_printf("Ready to accept KISS TCP client application %d on port %d ...\n", client, kiss_port);
|
||||
|
||||
client_sock = accept(listen_sock, (struct sockaddr*)(&sockaddr),&sockaddr_size);
|
||||
client_sock[client] = accept(listen_sock, (struct sockaddr*)(&sockaddr),&sockaddr_size);
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf("\nConnected to KISS client application ...\n\n");
|
||||
dw_printf("\nAttached to KISS TCP client application %d...\n\n", client);
|
||||
|
||||
// Reset the state and buffer.
|
||||
memset (&(kf[client]), 0, sizeof(kf[client]));
|
||||
}
|
||||
else {
|
||||
SLEEP_SEC(1); /* wait then check again if more clients allowed. */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -473,30 +522,80 @@ static THREAD_F connect_listen_thread (void *arg)
|
|||
* Inputs: chan - Channel number where packet was received.
|
||||
* 0 = first, 1 = second if any.
|
||||
*
|
||||
// TODO: add kiss_cmd
|
||||
*
|
||||
* fbuf - Address of raw received frame buffer
|
||||
* or a text string.
|
||||
*
|
||||
* kiss_cmd - Usually KISS_CMD_DATA_FRAME but we can also have
|
||||
* KISS_CMD_SET_HARDWARE when responding to a query.
|
||||
*
|
||||
* flen - Number of bytes for AX.25 frame.
|
||||
* or -1 for a text string.
|
||||
* When called from kiss_rec_byte, flen will be -1
|
||||
* indicating a text string rather than frame content.
|
||||
* This is used to fake out an application that thinks
|
||||
* it is using a traditional TNC and tries to put it
|
||||
* into KISS mode.
|
||||
*
|
||||
* tcpclient - It is possible to have more than client attached
|
||||
* at the same time with TCP KISS.
|
||||
* When a frame is received from the radio we want it
|
||||
* to go to all of the clients. In this case specify -1.
|
||||
* When responding to a command from the client, we want
|
||||
* to send only to that one client app. In this case
|
||||
* use the value 0 .. MAX_NET_CLIENTS-1.
|
||||
*
|
||||
* Description: Send message to client if connected.
|
||||
* Description: Send message to client(s) if connected.
|
||||
* Disconnect from client, and notify user, if any error.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
void kissnet_send_rec_packet (int chan, unsigned char *fbuf, int flen)
|
||||
void kissnet_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, int tcpclient)
|
||||
{
|
||||
unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN];
|
||||
int kiss_len;
|
||||
int err;
|
||||
int first, last, client;
|
||||
|
||||
// Something received over the radio would be sent to all attached clients.
|
||||
// However, there are times we want to send a response only to a particular client.
|
||||
// In the case of a serial port or pseudo terminal, there is only one potential client.
|
||||
// so the response would be sent to only one place. A new parameter has been added for this.
|
||||
|
||||
if (client_sock == -1) {
|
||||
if (tcpclient >= 0 && tcpclient < MAX_NET_CLIENTS) {
|
||||
first = tcpclient;
|
||||
last = tcpclient;
|
||||
}
|
||||
else if (tcpclient == -1) {
|
||||
first = 0;
|
||||
last = MAX_NET_CLIENTS - 1;
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("KISS TCP: Internal error, kissnet_send_rec_packet, tcpclient = %d.\n", tcpclient);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (client = first; client <= last; client++) {
|
||||
|
||||
if (client_sock[client] != -1) {
|
||||
|
||||
if (flen < 0) {
|
||||
|
||||
// A client app might think it is attached to a traditional TNC.
|
||||
// It might try sending commands over and over again trying to get the TNC into KISS mode.
|
||||
// We recognize this attempt and send it something to keep it happy.
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("KISS TCP: Something unexpected from client application.\n");
|
||||
dw_printf ("Is client app treating this like an old TNC with command mode?\n");
|
||||
dw_printf ("This can be caused by the application sending commands to put a\n");
|
||||
dw_printf ("traditional TNC into KISS mode. It is usually a harmless warning.\n");
|
||||
dw_printf ("For best results, configure for a KISS-only TNC to avoid this.\n");
|
||||
dw_printf ("In the case of APRSISCE/32, use \"Simply(KISS)\" rather than \"KISS.\"\n");
|
||||
|
||||
flen = strlen((char*)fbuf);
|
||||
if (kiss_debug) {
|
||||
kiss_debug_print (TO_CLIENT, "Fake command prompt", fbuf, flen);
|
||||
|
@ -505,13 +604,11 @@ void kissnet_send_rec_packet (int chan, unsigned char *fbuf, int flen)
|
|||
kiss_len = strlen((char *)kiss_buff);
|
||||
}
|
||||
else {
|
||||
|
||||
|
||||
unsigned char stemp[AX25_MAX_PACKET_LEN + 1];
|
||||
|
||||
assert (flen < (int)(sizeof(stemp)));
|
||||
|
||||
stemp[0] = (chan << 4) + 0;
|
||||
stemp[0] = (chan << 4) | kiss_cmd;
|
||||
memcpy (stemp+1, fbuf, flen);
|
||||
|
||||
if (kiss_debug >= 2) {
|
||||
|
@ -532,98 +629,41 @@ void kissnet_send_rec_packet (int chan, unsigned char *fbuf, int flen)
|
|||
}
|
||||
|
||||
#if __WIN32__
|
||||
err = send (client_sock, (char*)kiss_buff, kiss_len, 0);
|
||||
err = SOCK_SEND(client_sock[client], (char*)kiss_buff, kiss_len);
|
||||
if (err == SOCKET_ERROR)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nError %d sending message to KISS client application. Closing connection.\n\n", WSAGetLastError());
|
||||
closesocket (client_sock);
|
||||
client_sock = -1;
|
||||
dw_printf ("\nError %d sending message to KISS client %d application. Closing connection.\n\n", WSAGetLastError(), client);
|
||||
closesocket (client_sock[client]);
|
||||
client_sock[client] = -1;
|
||||
WSACleanup();
|
||||
}
|
||||
#else
|
||||
err = write (client_sock, kiss_buff, kiss_len);
|
||||
err = SOCK_SEND (client_sock[client], kiss_buff, kiss_len);
|
||||
if (err <= 0)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nError sending message to KISS client application. Closing connection.\n\n");
|
||||
close (client_sock);
|
||||
client_sock = -1;
|
||||
dw_printf ("\nError sending message to KISS client %d application. Closing connection.\n\n", client);
|
||||
close (client_sock[client]);
|
||||
client_sock[client] = -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
} /* end kissnet_send_rec_packet */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: read_from_socket
|
||||
*
|
||||
* Purpose: Read from socket until we have desired number of bytes.
|
||||
*
|
||||
* Inputs: fd - file descriptor.
|
||||
* ptr - address where data should be placed.
|
||||
* len - desired number of bytes.
|
||||
*
|
||||
* Description: Just a wrapper for the "read" system call but it should
|
||||
* never return fewer than the desired number of bytes.
|
||||
*
|
||||
* Not really needed for KISS because we are dealing with
|
||||
* a stream of bytes rather than message blocks.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static int read_from_socket (int fd, char *ptr, int len)
|
||||
{
|
||||
int got_bytes = 0;
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("read_from_socket (%d, %p, %d)\n", fd, ptr, len);
|
||||
#endif
|
||||
while (got_bytes < len) {
|
||||
int n;
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
//TODO: any flags for send/recv?
|
||||
//TODO: Would be useful to have more detailed explanation from the error code.
|
||||
|
||||
n = recv (fd, ptr + got_bytes, len - got_bytes, 0);
|
||||
#else
|
||||
n = read (fd, ptr + got_bytes, len - got_bytes);
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("read_from_socket: n = %d\n", n);
|
||||
#endif
|
||||
if (n <= 0) {
|
||||
return (n);
|
||||
}
|
||||
|
||||
got_bytes += n;
|
||||
}
|
||||
assert (got_bytes >= 0 && got_bytes <= len);
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("read_from_socket: return %d\n", got_bytes);
|
||||
#endif
|
||||
return (got_bytes);
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: kissnet_listen_thread
|
||||
*
|
||||
* Purpose: Wait for KISS messages from an application.
|
||||
*
|
||||
* Inputs: arg - Not used.
|
||||
* Inputs: arg - client number, 0 .. MAX_NET_CLIENTS-1
|
||||
*
|
||||
* Outputs: client_sock - File descriptor for communicating with client app.
|
||||
* Outputs: client_sock[n] - File descriptor for communicating with client app.
|
||||
*
|
||||
* Description: Process messages from the client application.
|
||||
* Note that the client can go away and come back again and
|
||||
|
@ -635,20 +675,20 @@ static int read_from_socket (int fd, char *ptr, int len)
|
|||
/* Return one byte (value 0 - 255) */
|
||||
|
||||
|
||||
static int kiss_get (void)
|
||||
static int kiss_get (int client)
|
||||
{
|
||||
unsigned char ch;
|
||||
int n;
|
||||
|
||||
while (1) {
|
||||
|
||||
while (client_sock <= 0) {
|
||||
while (client_sock[client] <= 0) {
|
||||
SLEEP_SEC(1); /* Not connected. Try again later. */
|
||||
}
|
||||
|
||||
/* Just get one byte at a time. */
|
||||
|
||||
n = read_from_socket (client_sock, (char *)(&ch), 1);
|
||||
n = SOCK_RECV (client_sock[client], (char *)(&ch), 1);
|
||||
|
||||
if (n == 1) {
|
||||
#if DEBUG9
|
||||
|
@ -668,13 +708,13 @@ static int kiss_get (void)
|
|||
}
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nError reading KISS byte from client application. Closing connection.\n\n");
|
||||
dw_printf ("\nKISS client application %d has gone away.\n\n", client);
|
||||
#if __WIN32__
|
||||
closesocket (client_sock);
|
||||
closesocket (client_sock[client]);
|
||||
#else
|
||||
close (client_sock);
|
||||
close (client_sock[client]);
|
||||
#endif
|
||||
client_sock = -1;
|
||||
client_sock[client] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -684,14 +724,32 @@ static THREAD_F kissnet_listen_thread (void *arg)
|
|||
{
|
||||
unsigned char ch;
|
||||
|
||||
|
||||
int client = (int)(long)arg;
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("kissnet_listen_thread ( socket = %d )\n", client_sock);
|
||||
dw_printf ("kissnet_listen_thread ( client = %d, socket fd = %d )\n", client, client_sock[client]);
|
||||
#endif
|
||||
|
||||
assert (client >= 0 && client < MAX_NET_CLIENTS);
|
||||
|
||||
|
||||
// So why is kissnet_send_rec_packet mentioned here for incoming from the client app?
|
||||
// The logic exists for the serial port case where the client might think it is
|
||||
// attached to a traditional TNC. It might try sending commands over and over again
|
||||
// trying to get the TNC into KISS mode. To keep it happy, we recognize this attempt
|
||||
// and send it something to keep it happy.
|
||||
// In the case of a serial port or pseudo terminal, there is only one potential client
|
||||
// so the response would be sent to only one place.
|
||||
// Starting in version 1.5, this now can have multiple attached clients. We wouldn't
|
||||
// want to send the response to all of them. Actually, we should be providing only
|
||||
// "Simply KISS" as some call it.
|
||||
|
||||
|
||||
while (1) {
|
||||
ch = kiss_get();
|
||||
kiss_rec_byte (&kf, ch, kiss_debug, kissnet_send_rec_packet);
|
||||
ch = kiss_get(client);
|
||||
kiss_rec_byte (&(kf[client]), ch, kiss_debug, client, kissnet_send_rec_packet);
|
||||
}
|
||||
|
||||
#if __WIN32__
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
void kissnet_init (struct misc_config_s *misc_config);
|
||||
|
||||
void kissnet_send_rec_packet (int chan, unsigned char *fbuf, int flen);
|
||||
void kissnet_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, int client);
|
||||
|
||||
void kiss_net_set_debug (int n);
|
||||
|
||||
|
|
|
@ -0,0 +1,501 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2011, 2013, 2014, 2016, 2017 John Langner, WB2OSZ
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Module: kissserial.c
|
||||
*
|
||||
* Purpose: Act as a virtual KISS TNC for use by other packet radio applications.
|
||||
* This file provides the service by good old fashioned serial port.
|
||||
* Other files implement a pseudo terminal or TCP KISS interface.
|
||||
*
|
||||
* Description: This implements the KISS TNC protocol as described in:
|
||||
* http://www.ka9q.net/papers/kiss.html
|
||||
*
|
||||
* Briefly, a frame is composed of
|
||||
*
|
||||
* * FEND (0xC0)
|
||||
* * Contents - with special escape sequences so a 0xc0
|
||||
* byte in the data is not taken as end of frame.
|
||||
* as part of the data.
|
||||
* * FEND
|
||||
*
|
||||
* The first byte of the frame contains:
|
||||
*
|
||||
* * port number in upper nybble.
|
||||
* * command in lower nybble.
|
||||
*
|
||||
* Commands from application recognized:
|
||||
*
|
||||
* _0 Data Frame AX.25 frame in raw format.
|
||||
*
|
||||
* _1 TXDELAY See explanation in xmit.c.
|
||||
*
|
||||
* _2 Persistence " "
|
||||
*
|
||||
* _3 SlotTime " "
|
||||
*
|
||||
* _4 TXtail " "
|
||||
* Spec says it is obsolete but Xastir
|
||||
* sends it and we respect it.
|
||||
*
|
||||
* _5 FullDuplex Ignored.
|
||||
*
|
||||
* _6 SetHardware TNC specific.
|
||||
*
|
||||
* FF Return Exit KISS mode. Ignored.
|
||||
*
|
||||
*
|
||||
* Messages sent to client application:
|
||||
*
|
||||
* _0 Data Frame Received AX.25 frame in raw format.
|
||||
*
|
||||
*
|
||||
* Platform differences:
|
||||
*
|
||||
* This file implements KISS over a serial port.
|
||||
* It should behave pretty much the same for both Windows and Linux.
|
||||
*
|
||||
* When running a client application on Windows, two applications
|
||||
* can be connected together using a a "Null-modem emulator"
|
||||
* such as com0com from http://sourceforge.net/projects/com0com/
|
||||
*
|
||||
* (When running a client application, on the same host, with Linux,
|
||||
* a pseudo terminal can be used for old applications. More modern
|
||||
* applications will generally have AGW and/or KISS over TCP.)
|
||||
*
|
||||
*
|
||||
* version 1.5: Split out from kiss.c, simplified, consistent for Windows and Linux.
|
||||
* Add polling option for use with Bluetooth.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
#include "serial_port.h"
|
||||
#include "kissserial.h"
|
||||
#include "kiss_frame.h"
|
||||
#include "xmit.h"
|
||||
|
||||
|
||||
/*
|
||||
* Save Configuration for later use.
|
||||
*/
|
||||
|
||||
static struct misc_config_s *g_misc_config_p;
|
||||
|
||||
/*
|
||||
* Accumulated KISS frame and state of decoder.
|
||||
*/
|
||||
|
||||
static kiss_frame_t kf;
|
||||
|
||||
|
||||
/*
|
||||
* The serial port device handle.
|
||||
* MYFD... are defined in kissserial.h
|
||||
*/
|
||||
|
||||
static MYFDTYPE serialport_fd = MYFDERROR;
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO: define in one place, use everywhere.
|
||||
#if __WIN32__
|
||||
#define THREAD_F unsigned __stdcall
|
||||
#else
|
||||
#define THREAD_F void *
|
||||
#endif
|
||||
|
||||
static THREAD_F kissserial_listen_thread (void *arg);
|
||||
|
||||
|
||||
static int kissserial_debug = 0; /* Print information flowing from and to client. */
|
||||
|
||||
void kissserial_set_debug (int n)
|
||||
{
|
||||
kissserial_debug = n;
|
||||
}
|
||||
|
||||
|
||||
/* In server.c. Should probably move to some misc. function file. */
|
||||
|
||||
void hex_dump (unsigned char *p, int len);
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: kissserial_init
|
||||
*
|
||||
* Purpose: Set up a serial port acting as a virtual KISS TNC.
|
||||
*
|
||||
* Inputs: mc->
|
||||
* kiss_serial_port - Name of device for real or virtual serial port.
|
||||
* kiss_serial_speed - Speed, bps, or 0 meaning leave it alone.
|
||||
* kiss_serial_poll - When non-zero, poll each n seconds to see if
|
||||
* device has appeared.
|
||||
*
|
||||
* Outputs:
|
||||
*
|
||||
* Description: (1) Open file descriptor for the device.
|
||||
* (2) Start a new thread to listen for commands from client app
|
||||
* so the main application doesn't block while we wait.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
void kissserial_init (struct misc_config_s *mc)
|
||||
{
|
||||
|
||||
#if __WIN32__
|
||||
HANDLE kissserial_listen_th;
|
||||
#else
|
||||
pthread_t kissserial_listen_tid;
|
||||
int e;
|
||||
#endif
|
||||
|
||||
g_misc_config_p = mc;
|
||||
|
||||
memset (&kf, 0, sizeof(kf));
|
||||
|
||||
|
||||
if (strlen(g_misc_config_p->kiss_serial_port) > 0) {
|
||||
|
||||
if (g_misc_config_p->kiss_serial_poll == 0) {
|
||||
|
||||
// Normal case, try to open the serial port at start up time.
|
||||
|
||||
serialport_fd = serial_port_open (g_misc_config_p->kiss_serial_port, g_misc_config_p->kiss_serial_speed);
|
||||
|
||||
if (serialport_fd != MYFDERROR) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Opened %s for serial port KISS.\n", g_misc_config_p->kiss_serial_port);
|
||||
}
|
||||
else {
|
||||
// An error message was already displayed.
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// Polling case. Defer until read and device not opened.
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Will be checking periodically for %s\n", g_misc_config_p->kiss_serial_port);
|
||||
}
|
||||
|
||||
if (g_misc_config_p->kiss_serial_poll != 0 || serialport_fd != MYFDERROR) {
|
||||
#if __WIN32__
|
||||
kissserial_listen_th = (HANDLE)_beginthreadex (NULL, 0, kissserial_listen_thread, NULL, 0, NULL);
|
||||
if (kissserial_listen_th == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Could not create kiss serial thread\n");
|
||||
return;
|
||||
}
|
||||
#else
|
||||
e = pthread_create (&kissserial_listen_tid, NULL, kissserial_listen_thread, NULL);
|
||||
if (e != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
perror("Could not create kiss serial thread.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if DEBUG
|
||||
text_color_set (DW_COLOR_DEBUG);
|
||||
|
||||
dw_printf ("end of kiss_init: serialport_fd = %d, polling = %d\n", serialport_fd, g_misc_config_p->kiss_serial_poll);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: kissserial_send_rec_packet
|
||||
*
|
||||
* Purpose: Send a received packet or text string to the client app.
|
||||
*
|
||||
* Inputs: chan - Channel number where packet was received.
|
||||
* 0 = first, 1 = second if any.
|
||||
*
|
||||
* kiss_cmd - Usually KISS_CMD_DATA_FRAME but we can also have
|
||||
* KISS_CMD_SET_HARDWARE when responding to a query.
|
||||
*
|
||||
* pp - Identifier for packet object.
|
||||
*
|
||||
* fbuf - Address of raw received frame buffer
|
||||
* or a text string.
|
||||
*
|
||||
* flen - Length of raw received frame not including the FCS
|
||||
* or -1 for a text string.
|
||||
*
|
||||
* client - Not used for serial port version.
|
||||
* Here so that 3 related functions all have
|
||||
* the same parameter list.
|
||||
*
|
||||
* Description: Send message to client.
|
||||
* We really don't care if anyone is listening or not.
|
||||
* I don't even know if we can find out.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
void kissserial_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, int client)
|
||||
{
|
||||
unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN + 2];
|
||||
int kiss_len;
|
||||
int err;
|
||||
|
||||
|
||||
/*
|
||||
* Quietly discard if we don't have open connection.
|
||||
*/
|
||||
if (serialport_fd == MYFDERROR) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (flen < 0) {
|
||||
flen = strlen((char*)fbuf);
|
||||
if (kissserial_debug) {
|
||||
kiss_debug_print (TO_CLIENT, "Fake command prompt", fbuf, flen);
|
||||
}
|
||||
strlcpy ((char *)kiss_buff, (char *)fbuf, sizeof(kiss_buff));
|
||||
kiss_len = strlen((char *)kiss_buff);
|
||||
}
|
||||
else {
|
||||
|
||||
unsigned char stemp[AX25_MAX_PACKET_LEN + 1];
|
||||
|
||||
if (flen > (int)(sizeof(stemp)) - 1) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nSerial Port KISS buffer too small. Truncated.\n\n");
|
||||
flen = (int)(sizeof(stemp)) - 1;
|
||||
}
|
||||
|
||||
stemp[0] = (chan << 4) | kiss_cmd;
|
||||
memcpy (stemp+1, fbuf, flen);
|
||||
|
||||
if (kissserial_debug >= 2) {
|
||||
/* AX.25 frame with the CRC removed. */
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("\n");
|
||||
dw_printf ("Packet content before adding KISS framing and any escapes:\n");
|
||||
hex_dump (fbuf, flen);
|
||||
}
|
||||
|
||||
kiss_len = kiss_encapsulate (stemp, flen+1, kiss_buff);
|
||||
|
||||
/* This has KISS framing and escapes for sending to client app. */
|
||||
|
||||
if (kissserial_debug) {
|
||||
kiss_debug_print (TO_CLIENT, NULL, kiss_buff, kiss_len);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This write can block on Windows if using the virtual null modem
|
||||
* and nothing is connected to the other end.
|
||||
* The solution is found in the com0com ReadMe file:
|
||||
*
|
||||
* Q. My application hangs during its startup when it sends anything to one paired
|
||||
* COM port. The only way to unhang it is to start HyperTerminal, which is connected
|
||||
* to the other paired COM port. I didn't have this problem with physical serial
|
||||
* ports.
|
||||
* A. Your application can hang because receive buffer overrun is disabled by
|
||||
* default. You can fix the problem by enabling receive buffer overrun for the
|
||||
* receiving port. Also, to prevent some flow control issues you need to enable
|
||||
* baud rate emulation for the sending port. So, if your application use port CNCA0
|
||||
* and other paired port is CNCB0, then:
|
||||
*
|
||||
* 1. Launch the Setup Command Prompt shortcut.
|
||||
* 2. Enter the change commands, for example:
|
||||
*
|
||||
* command> change CNCB0 EmuOverrun=yes
|
||||
* command> change CNCA0 EmuBR=yes
|
||||
*/
|
||||
|
||||
err = serial_port_write (serialport_fd, (char*)kiss_buff, kiss_len);
|
||||
|
||||
if (err != kiss_len)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nError sending KISS message to client application thru serial port.\n\n");
|
||||
serial_port_close (serialport_fd);
|
||||
serialport_fd = MYFDERROR;
|
||||
}
|
||||
|
||||
} /* kissserial_send_rec_packet */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: kissserial_get
|
||||
*
|
||||
* Purpose: Read one byte from the KISS client app.
|
||||
*
|
||||
* Global In: serialport_fd
|
||||
*
|
||||
* Returns: one byte (value 0 - 255) or terminate thread on error.
|
||||
*
|
||||
* Description: There is room for improvment here. Reading one byte
|
||||
* at a time is inefficient. We could read a large block
|
||||
* into a local buffer and return a byte from that most of the time.
|
||||
* Is it worth the effort? I don't know. With GHz processors and
|
||||
* the low data rate here it might not make a noticable difference.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static int kissserial_get (void)
|
||||
{
|
||||
int ch; // normally 0-255 but -1 for error.
|
||||
|
||||
|
||||
if (g_misc_config_p->kiss_serial_poll == 0) {
|
||||
/*
|
||||
* Normal case, was opened at start up time.
|
||||
*/
|
||||
ch = serial_port_get1 (serialport_fd);
|
||||
|
||||
if (ch < 0) {
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nSerial Port KISS read error. Closing connection.\n\n");
|
||||
serial_port_close (serialport_fd);
|
||||
serialport_fd = MYFDERROR;
|
||||
#if __WIN32__
|
||||
ExitThread (0);
|
||||
#else
|
||||
pthread_exit (NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if DEBUGx
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("kissserial_get(%d) returns 0x%02x\n", fd, ch);
|
||||
#endif
|
||||
return (ch);
|
||||
}
|
||||
|
||||
/*
|
||||
* Polling case. Wait until device is present and open.
|
||||
*/
|
||||
while (1) {
|
||||
|
||||
if (serialport_fd != MYFDERROR) {
|
||||
|
||||
// Open, try to read.
|
||||
|
||||
ch = serial_port_get1 (serialport_fd);
|
||||
|
||||
if (ch >= 0) {
|
||||
return (ch);
|
||||
}
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nSerial Port KISS read error. Closing connection.\n\n");
|
||||
serial_port_close (serialport_fd);
|
||||
serialport_fd = MYFDERROR;
|
||||
}
|
||||
else {
|
||||
|
||||
// Not open. Wait for it to appear and try opening.
|
||||
|
||||
struct stat buf;
|
||||
|
||||
SLEEP_SEC (g_misc_config_p->kiss_serial_poll);
|
||||
|
||||
if (stat(g_misc_config_p->kiss_serial_port, &buf) == 0) {
|
||||
|
||||
// It's there now. Try to open.
|
||||
|
||||
serialport_fd = serial_port_open (g_misc_config_p->kiss_serial_port, g_misc_config_p->kiss_serial_speed);
|
||||
|
||||
if (serialport_fd != MYFDERROR) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("\nOpened %s for serial port KISS.\n\n", g_misc_config_p->kiss_serial_port);
|
||||
|
||||
memset (&kf, 0, sizeof(kf)); // Start with clean state.
|
||||
}
|
||||
else {
|
||||
// An error message was already displayed.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* end kissserial_get */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: kissserial_listen_thread
|
||||
*
|
||||
* Purpose: Read messages from serial port KISS client application.
|
||||
*
|
||||
* Global In: serialport_fd
|
||||
*
|
||||
* Description: Reads bytes from the serial port KISS client app and
|
||||
* sends them to kiss_rec_byte for processing.
|
||||
* kiss_rec_byte is a common function used by all 3 KISS
|
||||
* interfaces: serial port, pseudo terminal, and TCP.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static THREAD_F kissserial_listen_thread (void *arg)
|
||||
{
|
||||
unsigned char ch;
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("kissserial_listen_thread ( %d )\n", fd);
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
ch = kissserial_get();
|
||||
kiss_rec_byte (&kf, ch, kissserial_debug, -1, kissserial_send_rec_packet);
|
||||
}
|
||||
|
||||
#if __WIN32__
|
||||
return(0);
|
||||
#else
|
||||
return (THREAD_F) 0; /* Unreachable but avoids compiler warning. */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* end kissserial.c */
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
/*
|
||||
* Name: kissserial.h
|
||||
*/
|
||||
|
||||
|
||||
#include "ax25_pad.h" /* for packet_t */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
|
||||
|
||||
|
||||
void kissserial_init (struct misc_config_s *misc_config);
|
||||
|
||||
void kissserial_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, int client);
|
||||
|
||||
void kissserial_set_debug (int n);
|
||||
|
||||
|
||||
/* end kissserial.h */
|
|
@ -0,0 +1,948 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2017 John Langner, WB2OSZ
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Module: kissutil.c
|
||||
*
|
||||
* Purpose: Utility for talking to a KISS TNC.
|
||||
*
|
||||
* Description: Convert between KISS format and usual text representation.
|
||||
* This might also serve as the starting point for an application
|
||||
* that uses a KISS TNC.
|
||||
* The TNC can be attached by TCP or a serial port.
|
||||
*
|
||||
* Usage: kissutil [ options ]
|
||||
*
|
||||
* Default is to connect to localhost:8001.
|
||||
* See the "usage" functions at the bottom for details.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h" // Sets _WIN32_WINNT for XP API level needed by ws2tcpip.h
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h> // _WIN32_WINNT must be set to 0x0501 before including this
|
||||
|
||||
#else
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
#include "serial_port.h"
|
||||
#include "kiss_frame.h"
|
||||
#include "sock.h"
|
||||
#include "dtime_now.h"
|
||||
#include "audio.h" // for DEFAULT_TXDELAY, etc.
|
||||
#include "dtime_now.h"
|
||||
|
||||
|
||||
// TODO: define in one place, use everywhere.
|
||||
#if __WIN32__
|
||||
#define THREAD_F unsigned __stdcall
|
||||
#else
|
||||
#define THREAD_F void *
|
||||
#endif
|
||||
|
||||
#if __WIN32__
|
||||
#define DIR_CHAR "\\"
|
||||
#else
|
||||
#define DIR_CHAR "/"
|
||||
#endif
|
||||
|
||||
static THREAD_F tnc_listen_net (void *arg);
|
||||
static THREAD_F tnc_listen_serial (void *arg);
|
||||
|
||||
static void send_to_kiss_tnc (int chan, int cmd, char *data, int dlen);
|
||||
static void hex_dump (unsigned char *p, int len);
|
||||
|
||||
static void usage(void);
|
||||
static void usage2(void);
|
||||
|
||||
|
||||
|
||||
|
||||
/* Obtained from the command line. */
|
||||
|
||||
static char hostname[50] = "localhost"; /* -h option. */
|
||||
/* DNS host name or IPv4 address. */
|
||||
/* Some of the code is there for IPv6 but */
|
||||
/* it needs more work. */
|
||||
/* Defaults to "localhost" if not specified. */
|
||||
|
||||
static char port[30] = "8001"; /* -p option. */
|
||||
/* If it begins with a digit, it is considered */
|
||||
/* a TCP port number at the hostname. */
|
||||
/* Otherwise, we treat it as a serial port name. */
|
||||
|
||||
static int using_tcp = 1; /* Are we using TCP or serial port for TNC? */
|
||||
/* Use corresponding one of the next two. */
|
||||
/* This is derived from the first character of port. */
|
||||
|
||||
static int server_sock = -1; /* File descriptor for socket interface. */
|
||||
/* Set to -1 if not used. */
|
||||
/* (Don't use SOCKET type because it is unsigned.) */
|
||||
|
||||
static MYFDTYPE serial_fd = (MYFDTYPE)(-1); /* Serial port handle. */
|
||||
|
||||
static int serial_speed = 9600; /* -s option. */
|
||||
/* Serial port speed, bps. */
|
||||
|
||||
static int verbose = 0; /* -v option. */
|
||||
/* Display the KISS protocol in hexadecimal for troubleshooting. */
|
||||
|
||||
static char transmit_from[120] = ""; /* -f option */
|
||||
/* When specified, files are read from this directory */
|
||||
/* rather than using stdin. Each file is one or more */
|
||||
/* lines in the standard monitoring format. */
|
||||
|
||||
static char receive_output[120] = ""; /* -o option */
|
||||
/* When specified, each received frame is stored as a file */
|
||||
/* with a unique name here. */
|
||||
/* Directory must already exist; we won't create it. */
|
||||
|
||||
static char timestamp_format[60] = ""; /* -T option */
|
||||
/* Precede received frames with timestamp. */
|
||||
/* Command line option uses "strftime" format string. */
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
#define THREAD_F unsigned __stdcall
|
||||
#else
|
||||
#define THREAD_F void *
|
||||
#endif
|
||||
|
||||
#if __WIN32__
|
||||
static HANDLE tnc_th;
|
||||
#else
|
||||
static pthread_t tnc_tid;
|
||||
#endif
|
||||
|
||||
static void process_input (char *stuff);
|
||||
|
||||
/* Trim any CR, LF from the end of line. */
|
||||
|
||||
static void trim (char *stuff)
|
||||
{
|
||||
char *p;
|
||||
p = stuff + strlen(stuff) - 1;
|
||||
while (strlen(stuff) > 0 && (*p == '\r' || *p == '\n')) {
|
||||
*p = '\0';
|
||||
p--;
|
||||
}
|
||||
} /* end trim */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: main
|
||||
*
|
||||
* Purpose: Attach to KISS TNC and exchange information.
|
||||
*
|
||||
* Usage: See "usage" functions at end.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
text_color_init (0); // Turn off text color.
|
||||
// It could interfere with trying to pipe stdout to some other application.
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
#else
|
||||
int e;
|
||||
setlinebuf (stdout); // TODO: What is the Windows equivalent?
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Extract command line args.
|
||||
*/
|
||||
while (1) {
|
||||
int option_index = 0;
|
||||
int c;
|
||||
static struct option long_options[] = {
|
||||
//{"future1", 1, 0, 0},
|
||||
//{"future2", 0, 0, 0},
|
||||
//{"future3", 1, 0, 'c'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
/* ':' following option character means arg is required. */
|
||||
|
||||
c = getopt_long(argc, argv, "h:p:s:vf:o:T:",
|
||||
long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
|
||||
case 'h': /* -h for hostname. */
|
||||
strlcpy (hostname, optarg, sizeof(hostname));
|
||||
break;
|
||||
|
||||
case 'p': /* -p for port, either TCP or serial device. */
|
||||
strlcpy (port, optarg, sizeof(port));
|
||||
break;
|
||||
|
||||
case 's': /* -s for serial port speed. */
|
||||
serial_speed = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'v': /* -v for verbose. */
|
||||
verbose++;
|
||||
break;
|
||||
|
||||
case 'f': /* -f for transmit files directory. */
|
||||
strlcpy (transmit_from, optarg, sizeof(transmit_from));
|
||||
break;
|
||||
|
||||
case 'o': /* -o for receive output directory. */
|
||||
strlcpy (receive_output, optarg, sizeof(receive_output));
|
||||
break;
|
||||
|
||||
case 'T': /* -T for receive timestamp. */
|
||||
strlcpy (timestamp_format, optarg, sizeof(timestamp_format));
|
||||
break;
|
||||
|
||||
case '?':
|
||||
/* Unknown option message was already printed. */
|
||||
usage ();
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Should not be here. */
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf("?? getopt returned character code 0%o ??\n", c);
|
||||
usage ();
|
||||
}
|
||||
} /* end while(1) for options */
|
||||
|
||||
if (optind < argc) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Warning: Unused command line arguments are ignored.\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* If receive queue directory was specified, make sure that it exists.
|
||||
*/
|
||||
if (strlen(receive_output) > 0) {
|
||||
struct stat s;
|
||||
|
||||
if (stat(receive_output, &s) == 0) {
|
||||
if ( ! S_ISDIR(s.st_mode)) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Receive queue location, %s, is not a directory.\n", receive_output);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Receive queue location, %s, does not exist.\n", receive_output);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* If port begins with digit, consider it to be TCP. */
|
||||
/* Otherwise, treat as serial port name. */
|
||||
|
||||
using_tcp = isdigit(port[0]);
|
||||
|
||||
#if __WIN32__
|
||||
if (using_tcp) {
|
||||
tnc_th = (HANDLE)_beginthreadex (NULL, 0, tnc_listen_net, (void *)99, 0, NULL);
|
||||
}
|
||||
else {
|
||||
tnc_th = (HANDLE)_beginthreadex (NULL, 0, tnc_listen_serial, (void *)99, 0, NULL);
|
||||
}
|
||||
if (tnc_th == NULL) {
|
||||
printf ("Internal error: Could not create TNC listen thread.\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
#else
|
||||
if (using_tcp) {
|
||||
e = pthread_create (&tnc_tid, NULL, tnc_listen_net, (void *)(long)99);
|
||||
}
|
||||
else {
|
||||
e = pthread_create (&tnc_tid, NULL, tnc_listen_serial, (void *)(long)99);
|
||||
}
|
||||
if (e != 0) {
|
||||
perror("Internal error: Could not create TNC listen thread.");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Process keyboard or other input source.
|
||||
*/
|
||||
char stuff[1000];
|
||||
|
||||
if (strlen(transmit_from) > 0) {
|
||||
/*
|
||||
* Process and delete all files in specified directory.
|
||||
* When done, sleep for a second and try again.
|
||||
* This doesn't take them in any particular order.
|
||||
* A future enhancement might sort by name or timestamp.
|
||||
*/
|
||||
while (1) {
|
||||
DIR *dp;
|
||||
struct dirent *ep;
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf("Get directory listing...\n");
|
||||
|
||||
dp = opendir (transmit_from);
|
||||
if (dp != NULL) {
|
||||
while ((ep = readdir(dp)) != NULL) {
|
||||
char path [300];
|
||||
FILE *fp;
|
||||
if (ep->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Processing %s for transmit...\n", ep->d_name);
|
||||
strlcpy (path, transmit_from, sizeof(path));
|
||||
strlcat (path, DIR_CHAR, sizeof(path));
|
||||
strlcat (path, ep->d_name, sizeof(path));
|
||||
fp = fopen (path, "r");
|
||||
if (fp != NULL) {
|
||||
while (fgets(stuff, sizeof(stuff), fp) != NULL) {
|
||||
trim (stuff);
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("%s\n", stuff);
|
||||
// TODO: Don't delete file if errors encountered?
|
||||
process_input (stuff);
|
||||
}
|
||||
fclose (fp);
|
||||
unlink (path);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Can't open for read: %s\n", path);
|
||||
}
|
||||
}
|
||||
closedir (dp);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Can't access transmit queue directory %s. Quitting.\n", transmit_from);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
SLEEP_SEC (1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Using stdin.
|
||||
*/
|
||||
while (fgets(stuff, sizeof(stuff), stdin) != NULL) {
|
||||
process_input (stuff);
|
||||
}
|
||||
}
|
||||
|
||||
return (EXIT_SUCCESS);
|
||||
|
||||
} /* end main */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: process_input
|
||||
*
|
||||
* Purpose: Process frames/commands from user, either interactively or from files.
|
||||
*
|
||||
* Inputs: stuff - A frame is in usual format like SOURCE>DEST,DIGI:whatever.
|
||||
* Commands begin with lower case letter.
|
||||
* Note that it can be modified by this function.
|
||||
*
|
||||
* Later Enhancement: Return success/fail status. The transmit queue processing might want
|
||||
* to preserve files that were not processed as expected.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static int parse_number (char *str, int de_fault)
|
||||
{
|
||||
int n;
|
||||
|
||||
while (isspace(*str)) {
|
||||
str++;
|
||||
}
|
||||
if (strlen(str) == 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Missing number for KISS command. Using default %d.\n", de_fault);
|
||||
return (de_fault);
|
||||
}
|
||||
n = atoi(str);
|
||||
if (n < 0 || n > 255) { // must fit in a byte.
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Number for KISS command is out of range 0-255. Using default %d.\n", de_fault);
|
||||
return (de_fault);
|
||||
}
|
||||
return (n);
|
||||
}
|
||||
|
||||
static void process_input (char *stuff)
|
||||
{
|
||||
char *p;
|
||||
int chan = 0;
|
||||
|
||||
/*
|
||||
* Remove any end of line character(s).
|
||||
*/
|
||||
trim (stuff);
|
||||
|
||||
/*
|
||||
* Optional prefix, like "[9]" or "[99]" to specify channel.
|
||||
*/
|
||||
p = stuff;
|
||||
while (isspace(*p)) p++;
|
||||
if (*p == '[') {
|
||||
p++;
|
||||
if (p[1] == ']') {
|
||||
chan = atoi(p);
|
||||
p += 2;
|
||||
}
|
||||
else if (p[2] == ']') {
|
||||
chan = atoi(p);
|
||||
p += 3;
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR! One or two digit channel number and ] was expected after [ at beginning of line.\n");
|
||||
usage2();
|
||||
return;
|
||||
}
|
||||
if (chan < 0 || chan > 15) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR! KISS channel number must be in range of 0 thru 15.\n");
|
||||
usage2();
|
||||
return;
|
||||
}
|
||||
while (isspace(*p)) p++;
|
||||
}
|
||||
|
||||
/*
|
||||
* If it starts with upper case letter or digit, assume it is an AX.25 frame in monitor format.
|
||||
* Lower case is a command (e.g. Persistence or set Hardware).
|
||||
* Anything else, print explanation of what is expected.
|
||||
*/
|
||||
if (isupper(*p) || isdigit(*p)) {
|
||||
|
||||
// Parse the "TNC2 monitor format" and convert to AX.25 frame.
|
||||
|
||||
unsigned char frame_data[AX25_MAX_PACKET_LEN];
|
||||
packet_t pp = ax25_from_text (p, 1);
|
||||
if (pp != NULL) {
|
||||
int frame_len = ax25_pack (pp, frame_data);
|
||||
send_to_kiss_tnc (chan, KISS_CMD_DATA_FRAME, (char*)frame_data, frame_len);
|
||||
ax25_delete (pp);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR! Could not convert to AX.25 frame: %s\n", p);
|
||||
}
|
||||
}
|
||||
else if (islower(*p)) {
|
||||
char value;
|
||||
|
||||
switch (*p) {
|
||||
case 'd': // txDelay, 10ms units
|
||||
value = parse_number(p+1, DEFAULT_TXDELAY);
|
||||
send_to_kiss_tnc (chan, KISS_CMD_TXDELAY, &value, 1);
|
||||
break;
|
||||
case 'p': // Persistence
|
||||
value = parse_number(p+1, DEFAULT_PERSIST);
|
||||
send_to_kiss_tnc (chan, KISS_CMD_PERSISTENCE, &value, 1);
|
||||
break;
|
||||
case 's': // Slot time, 10ms units
|
||||
value = parse_number(p+1, DEFAULT_SLOTTIME);
|
||||
send_to_kiss_tnc (chan, KISS_CMD_SLOTTIME, &value, 1);
|
||||
break;
|
||||
case 't': // txTail, 10ms units
|
||||
value = parse_number(p+1, DEFAULT_TXTAIL);
|
||||
send_to_kiss_tnc (chan, KISS_CMD_TXTAIL, &value, 1);
|
||||
break;
|
||||
case 'f': // Full duplex
|
||||
value = parse_number(p+1, 0);
|
||||
send_to_kiss_tnc (chan, KISS_CMD_FULLDUPLEX, &value, 1);
|
||||
break;
|
||||
case 'h': // set Hardware
|
||||
p++;
|
||||
while (*p != '\0' && isspace(*p)) { p++; }
|
||||
send_to_kiss_tnc (chan, KISS_CMD_SET_HARDWARE, p, strlen(p));
|
||||
break;
|
||||
default:
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Invalid command. Must be one of d p s t f h.\n");
|
||||
usage2 ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
usage2 ();
|
||||
}
|
||||
|
||||
} /* end process_input */
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: send_to_kiss_tnc
|
||||
*
|
||||
* Purpose: Encapsulate the data/command, into a KISS frame, and send to the TNC.
|
||||
*
|
||||
* Inputs: chan - channel number.
|
||||
*
|
||||
* cmd - KISS_CMD_DATA_FRAME, KISS_CMD_SET_HARDWARE, etc.
|
||||
*
|
||||
* data - Information for KISS frame.
|
||||
*
|
||||
* dlen - Number of bytes in data.
|
||||
*
|
||||
* Description: Encapsulate as KISS frame and send to TNC.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static void send_to_kiss_tnc (int chan, int cmd, char *data, int dlen)
|
||||
{
|
||||
unsigned char temp[1000];
|
||||
unsigned char kissed[2000];
|
||||
int klen;
|
||||
|
||||
if (chan < 0 || chan > 15) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR - Invalid channel %d - must be in range 0 to 15.\n", chan);
|
||||
chan = 0;
|
||||
}
|
||||
if (cmd < 0 || cmd > 15) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR - Invalid command %d - must be in range 0 to 15.\n", cmd);
|
||||
cmd = 0;
|
||||
}
|
||||
if (dlen < 0 || dlen > (int)(sizeof(temp)-1)) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR - Invalid data length %d - must be in range 0 to %d.\n", dlen, (int)(sizeof(temp)-1));
|
||||
dlen = sizeof(temp)-1;
|
||||
}
|
||||
|
||||
temp[0] = (chan << 4) | cmd;
|
||||
memcpy (temp+1, data, dlen);
|
||||
|
||||
klen = kiss_encapsulate(temp, dlen+1, kissed);
|
||||
|
||||
if (verbose) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Sending to KISS TNC:\n");
|
||||
hex_dump (kissed, klen);
|
||||
}
|
||||
|
||||
if (using_tcp) {
|
||||
int rc = SOCK_SEND(server_sock, (char*)kissed, klen);
|
||||
if (rc != klen) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR writing KISS frame to socket.\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
int rc = serial_port_write (serial_fd, (char*)kissed, klen);
|
||||
if (rc != klen) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR writing KISS frame to serial port.\n");
|
||||
}
|
||||
}
|
||||
|
||||
} /* end send_to_kiss_tnc */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: tnc_listen_net
|
||||
*
|
||||
* Purpose: Connect to KISS TNC via TCP port.
|
||||
* Print everything it sends to us.
|
||||
*
|
||||
* Inputs: arg - Currently not used.
|
||||
*
|
||||
* Global In: host
|
||||
* port
|
||||
*
|
||||
* Global Out: server_sock - Needed to send to the TNC.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static THREAD_F tnc_listen_net (void *arg)
|
||||
{
|
||||
int err;
|
||||
char ipaddr_str[SOCK_IPADDR_LEN]; // Text form of IP address.
|
||||
char data[4096];
|
||||
int allow_ipv6 = 0; // Maybe someday.
|
||||
int debug = 0;
|
||||
int client = 0; // Not used in this situation.
|
||||
kiss_frame_t kstate;
|
||||
|
||||
memset (&kstate, 0, sizeof(kstate));
|
||||
|
||||
err = sock_init ();
|
||||
if (err < 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Network interface failure. Can't go on.\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Connect to network KISS TNC.
|
||||
*/
|
||||
// For the IGate we would loop around and try to reconnect if the TNC
|
||||
// goes away. We should probably do the same here.
|
||||
|
||||
server_sock = sock_connect (hostname, port, "TCP KISS TNC", allow_ipv6, debug, ipaddr_str);
|
||||
|
||||
if (server_sock == -1) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
// Should have been a message already. What else is there to say?
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print what we get from TNC.
|
||||
*/
|
||||
int len;
|
||||
|
||||
while ((len = SOCK_RECV (server_sock, (char*)(data), sizeof(data))) > 0) {
|
||||
int j;
|
||||
for (j = 0; j < len; j++) {
|
||||
|
||||
// Feed in one byte at a time.
|
||||
// kiss_process_msg is called when a complete frame has been accumulated.
|
||||
|
||||
// When verbose is specified, we get debug output like this:
|
||||
//
|
||||
// <<< Data frame from KISS client application, port 0, total length = 46
|
||||
// 000: c0 00 82 a0 88 ae 62 6a e0 ae 84 64 9e a6 b4 ff ......bj...d....
|
||||
// ...
|
||||
// It says "from KISS client application" because it was written
|
||||
// on the assumption it was being used in only one direction.
|
||||
// Not worried enough about it to do anything at this time.
|
||||
|
||||
kiss_rec_byte (&kstate, data[j], verbose, client, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Read error from TCP KISS TNC. Terminating.\n");
|
||||
exit (EXIT_FAILURE);
|
||||
|
||||
} /* end tnc_listen_net */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: tnc_listen_serial
|
||||
*
|
||||
* Purpose: Connect to KISS TNC via serial port.
|
||||
* Print everything it sends to us.
|
||||
*
|
||||
* Inputs: arg - Currently not used.
|
||||
*
|
||||
* Global In: port
|
||||
* serial_speed
|
||||
*
|
||||
* Global Out: serial_fd - Need for sending to the TNC.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static THREAD_F tnc_listen_serial (void *arg)
|
||||
{
|
||||
int client = 0;
|
||||
kiss_frame_t kstate;
|
||||
|
||||
memset (&kstate, 0, sizeof(kstate));
|
||||
|
||||
serial_fd = serial_port_open (port, serial_speed);
|
||||
|
||||
if (serial_fd == MYFDERROR) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Unable to connect to KISS TNC serial port %s.\n", port);
|
||||
#if __WIN32__
|
||||
#else
|
||||
// More detail such as "permission denied" or "no such device"
|
||||
dw_printf("%s\n", strerror(errno));
|
||||
#endif
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read and print.
|
||||
*/
|
||||
while (1) {
|
||||
int ch;
|
||||
|
||||
ch = serial_port_get1(serial_fd);
|
||||
|
||||
if (ch < 0) {
|
||||
dw_printf("Read error from serial port KISS TNC.\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Feed in one byte at a time.
|
||||
// kiss_process_msg is called when a complete frame has been accumulated.
|
||||
|
||||
kiss_rec_byte (&kstate, ch, verbose, client, NULL);
|
||||
}
|
||||
|
||||
} /* end tnc_listen_serial */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: kiss_process_msg
|
||||
*
|
||||
* Purpose: Process a frame from the KISS TNC.
|
||||
* This is called when a complete frame has been accumulated.
|
||||
* In this case, we simply print it.
|
||||
*
|
||||
* Inputs: kiss_msg - Kiss frame with FEND and escapes removed.
|
||||
* The first byte contains channel and command.
|
||||
*
|
||||
* kiss_len - Number of bytes including the command.
|
||||
*
|
||||
* debug - Debug option is selected.
|
||||
*
|
||||
* client - Not used in this case.
|
||||
*
|
||||
* sendfun - Not used in this case.
|
||||
*
|
||||
*-----------------------------------------------------------------*/
|
||||
|
||||
void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int))
|
||||
{
|
||||
int chan;
|
||||
int cmd;
|
||||
packet_t pp;
|
||||
alevel_t alevel;
|
||||
|
||||
chan = (kiss_msg[0] >> 4) & 0xf;
|
||||
cmd = kiss_msg[0] & 0xf;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case KISS_CMD_DATA_FRAME: /* 0 = Data Frame */
|
||||
|
||||
memset (&alevel, 0, sizeof(alevel));
|
||||
pp = ax25_from_frame (kiss_msg+1, kiss_len-1, alevel);
|
||||
if (pp == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
printf ("ERROR - Invalid KISS data frame from TNC.\n");
|
||||
}
|
||||
else {
|
||||
char prefix[100]; // Channel and optional timestamp.
|
||||
// Like [0] or [2 12:34:56]
|
||||
|
||||
char addrs[AX25_MAX_ADDRS*AX25_MAX_ADDR_LEN]; // Like source>dest,digi,...,digi:
|
||||
unsigned char *pinfo;
|
||||
int info_len;
|
||||
|
||||
if (strlen(timestamp_format) > 0) {
|
||||
char ts[100];
|
||||
timestamp_user_format (ts, sizeof(ts), timestamp_format);
|
||||
snprintf (prefix, sizeof(prefix), "[%d %s]", chan, ts);
|
||||
}
|
||||
else {
|
||||
snprintf (prefix, sizeof(prefix), "[%d]", chan);
|
||||
}
|
||||
|
||||
ax25_format_addrs (pp, addrs);
|
||||
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
|
||||
text_color_set(DW_COLOR_REC);
|
||||
|
||||
dw_printf ("%s %s", prefix, addrs); // [channel] Addresses followed by :
|
||||
|
||||
// Safe print will replace any unprintable characters with
|
||||
// hexadecimal representation.
|
||||
|
||||
ax25_safe_print ((char *)pinfo, info_len, 0);
|
||||
dw_printf ("\n");
|
||||
#if __WIN32__
|
||||
fflush (stdout);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Add to receive queue directory if specified.
|
||||
* File name will be based on current local time.
|
||||
* If you want UTC, just set an environment variable like this:
|
||||
*
|
||||
* TZ=UTC kissutil ...
|
||||
*/
|
||||
if (strlen(receive_output) > 0) {
|
||||
char fname [30];
|
||||
char path [300];
|
||||
FILE *fp;
|
||||
|
||||
timestamp_filename (fname, (int)sizeof(fname));
|
||||
|
||||
strlcpy (path, receive_output, sizeof(path));
|
||||
strlcat (path, DIR_CHAR, sizeof(path));
|
||||
strlcat (path, fname, sizeof(path));
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Save received frame to %s\n", path);
|
||||
fp = fopen (path, "w");
|
||||
if (fp != NULL) {
|
||||
fprintf (fp, "%s %s%s\n", prefix, addrs, pinfo);
|
||||
fclose (fp);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Unable to open for write: %s\n", path);
|
||||
}
|
||||
}
|
||||
|
||||
ax25_delete (pp);
|
||||
}
|
||||
break;
|
||||
|
||||
case KISS_CMD_SET_HARDWARE: /* 6 = TNC specific */
|
||||
|
||||
kiss_msg[kiss_len] = '\0';
|
||||
text_color_set(DW_COLOR_REC);
|
||||
// Display as "h ..." for in/out symmetry.
|
||||
// Use safe print here?
|
||||
dw_printf ("[%d] h %s\n", chan, (char*)(kiss_msg+1));
|
||||
break;
|
||||
|
||||
/*
|
||||
* The rest should only go TO the TNC and not come FROM it.
|
||||
*/
|
||||
case KISS_CMD_TXDELAY: /* 1 = TXDELAY */
|
||||
case KISS_CMD_PERSISTENCE: /* 2 = Persistence */
|
||||
case KISS_CMD_SLOTTIME: /* 3 = SlotTime */
|
||||
case KISS_CMD_TXTAIL: /* 4 = TXtail */
|
||||
case KISS_CMD_FULLDUPLEX: /* 5 = FullDuplex */
|
||||
case KISS_CMD_END_KISS: /* 15 = End KISS mode, port should be 15. */
|
||||
default:
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
printf ("Unexpected KISS command %d, channel %d\n", cmd, chan);
|
||||
break;
|
||||
}
|
||||
|
||||
} /* end kiss_process_msg */
|
||||
|
||||
|
||||
// TODO: We have multiple copies of this. Move to some misc file.
|
||||
|
||||
void hex_dump (unsigned char *p, int len)
|
||||
{
|
||||
int n, i, offset;
|
||||
|
||||
offset = 0;
|
||||
while (len > 0) {
|
||||
n = len < 16 ? len : 16;
|
||||
printf (" %03x: ", offset);
|
||||
for (i=0; i<n; i++) {
|
||||
printf (" %02x", p[i]);
|
||||
}
|
||||
for (i=n; i<16; i++) {
|
||||
printf (" ");
|
||||
}
|
||||
printf (" ");
|
||||
for (i=0; i<n; i++) {
|
||||
printf ("%c", isprint(p[i]) ? p[i] : '.');
|
||||
}
|
||||
printf ("\n");
|
||||
p += 16;
|
||||
offset += 16;
|
||||
len -= 16;
|
||||
}
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("\n");
|
||||
dw_printf ("kissutil - Utility for testing a KISS TNC.\n");
|
||||
dw_printf ("\n");
|
||||
dw_printf ("Convert between KISS format and usual text representation.\n");
|
||||
dw_printf ("The TNC can be attached by TCP or a serial port.\n");
|
||||
dw_printf ("\n");
|
||||
dw_printf ("Usage: kissutil [ options ]\n");
|
||||
dw_printf ("\n");
|
||||
dw_printf (" -h hostname of TCP KISS TNC, default localhost.\n");
|
||||
dw_printf (" -p port, default 8001.\n");
|
||||
dw_printf (" If it does not start with a digit, it is\n");
|
||||
dw_printf (" a serial port. e.g. /dev/ttyAMA0 or COM3.\n");
|
||||
dw_printf (" -s Serial port speed, default 9600.\n");
|
||||
dw_printf (" -v Verbose. Show the KISS frame contents.\n");
|
||||
dw_printf (" -f Transmit files directory. Processs and delete files here.\n");
|
||||
dw_printf (" -o Receive output queue directory. Store received frames here.\n");
|
||||
dw_printf (" -T Precede received frames with 'strftime' format time stamp.\n");
|
||||
usage2();
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static void usage2 (void)
|
||||
{
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("\n");
|
||||
dw_printf ("Input, starting with upper case letter or digit, is assumed\n");
|
||||
dw_printf ("to be an AX.25 frame in the usual TNC2 monitoring format.\n");
|
||||
dw_printf ("\n");
|
||||
dw_printf ("Input, starting with a lower case letter is a commmand.\n");
|
||||
dw_printf ("Whitespace, as shown in examples, is optional.\n");
|
||||
dw_printf ("\n");
|
||||
dw_printf (" letter meaning example\n");
|
||||
dw_printf (" ------ ------- -------\n");
|
||||
dw_printf (" d txDelay, 10ms units d 30\n");
|
||||
dw_printf (" p Persistence p 63\n");
|
||||
dw_printf (" s Slot time, 10ms units s 10\n");
|
||||
dw_printf (" t txTail, 10ms units t 5\n");
|
||||
dw_printf (" f Full duplex f 0\n");
|
||||
dw_printf (" h set Hardware h TNC:\n");
|
||||
dw_printf ("\n");
|
||||
dw_printf (" Lines may be preceded by the form \"[9]\" to indicate a\n");
|
||||
dw_printf (" channel other than the default 0.\n");
|
||||
dw_printf ("\n");
|
||||
}
|
||||
|
||||
/* end kissutil.c */
|
147
latlong.c
147
latlong.c
|
@ -59,6 +59,17 @@
|
|||
*
|
||||
* Returns: None
|
||||
*
|
||||
* Idea for future:
|
||||
* Non zero ambiguity removes least significant digits without rounding.
|
||||
* Maybe we could use -1 and -2 to add extra digits using !DAO! as
|
||||
* documented in http://www.aprs.org/datum.txt
|
||||
*
|
||||
* For example, -1 adds one more human readable digit.
|
||||
* lat minutes 12.345 would produce "12.34" and !W5 !
|
||||
*
|
||||
* -2 would encode almost 2 digits in base 91.
|
||||
* lat minutes 10.0027 would produce "10.00" and !w: !
|
||||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
void latitude_to_str (double dlat, int ambiguity, char *slat)
|
||||
|
@ -555,6 +566,92 @@ double ll_distance_km (double lat1, double lon1, double lat2, double lon2)
|
|||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: ll_bearing_deg
|
||||
*
|
||||
* Purpose: Calculate bearing between two locations.
|
||||
*
|
||||
* Inputs: lat1, lon1 - starting location, in degrees.
|
||||
* lat2, lon2 - destination location
|
||||
*
|
||||
* Returns: Initial Bearing, in degrees.
|
||||
* The calculation produces Range +- 180 degrees.
|
||||
* But I think that 0 - 360 would be more customary?
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
double ll_bearing_deg (double lat1, double lon1, double lat2, double lon2)
|
||||
{
|
||||
double b;
|
||||
|
||||
lat1 *= M_PI / 180;
|
||||
lon1 *= M_PI / 180;
|
||||
lat2 *= M_PI / 180;
|
||||
lon2 *= M_PI / 180;
|
||||
|
||||
b = atan2 (sin(lon2-lon1) * cos(lat2),
|
||||
cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon2-lon1));
|
||||
|
||||
b *= 180 / M_PI;
|
||||
if (b < 0) b += 360;
|
||||
|
||||
return (b);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: ll_dest_lat
|
||||
* ll_dest_lon
|
||||
*
|
||||
* Purpose: Calculate the destination location given a starting point,
|
||||
* distance, and bearing,
|
||||
*
|
||||
* Inputs: lat1, lon1 - starting location, in degrees.
|
||||
* dist - distance in km.
|
||||
* bearing - direction in degrees. Shouldn't matter
|
||||
* if it is in +- 180 or 0 to 360 range.
|
||||
*
|
||||
* Returns: New latitude or longitude.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
double ll_dest_lat (double lat1, double lon1, double dist, double bearing)
|
||||
{
|
||||
double lat2;
|
||||
|
||||
lat1 *= M_PI / 180; // Everything to radians.
|
||||
lon1 *= M_PI / 180;
|
||||
bearing *= M_PI / 180;
|
||||
|
||||
lat2 = asin(sin(lat1) * cos(dist/R) + cos(lat1) * sin(dist/R) * cos(bearing));
|
||||
|
||||
lat2 *= 180 / M_PI; // Back to degrees.
|
||||
|
||||
return (lat2);
|
||||
}
|
||||
|
||||
double ll_dest_lon (double lat1, double lon1, double dist, double bearing)
|
||||
{
|
||||
double lon2;
|
||||
double lat2;
|
||||
|
||||
lat1 *= M_PI / 180; // Everything to radians.
|
||||
lon1 *= M_PI / 180;
|
||||
bearing *= M_PI / 180;
|
||||
|
||||
lat2 = asin(sin(lat1) * cos(dist/R) + cos(lat1) * sin(dist/R) * cos(bearing));
|
||||
|
||||
lon2 = lon1 + atan2(sin(bearing) * sin(dist/R) * cos(lat1), cos(dist/R) - sin(lat1) * sin(lat2));
|
||||
|
||||
lon2 *= 180 / M_PI; // Back to degrees.
|
||||
|
||||
return (lon2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: ll_from_grid_square
|
||||
|
@ -776,6 +873,7 @@ int main (int argc, char *argv[])
|
|||
int errors = 0;
|
||||
int ok;
|
||||
double dlat, dlon;
|
||||
double d, b;
|
||||
|
||||
/* Latitude to APRS format. */
|
||||
|
||||
|
@ -860,6 +958,55 @@ int main (int argc, char *argv[])
|
|||
// to be continued for others... NMEA...
|
||||
|
||||
|
||||
/* Distance & bearing - Take a couple examples from other places and see if we get similar results. */
|
||||
|
||||
// http://www.movable-type.co.uk/scripts/latlong.html
|
||||
|
||||
d = ll_distance_km (35., 45., 35., 135.);
|
||||
b = ll_bearing_deg (35., 45., 35., 135.);
|
||||
|
||||
if (d < 7862 || d > 7882) { errors++; dw_printf ("Error 5.1: Did not expect distance %.1f\n", d); }
|
||||
|
||||
if (b < 59.7 || b > 60.3) { errors++; dw_printf ("Error 5.2: Did not expect bearing %.1f\n", b); }
|
||||
|
||||
// Sydney to Kinsale. https://woodshole.er.usgs.gov/staffpages/cpolloni/manitou/ccal.htm
|
||||
|
||||
d = ll_distance_km (-33.8688, 151.2093, 51.7059, -8.5222);
|
||||
b = ll_bearing_deg (-33.8688, 151.2093, 51.7059, -8.5222);
|
||||
|
||||
if (d < 17435 || d > 17455) { errors++; dw_printf ("Error 5.3: Did not expect distance %.1f\n", d); }
|
||||
|
||||
if (b < 327-1 || b > 327+1) { errors++; dw_printf ("Error 5.4: Did not expect bearing %.1f\n", b); }
|
||||
|
||||
|
||||
/*
|
||||
* More distance and bearing.
|
||||
* Here we will start at some location1 (lat1,lon1) and go some distance (d1) at some bearing (b1).
|
||||
* This results in a new location2 (lat2, lon2).
|
||||
* We then calculate the distance and bearing from location1 to location2 and compare with the intention.
|
||||
*/
|
||||
int lat1, lon1, d1 = 10, b1;
|
||||
double lat2, lon2, d2, b2;
|
||||
|
||||
for (lat1 = -60; lat1 <= 60; lat1 += 30) {
|
||||
for (lon1 = -180; lon1 <= 180; lon1 +=30) {
|
||||
for (b1 = 0; b1 < 360; b1 += 15) {
|
||||
|
||||
lat2 = ll_dest_lat ((double)lat1, (double)lon1, (double)d1, (double)b1);
|
||||
lon2 = ll_dest_lon ((double)lat1, (double)lon1, (double)d1, (double)b1);
|
||||
|
||||
d2 = ll_distance_km ((double)lat1, (double)lon1, lat2, lon2);
|
||||
b2 = ll_bearing_deg ((double)lat1, (double)lon1, lat2, lon2);
|
||||
if (b2 > 359.9 && b2 < 360.1) b2 = 0;
|
||||
|
||||
// must be within 0.1% of distance and 0.1 degree.
|
||||
if (d2 < 0.999 * d1 || d2 > 1.001 * d1) { errors++; dw_printf ("Error 5.8: lat1=%d, lon2=%d, d1=%d, b1=%d, d2=%.2f\n", lat1, lon1, d1, b1, d2); }
|
||||
if (b2 < b1 - 0.1 || b2 > b1 + 0.1) { errors++; dw_printf ("Error 5.9: lat1=%d, lon2=%d, d1=%d, b1=%d, b2=%.2f\n", lat1, lon1, d1, b1, b2); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Maidenhead locator to lat/long. */
|
||||
|
||||
|
||||
|
|
129
log.c
129
log.c
|
@ -28,6 +28,13 @@
|
|||
* unreadable, format, write separated properties into
|
||||
* CSV format for easy reading and later processing.
|
||||
*
|
||||
* There are two alternatives here.
|
||||
*
|
||||
* -L logfile Specify full file path.
|
||||
*
|
||||
* -l logdir Daily names will be created here.
|
||||
*
|
||||
* Use one or the other but not both.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
|
@ -91,26 +98,39 @@ static void quote_for_csv (char *out, size_t outsize, const char *in) {
|
|||
*
|
||||
* Purpose: Initialization at start of application.
|
||||
*
|
||||
* Inputs: path - Path of log file directory.
|
||||
* Inputs: daily_names - True if daily names should be generated.
|
||||
* In this case path is a directory.
|
||||
* When false, path would be the file name.
|
||||
*
|
||||
* path - Log file name or just directory.
|
||||
* Use "." for current directory.
|
||||
* Empty string disables feature.
|
||||
*
|
||||
* Global Out: g_log_dir - Save directory here for later use.
|
||||
* Global Out: g_daily_names - True if daily names should be generated.
|
||||
*
|
||||
* g_log_path - Save directory or full name here for later use.
|
||||
*
|
||||
* g_log_fp - File pointer for writing.
|
||||
* Note that file is kept open.
|
||||
* We don't open/close for every new item.
|
||||
*
|
||||
* g_open_fname - Name of currently open file.
|
||||
* Applicable only when g_daily_names is true.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
static char g_log_dir[80];
|
||||
static int g_daily_names;
|
||||
static char g_log_path[80];
|
||||
static FILE *g_log_fp;
|
||||
static char g_open_fname[20];
|
||||
|
||||
|
||||
void log_init (char *path)
|
||||
void log_init (int daily_names, char *path)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
strlcpy (g_log_dir, "", sizeof(g_log_dir));
|
||||
g_daily_names = daily_names;
|
||||
strlcpy (g_log_path, "", sizeof(g_log_path));
|
||||
g_log_fp = NULL;
|
||||
strlcpy (g_open_fname, "", sizeof(g_open_fname));
|
||||
|
||||
|
@ -118,17 +138,21 @@ void log_init (char *path)
|
|||
return;
|
||||
}
|
||||
|
||||
if (g_daily_names) {
|
||||
|
||||
// Original strategy. Automatic daily file names.
|
||||
|
||||
if (stat(path,&st) == 0) {
|
||||
// Exists, but is it a directory?
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
// Specified directory exists.
|
||||
strlcpy (g_log_dir, path, sizeof(g_log_dir));
|
||||
strlcpy (g_log_path, path, sizeof(g_log_path));
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Log file location \"%s\" is not a directory.\n", path);
|
||||
dw_printf ("Using current working directory \".\" instead.\n");
|
||||
strlcpy (g_log_dir, ".", sizeof(g_log_dir));
|
||||
strlcpy (g_log_path, ".", sizeof(g_log_path));
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -143,17 +167,28 @@ void log_init (char *path)
|
|||
// Success.
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Log file location \"%s\" has been created.\n", path);
|
||||
strlcpy (g_log_dir, path, sizeof(g_log_dir));
|
||||
strlcpy (g_log_path, path, sizeof(g_log_path));
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Failed to create log file location \"%s\".\n", path);
|
||||
dw_printf ("%s\n", strerror(errno));
|
||||
dw_printf ("Using current working directory \".\" instead.\n");
|
||||
strlcpy (g_log_dir, ".", sizeof(g_log_dir));
|
||||
strlcpy (g_log_path, ".", sizeof(g_log_path));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// Added in version 1.5. Single file.
|
||||
// Typically logrotate would be used to keep size under control.
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Log file is \"%s\"\n", path);
|
||||
strlcpy (g_log_path, path, sizeof(g_log_path));
|
||||
}
|
||||
|
||||
} /* end log_init */
|
||||
|
||||
|
||||
|
||||
|
@ -177,17 +212,25 @@ void log_init (char *path)
|
|||
|
||||
void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_t retries)
|
||||
{
|
||||
time_t now; // make 'now' a parameter so we can process historical data ???
|
||||
char fname[20];
|
||||
time_t now;
|
||||
struct tm tm;
|
||||
|
||||
|
||||
if (strlen(g_log_dir) == 0) return;
|
||||
if (strlen(g_log_path) == 0) return;
|
||||
|
||||
now = time(NULL); // Get current time.
|
||||
(void)gmtime_r (&now, &tm);
|
||||
|
||||
|
||||
if (g_daily_names) {
|
||||
|
||||
// Original strategy. Automatic daily file names.
|
||||
|
||||
char fname[20];
|
||||
|
||||
// Generate the file name from current date, UTC.
|
||||
|
||||
now = time(NULL);
|
||||
(void)gmtime_r (&now, &tm);
|
||||
// Why UTC rather than local time? I don't recall the reasoning.
|
||||
// It's been there a few years and no on complained so leave it alone for now.
|
||||
|
||||
// Microsoft doesn't recognize %F as equivalent to %Y-%m-%d
|
||||
|
||||
|
@ -206,7 +249,7 @@ void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_
|
|||
struct stat st;
|
||||
int already_there;
|
||||
|
||||
strlcpy (full_path, g_log_dir, sizeof(full_path));
|
||||
strlcpy (full_path, g_log_path, sizeof(full_path));
|
||||
#if __WIN32__
|
||||
strlcat (full_path, "\\", sizeof(full_path));
|
||||
#else
|
||||
|
@ -214,10 +257,10 @@ void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_
|
|||
#endif
|
||||
strlcat (full_path, fname, sizeof(full_path));
|
||||
|
||||
// See if it already exists.
|
||||
// See if file already exists and not empty.
|
||||
// This is used later to write a header if it did not exist already.
|
||||
|
||||
already_there = stat(full_path,&st) == 0;
|
||||
already_there = (stat(full_path,&st) == 0) && (st.st_size > 0);
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf("Opening log file \"%s\".\n", fname);
|
||||
|
@ -239,9 +282,49 @@ void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_
|
|||
// only if this will be the first line.
|
||||
|
||||
if ( ! already_there) {
|
||||
fprintf (g_log_fp, "chan,utime,isotime,source,heard,level,error,dti,name,symbol,latitude,longitude,speed,course,altitude,frequency,offset,tone,system,status,comment\n");
|
||||
fprintf (g_log_fp, "chan,utime,isotime,source,heard,level,error,dti,name,symbol,latitude,longitude,speed,course,altitude,frequency,offset,tone,system,status,telemetry,comment\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// Added in version 1.5. Single file.
|
||||
|
||||
// Open for append if not already open.
|
||||
|
||||
if (g_log_fp == NULL) {
|
||||
struct stat st;
|
||||
int already_there;
|
||||
|
||||
// See if file already exists and not empty.
|
||||
// This is used later to write a header if it did not exist already.
|
||||
|
||||
already_there = (stat(g_log_path,&st) == 0) && (st.st_size > 0);
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf("Opening log file \"%s\"\n", g_log_path);
|
||||
|
||||
g_log_fp = fopen (g_log_path, "a");
|
||||
|
||||
if (g_log_fp == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Can't open log file \"%s\" for write.\n", g_log_path);
|
||||
dw_printf ("%s\n", strerror(errno));
|
||||
strlcpy (g_log_path, "", sizeof(g_log_path));
|
||||
return;
|
||||
}
|
||||
|
||||
// Write a header suitable for importing into a spreadsheet
|
||||
// only if this will be the first line.
|
||||
|
||||
if ( ! already_there) {
|
||||
fprintf (g_log_fp, "chan,utime,isotime,source,heard,level,error,dti,name,symbol,latitude,longitude,speed,course,altitude,frequency,offset,tone,system,status,telemetry,comment\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add line to file if it is now open.
|
||||
|
||||
if (g_log_fp != NULL) {
|
||||
|
||||
|
@ -439,7 +522,13 @@ void log_term (void)
|
|||
if (g_log_fp != NULL) {
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
|
||||
if (g_daily_names) {
|
||||
dw_printf("Closing log file \"%s\".\n", g_open_fname);
|
||||
}
|
||||
else {
|
||||
dw_printf("Closing log file \"%s\".\n", g_log_path);
|
||||
}
|
||||
|
||||
fclose (g_log_fp);
|
||||
|
||||
|
|
2
log.h
2
log.h
|
@ -10,7 +10,7 @@
|
|||
|
||||
|
||||
|
||||
void log_init (char *path);
|
||||
void log_init (int daily_names, char *path);
|
||||
|
||||
void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_t retries);
|
||||
|
||||
|
|
|
@ -48,5 +48,5 @@ of how to set up tests and the results.
|
|||
.SH SEE ALSO
|
||||
More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location.
|
||||
|
||||
Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, ll2utm, log2gpx, text2tt, tt2text, utm2ll
|
||||
Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll
|
||||
|
||||
|
|
|
@ -87,5 +87,5 @@ Try different combinations of options to find the best decoding performance.
|
|||
.SH SEE ALSO
|
||||
More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location.
|
||||
|
||||
Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, ll2utm, log2gpx, text2tt, tt2text, utm2ll
|
||||
Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll
|
||||
|
||||
|
|
|
@ -9,7 +9,9 @@ decode_aprs \- Convert APRS raw data to human readable form.
|
|||
[ \fItext-file\fR ]
|
||||
.RS
|
||||
.P
|
||||
\fItext-file\fR should contain AX.25 packets in the standard monitoring format.
|
||||
\fItext-file\fR should contain AX.25 packets in the standard monitoring format or
|
||||
as a series two digit hexadecimal numbers.
|
||||
If the first number is 00 or c0, it will be treated as a KISS frame.
|
||||
If no file specified, data will be read from stdin.
|
||||
.P
|
||||
.RE
|
||||
|
@ -43,12 +45,16 @@ Pipe it into decode_aprs to find out.
|
|||
http://www.findu.com/cgi-bin/errors.cgi has a never-ending collection of packets
|
||||
with errors. Sometimes it's not obvious what is wrong with them.
|
||||
Dire Wolf will usually tell you what is wrong. First,
|
||||
cut-n-paste the bad packets into a text file. Here a couple examples:
|
||||
cut-n-paste the bad packets into a text file. Here a few examples:
|
||||
.P
|
||||
.RS
|
||||
.nf
|
||||
n2cma>APRS,TCPIP*,qAC,SEVENTH:@212127z43.2333n/77.1w_338/002g001t025P000h65b10208.wview_5_19_0
|
||||
.P
|
||||
K0YTH-10>APNU3B,NULL,qAR,K0DMF-10:!4601.5NS09255.52W#PHG6360/W2,MNn 444.575
|
||||
.P
|
||||
00 82 a0 ae ae 62 60 e0 82 96 68 84 40 40 60 9c 68 b0 ae 86 40 e0 40 ae 92 88 8a 64 63 03 f0 3e 45 4d 36 34 6e 65 2f 23 20 45 63 68 6f 6c 69 6e 6b 20 31 34 35 2e 33 31 30 2f 31 30 30 68 7a 20 54 6f 6e 65
|
||||
+.fi
|
||||
.RE
|
||||
.P
|
||||
If you simply fed this into decode_aprs, it would complain about the
|
||||
|
@ -67,7 +73,7 @@ Address has lower case letters. "n2cma" must be all upper case.
|
|||
.P
|
||||
After changing the source address to upper case, there are other issues. Identifying them is left as an exercise for the reader.
|
||||
.P
|
||||
And in the second example,
|
||||
In the second example,
|
||||
.P
|
||||
.RS
|
||||
.PD 0
|
||||
|
@ -77,6 +83,17 @@ Invalid character in longitude. Found '9' when expecting 0 or 1 for hundreds of
|
|||
.PD
|
||||
.RE
|
||||
|
||||
.P
|
||||
In the third example,
|
||||
.P
|
||||
.RS
|
||||
.PD 0
|
||||
Warning: Lower case letter in Maidenhead locator. Specification requires upper case.
|
||||
.P
|
||||
Digi2 Address, " WIDE2-1" contains character other than letter or digit in character position 1.
|
||||
.PD
|
||||
.RE
|
||||
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
|
|
|
@ -27,8 +27,12 @@ RMS Express, and many others.
|
|||
Read configuration file from specified location rather than the default locations.
|
||||
|
||||
.TP
|
||||
.BI "-l " "dir"
|
||||
Generate log files in specified directory. Use "." for current directory.
|
||||
.BI "-l " "logdir"
|
||||
Generate daily log files in specified directory. Use "." for current directory.
|
||||
|
||||
.TP
|
||||
.BI "-L " "logfile"
|
||||
Generate single log file with fixed name.
|
||||
|
||||
.TP
|
||||
.BI "-r " "n"
|
||||
|
@ -135,18 +139,18 @@ gqrx (2.3 and later) has the ability to send streaming audio through a UDP socke
|
|||
direwolf can listen over a UDP port with options like this:
|
||||
.RS
|
||||
.P
|
||||
direwolf -n 1 -r 48000 -b 16 udp:7355
|
||||
direwolf \-n 1 \-r 48000 \-b 16 udp:7355
|
||||
.RE
|
||||
.P
|
||||
Other SDR applications might produce audio on stdout so it is convenient to pipe into the next application. In this example, the final "-" means read from stdin.
|
||||
.RS
|
||||
.P
|
||||
rtl_fm -f 144.39M -o 4 - | direwolf -n 1 -r 24000 -b 16 -
|
||||
rtl_fm \-f 144.39M \-o 4 \- | direwolf \-n 1 \-r 24000 \-b 16 \-
|
||||
.RE
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location.
|
||||
|
||||
Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, ll2utm, log2gpx, text2tt, tt2text, utm2ll
|
||||
Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@ gen_packets \- Generate audio file for AX.25 frames.
|
|||
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B gen_packets -o
|
||||
.B gen_packets \-o
|
||||
.I wav-file-out
|
||||
[ \fIoptions\fR ] [ \fItext-file\fR | - ]
|
||||
[ \fIoptions\fR ] [ \fItext-file\fR | \- ]
|
||||
.RS
|
||||
.P
|
||||
\fIwav-file-out\fR is the result. The -o option is required.
|
||||
\fIwav-file-out\fR is the result. The \-o option is required.
|
||||
.P
|
||||
\fItext-file\fR may contain AX.25 packets in the standard monitoring format. Use "-" to read from stdin. If not specified, a default builtin message will be used.
|
||||
.RE
|
||||
|
@ -25,7 +25,7 @@ It is very flexible allowing a wide range of audio sample rates, data speeds, an
|
|||
|
||||
.TP
|
||||
.BI "-a " "n"
|
||||
Signal amplitude in range of 0 - 200%. Default 50. Note that 100% is corresponds to signal peaks of +/- 16383 so we have plenty of headroom to avoid saturation.
|
||||
Signal amplitude in range of 0-200%. Default 50. Note that 100% is corresponds to signal peaks of +/- 16383 so we have plenty of headroom to avoid saturation.
|
||||
|
||||
.TP
|
||||
.BI "-b " "n"
|
||||
|
@ -70,7 +70,7 @@ Send output to .wav file.
|
|||
|
||||
.SH EXAMPLES
|
||||
.P
|
||||
.B gen_packets -o x.wav
|
||||
.B gen_packets \-o x.wav
|
||||
.P
|
||||
.RS
|
||||
With all defaults, a built-in test message is generated
|
||||
|
@ -78,27 +78,27 @@ with standard Bell 202 tones used for packet radio on ordinary
|
|||
VHF FM transceivers.
|
||||
.RE
|
||||
.P
|
||||
.B gen_packets -o x.wav -g -b 9600
|
||||
.B gen_packets \-o x.wav \-g \-b 9600
|
||||
.PD 0
|
||||
.P
|
||||
.PD
|
||||
.B gen_packets -o x.wav -B 9600
|
||||
.B gen_packets \-o x.wav \-B 9600
|
||||
.P
|
||||
.RS
|
||||
Both of these are equivalent. "-B 9600" automatically selects scrambled baseband rather than AFSK.
|
||||
.RE
|
||||
.P
|
||||
.B gen_packets -o x.wav -m 1600 -s 1800 -b 300
|
||||
.B gen_packets \-o x.wav \-m 1600 \-s 1800 \-b 300
|
||||
.PD 0
|
||||
.P
|
||||
.PD
|
||||
.B gen_packets -o x.wav -B 300
|
||||
.B gen_packets \-o x.wav \-B 300
|
||||
.P
|
||||
.RS
|
||||
Both of these generate 200 Hz shift, 300 baud, suitable for HF SSB transceiver.
|
||||
.RE
|
||||
.P
|
||||
.B echo -n 'WB2OSZ>WORLD:Hello, world!' | gen_packets -a 25 -o x.wav -
|
||||
.B echo \-n 'WB2OSZ>WORLD:Hello, world!' | gen_packets \-a 25 \-o x.wav \-
|
||||
.PD 0
|
||||
.P
|
||||
.PD
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
.TH KISSUTIL 1
|
||||
|
||||
.SH NAME
|
||||
kissutil \- KISS TNC troubleshooting and Application Interface.
|
||||
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B kissutil
|
||||
[ \fIoptions\fR ]
|
||||
|
||||
|
||||
|
||||
.SH DESCRIPTION
|
||||
\fBkissutil\fR can be used interactively for troubleshooting a KISS TNC.
|
||||
It is usable with direwolf and other generic KISS TNCs connected to a serial port.
|
||||
It can also be used as an application interface where each side places files in a
|
||||
directory for the other to process.
|
||||
See User Guide for more details.
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BI "-h " "host"
|
||||
Hostname or IP address for a TCP KISS TNC. Default is localhost.
|
||||
|
||||
.TP
|
||||
.BI "-p " "port"
|
||||
A number may be specified for a TCP port other than the default 8001.
|
||||
If not a number, it is considered to be a serial port name such as /dev/ttyS0 or COM3.
|
||||
|
||||
.TP
|
||||
.BI "-s " "speed"
|
||||
Speed for serial port. e.g. 9600.
|
||||
|
||||
.TP
|
||||
.BI "-o " "rec-directory"
|
||||
For each received frame, a new file is created here.
|
||||
It is expected that some other application will process files in this directory then delete them.
|
||||
|
||||
.TP
|
||||
.BI "-T " "format"
|
||||
Each received frame will be preceded by a timestamp in the specified format.
|
||||
See strftime documentation for a description of the format string.
|
||||
Example: %H:%M:%S for current time in hours, minutes, seconds.
|
||||
|
||||
.TP
|
||||
.BI "-f " "xmit-directory"
|
||||
Files in this directory are transmited and deleted.
|
||||
Another application places a file here when it wants something to be transmitted.
|
||||
|
||||
.TP
|
||||
.BI "-v "
|
||||
Verbose - Display the KISS frames going to and from the TNC.
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location.
|
||||
|
||||
Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll
|
||||
|
|
@ -30,5 +30,5 @@ zone = 19T, easting = 307504, northing = 4721177
|
|||
.SH SEE ALSO
|
||||
More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location.
|
||||
|
||||
Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, ll2utm, log2gpx, text2tt, tt2text, utm2ll
|
||||
Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll
|
||||
|
||||
|
|
|
@ -36,5 +36,5 @@ None.
|
|||
.SH SEE ALSO
|
||||
More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location.
|
||||
|
||||
Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, ll2utm, log2gpx, text2tt, tt2text, utm2ll
|
||||
Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll
|
||||
|
||||
|
|
|
@ -60,5 +60,5 @@ Push buttons for two-key method:
|
|||
.SH SEE ALSO
|
||||
More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location.
|
||||
|
||||
Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, ll2utm, log2gpx, text2tt, tt2text, utm2ll
|
||||
Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll
|
||||
|
||||
|
|
|
@ -62,5 +62,5 @@ Decoded text from two-key method:
|
|||
.SH SEE ALSO
|
||||
More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location.
|
||||
|
||||
Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, ll2utm, log2gpx, text2tt, tt2text, utm2ll
|
||||
Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll
|
||||
|
||||
|
|
|
@ -36,5 +36,5 @@ latitude = 42.662139, longitude = -71.365553
|
|||
.SH SEE ALSO
|
||||
More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location.
|
||||
|
||||
Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, ll2utm, log2gpx, text2tt, tt2text, utm2ll
|
||||
Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll
|
||||
|
||||
|
|
|
@ -27,11 +27,11 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "direwolf.h"
|
||||
|
||||
#include "textcolor.h"
|
||||
|
||||
|
||||
|
|
|
@ -49,10 +49,11 @@
|
|||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "direwolf.h"
|
||||
|
||||
#include "textcolor.h"
|
||||
|
||||
/*
|
||||
|
|
|
@ -292,6 +292,14 @@ unsigned char is_crc_in_queue(unsigned int chan, unsigned int crc) {
|
|||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
static float dc_average[MAX_CHANS];
|
||||
|
||||
int multi_modem_get_dc_average (int chan)
|
||||
{
|
||||
// Scale to +- 200 so it will like the deviation measurement.
|
||||
|
||||
return ( (int) ((float)(dc_average[chan]) * (200.0f / 32767.0f) ) );
|
||||
}
|
||||
|
||||
__attribute__((hot))
|
||||
void multi_modem_process_sample (int chan, int audio_sample)
|
||||
|
@ -300,12 +308,29 @@ void multi_modem_process_sample (int chan, int audio_sample)
|
|||
int subchan;
|
||||
static int i = 0; /* for interleaving among multiple demodulators. */
|
||||
|
||||
// TODO: temp debug, remove this.
|
||||
// Accumulate an average DC bias level.
|
||||
// Shouldn't happen with a soundcard but could with mistuned SDR.
|
||||
|
||||
assert (save_audio_config_p->achan[chan].num_subchan > 0 && save_audio_config_p->achan[chan].num_subchan <= MAX_SUBCHANS);
|
||||
assert (save_audio_config_p->achan[chan].num_slicers > 0 && save_audio_config_p->achan[chan].num_slicers <= MAX_SLICERS);
|
||||
dc_average[chan] = dc_average[chan] * 0.999f + (float)audio_sample * 0.001f;
|
||||
|
||||
|
||||
// Issue 128. Someone ran into this.
|
||||
|
||||
//assert (save_audio_config_p->achan[chan].num_subchan > 0 && save_audio_config_p->achan[chan].num_subchan <= MAX_SUBCHANS);
|
||||
//assert (save_audio_config_p->achan[chan].num_slicers > 0 && save_audio_config_p->achan[chan].num_slicers <= MAX_SLICERS);
|
||||
|
||||
if (save_audio_config_p->achan[chan].num_subchan <= 0 || save_audio_config_p->achan[chan].num_subchan > MAX_SUBCHANS ||
|
||||
save_audio_config_p->achan[chan].num_slicers <= 0 || save_audio_config_p->achan[chan].num_slicers > MAX_SLICERS) {
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR! Something is seriously wrong in %s %s.\n", __FILE__, __func__);
|
||||
dw_printf ("chan = %d, num_subchan = %d [max %d], num_slicers = %d [max %d]\n", chan,
|
||||
save_audio_config_p->achan[chan].num_subchan, MAX_SUBCHANS,
|
||||
save_audio_config_p->achan[chan].num_slicers, MAX_SLICERS);
|
||||
dw_printf ("Please report this message and include a copy of your configuration file.\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Formerly one loop. */
|
||||
/* 1.2: We can feed one demodulator but end up with multiple outputs. */
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ void multi_modem_init (struct audio_s *pmodem);
|
|||
|
||||
void multi_modem_process_sample (int c, int audio_sample);
|
||||
|
||||
int multi_modem_get_dc_average (int chan);
|
||||
|
||||
void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries);
|
||||
|
||||
#endif
|
||||
|
|
274
pfilter.c
274
pfilter.c
|
@ -874,11 +874,15 @@ static int filt_t (pfstate_t *pf)
|
|||
|
||||
case 'p': /* Position */
|
||||
if (*infop == '!') return (1);
|
||||
if (*infop == '\'') return (1);
|
||||
if (*infop == '/') return (1);
|
||||
if (*infop == '=') return (1);
|
||||
if (*infop == '@') return (1);
|
||||
if (*infop == '`') return (1);
|
||||
if (*infop == '\'') return (1); // MIC-E
|
||||
if (*infop == '`') return (1); // MIC-E
|
||||
|
||||
// What if we have "_" symbol code for weather?
|
||||
// Still consider as position.
|
||||
// The same packet can match more than one type here.
|
||||
break;
|
||||
|
||||
case 'o': /* Object */
|
||||
|
@ -897,6 +901,11 @@ static int filt_t (pfstate_t *pf)
|
|||
if (*infop == '?') return (1);
|
||||
break;
|
||||
|
||||
case 'c': /* station Capabilities - my extension */
|
||||
/* Most often used for IGate statistics. */
|
||||
if (*infop == '<') return (1);
|
||||
break;
|
||||
|
||||
case 's': /* Status */
|
||||
if (*infop == '>') return (1);
|
||||
break;
|
||||
|
@ -910,16 +919,32 @@ static int filt_t (pfstate_t *pf)
|
|||
if (*infop == '{') return (1);
|
||||
break;
|
||||
|
||||
case 'h': /* third party Header - my extension */
|
||||
if (*infop == '}') return (1);
|
||||
break;
|
||||
|
||||
case 'w': /* Weather */
|
||||
if (*infop == '@') return (1);
|
||||
if (*infop == '*') return (1);
|
||||
if (*infop == '_') return (1);
|
||||
|
||||
if (*infop == '*') return (1); // Peet Bros
|
||||
if (*infop == '_') return (1); // Weather report, no position.
|
||||
if (strncmp(infop, "!!", 2) == 0) return(1); // Ultimeter 2000.
|
||||
|
||||
/* '$' is normally raw GPS. Check for special case. */
|
||||
|
||||
if (strncmp(infop, "$ULTW", 5) == 0) return (1);
|
||||
|
||||
/* TODO: Positions !=/@ can be weather. */
|
||||
/* Need to check for _ symbol. */
|
||||
/* Positions !=/@ with symbol code _ are weather. */
|
||||
|
||||
if (strchr("!=/@", *infop) != NULL &&
|
||||
pf->decoded.g_symbol_code == '_') return (1);
|
||||
|
||||
/* Object with _ symbol is also weather. APRS protocol spec page 66. */
|
||||
|
||||
if (*infop == ';' &&
|
||||
pf->decoded.g_symbol_code == '_') return (1);
|
||||
|
||||
// TODO: need more test cases at end for new weather cases.
|
||||
|
||||
break;
|
||||
|
||||
case 'n': /* NWS format */
|
||||
|
@ -1059,15 +1084,51 @@ static int filt_r (pfstate_t *pf, char *sdist)
|
|||
*
|
||||
* Description:
|
||||
*
|
||||
* s/pri
|
||||
* s/pri/alt
|
||||
* s/pri/alt/
|
||||
* s/pri/alt/over
|
||||
*
|
||||
* "pri" is zero or more symbols from the primary symbol set.
|
||||
* Symbol codes are any printable ASCII character other than | or ~.
|
||||
* (Zero symbols here would be sensible only if later alt part is specified.)
|
||||
* "alt" is one or more symbols from the alternate symbol set.
|
||||
* "over" is overlay characters. Overlays apply only to the alternate symbol set.
|
||||
* "over" is overlay characters for the alternate symbol set.
|
||||
* Only upper case letters, digits, and \ are allowed here.
|
||||
* If the last part is not specified, any overlay or lack of overlay, is ignored.
|
||||
* If the last part is specified, only the listed overlays will match.
|
||||
* An explicit lack of overlay is represented by the \ character.
|
||||
*
|
||||
* Examples:
|
||||
* s/-> Allow house and car from primary symbol table.
|
||||
* s//# Allow alternate table digipeater, with or without overlay.
|
||||
* s//#/\ Allow alternate table digipeater, only if no overlay.
|
||||
* s//#/SL1 Allow alternate table digipeater, with overlay S, L, or 1
|
||||
* s/O Balloon.
|
||||
* s/-> House or car from primary symbol table.
|
||||
*
|
||||
* s//# Alternate table digipeater, with or without overlay.
|
||||
* s//#/\ Alternate table digipeater, only if no overlay.
|
||||
* s//#/SL1 Alternate table digipeater, with overlay S, L, or 1.
|
||||
* s//#/SL\ Alternate table digipeater, with S, L, or no overlay.
|
||||
*
|
||||
* s/s/s Any variation of watercraft. Either symbol table. With or without overlay.
|
||||
* s/s/s/ Ship or ship sideview, only if no overlay.
|
||||
* s//s/J Jet Ski.
|
||||
*
|
||||
* What if you want to use the / symbol when / is being used as a delimiter here? Recall that you
|
||||
* can use some other special character after the initial lower case letter and this becomes the
|
||||
* delimiter for the rest of the specification.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* s:/ Red Dot.
|
||||
* s::/ Waypoint Destination, with or without overlay.
|
||||
* s:/:/ Either Red Dot or Waypoint Destination.
|
||||
* s:/:/: Either Red Dot or Waypoint Destination, no overlay.
|
||||
*
|
||||
* Bad example:
|
||||
*
|
||||
* Someone tried using this to include ballons: s/'/O/-/#/_
|
||||
* probably following the buddy filter pattern of / between each alternative.
|
||||
* There should be an error message because it has more than 3 delimiter characters.
|
||||
*
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -1075,8 +1136,9 @@ static int filt_s (pfstate_t *pf)
|
|||
{
|
||||
char str[MAX_TOKEN_LEN];
|
||||
char *cp;
|
||||
char sep[2];
|
||||
char *pri, *alt, *over;
|
||||
char sep[2]; // Delimiter character. Typically / but it could be different.
|
||||
char *pri = NULL, *alt = NULL, *over = NULL, *extra = NULL;
|
||||
char *x;
|
||||
|
||||
|
||||
strlcpy (str, pf->token_str, sizeof(str));
|
||||
|
@ -1084,51 +1146,132 @@ static int filt_s (pfstate_t *pf)
|
|||
sep[1] = '\0';
|
||||
cp = str + 2;
|
||||
|
||||
// TODO: check here.
|
||||
|
||||
// First, separate the parts and do a strict syntax check.
|
||||
|
||||
pri = strsep (&cp, sep);
|
||||
|
||||
if (pri == NULL) {
|
||||
if (pri != NULL) {
|
||||
|
||||
// Zero length is acceptable if alternate symbol(s) specified. Will check that later.
|
||||
|
||||
for (x = pri; *x != '\0'; x++) {
|
||||
if ( ! isprint(*x) || *x == '|' || *x == '~') {
|
||||
print_error (pf, "Symbol filter, primary must be printable ASCII character(s) other than | or ~.");
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
alt = strsep (&cp, sep);
|
||||
|
||||
if (alt != NULL) {
|
||||
|
||||
// Zero length after second / would be pointless.
|
||||
|
||||
if (strlen(alt) == 0) {
|
||||
print_error (pf, "Nothing specified for alternate symbol table.");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
for (x = alt; *x != '\0'; x++) {
|
||||
if ( ! isprint(*x) || *x == '|' || *x == '~') {
|
||||
print_error (pf, "Symbol filter, alternate must be printable ASCII character(s) other than | or ~.");
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
over = strsep (&cp, sep);
|
||||
|
||||
if (over != NULL) {
|
||||
|
||||
// Zero length is acceptable and is not the same as missing.
|
||||
|
||||
for (x = over; *x != '\0'; x++) {
|
||||
if ( (! isupper(*x)) && (! isdigit(*x)) && *x != '\\') {
|
||||
print_error (pf, "Symbol filter, overlay must be upper case letter, digit, or \\.");
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
extra = strsep (&cp, sep);
|
||||
|
||||
if (extra != NULL) {
|
||||
print_error (pf, "More than 3 delimiter characters in Symbol filter.");
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No alt part is OK if at least one primary symbol was specified.
|
||||
if (strlen(pri) == 0) {
|
||||
print_error (pf, "No symbols specified for Symbol filter.");
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
print_error (pf, "Missing arguments for Symbol filter.");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (pf->decoded.g_symbol_table == '/' && strchr(pri, pf->decoded.g_symbol_code) != NULL) {
|
||||
/* Found in primary symbols. All done. */
|
||||
return (1);
|
||||
}
|
||||
|
||||
alt = strsep (&cp, sep);
|
||||
if (alt == NULL) {
|
||||
// This applies only for Position, Object, Item.
|
||||
// decode_aprs() should set symbol code to space to mean undefined.
|
||||
|
||||
if (pf->decoded.g_symbol_code == ' ') {
|
||||
return (0);
|
||||
}
|
||||
if (strlen(alt) == 0) {
|
||||
/* We have s/.../ */
|
||||
print_error (pf, "Missing alternate symbols for Symbol filter.");
|
||||
return (-1);
|
||||
|
||||
|
||||
// Look for Primary symbols.
|
||||
|
||||
if (pf->decoded.g_symbol_table == '/') {
|
||||
if (pri != NULL && strlen(pri) > 0) {
|
||||
return (strchr(pri, pf->decoded.g_symbol_code) != NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (alt == NULL) {
|
||||
return (0);
|
||||
}
|
||||
|
||||
//printf ("alt=\"%s\" sym='%c'\n", alt, pf->decoded.g_symbol_code);
|
||||
|
||||
if (strchr(alt, pf->decoded.g_symbol_code) == NULL) {
|
||||
/* Not found in alternate symbols. Reject. */
|
||||
return (0);
|
||||
}
|
||||
// Look for Alternate symbols.
|
||||
|
||||
over = strsep (&cp, sep);
|
||||
if (over == NULL) {
|
||||
/* alternate, with or without overlay. */
|
||||
return (pf->decoded.g_symbol_table != '/');
|
||||
}
|
||||
if (strchr(alt, pf->decoded.g_symbol_code) != NULL) {
|
||||
|
||||
// printf ("over=\"%s\" table='%c'\n", over, pf->decoded.g_symbol_table);
|
||||
// We have a match but that might not be enough.
|
||||
// We must see if there was an overlay part specified.
|
||||
|
||||
if (strlen(over) == 0) {
|
||||
return (pf->decoded.g_symbol_table == '\\');
|
||||
}
|
||||
if (over != NULL) {
|
||||
|
||||
if (strlen(over) > 0) {
|
||||
|
||||
// Non-zero length overlay part was specified.
|
||||
// Need to match one of them.
|
||||
|
||||
return (strchr(over, pf->decoded.g_symbol_table) != NULL);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// Zero length overlay part was specified.
|
||||
// We must have no overlay, i.e. table is \.
|
||||
|
||||
return (pf->decoded.g_symbol_table == '\\');
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// No check of overlay part. Just make sure it is not primary table.
|
||||
|
||||
return (pf->decoded.g_symbol_table != '/');
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
||||
} /* end filt_s */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
|
@ -1494,10 +1637,15 @@ int main ()
|
|||
pftest (126, "t/n", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", 1);
|
||||
pftest (127, "t/", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", 0);
|
||||
|
||||
pftest (130, "r/42.6/-71.3/10", "WB2OSZ-5>APDW12,WIDE1-1,WIDE2-1:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1);
|
||||
pftest (131, "r/42.6/-71.3/10", "WA1PLE-5>APWW10,W1MHL,N8VIM,WIDE2*:@022301h4208.75N/07115.16WoAPRS-IS for Win32", 0);
|
||||
pftest (128, "t/c", "S0RCE>DEST:<stationcapabilities", 1);
|
||||
pftest (129, "t/h", "S0RCE>DEST:<stationcapabilities", 0);
|
||||
pftest (130, "t/h", "S0RCE>DEST:}thirdpartyheaderwhatever", 1);
|
||||
pftest (131, "t/c", "S0RCE>DEST:}thirdpartyheaderwhatever", 0);
|
||||
|
||||
pftest (140, "( t/t & b/WB2OSZ ) | ( t/o & ! r/42.6/-71.3/1 )", "WB2OSZ>APDW12:;home *111111z4237.14N/07120.83W-Chelmsford MA", 1);
|
||||
pftest (140, "r/42.6/-71.3/10", "WB2OSZ-5>APDW12,WIDE1-1,WIDE2-1:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1);
|
||||
pftest (141, "r/42.6/-71.3/10", "WA1PLE-5>APWW10,W1MHL,N8VIM,WIDE2*:@022301h4208.75N/07115.16WoAPRS-IS for Win32", 0);
|
||||
|
||||
pftest (145, "( t/t & b/WB2OSZ ) | ( t/o & ! r/42.6/-71.3/1 )", "WB2OSZ>APDW12:;home *111111z4237.14N/07120.83W-Chelmsford MA", 1);
|
||||
|
||||
pftest (150, "s/->", "WB2OSZ-5>APDW12:!4237.14NS07120.83W#PHG7140Chelmsford MA", 0);
|
||||
pftest (151, "s/->", "WB2OSZ-5>APDW12:!4237.14N/07120.83W-PHG7140Chelmsford MA", 1);
|
||||
|
@ -1515,13 +1663,28 @@ int main ()
|
|||
pftest (160, "s//#/LS1", "WB2OSZ-5>APDW12:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1);
|
||||
pftest (161, "s//#/LS1", "WB2OSZ-5>APDW12:!4237.14N\\07120.83W#PHG7140Chelmsford MA", 0);
|
||||
pftest (162, "s//#/LS1", "WB2OSZ-5>APDW12:!4237.14N/07120.83W#PHG7140Chelmsford MA", 0);
|
||||
pftest (163, "s//#/LS\\", "WB2OSZ-5>APDW12:!4237.14N\\07120.83W#PHG7140Chelmsford MA", 1);
|
||||
|
||||
pftest (170, "v/DIGI2/DIGI3", "WB2OSZ-5>APDW12,DIGI1,DIGI2,DIGI3,DIGI4:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1);
|
||||
pftest (171, "v/DIGI2/DIGI3", "WB2OSZ-5>APDW12,DIGI1*,DIGI2,DIGI3,DIGI4:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1);
|
||||
pftest (172, "v/DIGI2/DIGI3", "WB2OSZ-5>APDW12,DIGI1,DIGI2*,DIGI3,DIGI4:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1);
|
||||
pftest (173, "v/DIGI2/DIGI3", "WB2OSZ-5>APDW12,DIGI1,DIGI2,DIGI3*,DIGI4:!4237.14NS07120.83W#PHG7140Chelmsford MA", 0);
|
||||
pftest (174, "v/DIGI2/DIGI3", "WB2OSZ-5>APDW12,DIGI1,DIGI2,DIGI3,DIGI4*:!4237.14NS07120.83W#PHG7140Chelmsford MA", 0);
|
||||
pftest (175, "v/DIGI9/DIGI2", "WB2OSZ-5>APDW12,DIGI1,DIGI2*,DIGI3,DIGI4:!4237.14NS07120.83W#PHG7140Chelmsford MA", 0);
|
||||
pftest (170, "s:/", "WB2OSZ-5>APDW12:!4237.14N/07120.83W/PHG7140Chelmsford MA", 1);
|
||||
pftest (171, "s:/", "WB2OSZ-5>APDW12:!4237.14N\\07120.83W/PHG7140Chelmsford MA", 0);
|
||||
pftest (172, "s::/", "WB2OSZ-5>APDW12:!4237.14N/07120.83W/PHG7140Chelmsford MA", 0);
|
||||
pftest (173, "s::/", "WB2OSZ-5>APDW12:!4237.14N\\07120.83W/PHG7140Chelmsford MA", 1);
|
||||
pftest (174, "s:/:/", "WB2OSZ-5>APDW12:!4237.14N/07120.83W/PHG7140Chelmsford MA", 1);
|
||||
pftest (175, "s:/:/", "WB2OSZ-5>APDW12:!4237.14N\\07120.83W/PHG7140Chelmsford MA", 1);
|
||||
pftest (176, "s:/:/", "WB2OSZ-5>APDW12:!4237.14NX07120.83W/PHG7140Chelmsford MA", 1);
|
||||
pftest (177, "s:/:/:X", "WB2OSZ-5>APDW12:!4237.14NX07120.83W/PHG7140Chelmsford MA", 1);
|
||||
|
||||
// FIXME: Different on Windows and 64 bit Linux.
|
||||
//pftest (178, "s:/:/:", "WB2OSZ-5>APDW12:!4237.14NX07120.83W/PHG7140Chelmsford MA", 1);
|
||||
|
||||
pftest (179, "s:/:/:\\", "WB2OSZ-5>APDW12:!4237.14NX07120.83W/PHG7140Chelmsford MA", 0);
|
||||
|
||||
pftest (180, "v/DIGI2/DIGI3", "WB2OSZ-5>APDW12,DIGI1,DIGI2,DIGI3,DIGI4:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1);
|
||||
pftest (181, "v/DIGI2/DIGI3", "WB2OSZ-5>APDW12,DIGI1*,DIGI2,DIGI3,DIGI4:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1);
|
||||
pftest (182, "v/DIGI2/DIGI3", "WB2OSZ-5>APDW12,DIGI1,DIGI2*,DIGI3,DIGI4:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1);
|
||||
pftest (183, "v/DIGI2/DIGI3", "WB2OSZ-5>APDW12,DIGI1,DIGI2,DIGI3*,DIGI4:!4237.14NS07120.83W#PHG7140Chelmsford MA", 0);
|
||||
pftest (184, "v/DIGI2/DIGI3", "WB2OSZ-5>APDW12,DIGI1,DIGI2,DIGI3,DIGI4*:!4237.14NS07120.83W#PHG7140Chelmsford MA", 0);
|
||||
pftest (185, "v/DIGI9/DIGI2", "WB2OSZ-5>APDW12,DIGI1,DIGI2*,DIGI3,DIGI4:!4237.14NS07120.83W#PHG7140Chelmsford MA", 0);
|
||||
|
||||
/* Test error reporting. */
|
||||
|
||||
|
@ -1540,12 +1703,23 @@ int main ()
|
|||
pftest (226, "i/30/8/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
|
||||
pftest (227, "i/30/8", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
|
||||
|
||||
// FIXME: behaves differently on Windows and Linux
|
||||
// FIXME: behaves differently on Windows and Linux. Why?
|
||||
// On Windows we have our own version of strsep because it's not in the MS library.
|
||||
// It must behave differently than the Linux version when nothing follows the last separator.
|
||||
//pftest (228, "i/30/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
|
||||
|
||||
pftest (229, "i/30", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
|
||||
pftest (230, "i/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
|
||||
|
||||
pftest (240, "s/", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
|
||||
pftest (241, "s/'/O/-/#/_", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
|
||||
pftest (242, "s/O/O/c", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
|
||||
pftest (243, "s/O/O/1/2", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
|
||||
pftest (244, "s/O/|/1", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
|
||||
pftest (245, "s//", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
|
||||
pftest (246, "s///", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
|
||||
|
||||
|
||||
// TODO: to be continued...
|
||||
|
||||
if (error_count > 0) {
|
||||
|
|
143
ptt.c
143
ptt.c
|
@ -52,6 +52,9 @@
|
|||
*
|
||||
* Handle more complicated gpio node names for CubieBoard, etc.
|
||||
*
|
||||
* Version 1.5: Ability to use GPIO pins of CM108/CM119 for PTT signal.
|
||||
*
|
||||
*
|
||||
* References: http://www.robbayer.com/files/serial-win.pdf
|
||||
*
|
||||
* https://www.kernel.org/doc/Documentation/gpio.txt
|
||||
|
@ -59,16 +62,20 @@
|
|||
*---------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
Idea for future enhancement:
|
||||
A growing number of people have been asking about support for the DMK URI
|
||||
or the similar RB-USB RIM.
|
||||
|
||||
A growing number of people have been asking about support for the DMK URI.
|
||||
This uses a C-Media CM108/CM119 with one interesting addition, a GPIO
|
||||
These use a C-Media CM108/CM119 with an interesting addition, a GPIO
|
||||
pin is used to drive PTT. Here is some related information.
|
||||
|
||||
DMK URI:
|
||||
|
||||
http://www.dmkeng.com/URI_Order_Page.htm
|
||||
http://dmkeng.com/images/URI%20Schematic.pdf
|
||||
|
||||
RB-USB RIM:
|
||||
|
||||
http://www.repeater-builder.com/products/usb-rim-lite.html
|
||||
http://www.repeater-builder.com/voip/pdf/cm119-datasheet.pdf
|
||||
|
||||
Homebrew versions of the same idea:
|
||||
|
@ -83,6 +90,7 @@
|
|||
http://docs.allstarlink.org/drupal/
|
||||
http://soundmodem.sourcearchive.com/documentation/0.16-1/ptt_8c_source.html
|
||||
https://github.com/N0NB/hamlib/blob/master/src/cm108.c#L190
|
||||
http://permalink.gmane.org/gmane.linux.hams.hamlib.devel/3420
|
||||
|
||||
Information about the "hidraw" device:
|
||||
|
||||
|
@ -92,12 +100,31 @@
|
|||
https://github.com/signal11/hidapi/blob/master/libusb/hid.c
|
||||
http://stackoverflow.com/questions/899008/howto-write-to-the-gpio-pin-of-the-cm108-chip-in-linux
|
||||
https://www.kernel.org/doc/Documentation/hid/hidraw.txt
|
||||
https://github.com/torvalds/linux/blob/master/samples/hidraw/hid-example.c
|
||||
|
||||
In version 1.3, we add HAMLIB support which should be able to do this.
|
||||
(Linux only & haven't verified that it actually works yet!)
|
||||
Similar chips: SSS1621, SSS1623
|
||||
|
||||
Might want to have CM108 GPIO support built in, someday, for simpler building & configuration.
|
||||
Maybe even for Windows. ;-)
|
||||
https://irongarment.wordpress.com/2011/03/29/cm108-compatible-chips-with-gpio/
|
||||
|
||||
Here is an attempt to add direct CM108 support.
|
||||
Seems to be hardcoded for only a single USB audio adapter.
|
||||
|
||||
https://github.com/donothingloop/direwolf_cm108
|
||||
|
||||
In version 1.3, we add HAMLIB support which should be able to do this in a roundabout way.
|
||||
(Linux only at this point.)
|
||||
|
||||
This is documented in the User Guide, section called,
|
||||
"Hamlib PTT Example 2: Use GPIO of USB audio adapter. (e.g. DMK URI)"
|
||||
|
||||
It's rather involved and the explantion doesn't cover the case of multiple
|
||||
USB-Audio adapters. It would be nice to have a little script which lists all
|
||||
of the USB-Audio adapters and the corresponding /dev/hidraw device.
|
||||
( We now have it. The included "cm108" application. )
|
||||
|
||||
In version 1.5 we have a flexible, easy to use implementation for Linux.
|
||||
Windows would be a lot of extra work because USB devices are nothing like Linux.
|
||||
We'd be starting from scratch to figure out how to do it.
|
||||
*/
|
||||
|
||||
|
||||
|
@ -126,6 +153,10 @@
|
|||
#include <hamlib/rig.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_CM108
|
||||
#include "cm108.h"
|
||||
#endif
|
||||
|
||||
/* So we can have more common code for fd. */
|
||||
typedef int HANDLE;
|
||||
#define INVALID_HANDLE_VALUE (-1)
|
||||
|
@ -157,10 +188,6 @@ typedef int HANDLE;
|
|||
#endif
|
||||
|
||||
|
||||
#if TEST
|
||||
#define dw_printf printf
|
||||
#endif
|
||||
|
||||
|
||||
static struct audio_s *save_audio_config_p; /* Save config information for later use. */
|
||||
|
||||
|
@ -603,10 +630,12 @@ void export_gpio(int ch, int ot, int invert, int direction)
|
|||
* PTT_METHOD_GPIO - general purpose I/O.
|
||||
* PTT_METHOD_LPT - Parallel printer port.
|
||||
* PTT_METHOD_HAMLIB - HAMLib rig control.
|
||||
* PTT_METHOD_CM108 - GPIO pins of CM108 etc. USB Audio.
|
||||
*
|
||||
* ptt_device Name of serial port device.
|
||||
* e.g. COM1 or /dev/ttyS0.
|
||||
* HAMLIB can also use hostaddr:port.
|
||||
* Like /dev/hidraw1 for CM108.
|
||||
*
|
||||
* ptt_line RTS or DTR when using serial port.
|
||||
*
|
||||
|
@ -992,6 +1021,32 @@ void ptt_init (struct audio_s *audio_config_p)
|
|||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Confirm what is going on with CM108 GPIO output.
|
||||
* Could use some error checking for overlap.
|
||||
*/
|
||||
|
||||
#if USE_CM108
|
||||
|
||||
for (ch = 0; ch < MAX_CHANS; ch++) {
|
||||
|
||||
if (audio_config_p->achan[ch].valid) {
|
||||
int ot;
|
||||
for (ot = 0; ot < NUM_OCTYPES; ot++) {
|
||||
if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_CM108) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Using %s GPIO %d for channel %d %s control.\n",
|
||||
audio_config_p->achan[ch].octrl[ot].ptt_device,
|
||||
audio_config_p->achan[ch].octrl[ot].out_gpio_num,
|
||||
ch,
|
||||
otnames[ot]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* Why doesn't it transmit? Probably forgot to specify PTT option. */
|
||||
|
||||
|
@ -1059,7 +1114,9 @@ void ptt_set (int ot, int chan, int ptt_signal)
|
|||
* This is a very convenient place to get that information.
|
||||
*/
|
||||
|
||||
#ifndef TEST
|
||||
dlq_channel_busy (chan, ot, ptt_signal);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Inverted output?
|
||||
|
@ -1221,6 +1278,21 @@ void ptt_set (int ot, int chan, int ptt_signal)
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Using CM108 USB Audio adapter GPIO?
|
||||
*/
|
||||
|
||||
#ifdef USE_CM108
|
||||
|
||||
if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_CM108) {
|
||||
|
||||
if (cm108_set_gpio_pin (save_audio_config_p->achan[chan].octrl[ot].ptt_device,
|
||||
save_audio_config_p->achan[chan].octrl[ot].out_gpio_num, ptt) != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR: %s for channel %d has failed. See User Guide for troubleshooting tips.\n", otnames[ot], chan);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} /* end ptt_set */
|
||||
|
||||
|
@ -1354,18 +1426,15 @@ void ptt_term (void)
|
|||
/*
|
||||
* Quick stand-alone test for above.
|
||||
*
|
||||
* gcc -DTEST -o ptest ptt.c ; ./ptest
|
||||
* gcc -DTEST -o ptest ptt.c textcolor.o misc.a ; ./ptest
|
||||
*
|
||||
* TODO: Retest this, add CM108 GPIO to test.
|
||||
*/
|
||||
|
||||
|
||||
#if TEST
|
||||
|
||||
void text_color_set (dw_color_t c) { }
|
||||
|
||||
#define dw_printf printf
|
||||
|
||||
main ()
|
||||
int main ()
|
||||
{
|
||||
struct audio_s my_audio_config;
|
||||
int n;
|
||||
|
@ -1375,17 +1444,18 @@ main ()
|
|||
|
||||
my_audio_config.adev[0].num_channels = 2;
|
||||
|
||||
my_audio_config.valid[0] = 1;
|
||||
my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_SERIAL;
|
||||
//strlcpy (my_audio_config.ptt_device, "COM1", sizeof(my_audio_config.ptt_device));
|
||||
strlcpy (my_audio_config.ptt_device, "/dev/ttyUSB0", sizeof(my_audio_config.ptt_device));
|
||||
my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_RTS;
|
||||
my_audio_config.achan[0].valid = 1;
|
||||
my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_SERIAL;
|
||||
// TODO: device should be command line argument.
|
||||
strlcpy (my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_device, "COM3", sizeof(my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_device));
|
||||
//strlcpy (my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_device, "/dev/ttyUSB0", sizeof(my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_device));
|
||||
my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_RTS;
|
||||
|
||||
my_audio_config.valid[1] = 1;
|
||||
my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_SERIAL;
|
||||
//strlcpy (my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device, "COM1", sizeof(my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device));
|
||||
strlcpy (my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device, "/dev/ttyUSB0", sizeof(my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device));
|
||||
my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_DTR;
|
||||
my_audio_config.achan[1].valid = 1;
|
||||
my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_SERIAL;
|
||||
strlcpy (my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_device, "COM3", sizeof(my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_device));
|
||||
//strlcpy (my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_device, "/dev/ttyUSB0", sizeof(my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_device));
|
||||
my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_DTR;
|
||||
|
||||
|
||||
/* initialize - both off */
|
||||
|
@ -1420,7 +1490,7 @@ main ()
|
|||
|
||||
/* Same thing again but invert RTS. */
|
||||
|
||||
my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_invert = 1;
|
||||
my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_invert = 1;
|
||||
|
||||
ptt_init (&my_audio_config);
|
||||
|
||||
|
@ -1476,6 +1546,14 @@ main ()
|
|||
#endif
|
||||
|
||||
|
||||
|
||||
/* Parallel printer port. */
|
||||
|
||||
#if ( defined(__i386__) || defined(__x86_64__) ) && ( defined(__linux__) || defined(__unix__) )
|
||||
|
||||
// TODO
|
||||
|
||||
#if 0
|
||||
memset (&my_audio_config, 0, sizeof(my_audio_config));
|
||||
my_audio_config.num_channels = 2;
|
||||
my_audio_config.valid[0] = 1;
|
||||
|
@ -1496,16 +1574,11 @@ main ()
|
|||
}
|
||||
|
||||
ptt_term ();
|
||||
|
||||
/* Parallel printer port. */
|
||||
|
||||
#if ( defined(__i386__) || defined(__x86_64__) ) && ( defined(__linux__) || defined(__unix__) )
|
||||
|
||||
// TODO
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
#endif /* TEST */
|
||||
|
|
5
recv.c
5
recv.c
|
@ -378,6 +378,11 @@ void recv_process (void)
|
|||
lm_channel_busy (pitem);
|
||||
break;
|
||||
|
||||
case DLQ_SEIZE_CONFIRM:
|
||||
|
||||
lm_seize_confirm (pitem);
|
||||
break;
|
||||
|
||||
case DLQ_CLIENT_CLEANUP:
|
||||
|
||||
dl_client_cleanup (pitem);
|
||||
|
|
|
@ -43,7 +43,7 @@ if [ $valid_flag -eq "0" ]; then
|
|||
|
||||
prompt="Select SDK to use:"
|
||||
|
||||
loc1=( $(find /Applications/XCode.app -type d -name "MacOSX10.*.sdk") )
|
||||
loc1=( $(find /Applications/Xcode.app -type l -name "MacOSX10.*.sdk") )
|
||||
loc2=( $(find /Developer/SDKs -maxdepth 1 -type d -name "MacOSX10.*.sdk") )
|
||||
|
||||
options=("${loc1[@]}" "${loc2[@]}")
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
|
||||
// TODO: Needs more clean up and testing of error conditions.
|
||||
|
||||
// TODO: use this in place of other similar code.
|
||||
|
||||
|
||||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2014, 2015 John Langner, WB2OSZ
|
||||
// Copyright (C) 2014, 2015, 2017 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
|
||||
|
@ -73,8 +67,10 @@
|
|||
* Inputs: devicename - For Windows, usually like COM5.
|
||||
* For Linux, usually /dev/tty...
|
||||
* "COMn" also allowed and converted to /dev/ttyS(n-1)
|
||||
* Could be /dev/rfcomm0 for Bluetooth.
|
||||
*
|
||||
* baud - Speed. 4800, 9600, etc.
|
||||
* baud - Speed. 1200, 4800, 9600 bps, etc.
|
||||
* If 0, leave it alone.
|
||||
*
|
||||
* Returns Handle for serial port or MYFDERROR for error.
|
||||
*
|
||||
|
@ -143,6 +139,7 @@ MYFDTYPE serial_port_open (char *devicename, int baud)
|
|||
|
||||
switch (baud) {
|
||||
|
||||
case 0: /* Leave it alone. */ break;
|
||||
case 1200: dcb.BaudRate = CBR_1200; break;
|
||||
case 2400: dcb.BaudRate = CBR_2400; break;
|
||||
case 4800: dcb.BaudRate = CBR_4800; break;
|
||||
|
@ -233,15 +230,19 @@ MYFDTYPE serial_port_open (char *devicename, int baud)
|
|||
|
||||
switch (baud) {
|
||||
|
||||
case 0: /* Leave it alone. */ break;
|
||||
case 1200: cfsetispeed (&ts, B1200); cfsetospeed (&ts, B1200); break;
|
||||
case 2400: cfsetispeed (&ts, B2400); cfsetospeed (&ts, B2400); break;
|
||||
case 4800: cfsetispeed (&ts, B4800); cfsetospeed (&ts, B4800); break;
|
||||
case 9600: cfsetispeed (&ts, B9600); cfsetospeed (&ts, B9600); break;
|
||||
case 19200: cfsetispeed (&ts, B19200); cfsetospeed (&ts, B19200); break;
|
||||
case 38400: cfsetispeed (&ts, B38400); cfsetospeed (&ts, B38400); break;
|
||||
#ifndef __APPLE__
|
||||
// Not defined for Mac OSX.
|
||||
// https://groups.yahoo.com/neo/groups/direwolf_packet/conversations/messages/2072
|
||||
case 57600: cfsetispeed (&ts, B57600); cfsetospeed (&ts, B57600); break;
|
||||
case 115200: cfsetispeed (&ts, B115200); cfsetospeed (&ts, B115200); break;
|
||||
|
||||
#endif
|
||||
default: text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("serial_port_open: Unsupported speed %d. Using 4800.\n", baud);
|
||||
cfsetispeed (&ts, B4800); cfsetospeed (&ts, B4800);
|
||||
|
@ -305,8 +306,10 @@ int serial_port_write (MYFDTYPE fd, char *str, int len)
|
|||
}
|
||||
else if ((int)nwritten != len)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Error writing to serial port. Only %d of %d written.\n\n", (int)nwritten, len);
|
||||
// Do we want this message here?
|
||||
// Or rely on caller to check and provide something more meaningful for the usage?
|
||||
//text_color_set(DW_COLOR_ERROR);
|
||||
//dw_printf ("Error writing to serial port. Only %d of %d written.\n\n", (int)nwritten, len);
|
||||
}
|
||||
|
||||
return (nwritten);
|
||||
|
@ -317,8 +320,10 @@ int serial_port_write (MYFDTYPE fd, char *str, int len)
|
|||
written = write (fd, str, (size_t)len);
|
||||
if (written != len)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Error writing to serial port. err=%d\n\n", written);
|
||||
// Do we want this message here?
|
||||
// Or rely on caller to check and provide something more meaningful for the usage?
|
||||
//text_color_set(DW_COLOR_ERROR);
|
||||
//dw_printf ("Error writing to serial port. err=%d\n\n", written);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
|
@ -402,8 +407,9 @@ int serial_port_get1 (MYFDTYPE fd)
|
|||
CloseHandle(ov_rd.hEvent);
|
||||
|
||||
if (n != 1) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Serial port failed to get one byte. n=%d.\n\n", (int)n);
|
||||
//text_color_set(DW_COLOR_ERROR);
|
||||
//dw_printf ("Serial port failed to get one byte. n=%d.\n\n", (int)n);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
|
||||
|
@ -414,8 +420,8 @@ int serial_port_get1 (MYFDTYPE fd)
|
|||
n = read(fd, &ch, (size_t)1);
|
||||
|
||||
if (n != 1) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("serial_port_get1(%d) returns -1 for error.\n", fd);
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("serial_port_get1(%d) returns -1 for error.\n", fd);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
|
@ -447,8 +453,14 @@ int serial_port_get1 (MYFDTYPE fd)
|
|||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
// TODO:
|
||||
void serial_port_close (MYFDTYPE fd)
|
||||
{
|
||||
#if __WIN32__
|
||||
CloseHandle (fd);
|
||||
#else
|
||||
close (fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* end serial_port.c */
|
||||
|
|
104
server.c
104
server.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 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017 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
|
||||
|
@ -59,6 +59,8 @@
|
|||
*
|
||||
* 'y' Ask Outstanding frames waiting on a Port (new in 1.2)
|
||||
*
|
||||
* 'Y' How many frames waiting for transmit for a particular station (new in 1.5)
|
||||
*
|
||||
* 'C' Connect, Start an AX.25 Connection (new in 1.4)
|
||||
*
|
||||
* 'v' Connect VIA, Start an AX.25 circuit thru digipeaters (new in 1.4)
|
||||
|
@ -92,6 +94,8 @@
|
|||
*
|
||||
* 'y' Outstanding frames waiting on a Port (new in 1.2)
|
||||
*
|
||||
* 'Y' How many frames waiting for transmit for a particular station (new in 1.5)
|
||||
*
|
||||
* 'C' AX.25 Connection Received (new in 1.4)
|
||||
*
|
||||
* 'D' Connected AX.25 Data (new in 1.4)
|
||||
|
@ -487,14 +491,16 @@ void server_init (struct audio_s *audio_config_p, struct misc_config_s *mc)
|
|||
cmd_listen_th[client] = (HANDLE)_beginthreadex (NULL, 0, cmd_listen_thread, (void*)client, 0, NULL);
|
||||
if (cmd_listen_th[client] == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Could not create AGW command listening thread\n");
|
||||
dw_printf ("Could not create AGW command listening thread for client %d\n", client);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
e = pthread_create (&cmd_listen_tid[client], NULL, cmd_listen_thread, (void *)(long)client);
|
||||
if (e != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
perror("Could not create AGW command listening thread");
|
||||
dw_printf ("Could not create AGW command listening thread for client %d\n", client);
|
||||
// Replace add perror with better message handling.
|
||||
perror("");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
@ -637,8 +643,7 @@ static THREAD_F connect_listen_thread (void *arg)
|
|||
}
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
// TODO: "attached" or some other term would be better because "connected" has a different meaning for AX.25.
|
||||
dw_printf("\nConnected to AGW client application %d ...\n\n", client);
|
||||
dw_printf("\nAttached to AGW client application %d ...\n\n", client);
|
||||
|
||||
/*
|
||||
* The command to change this is actually a toggle, not explicit on or off.
|
||||
|
@ -729,7 +734,7 @@ static THREAD_F connect_listen_thread (void *arg)
|
|||
client_sock[client] = accept(listen_sock, (struct sockaddr*)(&sockaddr),&sockaddr_size);
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf("\nConnected to AGW client application %d...\n\n", client);
|
||||
dw_printf("\nAttached to AGW client application %d...\n\n", client);
|
||||
|
||||
/*
|
||||
* The command to change this is actually a toggle, not explicit on or off.
|
||||
|
@ -813,7 +818,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
|
|||
}
|
||||
|
||||
#if __WIN32__
|
||||
err = send (client_sock[client], (char*)(&agwpe_msg), sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE), 0);
|
||||
err = SOCK_SEND (client_sock[client], (char*)(&agwpe_msg), sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE));
|
||||
if (err == SOCKET_ERROR)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
|
@ -824,7 +829,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
|
|||
dlq_client_cleanup (client);
|
||||
}
|
||||
#else
|
||||
err = write (client_sock[client], &agwpe_msg, sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE));
|
||||
err = SOCK_SEND (client_sock[client], &agwpe_msg, sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE));
|
||||
if (err <= 0)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
|
@ -918,7 +923,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
|
|||
}
|
||||
|
||||
#if __WIN32__
|
||||
err = send (client_sock[client], (char*)(&agwpe_msg), sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE), 0);
|
||||
err = SOCK_SEND (client_sock[client], (char*)(&agwpe_msg), sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE));
|
||||
if (err == SOCKET_ERROR)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
|
@ -929,7 +934,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
|
|||
dlq_client_cleanup (client);
|
||||
}
|
||||
#else
|
||||
err = write (client_sock[client], &agwpe_msg, sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE));
|
||||
err = SOCK_SEND (client_sock[client], &agwpe_msg, sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE));
|
||||
if (err <= 0)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
|
@ -1142,14 +1147,7 @@ static int read_from_socket (int fd, char *ptr, int len)
|
|||
while (got_bytes < len) {
|
||||
int n;
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
//TODO: any flags for send/recv?
|
||||
|
||||
n = recv (fd, ptr + got_bytes, len - got_bytes, 0);
|
||||
#else
|
||||
n = read (fd, ptr + got_bytes, len - got_bytes);
|
||||
#endif
|
||||
n = SOCK_RECV (fd, ptr + got_bytes, len - got_bytes);
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
|
@ -1192,10 +1190,7 @@ static void send_to_client (int client, void *reply_p)
|
|||
{
|
||||
struct agwpe_s *ph;
|
||||
int len;
|
||||
#if __WIN32__
|
||||
#else
|
||||
int err;
|
||||
#endif
|
||||
|
||||
ph = (struct agwpe_s *) reply_p; // Replies are often hdr + other stuff.
|
||||
|
||||
|
@ -1213,12 +1208,8 @@ static void send_to_client (int client, void *reply_p)
|
|||
debug_print (TO_CLIENT, client, ph, len);
|
||||
}
|
||||
|
||||
#if __WIN32__
|
||||
send (client_sock[client], (char*)(ph), len, 0);
|
||||
#else
|
||||
err = write (client_sock[client], ph, len);
|
||||
err = SOCK_SEND (client_sock[client], (char*)(ph), len);
|
||||
(void)err;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -1260,16 +1251,20 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
}
|
||||
|
||||
/*
|
||||
* Take some precautions to guard against bad data
|
||||
* which could cause problems later.
|
||||
* Take some precautions to guard against bad data which could cause problems later.
|
||||
*/
|
||||
if (cmd.hdr.portx < 0 || cmd.hdr.portx >= MAX_CHANS) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nInvalid port number, %d, in command '%c', from AGW client application %d.\n",
|
||||
cmd.hdr.portx, cmd.hdr.datakind, client);
|
||||
cmd.hdr.portx = 0; // avoid subscript out of bounds, try to keep going.
|
||||
}
|
||||
|
||||
/*
|
||||
* Call to/from must not exceeed 9 characters.
|
||||
* Call to/from fields are 10 bytes but contents must not exceeed 9 characters.
|
||||
* It's not guaranteed that unused bytes will contain 0 so we
|
||||
* don't issue error message in this case.
|
||||
*/
|
||||
|
||||
cmd.hdr.call_from[sizeof(cmd.hdr.call_from)-1] = '\0';
|
||||
cmd.hdr.call_to[sizeof(cmd.hdr.call_to)-1] = '\0';
|
||||
|
||||
|
@ -1456,6 +1451,7 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
|
||||
// YAAC asks for this.
|
||||
// Fake it to keep application happy.
|
||||
// TODO: Supply real values instead of just faking it.
|
||||
|
||||
reply.on_air_baud_rate = 0;
|
||||
reply.traffic_level = 1;
|
||||
|
@ -1697,7 +1693,17 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
struct via_info {
|
||||
unsigned char num_digi; /* Expect to be in range 1 to 7. Why not up to 8? */
|
||||
char dcall[7][10];
|
||||
} *v = (struct via_info *)cmd.data;
|
||||
}
|
||||
#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.
|
||||
// If this upsets your compiler, take it out.
|
||||
// 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];
|
||||
int num_calls = 2; /* 2 plus any digipeaters. */
|
||||
|
@ -1843,6 +1849,7 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
|
||||
case 'y': /* Ask Outstanding frames waiting on a Port */
|
||||
|
||||
/* Number of frames sitting in transmit queue for specified channel. */
|
||||
{
|
||||
struct {
|
||||
struct agwpe_s hdr;
|
||||
|
@ -1857,7 +1864,40 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
|
||||
int n = 0;
|
||||
if (cmd.hdr.portx >= 0 && cmd.hdr.portx < MAX_CHANS) {
|
||||
n = tq_count (cmd.hdr.portx, TQ_PRIO_0_HI) + tq_count (cmd.hdr.portx, TQ_PRIO_1_LO);
|
||||
n = tq_count (cmd.hdr.portx, -1, "", "", 0);
|
||||
}
|
||||
reply.data_NETLE = host2netle(n);
|
||||
|
||||
send_to_client (client, &reply);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Y': /* How Many Outstanding frames wait for tx for a particular station */
|
||||
|
||||
/* Number of frames sitting in transmit queue for given channel, */
|
||||
/* source (optional) and destination addresses. */
|
||||
{
|
||||
char source[AX25_MAX_ADDR_LEN];
|
||||
char dest[AX25_MAX_ADDR_LEN];
|
||||
|
||||
struct {
|
||||
struct agwpe_s hdr;
|
||||
int data_NETLE; // Little endian order.
|
||||
} reply;
|
||||
|
||||
strlcpy (source, cmd.hdr.call_from, sizeof(source));
|
||||
strlcpy (dest, cmd.hdr.call_to, sizeof(dest));
|
||||
|
||||
memset (&reply, 0, sizeof(reply));
|
||||
reply.hdr.portx = cmd.hdr.portx; /* Reply with same port number, addresses. */
|
||||
reply.hdr.datakind = 'Y';
|
||||
strlcpy (reply.hdr.call_from, source, sizeof(reply.hdr.call_from));
|
||||
strlcpy (reply.hdr.call_to, dest, sizeof(reply.hdr.call_to));
|
||||
reply.hdr.data_len_NETLE = host2netle(4);
|
||||
|
||||
int n = 0;
|
||||
if (cmd.hdr.portx >= 0 && cmd.hdr.portx < MAX_CHANS) {
|
||||
n = tq_count (cmd.hdr.portx, -1, source, dest, 0);
|
||||
}
|
||||
reply.data_NETLE = host2netle(n);
|
||||
|
||||
|
|
|
@ -0,0 +1,438 @@
|
|||
|
||||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2017 John Langner, WB2OSZ
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Module: sock.c
|
||||
*
|
||||
* Purpose: Functions for TCP sockets.
|
||||
*
|
||||
* Description: These are used for connecting between different applications,
|
||||
* possibly on different hosts.
|
||||
*
|
||||
* New in version 1.5:
|
||||
* Duplicate code already exists in multiple places and I was about
|
||||
* to add another one. Instead, we will gather the common code here
|
||||
* instead of having yet another copy.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h" // Sets _WIN32_WINNT for XP API level needed by ws2tcpip.h
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h> // _WIN32_WINNT must be set to 0x0501 before including this
|
||||
|
||||
#else
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <fcntl.h>
|
||||
//#include <termios.h>
|
||||
#include <sys/errno.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "textcolor.h"
|
||||
#include "sock.h"
|
||||
|
||||
static void shuffle (struct addrinfo *host[], int nhosts);
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: sock_init
|
||||
*
|
||||
* Purpose: Preparation before using socket interface.
|
||||
*
|
||||
* Inputs: none
|
||||
*
|
||||
* Returns: 0 for success, -1 for error.
|
||||
*
|
||||
* Errors: Message is printed. I've never seen it fail.
|
||||
*
|
||||
* Description: Doesn't do anything for Linux.
|
||||
*
|
||||
* TODO: Use this instead of own copy in aclients.c
|
||||
* TODO: Use this instead of own copy in appserver.c
|
||||
* TODO: Use this instead of own copy in audio_win.c
|
||||
* TODO: Use this instead of own copy in igate.c
|
||||
* TODO: Use this instead of own copy in kissnet.c
|
||||
* TODO: Use this instead of own copy in kissutil.c
|
||||
* TODO: Use this instead of own copy in server.c
|
||||
* TODO: Use this instead of own copy in tnctest.c
|
||||
* TODO: Use this instead of own copy in ttcalc.c
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
int sock_init(void)
|
||||
{
|
||||
#if __WIN32__
|
||||
WSADATA wsadata;
|
||||
int err;
|
||||
|
||||
err = WSAStartup (MAKEWORD(2,2), &wsadata);
|
||||
if (err != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("WSAStartup failed, error: %d\n", err);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Could not find a usable version of Winsock.dll\n");
|
||||
WSACleanup();
|
||||
return (-1);
|
||||
}
|
||||
#endif
|
||||
return (0);
|
||||
|
||||
} /* end sock_init */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: sock_connect
|
||||
*
|
||||
* Purpose: Connect to given host / port.
|
||||
*
|
||||
* Inputs: hostname - Host name or IP address.
|
||||
*
|
||||
* port - TCP port as text string.
|
||||
*
|
||||
* description - Description of the remote server to be used in error message.
|
||||
* e.g. "APRS-IS (Igate) Server" or "TCP KISS TNC".
|
||||
*
|
||||
* allow_ipv6 - True to allow IPv6. Otherwise only IPv4.
|
||||
*
|
||||
* debug - Print debugging information.
|
||||
*
|
||||
* Outputs: ipaddr_str - The IP address, in text form, is placed here in case
|
||||
* the caller wants it. Should be SOCK_IPADDR_LEN bytes.
|
||||
*
|
||||
* Returns: Socket Handle / file descriptor or -1 for error.
|
||||
*
|
||||
* Errors: (1) Can't find address for given host name.
|
||||
*
|
||||
* Print error and return -1.
|
||||
*
|
||||
* (2) Can't connect to one of the address(es).
|
||||
*
|
||||
* Silently try the next one.
|
||||
*
|
||||
* (3) Can't connect to any of the address(es).
|
||||
*
|
||||
* Nothing is printed for success. The caller might do that
|
||||
* to provide confirmation on what is happening.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
int sock_connect (char *hostname, char *port, char *description, int allow_ipv6, int debug, char ipaddr_str[SOCK_IPADDR_LEN])
|
||||
{
|
||||
#define MAX_HOSTS 50
|
||||
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *ai_head = NULL;
|
||||
struct addrinfo *ai;
|
||||
struct addrinfo *hosts[MAX_HOSTS];
|
||||
int num_hosts, n;
|
||||
int err;
|
||||
int server_sock = -1;
|
||||
|
||||
strlcpy (ipaddr_str, "???", SOCK_IPADDR_LEN);
|
||||
memset (&hints, 0, sizeof(hints));
|
||||
|
||||
hints.ai_family = AF_UNSPEC; /* Allow either IPv4 or IPv6. */
|
||||
if ( ! allow_ipv6) {
|
||||
hints.ai_family = AF_INET; /* IPv4 only. */
|
||||
}
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
/*
|
||||
* First, we need to look up the DNS name to get IP address.
|
||||
* It is possible to have multiple addresses.
|
||||
*/
|
||||
|
||||
ai_head = NULL;
|
||||
err = getaddrinfo(hostname, port, &hints, &ai_head);
|
||||
if (err != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
#if __WIN32__
|
||||
dw_printf ("Can't get address for %s, %s, err=%d\n",
|
||||
description, hostname, WSAGetLastError());
|
||||
#else
|
||||
dw_printf ("Can't get address for %s, %s, %s\n",
|
||||
description, hostname, gai_strerror(err));
|
||||
#endif
|
||||
freeaddrinfo(ai_head);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("getaddrinfo returns:\n");
|
||||
}
|
||||
|
||||
num_hosts = 0;
|
||||
for (ai = ai_head; ai != NULL; ai = ai->ai_next) {
|
||||
|
||||
if (debug) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
sock_ia_to_text (ai->ai_family, ai->ai_addr, ipaddr_str, SOCK_IPADDR_LEN);
|
||||
dw_printf (" %s\n", ipaddr_str);
|
||||
}
|
||||
|
||||
hosts[num_hosts] = ai;
|
||||
if (num_hosts < MAX_HOSTS) num_hosts++;
|
||||
}
|
||||
|
||||
shuffle (hosts, num_hosts);
|
||||
|
||||
if (debug) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("addresses for hostname:\n");
|
||||
for (n=0; n<num_hosts; n++) {
|
||||
sock_ia_to_text (hosts[n]->ai_family, hosts[n]->ai_addr, ipaddr_str, SOCK_IPADDR_LEN);
|
||||
dw_printf (" %s\n", ipaddr_str);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try each address until we find one that is successful.
|
||||
*/
|
||||
for (n = 0; n < num_hosts; n++) {
|
||||
#if __WIN32__
|
||||
SOCKET is;
|
||||
#else
|
||||
int is;
|
||||
#endif
|
||||
ai = hosts[n];
|
||||
|
||||
sock_ia_to_text (ai->ai_family, ai->ai_addr, ipaddr_str, SOCK_IPADDR_LEN);
|
||||
is = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
||||
#if __WIN32__
|
||||
if (is == INVALID_SOCKET) {
|
||||
printf ("Socket creation failed, err=%d", WSAGetLastError());
|
||||
WSACleanup();
|
||||
is = -1;
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
if (err != 0) {
|
||||
printf ("Socket creation failed, err=%s", gai_strerror(err));
|
||||
(void) close (is);
|
||||
is = -1;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_DNS
|
||||
err = connect(is, ai->ai_addr, (int)ai->ai_addrlen);
|
||||
#if __WIN32__
|
||||
if (err == SOCKET_ERROR) {
|
||||
#if DEBUGx
|
||||
printf("Connect to %s on %s (%s), port %s failed.\n",
|
||||
description, hostname, ipaddr_str, port);
|
||||
#endif
|
||||
closesocket (is);
|
||||
is = -1;
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
if (err != 0) {
|
||||
#if DEBUGx
|
||||
printf("Connect to %s on %s (%s), port %s failed.\n",
|
||||
description, hostname, ipaddr_str, port);
|
||||
#endif
|
||||
(void) close (is);
|
||||
is = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* IGate documentation says to use no delay. */
|
||||
/* Does it really make a difference? */
|
||||
int flag = 1;
|
||||
err = setsockopt (is, IPPROTO_TCP, TCP_NODELAY, (void*)(long)(&flag), (socklen_t)sizeof(flag));
|
||||
if (err < 0) {
|
||||
printf("setsockopt TCP_NODELAY failed.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Success. */
|
||||
|
||||
|
||||
server_sock = is;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
freeaddrinfo(ai_head);
|
||||
|
||||
// no, caller should handle this.
|
||||
// function should be generally be silent unless debug option.
|
||||
|
||||
if (server_sock == -1) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Unable to connect to %s at %s (%s), port %s\n",
|
||||
description, hostname, ipaddr_str, port );
|
||||
}
|
||||
|
||||
return (server_sock);
|
||||
|
||||
} /* end sock_connect */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: sock_bind
|
||||
*
|
||||
* Purpose: We also have a bunch of duplicate code for the server side.
|
||||
*
|
||||
* Inputs:
|
||||
*
|
||||
* TODO: Use this instead of own copy in audio.c
|
||||
* TODO: Use this instead of own copy in audio_portaudio.c
|
||||
* TODO: Use this instead of own copy in audio_win.c
|
||||
* TODO: Use this instead of own copy in kissnet.c
|
||||
* TODO: Use this instead of own copy in server.c
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
// Not implemented yet.
|
||||
|
||||
|
||||
/*
|
||||
* Addresses don't get mixed up very well.
|
||||
* IPv6 always shows up last so we'd probably never
|
||||
* end up using any of them for APRS-IS server.
|
||||
* Add our own shuffle.
|
||||
*/
|
||||
|
||||
static void shuffle (struct addrinfo *host[], int nhosts)
|
||||
{
|
||||
int j, k;
|
||||
|
||||
assert (RAND_MAX >= nhosts); /* for % to work right */
|
||||
|
||||
if (nhosts < 2) return;
|
||||
|
||||
srand (time(NULL));
|
||||
|
||||
for (j=0; j<nhosts; j++) {
|
||||
k = rand() % nhosts;
|
||||
assert (k >=0 && k<nhosts);
|
||||
if (j != k) {
|
||||
struct addrinfo *temp;
|
||||
temp = host[j]; host[j] = host[k]; host[k] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: sock_ia_to_text
|
||||
*
|
||||
* Purpose: Convert binary IP Address to text form.
|
||||
*
|
||||
* Inputs: Family - AF_INET or AF_INET6.
|
||||
*
|
||||
* pAddr - Pointer to the IP Address storage location.
|
||||
*
|
||||
* StringBufSize - Number of bytes in pStringBuf.
|
||||
*
|
||||
* Outputs: pStringBuf - Text result is placed here.
|
||||
*
|
||||
* Returns: pStringBuf
|
||||
*
|
||||
* Description: Can't use InetNtop because it is supported only on Windows Vista and later.
|
||||
* At one time Dire Wolf worked on Win XP. Haven't tried it for years.
|
||||
* Maybe some other dependency on a newer OS version has crept in.
|
||||
*
|
||||
* TODO: Use this instead of own copy in aclients.c
|
||||
* TODO: Use this instead of own copy in appserver.c
|
||||
* TODO: Use this instead of own copy in igate.c
|
||||
* TODO: Use this instead of own copy in tnctest.c
|
||||
* TODO: Use this instead of own copy in ttcalc.c
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
char *sock_ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t StringBufSize)
|
||||
{
|
||||
struct sockaddr_in *sa4;
|
||||
struct sockaddr_in6 *sa6;
|
||||
|
||||
switch (Family) {
|
||||
|
||||
case AF_INET:
|
||||
sa4 = (struct sockaddr_in *)pAddr;
|
||||
#if __WIN32__
|
||||
snprintf (pStringBuf, StringBufSize, "%d.%d.%d.%d", sa4->sin_addr.S_un.S_un_b.s_b1,
|
||||
sa4->sin_addr.S_un.S_un_b.s_b2,
|
||||
sa4->sin_addr.S_un.S_un_b.s_b3,
|
||||
sa4->sin_addr.S_un.S_un_b.s_b4);
|
||||
#else
|
||||
inet_ntop (AF_INET, &(sa4->sin_addr), pStringBuf, StringBufSize);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
sa6 = (struct sockaddr_in6 *)pAddr;
|
||||
#if __WIN32__
|
||||
snprintf (pStringBuf, StringBufSize, "%x:%x:%x:%x:%x:%x:%x:%x",
|
||||
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[0]),
|
||||
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[1]),
|
||||
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[2]),
|
||||
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[3]),
|
||||
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[4]),
|
||||
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[5]),
|
||||
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[6]),
|
||||
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[7]));
|
||||
#else
|
||||
inet_ntop (AF_INET6, &(sa6->sin6_addr), pStringBuf, StringBufSize);
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
snprintf (pStringBuf, StringBufSize, "Invalid address family!");
|
||||
}
|
||||
return pStringBuf;
|
||||
|
||||
} /* end sock_ia_to_text */
|
||||
|
||||
/* end sock.c */
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
/* sock.h - Socket helper functions. */
|
||||
|
||||
#ifndef SOCK_H
|
||||
#define SOCK_H 1
|
||||
|
||||
#define SOCK_IPADDR_LEN 48 // Size of string to hold IPv4 or IPv6 address.
|
||||
// I think 40 would be adequate but we'll make
|
||||
// it a little larger just to be safe.
|
||||
// Use INET6_ADDRSTRLEN (from netinet/in.h) instead?
|
||||
|
||||
int sock_init (void);
|
||||
|
||||
int sock_connect (char *hostname, char *port, char *description, int allow_ipv6, int debug, char *ipaddr_str);
|
||||
/* ipaddr_str needs to be at least SOCK_IPADDR_LEN bytes */
|
||||
|
||||
char *sock_ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t StringBufSize);
|
||||
|
||||
#endif
|
11
symbols.c
11
symbols.c
|
@ -261,11 +261,20 @@ static const struct {
|
|||
|
||||
|
||||
// Make sure the array is null terminated.
|
||||
// If search order is changed, do the same in decode_aprs.c
|
||||
|
||||
static const char *search_locations[] = {
|
||||
(const char *) "symbols-new.txt",
|
||||
#ifndef __WIN32__
|
||||
(const char *) "/usr/share/direwolf/symbols-new.txt",
|
||||
(const char *) "/usr/local/share/direwolf/symbols-new.txt",
|
||||
(const char *) "/usr/share/direwolf/symbols-new.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/symbols-new.txt",
|
||||
#endif
|
||||
(const char *) NULL
|
||||
};
|
||||
|
|
72
telemetry.c
72
telemetry.c
|
@ -143,9 +143,10 @@ static struct t_metadata_s * t_get_metadata (char *station)
|
|||
for (p = md_list_head; p != NULL; p = p->pnext) {
|
||||
if (strcmp(station, p->station) == 0) {
|
||||
|
||||
assert (p->magic1 == MAGIC1);
|
||||
assert (p->magic2 == MAGIC2);
|
||||
|
||||
if (p->magic1 != MAGIC1 || p->magic2 != MAGIC2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Internal error: REPORT THIS! Bad magic values %s %d\n", __func__, __LINE__);
|
||||
}
|
||||
return (p);
|
||||
}
|
||||
}
|
||||
|
@ -252,7 +253,10 @@ static int t_ndp (char *str)
|
|||
* KB1GKN-10>APRX27,UNCAN,WIDE1*:T#491,4.9,0.3,25.0,0.0,1.0,00000000
|
||||
*
|
||||
* Not integers. Not fixed width fields.
|
||||
* We will accept these but issue a warning that others might not.
|
||||
*
|
||||
* Originally I printed a warning if values were not in range of 000 to 255
|
||||
* but later took it out because no one pays attention to that original
|
||||
* restriction anymore.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
@ -282,8 +286,10 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output
|
|||
|
||||
pm = t_get_metadata(station);
|
||||
|
||||
assert (pm->magic1 == MAGIC1);
|
||||
assert (pm->magic2 == MAGIC2);
|
||||
if (pm->magic1 != MAGIC1 || pm->magic2 != MAGIC2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Internal error: REPORT THIS! Bad magic values %s %d\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
seq = 0;
|
||||
for (n = 0; n < T_NUM_ANALOG; n++) {
|
||||
|
@ -297,13 +303,13 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output
|
|||
if (strncmp(info, "T#", 2) != 0) {
|
||||
if ( ! quiet) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Error: Information part of telemetry packet must begin with \"#\"\n");
|
||||
dw_printf("Error: Information part of telemetry packet must begin with \"T#\"\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a copy of the input string because this will alter it.
|
||||
* Make a copy of the input string (excluding T#) because this will alter it.
|
||||
* Remove any trailing CR/LF.
|
||||
*/
|
||||
|
||||
|
@ -315,6 +321,15 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output
|
|||
|
||||
next = stemp;
|
||||
p = strsep(&next,",");
|
||||
|
||||
if (p == NULL) {
|
||||
if ( ! quiet) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Nothing after \"T#\" for telemetry data.\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
seq = atoi(p);
|
||||
n = 0;
|
||||
while ((p = strsep(&next,",")) != NULL) {
|
||||
|
@ -470,8 +485,10 @@ void telemetry_data_base91 (char *station, char *cdata, char *output, size_t out
|
|||
|
||||
pm = t_get_metadata(station);
|
||||
|
||||
assert (pm->magic1 == MAGIC1);
|
||||
assert (pm->magic2 == MAGIC2);
|
||||
if (pm->magic1 != MAGIC1 || pm->magic2 != MAGIC2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Internal error: REPORT THIS! Bad magic values %s %d\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
seq = 0;
|
||||
for (n = 0; n < T_NUM_ANALOG; n++) {
|
||||
|
@ -576,8 +593,11 @@ void telemetry_name_message (char *station, char *msg)
|
|||
}
|
||||
|
||||
pm = t_get_metadata(station);
|
||||
assert (pm->magic1 == MAGIC1);
|
||||
assert (pm->magic2 == MAGIC2);
|
||||
|
||||
if (pm->magic1 != MAGIC1 || pm->magic2 != MAGIC2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Internal error: REPORT THIS! Bad magic values %s %d\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
next = stemp;
|
||||
|
||||
|
@ -652,8 +672,11 @@ void telemetry_unit_label_message (char *station, char *msg)
|
|||
}
|
||||
|
||||
pm = t_get_metadata(station);
|
||||
assert (pm->magic1 == MAGIC1);
|
||||
assert (pm->magic2 == MAGIC2);
|
||||
|
||||
if (pm->magic1 != MAGIC1 || pm->magic2 != MAGIC2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Internal error: REPORT THIS! Bad magic values %s %d\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
next = stemp;
|
||||
|
||||
|
@ -729,8 +752,11 @@ void telemetry_coefficents_message (char *station, char *msg, int quiet)
|
|||
}
|
||||
|
||||
pm = t_get_metadata(station);
|
||||
assert (pm->magic1 == MAGIC1);
|
||||
assert (pm->magic2 == MAGIC2);
|
||||
|
||||
if (pm->magic1 != MAGIC1 || pm->magic2 != MAGIC2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Internal error: REPORT THIS! Bad magic values %s %d\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
next = stemp;
|
||||
|
||||
|
@ -809,8 +835,11 @@ void telemetry_bit_sense_message (char *station, char *msg, int quiet)
|
|||
#endif
|
||||
|
||||
pm = t_get_metadata(station);
|
||||
assert (pm->magic1 == MAGIC1);
|
||||
assert (pm->magic2 == MAGIC2);
|
||||
|
||||
if (pm->magic1 != MAGIC1 || pm->magic2 != MAGIC2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Internal error: REPORT THIS! Bad magic values %s %d\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
if (strlen(msg) < 8) {
|
||||
if ( ! quiet) {
|
||||
|
@ -919,8 +948,11 @@ static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_A
|
|||
|
||||
|
||||
assert (pm != NULL);
|
||||
assert (pm->magic1 == MAGIC1);
|
||||
assert (pm->magic2 == MAGIC2);
|
||||
|
||||
if (pm->magic1 != MAGIC1 || pm->magic2 != MAGIC2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Internal error: REPORT THIS! Bad magic values %s %d\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
strlcpy (output, "", outputsize);
|
||||
|
||||
|
|
46
tocalls.txt
46
tocalls.txt
|
@ -1,6 +1,21 @@
|
|||
APRS TO-CALL VERSION NUMBERS 06 Feb 2017
|
||||
APRS TO-CALL VERSION NUMBERS 12 Dec 2017
|
||||
-------------------------------------------------------------------
|
||||
WB4APR
|
||||
12 Dec 17 Added APHWxx for use in "HamWAN
|
||||
11 Dec 17 Added APDVxx for OE6PLD's SSTV with APRS status exchange
|
||||
20 Nov 17 added APPICO DB1NTO' PicoAPRS
|
||||
18 Oct 17 Added APBMxx BrandMeister DMR Server for R3ABM
|
||||
25 Sep 17 added APP6xx for APRSlib
|
||||
05 Sep 17 Chged APTAxx to APTBxx for TinyAPRS by BG5HHP
|
||||
17 Aug 17 Added APOCSG for POCSAG - N0AGI's APRS to Pagers
|
||||
21 Jun 17 Added APCSMS for Cosmos (used for sending commands @USNA
|
||||
08 Jun 17 Added APPMxx for DL1MX's RTL-SDR pytohon Igate
|
||||
01 Jun 17 added APOFF digi off on PSAT,PSAT2
|
||||
and APDTMF digi off mode on QIKCOM2 and DTMF ON
|
||||
and APRSON digi ON for PSAT
|
||||
and APDIGI digi ON for PSAT2 and QIKCOM-2
|
||||
and APSAT digi ON for QIKCOM-1
|
||||
20 Mar 17 added APTBxx for TinyAPRS by BG5HHP
|
||||
06 Feb 17 added APIExx for W7KMV's PiAPRS system
|
||||
25 Jan 17 added APSFxx F5OPV embedded devices - was APZ40
|
||||
16 Dec 16 added APYSxx for W2GMD's Python APRS
|
||||
|
@ -11,16 +26,8 @@ APRS TO-CALL VERSION NUMBERS 06 Feb 2017
|
|||
29 Apr 16 added APFPRS for FreeDV by Jeroen PE1RXQ
|
||||
25 Feb 16 Added APCDS0 for Leon Lessing ZS6LMG's cell tracker
|
||||
21 Jan 16 added APDNOx for APRSduino by DO3SWW
|
||||
18 Nov 15 Added APSTPO for N0AGI
|
||||
03 Nov 15 Updated APAND1 and APDRxx for androids
|
||||
26 Oct 15 added APZ247 for UPRS
|
||||
09 Sep 15 added APHTxx for HMTracker by IU0AAC
|
||||
06 Aug 15 added APMTxx for LZ1PPL for tracker
|
||||
27 Apr 15 added APZMAJ for Martyn M1MAJ DeLorme inReach Tracker
|
||||
21 Apr 15 added APB2MF & APR2MF DL2MF - MF2APRS Radiosonde
|
||||
06 Apr 15 added APAVT5 SainSonic AP510 - a 1watt tracker
|
||||
. . . . . ...
|
||||
11 Jan 12 added APYTxx for YagTracker and updated Yaesu APY008/350
|
||||
In 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
|
||||
|
@ -52,12 +59,14 @@ a TOCALL number series:
|
|||
APBLxx BigRedBee BeeLine
|
||||
APBLO MOdel Rocketry K7RKT
|
||||
APBPQx John G8BPQ Digipeater/IGate
|
||||
APBMxx BrandMeister DMR Server for R3ABM
|
||||
APC APCxxx Cellular applications
|
||||
APCBBx VE7UDP Blackberry Applications
|
||||
APCDS0 Leon Lessing ZS6LMG's cell tracker
|
||||
APCLEY EYTraker GPRS/GSM tracker by ZS6EY
|
||||
APCLWX EYWeather GPRS/GSM WX station by ZS6EY
|
||||
APCLEZ Telit EZ10 GSM application ZS6CEY
|
||||
APCSMS for Cosmos (used for sending commands @USNA)
|
||||
APCWP8 John GM7HHB, WinphoneAPRS
|
||||
APCYxx Cybiko applications
|
||||
APD APD4xx UP4DAR platform
|
||||
|
@ -66,6 +75,8 @@ a TOCALL number series:
|
|||
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
|
||||
|
@ -73,7 +84,9 @@ a TOCALL number series:
|
|||
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
|
||||
APECAN Pecan Pico APRS Balloon Tracker
|
||||
|
@ -87,6 +100,7 @@ a TOCALL number series:
|
|||
APH APHKxx for LA1BR tracker/digipeater
|
||||
APHAXn SM2APRS by PY2UEP
|
||||
APHTxx HMTracker by IU0AAC
|
||||
APHWxx for use in "HamWAN
|
||||
API APICQx for ICQ
|
||||
APICxx HA9MCQ's Pic IGate
|
||||
APIExx W7KMV's PiAPRS system
|
||||
|
@ -124,13 +138,18 @@ a TOCALL number series:
|
|||
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
|
||||
APOTxx Open Track
|
||||
APOD1w Open Track with 1 wire WX
|
||||
APOU2k Open Track for Ultimeter
|
||||
APOZxx www.KissOZ.dk Tracker. OZ1EKD and OZ7HVO
|
||||
APP APPTxx KetaiTracker by JF6LZE, Takeki (msg capable)
|
||||
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
|
||||
APR APR8xx APRSdos versions 800+
|
||||
APR2MF DL2MF - MF2APRS Radiosonde WX reporting
|
||||
|
@ -141,6 +160,7 @@ a TOCALL number series:
|
|||
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
|
||||
|
@ -148,6 +168,7 @@ a TOCALL number series:
|
|||
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
|
||||
|
@ -158,6 +179,7 @@ a TOCALL number series:
|
|||
APT APT2xx Tiny Track II
|
||||
APT3xx Tiny Track III
|
||||
APTAxx K4ATM's tiny track
|
||||
APTBxx TinyAPRS by BG5HHP Was APTAxx till Sep 2017
|
||||
APTIGR TigerTrack
|
||||
APTKPT TrackPoint N0LP
|
||||
APTTxx Tiny Track
|
||||
|
|
71
tq.c
71
tq.c
|
@ -281,7 +281,7 @@ void tq_append (int chan, int prio, packet_t pp)
|
|||
* Implementing the 6PACK protocol is probably the proper solution.
|
||||
*/
|
||||
|
||||
if (ax25_is_aprs(pp) && tq_count(chan,prio) > 100) {
|
||||
if (ax25_is_aprs(pp) && tq_count(chan,prio,"","",0) > 100) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Transmit packet queue for channel %d is too long. Discarding packet.\n", chan);
|
||||
dw_printf ("Perhaps the channel is so busy there is no opportunity to send.\n");
|
||||
|
@ -458,7 +458,7 @@ void lm_data_request (int chan, int prio, packet_t pp)
|
|||
* Is transmit queue out of control?
|
||||
*/
|
||||
|
||||
if (tq_count(chan,prio) > 250) {
|
||||
if (tq_count(chan,prio,"","",0) > 250) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Warning: Transmit packet queue for channel %d is extremely long.\n", chan);
|
||||
dw_printf ("Perhaps the channel is so busy there is no opportunity to send.\n");
|
||||
|
@ -912,36 +912,83 @@ static int tq_is_empty (int chan)
|
|||
*
|
||||
* Name: tq_count
|
||||
*
|
||||
* Purpose: Return count of the number of packets in the specified transmit queue.
|
||||
* Purpose: Return count of the number of packets (or bytes) in the specified transmit queue.
|
||||
* This is used only for queries from KISS or AWG client applications.
|
||||
*
|
||||
* Inputs: chan - Channel, 0 is first.
|
||||
*
|
||||
* prio - Priority, use TQ_PRIO_0_HI or TQ_PRIO_1_LO.
|
||||
* Specify -1 for total of both.
|
||||
*
|
||||
* source - If specified, count only those with this source address.
|
||||
*
|
||||
* dest - If specified, count only those with this destination address.
|
||||
*
|
||||
* bytes - If true, return number of bytes rather than packets.
|
||||
*
|
||||
* Returns: Number of items in specified queue.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
int tq_count (int chan, int prio)
|
||||
int tq_count (int chan, int prio, char *source, char *dest, int bytes)
|
||||
{
|
||||
|
||||
packet_t p;
|
||||
packet_t pp;
|
||||
int n;
|
||||
|
||||
if (prio == -1) {
|
||||
return (tq_count(chan, TQ_PRIO_0_HI, source, dest, bytes)
|
||||
+ tq_count(chan, TQ_PRIO_1_LO, source, dest, bytes));
|
||||
}
|
||||
|
||||
/* Don't bother with critical section. */
|
||||
/* Only used for debugging a problem. */
|
||||
// Array bounds check. FIXME: TODO: should have internal error instead of dying.
|
||||
|
||||
if (chan < 0 || chan >= MAX_CHANS || prio < 0 || prio >= TQ_NUM_PRIO) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("INTERNAL ERROR - tq_count(%d, %d, \"%s\", \"%s\", %d)\n", chan, prio, source, dest, bytes);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (queue_head[chan][prio] == 0) {
|
||||
return (0);
|
||||
}
|
||||
|
||||
// Don't want lists being rearranged while we are traversing them.
|
||||
|
||||
dw_mutex_lock (&tq_mutex);
|
||||
|
||||
n = 0;
|
||||
p = queue_head[chan][prio];
|
||||
while (p != NULL) {
|
||||
n++;
|
||||
p = ax25_get_nextp(p);
|
||||
pp = queue_head[chan][prio];
|
||||
while (pp != NULL) {
|
||||
int count_it = 1;
|
||||
|
||||
if (source != NULL && *source != '\0') {
|
||||
char frame_source[AX25_MAX_ADDR_LEN];
|
||||
ax25_get_addr_with_ssid (pp, AX25_SOURCE, frame_source);
|
||||
if (strcmp(source,frame_source) != 0) count_it = 0;
|
||||
}
|
||||
if (count_it && dest != NULL && *dest != '\0') {
|
||||
char frame_dest[AX25_MAX_ADDR_LEN];
|
||||
ax25_get_addr_with_ssid (pp, AX25_DESTINATION, frame_dest);
|
||||
if (strcmp(dest,frame_dest) != 0) count_it = 0;
|
||||
}
|
||||
|
||||
if (count_it) {
|
||||
if (bytes) {
|
||||
n += ax25_get_frame_len(pp);
|
||||
}
|
||||
else {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
pp = ax25_get_nextp(pp);
|
||||
}
|
||||
|
||||
dw_mutex_unlock (&tq_mutex);
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("tq_count(%d,%d) returns %d\n", chan, prio, n);
|
||||
dw_printf ("tq_count(%d, %d, \"%s\", \"%s\", %d) returns %d\n", chan, prio, source, dest, bytes, n);
|
||||
#endif
|
||||
|
||||
return (n);
|
||||
|
|
2
tq.h
2
tq.h
|
@ -34,7 +34,7 @@ packet_t tq_remove (int chan, int prio);
|
|||
|
||||
packet_t tq_peek (int chan, int prio);
|
||||
|
||||
int tq_count (int chan, int prio);
|
||||
int tq_count (int chan, int prio, char *source, char *dest, int bytes);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -468,7 +468,7 @@ int tt_text_to_call10 (const char *text, int quiet, char *buttons)
|
|||
int errors = 0;
|
||||
int found;
|
||||
char padded[8];
|
||||
char stemp[8];
|
||||
char stemp[11];
|
||||
|
||||
|
||||
strcpy (buttons, "");
|
||||
|
|
|
@ -58,8 +58,9 @@
|
|||
|
||||
#include "server.h"
|
||||
#include "kiss.h"
|
||||
#include "kissserial.h"
|
||||
#include "kissnet.h"
|
||||
|
||||
#include "kiss_frame.h"
|
||||
|
||||
/*
|
||||
* Information kept about local APRStt users.
|
||||
|
@ -891,12 +892,14 @@ static void xmit_object_report (int i, int first_time)
|
|||
int flen;
|
||||
|
||||
// TODO1.3: Put a wrapper around this so we only call one function to send by all methods.
|
||||
// We see the same sequence in direwolf.c.
|
||||
|
||||
flen = ax25_pack(pp, fbuf);
|
||||
|
||||
server_send_rec_packet (save_tt_config_p->obj_recv_chan, pp, fbuf, flen);
|
||||
kissnet_send_rec_packet (save_tt_config_p->obj_recv_chan, fbuf, flen);
|
||||
kiss_send_rec_packet (save_tt_config_p->obj_recv_chan, fbuf, flen);
|
||||
kissnet_send_rec_packet (save_tt_config_p->obj_recv_chan, KISS_CMD_DATA_FRAME, fbuf, flen, -1);
|
||||
kissserial_send_rec_packet (save_tt_config_p->obj_recv_chan, KISS_CMD_DATA_FRAME, fbuf, flen, -1);
|
||||
kisspt_send_rec_packet (save_tt_config_p->obj_recv_chan, KISS_CMD_DATA_FRAME, fbuf, flen, -1);
|
||||
}
|
||||
|
||||
if (first_time && save_tt_config_p->obj_send_to_ig) {
|
||||
|
|
31
ttcalc.c
31
ttcalc.c
|
@ -1,4 +1,3 @@
|
|||
|
||||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
|
@ -108,11 +107,10 @@ int main (int argc, char *argv[])
|
|||
char data[1024];
|
||||
char hostname[30] = "localhost";
|
||||
char port[10] = "8000";
|
||||
int err;
|
||||
|
||||
#if __WIN32__
|
||||
#else
|
||||
int err;
|
||||
|
||||
setlinebuf (stdout);
|
||||
#endif
|
||||
|
||||
|
@ -142,13 +140,8 @@ int main (int argc, char *argv[])
|
|||
|
||||
mon_cmd.kind_lo = 'k';
|
||||
|
||||
#if __WIN32__
|
||||
send (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd), 0);
|
||||
#else
|
||||
err = write (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd));
|
||||
err = SOCK_SEND (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd));
|
||||
(void)err;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Print all of the monitored packets.
|
||||
|
@ -157,11 +150,7 @@ int main (int argc, char *argv[])
|
|||
while (1) {
|
||||
int n;
|
||||
|
||||
#if __WIN32__
|
||||
n = recv (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd), 0);
|
||||
#else
|
||||
n = read (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd));
|
||||
#endif
|
||||
n = SOCK_RECV (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd));
|
||||
|
||||
if (n != sizeof(mon_cmd)) {
|
||||
printf ("Read error, received %d command bytes.\n", n);
|
||||
|
@ -171,14 +160,10 @@ int main (int argc, char *argv[])
|
|||
assert (mon_cmd.data_len >= 0 && mon_cmd.data_len < (int)(sizeof(data)));
|
||||
|
||||
if (mon_cmd.data_len > 0) {
|
||||
#if __WIN32__
|
||||
n = recv (server_sock, data, mon_cmd.data_len, 0);
|
||||
#else
|
||||
n = read (server_sock, data, mon_cmd.data_len);
|
||||
#endif
|
||||
n = SOCK_RECV (server_sock, data, mon_cmd.data_len);
|
||||
|
||||
if (n != mon_cmd.data_len) {
|
||||
printf ("Read error, client received %d data bytes when %d expected.\n", n, mon_cmd.data_len);
|
||||
printf ("Read error, client received %d data bytes when %d expected. Terminating.\n", n, mon_cmd.data_len);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
@ -254,11 +239,7 @@ int main (int argc, char *argv[])
|
|||
xmit_raw.hdr.kind_lo = 'K';
|
||||
xmit_raw.hdr.data_len = 1 + ax25_pack (reply_pp, xmit_raw.frame);
|
||||
|
||||
#if __WIN32__
|
||||
send (server_sock, (char*)(&xmit_raw), sizeof(xmit_raw.hdr)+xmit_raw.hdr.data_len, 0);
|
||||
#else
|
||||
err = write (server_sock, (char*)(&xmit_raw), sizeof(xmit_raw.hdr)+xmit_raw.hdr.data_len);
|
||||
#endif
|
||||
err = SOCK_SEND (server_sock, (char*)(&xmit_raw), sizeof(xmit_raw.hdr)+xmit_raw.hdr.data_len);
|
||||
ax25_delete (reply_pp);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
/* Dire Wolf version 1.4 */
|
||||
/* Dire Wolf version 1.5 */
|
||||
|
||||
#define APP_TOCALL "APDW"
|
||||
|
||||
#define MAJOR_VERSION 1
|
||||
#define MINOR_VERSION 4
|
||||
#define MINOR_VERSION 5
|
||||
//#define EXTRA_VERSION "Beta Test"
|
||||
|
|
205
xid.c
205
xid.c
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2014, 2016 John Langner, WB2OSZ
|
||||
// Copyright (C) 2014, 2016, 2017 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
|
||||
|
@ -50,6 +50,7 @@
|
|||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "textcolor.h"
|
||||
#include "xid.h"
|
||||
|
@ -66,10 +67,36 @@
|
|||
#define PI_Ack_Timer 9
|
||||
#define PI_Retries 10
|
||||
|
||||
// Forget about the bit order at the physical layer (e.g. HDLC).
|
||||
// It doesn't matter at all here. We are dealing with bytes.
|
||||
// A different encoding could send the bits in the opposite order.
|
||||
|
||||
// The bit numbers are confusing because this one table (Fig. 4.5) starts
|
||||
// with 1 for the LSB when everywhere else refers to the LSB as bit 0.
|
||||
|
||||
// The first byte must be of the form 0xx0 0001
|
||||
// The second byte must be of the form 0000 0000
|
||||
// If we process the two byte "Classes of Procedures" like
|
||||
// the other multibyte numeric fields, with the more significant
|
||||
// byte first, we end up with the bit masks below.
|
||||
// The bit order would be 8 7 6 5 4 3 2 1 16 15 14 13 12 11 10 9
|
||||
|
||||
// (This has nothing to do with the HDLC serializing order.
|
||||
// I'm talking about the way we would normally write binary numbers.)
|
||||
|
||||
#define PV_Classes_Procedures_Balanced_ABM 0x0100
|
||||
#define PV_Classes_Procedures_Half_Duplex 0x2000
|
||||
#define PV_Classes_Procedures_Full_Duplex 0x4000
|
||||
|
||||
|
||||
// The first byte must be of the form 1000 0xx0
|
||||
// The second byte must be of the form 1010 xx00
|
||||
// The third byte must be of the form 0000 0010
|
||||
// If we process the three byte "HDLC Optional Parmeters" like
|
||||
// the other multibyte numeric fields, with the most significant
|
||||
// byte first, we end up with bit masks like this.
|
||||
// The bit order would be 8 7 6 5 4 3 2 1 16 15 14 13 12 11 10 9 24 23 22 21 20 19 18 17
|
||||
|
||||
#define PV_HDLC_Optional_Functions_REJ_cmd_resp 0x020000
|
||||
#define PV_HDLC_Optional_Functions_SREJ_cmd_resp 0x040000
|
||||
#define PV_HDLC_Optional_Functions_Extended_Address 0x800000
|
||||
|
@ -79,6 +106,9 @@
|
|||
#define PV_HDLC_Optional_Functions_TEST_cmd_resp 0x002000
|
||||
#define PV_HDLC_Optional_Functions_16_bit_FCS 0x008000
|
||||
|
||||
#define PV_HDLC_Optional_Functions_Multi_SREJ_cmd_resp 0x000020
|
||||
#define PV_HDLC_Optional_Functions_Segmenter 0x000040
|
||||
|
||||
#define PV_HDLC_Optional_Functions_Synchronous_Tx 0x000002
|
||||
|
||||
|
||||
|
@ -127,7 +157,7 @@ int xid_parse (unsigned char *info, int info_len, struct xid_param_s *result, ch
|
|||
// rej and modulo are enum so we can't use G_UNKNOWN there.
|
||||
|
||||
result->full_duplex = G_UNKNOWN;
|
||||
result->rej = unknown_reject;
|
||||
result->srej = srej_not_specified;
|
||||
result->modulo = modulo_unknown;
|
||||
result->i_field_length_rx = G_UNKNOWN;
|
||||
result->window_size_rx = G_UNKNOWN;
|
||||
|
@ -204,21 +234,31 @@ int xid_parse (unsigned char *info, int info_len, struct xid_param_s *result, ch
|
|||
|
||||
case PI_HDLC_Optional_Functions:
|
||||
|
||||
if ((pval & PV_HDLC_Optional_Functions_REJ_cmd_resp) && (pval & PV_HDLC_Optional_Functions_SREJ_cmd_resp)) {
|
||||
result->rej = selective_reject_reject; /* Both bits set */
|
||||
strlcat (desc, "SREJ-REJ ", desc_size);
|
||||
}
|
||||
else if ((pval & PV_HDLC_Optional_Functions_REJ_cmd_resp) && ! (pval & PV_HDLC_Optional_Functions_SREJ_cmd_resp)) {
|
||||
result->rej = implicit_reject; /* Only REJ is set */
|
||||
// Pick highest of those offered.
|
||||
|
||||
if (pval & PV_HDLC_Optional_Functions_REJ_cmd_resp) {
|
||||
strlcat (desc, "REJ ", desc_size);
|
||||
}
|
||||
else if ( ! (pval & PV_HDLC_Optional_Functions_REJ_cmd_resp) && pval & PV_HDLC_Optional_Functions_SREJ_cmd_resp) {
|
||||
result->rej = selective_reject; /* Only SREJ is set */
|
||||
if (pval & PV_HDLC_Optional_Functions_SREJ_cmd_resp) {
|
||||
strlcat (desc, "SREJ ", desc_size);
|
||||
}
|
||||
if (pval & PV_HDLC_Optional_Functions_Multi_SREJ_cmd_resp) {
|
||||
strlcat (desc, "Multi-SREJ ", desc_size);
|
||||
}
|
||||
|
||||
if (pval & PV_HDLC_Optional_Functions_Multi_SREJ_cmd_resp) {
|
||||
result->srej = srej_multi;
|
||||
}
|
||||
else if (pval & PV_HDLC_Optional_Functions_SREJ_cmd_resp) {
|
||||
result->srej = srej_single;
|
||||
}
|
||||
else if (pval & PV_HDLC_Optional_Functions_REJ_cmd_resp) {
|
||||
result->srej = srej_none;
|
||||
}
|
||||
else {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("XID error: Expected one or both of REJ, SREJ to be set.\n");
|
||||
dw_printf ("XID error: Expected at least one of REJ, SREJ, Multi-SREJ to be set.\n");
|
||||
result->srej = srej_none;
|
||||
}
|
||||
|
||||
if ((pval & PV_HDLC_Optional_Functions_Modulo_8) && ! (pval & PV_HDLC_Optional_Functions_Modulo_128)) {
|
||||
|
@ -331,10 +371,10 @@ int xid_parse (unsigned char *info, int info_len, struct xid_param_s *result, ch
|
|||
* 0 = half duplex.
|
||||
* 1 = full duplex.
|
||||
*
|
||||
* rej - One of: implicit_reject, selective_reject, selective_reject_reject.
|
||||
* As command, what am I capable of processing?
|
||||
* As response, take minimum of
|
||||
*
|
||||
* srej - Level of selective reject.
|
||||
* srej_none (use REJ), srej_single, srej_multi
|
||||
* As command, offer a menu of what I can handle. (i.e. perhaps multiple bits set)
|
||||
* As response, take minimum of what is offered and what I can handle. (one bit set)
|
||||
*
|
||||
* modulo - 8 or 128.
|
||||
*
|
||||
|
@ -355,6 +395,8 @@ int xid_parse (unsigned char *info, int info_len, struct xid_param_s *result, ch
|
|||
* Default is 10.
|
||||
* Use G_UNKNOWN to omit this.
|
||||
*
|
||||
* cr - Is it a command or response?
|
||||
*
|
||||
* Outputs: info - Information part of XID frame.
|
||||
* Does not include the control byte.
|
||||
* Use buffer of 40 bytes just to be safe.
|
||||
|
@ -382,10 +424,17 @@ int xid_parse (unsigned char *info, int info_len, struct xid_param_s *result, ch
|
|||
* Comment: I have a problem with "... occurs at any time." What if we were in the middle
|
||||
* of transferring a large file with k=32 then along comes XID which says switch to modulo 8?
|
||||
*
|
||||
* Insight: Or is it Erratum?
|
||||
* After reading the base standards documents, it seems that the XID command should offer
|
||||
* up a menu of all the acceptable choices. e.g. REJ, SREJ, Multi-SREJ. One or more bits
|
||||
* can be set. The XID response, would set a single bit which is the desired choice from
|
||||
* among those offered.
|
||||
* Should go back and review half/full duplex and modulo.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
int xid_encode (struct xid_param_s *param, unsigned char *info)
|
||||
int xid_encode (struct xid_param_s *param, unsigned char *info, cmdres_t cr)
|
||||
{
|
||||
unsigned char *p;
|
||||
int len;
|
||||
|
@ -438,11 +487,43 @@ int xid_encode (struct xid_param_s *param, unsigned char *info)
|
|||
PV_HDLC_Optional_Functions_16_bit_FCS |
|
||||
PV_HDLC_Optional_Functions_Synchronous_Tx;
|
||||
|
||||
if (param->rej == implicit_reject || param->rej == selective_reject_reject || param->rej == unknown_reject)
|
||||
x |= PV_HDLC_Optional_Functions_REJ_cmd_resp;
|
||||
//text_color_set (DW_COLOR_ERROR);
|
||||
//dw_printf ("****** XID temp hack - test no SREJ ******\n");
|
||||
// param->srej = srej_none;
|
||||
|
||||
if (param->rej == selective_reject || param->rej == selective_reject_reject)
|
||||
if (cr == cr_cmd) {
|
||||
// offer a "menu" of acceptable choices. i.e. 1, 2 or 3 bits set.
|
||||
switch (param->srej) {
|
||||
case srej_none:
|
||||
default:
|
||||
x |= PV_HDLC_Optional_Functions_REJ_cmd_resp;
|
||||
break;
|
||||
case srej_single:
|
||||
x |= PV_HDLC_Optional_Functions_REJ_cmd_resp |
|
||||
PV_HDLC_Optional_Functions_SREJ_cmd_resp;
|
||||
break;
|
||||
case srej_multi:
|
||||
x |= PV_HDLC_Optional_Functions_REJ_cmd_resp |
|
||||
PV_HDLC_Optional_Functions_SREJ_cmd_resp |
|
||||
PV_HDLC_Optional_Functions_Multi_SREJ_cmd_resp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// for response, set only a single bit.
|
||||
switch (param->srej) {
|
||||
case srej_none:
|
||||
default:
|
||||
x |= PV_HDLC_Optional_Functions_REJ_cmd_resp;
|
||||
break;
|
||||
case srej_single:
|
||||
x |= PV_HDLC_Optional_Functions_SREJ_cmd_resp;
|
||||
break;
|
||||
case srej_multi:
|
||||
x |= PV_HDLC_Optional_Functions_Multi_SREJ_cmd_resp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (param->modulo == modulo_128)
|
||||
x |= PV_HDLC_Optional_Functions_Modulo_128;
|
||||
|
@ -531,7 +612,7 @@ static unsigned char example[27] = {
|
|||
/* GL */ 0x17, /* (2 bytes) */
|
||||
/* PI */ 0x02, /* Parameter Indicator - classes of procedures */
|
||||
/* PL */ 0x02, /* Parameter Length */
|
||||
#if 0 // Example in the protocol spec looks wrong.
|
||||
#if 0 // Erratum: Example in the protocol spec looks wrong.
|
||||
/* PV */ 0x00, /* Parameter Variable - Half Duplex, Async, Balanced Mode */
|
||||
/* PV */ 0x20, /* */
|
||||
#else // I think it should be like this instead.
|
||||
|
@ -545,6 +626,11 @@ static unsigned char example[27] = {
|
|||
/* PV */ 0x02, /* synchronous transmit */
|
||||
/* PI */ 0x06, /* Parameter Indicator - Rx I field length (bits) */
|
||||
/* PL */ 0x02, /* Parameter Length */
|
||||
|
||||
// Erratum: The text does not say anything about the byte order for multibyte
|
||||
// numeric values. In the example, we have two cases where 16 bit numbers are
|
||||
// sent with the more significant byte first.
|
||||
|
||||
/* PV */ 0x04, /* Parameter Variable - 1024 bits (128 octets) */
|
||||
/* PV */ 0x00, /* */
|
||||
/* PI */ 0x08, /* Parameter Indicator - Rx window size */
|
||||
|
@ -565,7 +651,7 @@ int main (int argc, char *argv[]) {
|
|||
struct xid_param_s param2;
|
||||
int n;
|
||||
unsigned char info[40]; // Currently max of 27 but things can change.
|
||||
char desc[100]; // 80 is not adequate.
|
||||
char desc[150]; // I've seen 109.
|
||||
|
||||
|
||||
/* parse example. */
|
||||
|
@ -573,13 +659,15 @@ int main (int argc, char *argv[]) {
|
|||
n = xid_parse (example, sizeof(example), ¶m, desc, sizeof(desc));
|
||||
|
||||
text_color_set (DW_COLOR_DEBUG);
|
||||
dw_printf ("%s\n", desc);
|
||||
dw_printf ("%d: %s\n", __LINE__, desc);
|
||||
fflush (stdout);
|
||||
SLEEP_SEC (1);
|
||||
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
|
||||
assert (n==1);
|
||||
assert (param.full_duplex == 0);
|
||||
assert (param.rej == selective_reject_reject);
|
||||
assert (param.srej == srej_single);
|
||||
assert (param.modulo == modulo_128);
|
||||
assert (param.i_field_length_rx == 128);
|
||||
assert (param.window_size_rx == 2);
|
||||
|
@ -588,7 +676,7 @@ int main (int argc, char *argv[]) {
|
|||
|
||||
/* encode and verify it comes out the same. */
|
||||
|
||||
n = xid_encode (¶m, info);
|
||||
n = xid_encode (¶m, info, cr_cmd);
|
||||
|
||||
assert (n == sizeof(example));
|
||||
n = memcmp(info, example, 27);
|
||||
|
@ -599,52 +687,56 @@ int main (int argc, char *argv[]) {
|
|||
|
||||
assert (n == 0);
|
||||
|
||||
/* try a couple different values. */
|
||||
/* try a couple different values, no srej. */
|
||||
|
||||
param.full_duplex = 1;
|
||||
param.rej = implicit_reject;
|
||||
param.srej = srej_none;
|
||||
param.modulo = modulo_8;
|
||||
param.i_field_length_rx = 2048;
|
||||
param.window_size_rx = 3;
|
||||
param.ack_timer = 1234;
|
||||
param.retries = 12;
|
||||
|
||||
n = xid_encode (¶m, info);
|
||||
n = xid_encode (¶m, info, cr_cmd);
|
||||
n = xid_parse (info, n, ¶m2, desc, sizeof(desc));
|
||||
|
||||
text_color_set (DW_COLOR_DEBUG);
|
||||
dw_printf ("%s\n", desc);
|
||||
dw_printf ("%d: %s\n", __LINE__, desc);
|
||||
fflush (stdout);
|
||||
SLEEP_SEC (1);
|
||||
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
|
||||
assert (param2.full_duplex == 1);
|
||||
assert (param2.rej == implicit_reject);
|
||||
assert (param2.srej == srej_none);
|
||||
assert (param2.modulo == modulo_8);
|
||||
assert (param2.i_field_length_rx == 2048);
|
||||
assert (param2.window_size_rx == 3);
|
||||
assert (param2.ack_timer == 1234);
|
||||
assert (param2.retries == 12);
|
||||
|
||||
/* The third possbility for rej. We don't use this. */
|
||||
/* Other values, single srej. */
|
||||
|
||||
param.full_duplex = 0;
|
||||
param.rej = selective_reject;
|
||||
param.srej = srej_single;
|
||||
param.modulo = modulo_8;
|
||||
param.i_field_length_rx = 61;
|
||||
param.window_size_rx = 4;
|
||||
param.ack_timer = 5555;
|
||||
param.retries = 9;
|
||||
|
||||
n = xid_encode (¶m, info);
|
||||
n = xid_encode (¶m, info, cr_cmd);
|
||||
n = xid_parse (info, n, ¶m2, desc, sizeof(desc));
|
||||
|
||||
text_color_set (DW_COLOR_DEBUG);
|
||||
dw_printf ("%s\n", desc);
|
||||
dw_printf ("%d: %s\n", __LINE__, desc);
|
||||
fflush (stdout);
|
||||
SLEEP_SEC (1);
|
||||
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
|
||||
assert (param2.full_duplex == 0);
|
||||
assert (param2.rej == selective_reject);
|
||||
assert (param2.srej == srej_single);
|
||||
assert (param2.modulo == modulo_8);
|
||||
assert (param2.i_field_length_rx == 61);
|
||||
assert (param2.window_size_rx == 4);
|
||||
|
@ -652,26 +744,57 @@ int main (int argc, char *argv[]) {
|
|||
assert (param2.retries == 9);
|
||||
|
||||
|
||||
/* Other values, multi srej. */
|
||||
|
||||
param.full_duplex = 0;
|
||||
param.srej = srej_multi;
|
||||
param.modulo = modulo_128;
|
||||
param.i_field_length_rx = 61;
|
||||
param.window_size_rx = 4;
|
||||
param.ack_timer = 5555;
|
||||
param.retries = 9;
|
||||
|
||||
n = xid_encode (¶m, info, cr_cmd);
|
||||
n = xid_parse (info, n, ¶m2, desc, sizeof(desc));
|
||||
|
||||
text_color_set (DW_COLOR_DEBUG);
|
||||
dw_printf ("%d: %s\n", __LINE__, desc);
|
||||
fflush (stdout);
|
||||
SLEEP_SEC (1);
|
||||
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
|
||||
assert (param2.full_duplex == 0);
|
||||
assert (param2.srej == srej_multi);
|
||||
assert (param2.modulo == modulo_128);
|
||||
assert (param2.i_field_length_rx == 61);
|
||||
assert (param2.window_size_rx == 4);
|
||||
assert (param2.ack_timer == 5555);
|
||||
assert (param2.retries == 9);
|
||||
|
||||
|
||||
/* Specify some and not others. */
|
||||
|
||||
param.full_duplex = 0;
|
||||
param.rej = selective_reject;
|
||||
param.srej = srej_single;
|
||||
param.modulo = modulo_8;
|
||||
param.i_field_length_rx = G_UNKNOWN;
|
||||
param.window_size_rx = G_UNKNOWN;
|
||||
param.ack_timer = 999;
|
||||
param.retries = G_UNKNOWN;
|
||||
|
||||
n = xid_encode (¶m, info);
|
||||
n = xid_encode (¶m, info, cr_cmd);
|
||||
n = xid_parse (info, n, ¶m2, desc, sizeof(desc));
|
||||
|
||||
text_color_set (DW_COLOR_DEBUG);
|
||||
dw_printf ("%s\n", desc);
|
||||
dw_printf ("%d: %s\n", __LINE__, desc);
|
||||
fflush (stdout);
|
||||
SLEEP_SEC (1);
|
||||
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
|
||||
assert (param2.full_duplex == 0);
|
||||
assert (param2.rej == selective_reject);
|
||||
assert (param2.srej == srej_single);
|
||||
assert (param2.modulo == modulo_8);
|
||||
assert (param2.i_field_length_rx == G_UNKNOWN);
|
||||
assert (param2.window_size_rx == G_UNKNOWN);
|
||||
|
@ -684,12 +807,14 @@ int main (int argc, char *argv[]) {
|
|||
n = xid_parse (info, n, ¶m2, desc, sizeof(desc));
|
||||
|
||||
text_color_set (DW_COLOR_DEBUG);
|
||||
dw_printf ("%s\n", desc);
|
||||
dw_printf ("%d: %s\n", __LINE__, desc);
|
||||
fflush (stdout);
|
||||
SLEEP_SEC (1);
|
||||
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
|
||||
assert (param2.full_duplex == G_UNKNOWN);
|
||||
assert (param2.rej == unknown_reject);
|
||||
assert (param2.srej == srej_not_specified);
|
||||
assert (param2.modulo == modulo_unknown);
|
||||
assert (param2.i_field_length_rx == G_UNKNOWN);
|
||||
assert (param2.window_size_rx == G_UNKNOWN);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue