Development snapshot 1.3 dev F.

Changes to be committed:
	modified:   .gitattributes
	modified:   CHANGES.md
	modified:   Makefile
	modified:   Makefile.linux
	new file:   Makefile.macosx
	modified:   Makefile.win
	modified:   aclients.c
	modified:   aprs_tt.c
	modified:   aprs_tt.h
	modified:   atest.c
	modified:   audio.c
	modified:   audio.h
	new file:   audio_portaudio.c
	new file:   audio_stats.c
	new file:   audio_stats.h
	modified:   audio_win.c
	modified:   ax25_pad.c
	modified:   ax25_pad.h
	modified:   beacon.c
	modified:   config.c
	modified:   config.h
	modified:   decode_aprs.c
	modified:   decode_aprs.h
	modified:   demod.c
	modified:   digipeater.c
	modified:   direwolf.c
	modified:   direwolf.h
	modified:   dlq.c
	modified:   doc/README.md
	modified:   doc/Raspberry-Pi-APRS.pdf
	modified:   doc/User-Guide.pdf
	new file:   doc/WA8LMF-TNC-Test-CD-Results.pdf
	modified:   dtime_now.c
	modified:   dtmf.c
	modified:   dw-start.sh
	modified:   encode_aprs.c
	modified:   encode_aprs.h
	modified:   gen_packets.c
	modified:   gen_tone.c
	modified:   gen_tone.h
	new file:   generic.conf
	modified:   grm_sym.h
	modified:   hdlc_rec.c
	modified:   hdlc_rec.h
	modified:   igate.c
	modified:   kiss.c
	modified:   kiss_frame.c
	modified:   kissnet.c
	modified:   latlong.c
	modified:   man1/direwolf.1
	modified:   mgn_icon.h
	modified:   misc/README-dire-wolf.txt
	new file:   misc/strlcat.c
	new file:   misc/strlcpy.c
	modified:   morse.c
	new file:   morse.h
	modified:   nmea.c
	modified:   pfilter.c
	modified:   ptt.c
	new file:   serial_port.c
	new file:   serial_port.h
	modified:   server.c
	modified:   symbols.c
	modified:   symbols.h
	new file:   telemetry-toolkit/telem-balloon.conf
	new file:   telemetry-toolkit/telem-balloon.pl
	new file:   telemetry-toolkit/telem-bits.pl
	new file:   telemetry-toolkit/telem-data.pl
	new file:   telemetry-toolkit/telem-data91.pl
	new file:   telemetry-toolkit/telem-eqns.pl
	new file:   telemetry-toolkit/telem-m0xer-3.txt
	new file:   telemetry-toolkit/telem-parm.pl
	new file:   telemetry-toolkit/telem-unit.pl
	new file:   telemetry-toolkit/telem-volts.conf
	new file:   telemetry-toolkit/telem-volts.py
	modified:   telemetry.c
	modified:   textcolor.c
	modified:   tq.c
	modified:   tt_text.c
	modified:   tt_text.h
	modified:   tt_user.c
	modified:   tt_user.h
	modified:   utm2ll.c
	modified:   version.h
	new file:   walk96.c
	modified:   xmit.c
This commit is contained in:
WB2OSZ 2015-09-07 19:56:20 -04:00
parent 558315fe19
commit dd27f9960b
86 changed files with 8207 additions and 1809 deletions

5
.gitattributes vendored
View File

@ -22,7 +22,8 @@
*.cpp text
*.h text
*.pl text
Makefile* text
*.py text
*.sh text
*.txt text
*.desktop text
*.conf text
@ -30,3 +31,5 @@ Makefile* text
*.ico binary
*.png binary
Makefile* text

View File

@ -3,6 +3,41 @@
----------
## Version 1.3 -- Development snapshot F -- September 2015 ##
### New Feature: ###
- Command line option "-a n" to print audio device statistics each n seconds. Previously this was always each 100 seconds on Linux and not available on Windows.
### Bug Fixed: ###
- Crashed when receiving packet with comment of a few hundred characters.
----------
## Version 1.3 -- Development snapshot E -- August 2015 ##
### Bug Fixed: ###
- Crashed when receiving packet with unexpected form of GPS NMEA sentence.
----------
## Version 1.3 -- Development snapshot D -- July 2015 ##
### New Features: ###
- Enhancements to APRStt gateway including Morse code and speech responses to to APRStt tone sequences.
- Preliminary support for Mac OS X. NEEDS MORE TESTING!
- A list of all symbols available can be obtained with the -S
command line option.
- APRS Telemetry Toolkit (incomplete).
----------
## Version 1.2 -- June 2015 ##
### New Features ###

View File

@ -1,9 +1,16 @@
# Select proper Makefile for operating system.
# The Windows version is built with the help of Cygwin.
# In my case, I see CYGWIN_NT-6.1-WOW so we don't check for
# equal to some value. Your mileage my vary.
win := $(shell uname | grep CYGWIN)
dar := $(shell uname | grep Darwin)
ifneq ($(win),)
include Makefile.win
include Makefile.win
else ifeq ($(dar),Darwin)
include Makefile.macosx
else
include Makefile.linux
include Makefile.linux
endif

View File

@ -2,7 +2,7 @@
# Makefile for Linux version of Dire Wolf.
#
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc direwolf.desktop
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc direwolf.desktop direwolf.conf
@echo " "
@echo "Next step - install with:"
@echo " "
@ -216,10 +216,10 @@ direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_9600.o hd
hdlc_rec2.o multi_modem.o redecode.o rdq.o rrbb.o dlq.o \
fcs_calc.o ax25_pad.o \
decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
gen_tone.o audio.o digipeater.o pfilter.o dedupe.o tq.o xmit.o \
gen_tone.o audio.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o morse.o \
ptt.o beacon.o dwgps.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 nmea.o log.o telemetry.o dtime_now.o \
geotranz.a
dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o serial_port.o log.o telemetry.o dtime_now.o \
misc.a geotranz.a
$(CC) $(CFLAGS) -o $@ $^ -lpthread -lrt $(LDLIBS) -lm
@ -263,6 +263,25 @@ utm.o : geotranz/utm.c
$(CC) $(CFLAGS) -c -o $@ $^
# Provide our own copy of strlcpy and strlcat because they are not included with Linux.
# We don't need the others in that same directory.
misc.a : strlcpy.o strlcat.o
ar -cr $@ $^
strlcpy.o : misc/strlcpy.c
$(CC) $(CFLAGS) -I. -c -o $@ $^
strlcat.o : misc/strlcat.c
$(CC) $(CFLAGS) -I. -c -o $@ $^
# Generate apprpriate sample configuration file for this platform.
direwolf.conf : generic.conf
egrep '^C|^L' generic.conf | cut -c2-999 > direwolf.conf
# Where should we install it?
@ -282,7 +301,7 @@ INSTALLDIR := /usr/local
# It was hardcoded with lxterminal, /home/pi, and so on.
# In version 1.2, try to customize this to match other situations better.
# TODO1.2: Test this better.
# TODO: Test this better.
direwolf.desktop :
@ -310,7 +329,7 @@ endif
# TODO: Review file locations.
install : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets \
tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png direwolf.desktop
tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png direwolf.desktop
install direwolf $(INSTALLDIR)/bin
install decode_aprs $(INSTALLDIR)/bin
install text2tt $(INSTALLDIR)/bin
@ -323,20 +342,21 @@ install : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx ge
install atest $(INSTALLDIR)/bin
install ttcalc $(INSTALLDIR)/bin
install dwespeak.sh $(INSTALLDIR)/bin
install telemetry-toolkit/*.p[ly] $(INSTALLDIR)/bin
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 CHANGES.txt $(INSTALLDIR)/share/doc/direwolf/CHANGES.txt
install -D --mode=644 README.md $(INSTALLDIR)/share/doc/direwolf/README.md
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 User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf
install -D --mode=644 Raspberry-Pi-APRS.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf
install -D --mode=644 Raspberry-Pi-APRS-Tracker.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf
install -D --mode=644 APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf
install -D --mode=644 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 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/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/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf
install -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf
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
@ -348,15 +368,15 @@ install : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx ge
install -D --mode=644 man1/tt2text.1 $(INSTALLDIR)/man/man1/tt2text.1
install -D --mode=644 man1/utm2ll.1 $(INSTALLDIR)/man/man1/utm2ll.1
@echo " "
@echo "If this is your first install, not an upgrade, type this"
@echo "to put a copy of the sample configuration file in your home directory:"
@echo "If this is your first install, not an upgrade, type this to put a copy"
@echo "of the sample configuration file (direwolf.conf) in your home directory:"
@echo " "
@echo " make install-conf"
@echo " "
# TODO: Should we put the sample direwolf.conf file somewhere like
# /usr/share/doc/direwolf/examples or /etc/ax25 and add that to the
# /usr/share/doc/direwolf/examples and add that to the
# end of the search path list?
# That would make it easy to see user customizations compared to the
# latest sample.
@ -371,6 +391,8 @@ install : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx ge
.PHONY: install-conf
install-conf : direwolf.conf
cp direwolf.conf ~
cp telemetry-toolkit/telem-m0xer-3.txt ~
cp telemetry-toolkit/telem-*.conf ~
ifneq ($(wildcard $(HOME)/Desktop),)
@echo " "
@echo "This will add a desktop icon on some systems:"
@ -389,7 +411,7 @@ install-rpi : dw-start.sh
# Separate application to decode raw data.
decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c latlong.c log.c telemetry.o
decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c latlong.c log.c telemetry.o tt_text.o misc.a
$(CC) $(CFLAGS) -o decode_aprs -DTEST $^ -lm
@ -405,22 +427,22 @@ tt2text : tt_text.c
# Convert between Latitude/Longitude and UTM coordinates.
ll2utm : ll2utm.c geotranz.a
ll2utm : ll2utm.c geotranz.a textcolor.o misc.a
$(CC) $(CFLAGS) -o $@ $^ -lm
utm2ll : utm2ll.c geotranz.a
utm2ll : utm2ll.c geotranz.a textcolor.o misc.a
$(CC) $(CFLAGS) -o $@ $^ -lm
# Convert from log file to GPX.
log2gpx : log2gpx.c
log2gpx : log2gpx.c textcolor.o misc.a
$(CC) $(CFLAGS) -o $@ $^ -lm
# Test application to generate sound.
gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c textcolor.c dsp.c
gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c morse.c textcolor.c dsp.c misc.a
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -lm
demod.o : tune.h
@ -428,7 +450,7 @@ demod_afsk.o : tune.h
demod_9600.o : tune.h
testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \
fcs_calc.c ax25_pad.c decode_aprs.c telemetry.c latlong.c symbols.c tune.h textcolor.c
fcs_calc.c ax25_pad.c decode_aprs.c telemetry.c latlong.c symbols.c tune.h textcolor.c misc.a
$(CC) $(CFLAGS) -o atest $^ -lm
./atest 02_Track_2.wav | grep "packets decoded in" > atest.out
@ -437,34 +459,36 @@ testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o
atest : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \
fcs_calc.c ax25_pad.c decode_aprs.c telemetry.c latlong.c symbols.c textcolor.c
fcs_calc.c ax25_pad.c decode_aprs.c telemetry.c latlong.c symbols.c tt_text.c textcolor.c \
misc.a
$(CC) $(CFLAGS) -o $@ $^ -lm -lrt
# Unit test for inner digipeater algorithm
dtest : digipeater.c pfilter.c ax25_pad.c dedupe.c fcs_calc.c tq.c textcolor.c
dtest : digipeater.c pfilter.c ax25_pad.c dedupe.c fcs_calc.c tq.c textcolor.c misc.a
$(CC) $(CFLAGS) -DTEST -o $@ $^
./dtest
# Unit test for APRStt.
ttest : aprs_tt.c tt_text.c latlong.c misc.a geotranz.a
ttest : aprs_tt.c tt_text.c latlong.c textcolor.o misc.a geotranz.a misc.a
$(CC) $(CFLAGS) -DTT_MAIN -o $@ $^
# Unit test for IGate
itest : igate.c textcolor.c ax25_pad.c fcs_calc.c
itest : igate.c textcolor.c ax25_pad.c fcs_calc.c textcolor.o misc.a
$(CC) $(CFLAGS) -DITEST -o $@ $^
./itest
# Unit test for UDP reception with AFSK demodulator
udptest : udp_test.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c
udptest : udp_test.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c rrbb.c
fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c misc.a
$(CC) $(CFLAGS) -o $@ $^ -lm -lrt
./udptest
@ -472,20 +496,20 @@ udptest : udp_test.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec
# Unit test for telemetry decoding.
etest : telemetry.c ax25_pad.c fcs_calc.c textcolor.c misc.a regex.a
etest : telemetry.c ax25_pad.c fcs_calc.c textcolor.o misc.a regex.a
$(CC) $(CFLAGS) -o $@ $^ -lm -lrt
./etest
# Multiple AGWPE network or serial port clients to test TNCs side by side.
aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.c
aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.o misc.a
$(CC) $(CFLAGS) -g -o $@ $^
# Touch Tone to Speech sample application.
ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o
ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a
$(CC) $(CFLAGS) -g -o $@ $^
@ -503,49 +527,35 @@ clean :
# Package it up for distribution.
.PHONY: dist-src
dist-src : CHANGES.txt User-Guide.pdf Raspberry-Pi-APRS.pdf \
Raspberry-Pi-APRS-Tracker.pdf APRStt-Implementation-Notes.pdf \
A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf \
dist-src : README.md CHANGES.md
doc/User-Guide.pdf doc/Raspberry-Pi-APRS.pdf \
doc/Raspberry-Pi-APRS-Tracker.pdf doc/APRStt-Implementation-Notes.pdf \
dw-start.sh dwespeak.bat dwespeak.sh \
tocalls.txt symbols-new.txt symbolsX.txt direwolf.spec
rm -f fsk_fast_filter.h
echo " " > tune.h
rm -f ../$z-src.zip
egrep '^C|^L' direwolf.txt | cut -c2-999 > direwolf.conf
(cd .. ; zip $z-src.zip \
$z/CHANGES.txt \
$z/README.md \
$z/CHANGES.md \
$z/LICENSE* \
$z/User-Guide.pdf \
$z/Raspberry-Pi-APRS.pdf \
$z/Raspberry-Pi-APRS-Tracker.pdf \
$z/APRStt-Implementation-Notes.pdf \
$z/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf \
$z/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf \
$z/doc/User-Guide.pdf \
$z/doc/Raspberry-Pi-APRS.pdf \
$z/doc/Raspberry-Pi-APRS-Tracker.pdf \
$z/doc/APRStt-Implementation-Notes.pdf \
$z/doc/APRS-Telemetry-Toolkit.pdf \
$z/Makefile* \
$z/*.c $z/*.h \
$z/regex/* $z/misc/* $z/geotranz/* \
$z/man1/* \
$z/direwolf.conf $z/direwolf.txt \
$z/generic.conf \
$z/tocalls.txt $z/symbols-new.txt $z/symbolsX.txt \
$z/dw-icon.png $z/dw-icon.rc $z/dw-icon.ico \
$z/dw-start.sh $z/direwolf.spec \
$z/dwespeak.bat $z/dwespeak.sh )
$z/dwespeak.bat $z/dwespeak.sh \
$z/telemetry-toolkit/* )
#User-Guide.pdf : User-Guide.docx
# echo "***** User-Guide.pdf is out of date *****"
#Raspberry-Pi-APRS.pdf : Raspberry-Pi-APRS.docx
# echo "***** Raspberry-Pi-APRS.pdf is out of date *****"
#Raspberry-Pi-APRS-Tracker.pdf : Raspberry-Pi-APRS-Tracker.docx
# echo "***** Raspberry-Pi-APRS-Tracker.pdf is out of date *****"
#A-Better-APRS-Packet-Demodulator.pdf : A-Better-APRS-Packet-Demodulator.docx
# echo "***** A-Better-APRS-Packet-Demodulator.pdf is out of date *****"
#
# The locations below appear to be the most recent.
# The copy at http://www.aprs.org/tocalls.txt is out of date.

516
Makefile.macosx Normal file
View File

@ -0,0 +1,516 @@
#
# Makefile for Macintosh 10.8+ version of Dire Wolf.
#
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc direwolf.conf
@echo " "
@echo "Next step install with: "
@echo " "
@echo " sudo make install"
@echo " "
@echo "System SDK's (10.8 - 10.10) must be located here to make use of them. "
@echo " /Developer/SDKs "
@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
EXTRA_CFLAGS :=
DARWIN_CC := $(shell which clang)
ifeq (${DARWIN_CC},)
DARWIN_CC := $(shell which gcc)
EXTRA_CFLAGS :=
else
EXTRA_CFLAGS := -fvectorize -fslp-vectorize -fslp-vectorize-aggressive -pthread
endif
# Change as required in support of the available libraries
#CC := $(DARWIN_CC) -m64 $(SYS_LIBS) $(SYS_MIN)
CC := $(DARWIN_CC) -m32 $(SYS_LIBS) $(SYS_MIN)
CFLAGS := -Os -pthread -Igeotranz $(EXTRA_CFLAGS)
# $(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.
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.
useffast := $(shell gcc --help -v 2>/dev/null | grep ffast-math)
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
# Force static linking of portaudio if the static library is available.
PA_LIB_STATIC := $(shell find /opt/local/lib -maxdepth 1 -type f -name "libportaudio.a")
#$(info $$PA_LIB_STATIC is [${PA_LIB_STATIC}])
ifeq (${PA_LIB_STATIC},)
LDLIBS += -L/opt/local/lib -lportaudio
else
LDLIBS += /opt/local/lib/libportaudio.a
endif
# Include libraries portaudio requires.
LDLIBS += -framework CoreAudio -framework AudioUnit -framework AudioToolbox
LDLIBS += -framework Foundation -framework CoreServices
CFLAGS += -DUSE_PORTAUDIO -I/opt/local/include
# Uncomment following lines to enable GPS interface & tracker function.
# Not available for MacOSX.
# Although MacPorts has gpsd, wonder if it's the same thing.
#CFLAGS += -DENABLE_GPS
#LDLIBS += -lgps
# Name of current directory.
# Used to generate zip file name for distribution.
z := $(notdir ${CURDIR})
# Main application.
direwolf : direwolf.o aprs_tt.o audio_portaudio.o audio_stats.o ax25_pad.o beacon.o \
config.o decode_aprs.o dedupe.o demod_9600.o demod_afsk.o \
demod.o digipeater.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 \
nmea.o serial_port.o pfilter.o ptt.o rdq.o recv.o redecode.o rrbb.o server.o \
symbols.o telemetry.o textcolor.o tq.o tt_text.o tt_user.o xmit.o
$(CC) $(CFLAGS) -o $@ $^ -lpthread $(LDLIBS) -lm
# Optimization for slow processors.
demod.o : fsk_fast_filter.h
demod_afsk.o : fsk_fast_filter.h
fsk_fast_filter.h : demod_afsk.c
$(CC) $(CFLAGS) -o gen_fff -DGEN_FFF demod_afsk.c dsp.c textcolor.c -lm
./gen_fff > fsk_fast_filter.h
# UTM, USNG, MGRS conversions.
geotranz.a : error_string.o mgrs.o polarst.o tranmerc.o ups.o usng.o utm.o
ar -cr $@ $^
error_string.o : geotranz/error_string.c
$(CC) $(CFLAGS) -c -o $@ $^
mgrs.o : geotranz/mgrs.c
$(CC) $(CFLAGS) -c -o $@ $^
polarst.o : geotranz/polarst.c
$(CC) $(CFLAGS) -c -o $@ $^
tranmerc.o : geotranz/tranmerc.c
$(CC) $(CFLAGS) -c -o $@ $^
ups.o : geotranz/ups.c
$(CC) $(CFLAGS) -c -o $@ $^
usng.o : geotranz/usng.c
$(CC) $(CFLAGS) -c -o $@ $^
utm.o : geotranz/utm.c
$(CC) $(CFLAGS) -c -o $@ $^
# Generate apprpriate sample configuration file for this platform.
direwolf.conf : generic.conf
egrep '^C|^M' generic.conf | cut -c2-999 > direwolf.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.
INSTALLDIR := /usr/local
# TODO: Test this better.
# Optional installation into /usr/local/...
# Needs to be run as root or with sudo.
# TODO: Review file locations.
install : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets \
tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png
ginstall direwolf $(INSTALLDIR)/bin
ginstall decode_aprs $(INSTALLDIR)/bin
ginstall text2tt $(INSTALLDIR)/bin
ginstall tt2text $(INSTALLDIR)/bin
ginstall ll2utm $(INSTALLDIR)/bin
ginstall utm2ll $(INSTALLDIR)/bin
ginstall aclients $(INSTALLDIR)/bin
ginstall log2gpx $(INSTALLDIR)/bin
ginstall gen_packets $(INSTALLDIR)/bin
ginstall atest $(INSTALLDIR)/bin
ginstall ttcalc $(INSTALLDIR)/bin
ginstall dwespeak.sh $(INSTALLDIR)/bin
ginstall telemetry-toolkit/*.p[ly] $(INSTALLDIR)/bin
ginstall -D --mode=644 tocalls.txt $(INSTALLDIR)/share/direwolf/tocalls.txt
ginstall -D --mode=644 symbols-new.txt $(INSTALLDIR)/share/direwolf/symbols-new.txt
ginstall -D --mode=644 symbolsX.txt $(INSTALLDIR)/share/direwolf/symbolsX.txt
ginstall -D --mode=644 dw-icon.png $(INSTALLDIR)/share/direwolf/dw-icon.png
ginstall -D --mode=644 README.md $(INSTALLDIR)/share/doc/direwolf/README.md
ginstall -D --mode=644 CHANGES.md $(INSTALLDIR)/share/doc/direwolf/CHANGES.md
ginstall -D --mode=644 direwolf.conf $(INSTALLDIR)/share/direwolf/config/direwolf.conf
ginstall -D --mode=644 LICENSE-dire-wolf.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-dire-wolf.txt
ginstall -D --mode=644 LICENSE-other.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-other.txt
ginstall -D --mode=644 doc/User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf
ginstall -D --mode=644 doc/Raspberry-Pi-APRS.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf
ginstall -D --mode=644 doc/Raspberry-Pi-APRS-Tracker.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf
ginstall -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf
ginstall -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf
ginstall -D --mode=644 man1/aclients.1 $(INSTALLDIR)/man/man1/aclients.1
ginstall -D --mode=644 man1/atest.1 $(INSTALLDIR)/man/man1/atest.1
ginstall -D --mode=644 man1/decode_aprs.1 $(INSTALLDIR)/man/man1/decode_aprs.1
ginstall -D --mode=644 man1/direwolf.1 $(INSTALLDIR)/man/man1/direwolf.1
ginstall -D --mode=644 man1/gen_packets.1 $(INSTALLDIR)/man/man1/gen_packets.1
ginstall -D --mode=644 man1/ll2utm.1 $(INSTALLDIR)/man/man1/ll2utm.1
ginstall -D --mode=644 man1/log2gpx.1 $(INSTALLDIR)/man/man1/log2gpx.1
ginstall -D --mode=644 man1/text2tt.1 $(INSTALLDIR)/man/man1/text2tt.1
ginstall -D --mode=644 man1/tt2text.1 $(INSTALLDIR)/man/man1/tt2text.1
ginstall -D --mode=644 man1/utm2ll.1 $(INSTALLDIR)/man/man1/utm2ll.1
@echo " "
@echo "If this is your first install, not an upgrade, type this to put a copy"
@echo "of the sample configuration file (direwolf.conf) in your home directory:"
@echo " "
@echo " make install-conf"
@echo " "
# TODO: Should we put the sample direwolf.conf file somewhere like
# /usr/share/doc/direwolf/examples and add that to the
# end of the search path list?
# That would make it easy to see user customizations compared to the
# latest sample.
# These would be done as ordinary user.
# The Raspberry Pi has ~/Desktop but Ubuntu does not.
# TODO: Handle Linux variations correctly.
.PHONY: install-conf
install-conf : direwolf.conf
cp direwolf.conf ~
cp telemetry-toolkit/telem-m0xer-3.txt ~
cp telemetry-toolkit/telem-*.conf ~
# Separate application to decode raw data.
decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c latlong.c log.c telemetry.o tt_text.o
$(CC) $(CFLAGS) -o decode_aprs -DTEST $^ -lm
# Convert between text and touch tone representation.
text2tt : tt_text.c
$(CC) $(CFLAGS) -DENC_MAIN -o text2tt tt_text.c
tt2text : tt_text.c
$(CC) $(CFLAGS) -DDEC_MAIN -o tt2text tt_text.c
# Convert between Latitude/Longitude and UTM coordinates.
ll2utm : ll2utm.c geotranz.a
$(CC) $(CFLAGS) -o $@ $^ -lm
utm2ll : utm2ll.c geotranz.a
$(CC) $(CFLAGS) -o $@ $^ -lm
# Convert from log file to GPX.
log2gpx : log2gpx.c
$(CC) $(CFLAGS) -o $@ $^ -lm
# Test application to generate sound.
gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c morse.c textcolor.c dsp.c
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -lm
demod.o : tune.h
demod_afsk.o : tune.h
demod_9600.o : tune.h
testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \
fcs_calc.c ax25_pad.c decode_aprs.c telemetry.c latlong.c symbols.c tune.h textcolor.c
$(CC) $(CFLAGS) -o atest $^ -lm
./atest 02_Track_2.wav | grep "packets decoded in" > atest.out
# Unit test for AFSK demodulator
atest : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \
fcs_calc.c ax25_pad.c decode_aprs.c telemetry.c latlong.c symbols.c textcolor.c tt_text.c
$(CC) $(CFLAGS) -o $@ $^ -lm
# Unit test for inner digipeater algorithm
dtest : digipeater.c pfilter.c ax25_pad.c dedupe.c fcs_calc.c tq.c textcolor.c
$(CC) $(CFLAGS) -DTEST -o $@ $^
./dtest
# Unit test for APRStt.
ttest : aprs_tt.c tt_text.c latlong.c misc.a geotranz.a
$(CC) $(CFLAGS) -DTT_MAIN -o $@ $^
# Unit test for IGate
itest : igate.c textcolor.c ax25_pad.c fcs_calc.c
$(CC) $(CFLAGS) -DITEST -o $@ $^
./itest
# Unit test for UDP reception with AFSK demodulator
udptest : udp_test.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c
$(CC) $(CFLAGS) -o $@ $^ -lm
./udptest
# Unit test for telemetry decoding.
etest : telemetry.c ax25_pad.c fcs_calc.c textcolor.c misc.a regex.a
$(CC) $(CFLAGS) -o $@ $^ -lm
./etest
# Multiple AGWPE network or serial port clients to test TNCs side by side.
aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.c
$(CC) $(CFLAGS) -g -o $@ $^
# Touch Tone to Speech sample application.
ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o
$(CC) $(CFLAGS) -g -o $@ $^
depend : $(wildcard *.c)
makedepend -f $(lastword $(MAKEFILE_LIST)) -- $(CFLAGS) -- $^
.PHONY: clean
clean :
rm -f direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc \
fsk_fast_filter.h *.o *.a
echo " " > tune.h
.PHONY: dist-mac
dist-mac: direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets \
tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png
rm -f ../direwolf_dist_bin.zip
(cd .. ; zip direwolf_dist_bin.zip \
$(INSTALLDIR)/bin/direwolf \
$(INSTALLDIR)/bin/decode_aprs \
$(INSTALLDIR)/bin/text2tt \
$(INSTALLDIR)/bin/tt2text \
$(INSTALLDIR)/bin/ll2utm \
$(INSTALLDIR)/bin/utm2ll \
$(INSTALLDIR)/bin/aclients \
$(INSTALLDIR)/bin/log2gpx \
$(INSTALLDIR)/bin/gen_packets \
$(INSTALLDIR)/bin/atest \
$(INSTALLDIR)/bin/ttcalc \
$(INSTALLDIR)/bin/dwespeak.sh \
$(INSTALLDIR)/share/direwolf/tocalls.txt \
$(INSTALLDIR)/share/direwolf/config/direwolf.conf \
$(INSTALLDIR)/share/direwolf/symbols-new.txt \
$(INSTALLDIR)/share/direwolf/symbolsX.txt \
$(INSTALLDIR)/share/direwolf/dw-icon.png \
$(INSTALLDIR)/share/doc/direwolf/README.md \
$(INSTALLDIR)/share/doc/direwolf/CHANGES.md \
$(INSTALLDIR)/share/doc/direwolf/LICENSE-dire-wolf.txt \
$(INSTALLDIR)/share/doc/direwolf/LICENSE-other.txt \
$(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf \
$(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf \
$(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf \
$(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf \
$(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf \
$(INSTALLDIR)/man/man1/aclients.1 \
$(INSTALLDIR)/man/man1/atest.1 \
$(INSTALLDIR)/man/man1/decode_aprs.1 \
$(INSTALLDIR)/man/man1/direwolf.1 \
$(INSTALLDIR)/man/man1/gen_packets.1 \
$(INSTALLDIR)/man/man1/ll2utm.1 \
$(INSTALLDIR)/man/man1/log2gpx.1 \
$(INSTALLDIR)/man/man1/text2tt.1 \
$(INSTALLDIR)/man/man1/tt2text.1 \
$(INSTALLDIR)/man/man1/utm2ll.1 \
)
# Package it up for distribution.
.PHONY: dist-src
dist-src : README.md CHANGES.md \
doc/User-Guide.pdf doc/Raspberry-Pi-APRS.pdf \
doc/Raspberry-Pi-APRS-Tracker.pdf doc/APRStt-Implementation-Notes.pdf \
dw-start.sh dwespeak.bat dwespeak.sh \
tocalls.txt symbols-new.txt symbolsX.txt direwolf.spec
rm -f fsk_fast_filter.h
echo " " > tune.h
rm -f ../$z-src.zip
(cd .. ; zip $z-src.zip \
$z/README.md \
$z/CHANGES.md \
$z/LICENSE* \
$z/doc/User-Guide.pdf \
$z/doc/Raspberry-Pi-APRS.pdf \
$z/doc/Raspberry-Pi-APRS-Tracker.pdf \
$z/doc/APRStt-Implementation-Notes.pdf \
$z/Makefile* \
$z/*.c $z/*.h \
$z/regex/* $z/misc/* $z/geotranz/* \
$z/man1/* \
$z/generic.conf \
$z/tocalls.txt $z/symbols-new.txt $z/symbolsX.txt \
$z/dw-icon.png $z/dw-icon.rc $z/dw-icon.ico \
$z/dw-start.sh $z/direwolf.spec \
$z/dwespeak.bat $z/dwespeak.sh \
$z/telemetry-toolkit/* )

View File

@ -26,7 +26,7 @@ CC := gcc
CFLAGS := -Wall -Ofast -march=pentium3 -msse -Iregex -Iutm -Igeotranz -mthreads -DUSE_REGEX_STATIC
AR := ar
#CFLAGS += -g
CFLAGS += -g
#
# Let's see impact of various optimization levels.
@ -73,9 +73,9 @@ direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_9600.o hd
hdlc_rec2.o multi_modem.o redecode.o rdq.o rrbb.o dlq.o \
fcs_calc.o ax25_pad.o \
decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
gen_tone.o audio_win.o digipeater.o pfilter.o dedupe.o tq.o xmit.o \
gen_tone.o morse.o audio_win.o audio_stats.o digipeater.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 nmea.o log.o telemetry.o dtime_now.o \
dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o serial_port.o log.o telemetry.o dtime_now.o \
dw-icon.o regex.a misc.a geotranz.a
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
@ -139,7 +139,7 @@ regex.o : regex/regex.c
# There are also a couple other functions in the misc
# subdirectory that are missing on Windows.
misc.a : strsep.o strtok_r.o strcasestr.o
misc.a : strsep.o strtok_r.o strcasestr.o strlcpy.o strlcat.o
ar -cr $@ $^
strsep.o : misc/strsep.c
@ -151,11 +151,16 @@ strtok_r.o : misc/strtok_r.c
strcasestr.o : misc/strcasestr.c
$(CC) $(CFLAGS) -c -o $@ $^
strlcpy.o : misc/strlcpy.c
$(CC) $(CFLAGS) -I. -c -o $@ $^
strlcat.o : misc/strlcat.c
$(CC) $(CFLAGS) -I. -c -o $@ $^
# Separate application to decode raw data.
decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c latlong.c log.o telemetry.o regex.a misc.a geotranz.a
decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c latlong.c log.o telemetry.o tt_text.o regex.a misc.a geotranz.a
$(CC) $(CFLAGS) -o decode_aprs -DTEST $^
@ -170,22 +175,23 @@ tt2text : tt_text.c
# Convert between Latitude/Longitude and UTM coordinates.
ll2utm : ll2utm.c geotranz.a
ll2utm : ll2utm.c textcolor.c geotranz.a misc.a
$(CC) $(CFLAGS) -o $@ $^
utm2ll : utm2ll.c geotranz.a
utm2ll : utm2ll.c textcolor.c geotranz.a misc.a
$(CC) $(CFLAGS) -o $@ $^
# Convert from log file to GPX.
log2gpx : log2gpx.c misc/strsep.c misc/strtok_r.c
#log2gpx : log2gpx.c misc/strsep.c misc/strtok_r.c
log2gpx : log2gpx.c misc.a
$(CC) $(CFLAGS) -o $@ $^
# Test application to generate sound.
gen_packets : gen_packets.o ax25_pad.o hdlc_send.o fcs_calc.o gen_tone.o textcolor.o dsp.o misc.a regex.a
gen_packets : gen_packets.o ax25_pad.o hdlc_send.o fcs_calc.o gen_tone.o morse.o textcolor.o dsp.o misc.a regex.a
$(CC) $(CFLAGS) -o $@ $^
# For tweaking the demodulator.
@ -234,7 +240,7 @@ testagc9 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.
# Unit test for AFSK demodulator
atest : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \
rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c latlong.c symbols.c textcolor.c telemetry.c misc.a regex.a \
rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c latlong.c symbols.c tt_text.c textcolor.c telemetry.c misc.a regex.a \
fsk_fast_filter.h
echo " " > tune.h
$(CC) $(CFLAGS) -o $@ $^
@ -291,6 +297,16 @@ ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a regex.a
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
# Send GPS location to KISS TNC each second.
walk96 : walk96.c nmea.c kiss_frame.c \
latlong.o encode_aprs.o serial_port.o textcolor.o \
ax25_pad.o fcs_calc.o regex.a \
misc.a
$(CC) $(CFLAGS) -DWALK96 -o $@ $^ -lwinmm -lws2_32
.PHONY: depend
depend : $(wildcard *.c)
@ -310,21 +326,21 @@ clean :
.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 \
direwolf.txt dwespeak.bat \
CHANGES.txt User-Guide.pdf \
Raspberry-Pi-APRS.pdf APRStt-Implementation-Notes.pdf \
A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf \
A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf
generic.conf dwespeak.bat \
README.md CHANGES.md \
doc/User-Guide.pdf \
doc/Raspberry-Pi-APRS.pdf \
doc/APRStt-Implementation-Notes.pdf
rm -f ../$z-win.zip
egrep '^C|^W' direwolf.txt | cut -c2-999 > direwolf.conf
egrep '^C|^W' generic.conf | cut -c2-999 > direwolf.conf
unix2dos direwolf.conf
zip ../$z-win.zip \
CHANGES.txt \
User-Guide.pdf \
Raspberry-Pi-APRS.pdf \
APRStt-Implementation-Notes.pdf \
A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf \
A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf \
zip --junk-paths ../$z-win.zip \
README.md \
CHANGES.md \
doc/User-Guide.pdf \
doc/Raspberry-Pi-APRS.pdf \
doc/APRStt-Implementation-Notes.pdf \
doc/APRS-Telemetry-Toolkit.pdf \
LICENSE* \
direwolf.conf \
direwolf.exe \
@ -337,67 +353,96 @@ dist-win : direwolf.exe decode_aprs.exe text2tt.exe tt2text.exe ll2utm.exe utm2l
gen_packets.exe \
atest.exe \
ttcalc.exe \
dwespeak.bat
dwespeak.bat \
telemetry-toolkit/*
.PHONY: dist-src
dist-src : CHANGES.txt \
User-Guide.pdf \
Raspberry-Pi-APRS.pdf \
Raspberry-Pi-APRS-Tracker.pdf \
APRStt-Implementation-Notes.pdf \
A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf \
A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf \
dist-src : README.md CHANGES.md \
doc/User-Guide.pdf \
doc/Raspberry-Pi-APRS.pdf \
doc/Raspberry-Pi-APRS-Tracker.pdf \
doc/APRStt-Implementation-Notes.pdf \
doc/APRS-Telemetry-Toolkit.pdf \
dw-start.sh dwespeak.bat dwespeak.sh \
tocalls.txt symbols-new.txt symbolsX.txt direwolf.spec
rm -f fsk_fast_filter.h
echo " " > tune.h
rm -f ../$z-src.zip
egrep '^C|^L' direwolf.txt | cut -c2-999 > direwolf.conf
dos2unix direwolf.conf
dos2unix generic.conf
dos2unix Makefile
dos2unix Makefile.linux
dos2unix Makefile.macosx
dos2unix telemetry-toolkit/telem-balloon.conf
dos2unix telemetry-toolkit/telem-balloon.pl
dos2unix telemetry-toolkit/telem-bits.pl
dos2unix telemetry-toolkit/telem-data.pl
dos2unix telemetry-toolkit/telem-data91.pl
dos2unix telemetry-toolkit/telem-eqns.pl
dos2unix telemetry-toolkit/telem-m0xer-3.txt
dos2unix telemetry-toolkit/telem-parm.pl
dos2unix telemetry-toolkit/telem-unit.pl
dos2unix telemetry-toolkit/telem-volts.py
dos2unix telemetry-toolkit/telem-volts.conf
(cd .. ; zip $z-src.zip \
$z/CHANGES.txt \
$z/README.md \
$z/CHANGES.md \
$z/LICENSE* \
$z/User-Guide.pdf \
$z/Raspberry-Pi-APRS.pdf \
$z/Raspberry-Pi-APRS-Tracker.pdf \
$z/APRStt-Implementation-Notes.pdf \
$z/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf \
$z/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf \
$z/Makefile.win $z/Makefile.linux $z/Makefile \
$z/doc/User-Guide.pdf \
$z/doc/Raspberry-Pi-APRS.pdf \
$z/doc/Raspberry-Pi-APRS-Tracker.pdf \
$z/doc/APRStt-Implementation-Notes.pdf \
$z/doc/APRS-Telemetry-Toolkit.pdf \
$z/Makefile.win $z/Makefile.linux $z/Makefile.macosx $z/Makefile \
$z/*.c $z/*.h \
$z/regex/* $z/misc/* $z/geotranz/* \
$z/man1/* \
$z/direwolf.conf $z/direwolf.txt \
$z/generic.conf \
$z/tocalls.txt $z/symbols-new.txt $z/symbolsX.txt \
$z/dw-icon.png $z/dw-icon.rc $z/dw-icon.ico \
$z/dw-start.sh $z/direwolf.spec \
$z/dwespeak.bat $z/dwespeak.sh )
$z/dwespeak.bat $z/dwespeak.sh \
$z/telemetry-toolkit/* )
unix2dos Makefile
unix2dos Makefile.linux
rm direwolf.conf
unix2dos Makefile.macosx
unix2dos telemetry-toolkit/telem-balloon.conf
unix2dos telemetry-toolkit/telem-balloon.pl
dos2unix telemetry-toolkit/telem-bits.pl
unix2dos telemetry-toolkit/telem-data.pl
unix2dos telemetry-toolkit/telem-data91.pl
unix2dos telemetry-toolkit/telem-eqns.pl
unix2dos telemetry-toolkit/telem-m0xer-3.txt
unix2dos telemetry-toolkit/telem-parm.pl
unix2dos telemetry-toolkit/telem-unit.pl
unix2dos telemetry-toolkit/telem-volts.py
unix2dos telemetry-toolkit/telem-volts.conf
User-Guide.pdf : User-Guide.docx
doc/User-Guide.pdf : doc/User-Guide.docx
echo "***** User-Guide.pdf is out of date *****"
Raspberry-Pi-APRS.pdf : Raspberry-Pi-APRS.docx
doc/Raspberry-Pi-APRS.pdf : doc/Raspberry-Pi-APRS.docx
echo "***** Raspberry-Pi-APRS.pdf is out of date *****"
Raspberry-Pi-APRS-Tracker.pdf : Raspberry-Pi-APRS-Tracker.docx
doc/Raspberry-Pi-APRS-Tracker.pdf : doc/Raspberry-Pi-APRS-Tracker.docx
echo "***** Raspberry-Pi-APRS-Tracker.pdf is out of date *****"
APRStt-Implementation-Notes.pdf : APRStt-Implementation-Notes.docx
doc/APRStt-Implementation-Notes.pdf : doc/APRStt-Implementation-Notes.docx
echo "***** APRStt-Implementation-Notes.pdf is out of date *****"
A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf : A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.docx
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 *****"
A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf : A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.docx
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 *****"
.PHONY: backup
backup :
mkdir /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"`

View File

@ -582,6 +582,7 @@ static void * client_thread_net (void *arg)
use_chan == mon_cmd.portx;
memset (&alevel, 0xff, sizeof(alevel));
pp = ax25_from_frame ((unsigned char *)(data+1), mon_cmd.data_len-1, alevel);
assert (pp != NULL);
ax25_format_addrs (pp, result);
info_len = ax25_get_info (pp, (unsigned char **)(&pinfo));
pinfo[info_len] = '\0';

316
aprs_tt.c
View File

@ -42,12 +42,12 @@
// What do we call the parts separated by * key? Entry? Field?
#include <math.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
@ -64,6 +64,8 @@
#include "latlong.h"
#include "dlq.h"
#include "demod.h" /* for alevel_t & demod_get_audio_level() */
#include "tq.h"
#if __WIN32__
char *strtok_r(char *str, const char *delim, char **saveptr);
@ -144,6 +146,8 @@ static struct ttloc_s test_config[] = {
{ TTLOC_GRID, "Byyyxxx", .grid.lat0 = 37 + 50./60.0, .grid.lon0 = 81,
.grid.lat9 = 37 + 59.99/60.0, .grid.lon9 = 81 + 9.99/60.0 },
{ TTLOC_MHEAD, "BAxxxxxx", .mhead.prefix = "326129" },
{ TTLOC_SATSQ, "BAxxxx" },
{ TTLOC_MACRO, "xxyyy", .macro.definition = "B9xx*AB166*AA2B4C5B3B0Ayyy" },
@ -310,6 +314,7 @@ static char m_callsign[20]; /* really object name */
static char m_symtab_or_overlay;
static char m_symbol_code;
static char m_loc_text[24];
static double m_longitude;
static double m_latitude;
static char m_comment[200];
@ -325,6 +330,10 @@ static int m_ssid;
void aprs_tt_sequence (int chan, char *msg)
{
int err;
char audible_response[1000];
packet_t pp;
char script_response[1000];
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
@ -341,15 +350,16 @@ void aprs_tt_sequence (int chan, char *msg)
/*
* The parse functions will fill these in.
*/
strcpy (m_callsign, "");
strlcpy (m_callsign, "", sizeof(m_callsign));
m_symtab_or_overlay = '\\';
m_symbol_code = 'A';
strlcpy (m_loc_text, "", sizeof(m_loc_text));
m_longitude = G_UNKNOWN;
m_latitude = G_UNKNOWN;
strcpy (m_comment, "");
strcpy (m_freq, "");
strlcpy (m_comment, "", sizeof(m_comment));
strlcpy (m_freq, "", sizeof(m_freq));
m_mic_e = ' ';
strcpy (m_dao, "!T !"); /* start out unknown */
strlcpy (m_dao, "!T !", sizeof(m_dao)); /* start out unknown */
m_ssid = 12;
/*
@ -371,13 +381,46 @@ void aprs_tt_sequence (int chan, char *msg)
*/
#ifndef TT_MAIN
err = tt_user_heard (m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code, m_latitude, m_longitude,
err = tt_user_heard (m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code,
m_loc_text, m_latitude, m_longitude,
m_freq, m_comment, m_mic_e, m_dao);
#endif
}
// TODO send response to user.
// err == 0 OK, others, suitable error response.
/*
* If a command / script was supplied, run it now.
* This can do additional processing and provide a custom audible response.
*/
strlcpy (script_response, "", sizeof(script_response));
if (strlen(tt_config.ttcmd) > 0) {
dw_run_cmd (tt_config.ttcmd, 1, script_response, (int)sizeof(script_response));
}
/*
* Send response to user by constructing packet with SPEECH or MORSE as destination.
* Source shouldn't matter because it doesn't get transmitted as AX.25 frame.
* Use high priority queue for consistent timing.
*/
snprintf (audible_response, sizeof(audible_response),
"APRSTT>%s:%s",
tt_config.response[err].method,
(strlen(script_response) > 0) ? script_response : tt_config.response[err].mtext);
pp = ax25_from_text (audible_response, 0);
if (pp == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Internal error. Couldn't make frame from \"%s\"\n", audible_response);
return;
}
tq_append (chan, TQ_PRIO_0_HI, pp);
} /* end aprs_tt_sequence */
@ -414,42 +457,55 @@ static int parse_fields (char *msg)
char stemp[MAX_MSG_LEN+1];
char *e;
char *save;
int err;
strcpy (stemp, msg);
//text_color_set(DW_COLOR_DEBUG);
//printf ("parse_fields (%s).\n", msg);
strlcpy (stemp, msg, sizeof(stemp));
e = strtok_r (stemp, "*#", &save);
while (e != NULL) {
//text_color_set(DW_COLOR_DEBUG);
//printf ("parse_fields () field = %s\n", e);
switch (*e) {
case 'A':
switch (e[1]) {
case 'A':
parse_object_name (e);
err = parse_object_name (e);
if (err != 0) return (err);
break;
case 'B':
parse_symbol (e);
err = parse_symbol (e);
if (err != 0) return (err);
break;
case 'C':
/*
* New in 1.2: test for 10 digit callsign.
*/
if (tt_call10_to_text(e+2,1,stemp) == 0) {
strcpy(m_callsign, stemp);
strlcpy(m_callsign, stemp, sizeof(m_callsign));
}
// TODO1.3: else return (?)
break;
default:
parse_callsign (e);
err = parse_callsign (e);
break;
}
break;
case 'B':
parse_location (e);
err = parse_location (e);
if (err != 0) return (err);
break;
case 'C':
parse_comment (e);
err = parse_comment (e);
if (err != 0) return (err);
break;
case '0':
@ -462,7 +518,8 @@ static int parse_fields (char *msg)
case '7':
case '8':
case '9':
expand_macro (e);
err = expand_macro (e);
if (err != 0) return (err);
break;
case '\0':
@ -473,7 +530,7 @@ static int parse_fields (char *msg)
default:
text_color_set(DW_COLOR_ERROR);
dw_printf ("Entry does not start with A, B, C, or digit: \"%s\"\n", msg);
dw_printf ("Field does not start with A, B, C, or digit: \"%s\"\n", msg);
return (TT_ERROR_D_MSG);
}
@ -481,6 +538,9 @@ static int parse_fields (char *msg)
e = strtok_r (NULL, "*#", &save);
}
//text_color_set(DW_COLOR_DEBUG);
//printf ("parse_fields () normal return\n");
return (0);
} /* end parse_fields */
@ -546,7 +606,7 @@ static int expand_macro (char *e)
* Substitute values in to the definition.
*/
strcpy (stemp, "");
strlcpy (stemp, "", sizeof(stemp));
for (d = tt_config.ttloc_ptr[ipat].macro.definition; *d != '\0'; d++) {
@ -557,20 +617,20 @@ static int expand_macro (char *e)
switch (*d) {
case 'x':
strcat (stemp, xstr);
strlcat (stemp, xstr, sizeof(stemp));
break;
case 'y':
strcat (stemp, ystr);
strlcat (stemp, ystr, sizeof(stemp));
break;
case 'z':
strcat (stemp, zstr);
strlcat (stemp, zstr, sizeof(stemp));
break;
default:
{
char c1[2];
c1[0] = *d;
c1[1] = '\0';
strcat (stemp, c1);
strlcat (stemp, c1, sizeof(stemp));
}
break;
}
@ -675,7 +735,7 @@ static int parse_callsign (char *e)
*/
if (len == 4 && isdigit(e[1]) && isdigit(e[2]) && isdigit(e[3])) {
strcpy (m_callsign, e+1);
strlcpy (m_callsign, e+1, sizeof(m_callsign));
return (0);
}
@ -844,7 +904,7 @@ static int parse_object_name (char *e)
static int parse_symbol (char *e)
{
int len;
char nstr[4];
char nstr[3];
int nn;
char stemp[10];
@ -950,6 +1010,8 @@ static int parse_location (char *e)
long lerr;
double easting, northing;
char mh[20];
char stemp[32];
assert (*e == 'B');
@ -1020,12 +1082,12 @@ static int parse_location (char *e)
if (strlen(xstr) == 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Missing X coordinate.\n");
strcpy (xstr, "0");
strlcpy (xstr, "0", sizeof(xstr));
}
if (strlen(ystr) == 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Missing Y coordinate.\n");
strcpy (ystr, "0");
strlcpy (ystr, "0", sizeof(ystr));
}
lat0 = tt_config.ttloc_ptr[ipat].grid.lat0;
@ -1046,13 +1108,13 @@ static int parse_location (char *e)
text_color_set(DW_COLOR_ERROR);
dw_printf ("Missing X coordinate.\n");
/* Avoid divide by zero later. Put in middle of range. */
strcpy (xstr, "5");
strlcpy (xstr, "5", sizeof(xstr));
}
if (strlen(ystr) == 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Missing Y coordinate.\n");
/* Avoid divide by zero later. Put in middle of range. */
strcpy (ystr, "5");
strlcpy (ystr, "5", sizeof(ystr));
}
x = atof(xstr);
@ -1060,7 +1122,17 @@ static int parse_location (char *e)
y = atof(ystr);
northing = y * tt_config.ttloc_ptr[ipat].utm.scale + tt_config.ttloc_ptr[ipat].utm.y_offset;
if (isalpha(tt_config.ttloc_ptr[ipat].utm.latband)) {
snprintf (m_loc_text, sizeof(m_loc_text), "%d%c %.0f %.0f", (int)(tt_config.ttloc_ptr[ipat].utm.lzone), tt_config.ttloc_ptr[ipat].utm.latband, easting, northing);
}
else if (tt_config.ttloc_ptr[ipat].utm.latband == '-') {
snprintf (m_loc_text, sizeof(m_loc_text), "%d %.0f %.0f", (int)(- tt_config.ttloc_ptr[ipat].utm.lzone), easting, northing);
}
else {
snprintf (m_loc_text, sizeof(m_loc_text), "%d %.0f %.0f", (int)(tt_config.ttloc_ptr[ipat].utm.lzone), easting, northing);
}
lerr = Convert_UTM_To_Geodetic(tt_config.ttloc_ptr[ipat].utm.lzone,
tt_config.ttloc_ptr[ipat].utm.hemi, easting, northing, &lat0, &lon0);
@ -1088,24 +1160,26 @@ static int parse_location (char *e)
text_color_set(DW_COLOR_ERROR);
dw_printf ("MGRS/USNG: Missing X (easting) coordinate.\n");
/* Should not be possible to get here. Fake it and carry on. */
strcpy (xstr, "5");
strlcpy (xstr, "5", sizeof(xstr));
}
if (strlen(ystr) == 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("MGRS/USNG: Missing Y (northing) coordinate.\n");
/* Should not be possible to get here. Fake it and carry on. */
strcpy (ystr, "5");
strlcpy (ystr, "5", sizeof(ystr));
}
char loc[40];
strcpy (loc, tt_config.ttloc_ptr[ipat].mgrs.zone);
strcat (loc, xstr);
strcat (loc, ystr);
strlcpy (loc, tt_config.ttloc_ptr[ipat].mgrs.zone, sizeof(loc));
strlcat (loc, xstr, sizeof(loc));
strlcat (loc, ystr, sizeof(loc));
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("MGRS/USNG location debug: %s\n", loc);
strlcpy (m_loc_text, loc, sizeof(m_loc_text));
if (tt_config.ttloc_ptr[ipat].type == TTLOC_MGRS)
lerr = Convert_MGRS_To_Geodetic(loc, &lat0, &lon0);
else
@ -1127,17 +1201,48 @@ static int parse_location (char *e)
}
break;
case TTLOC_MHEAD:
/* Combine prefix from configuration and digits from user. */
strlcpy (stemp, tt_config.ttloc_ptr[ipat].mhead.prefix, sizeof(stemp));
strlcat (stemp, xstr, sizeof(stemp));
if (strlen(stemp) != 4 && strlen(stemp) != 6 && strlen(stemp) != 10 && strlen(stemp) != 12) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Expected total of 4, 6, 10, or 12 digits for the Maidenhead Locator \"%s\" + \"%s\"\n",
tt_config.ttloc_ptr[ipat].mhead.prefix, xstr);
return (TT_ERROR_INVALID_MHEAD);
}
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("Case MHEAD: Convert to text \"%s\".\n", stemp);
if (tt_mhead_to_text (stemp, 0, mh) == 0) {
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("Case MHEAD: Resulting text \"%s\".\n", mh);
strlcpy (m_loc_text, mh, sizeof(m_loc_text));
ll_from_grid_square (mh, &m_latitude, &m_longitude);
}
break;
case TTLOC_SATSQ:
if (strlen(xstr) != 4) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Expected 4 digits for the Satellite Square.\n");
return (TT_ERROR_SATSQ);
return (TT_ERROR_INVALID_SATSQ);
}
/* Convert 4 digits to usual AA99 form, then to location. */
if (tt_gridsquare_to_text (xstr, 0, mh) == 0) {
if (tt_satsq_to_text (xstr, 0, mh) == 0) {
strlcpy (m_loc_text, mh, sizeof(m_loc_text));
ll_from_grid_square (mh, &m_latitude, &m_longitude);
}
break;
@ -1149,9 +1254,13 @@ static int parse_location (char *e)
return (0);
}
/* Send reject sound. */
/* Does not match any location specification. */
text_color_set(DW_COLOR_ERROR);
dw_printf ("Received location \"%s\" does not match any definitions.\n", e);
/* Send reject sound. */
return (TT_ERROR_INVALID_LOC);
} /* end parse_location */
@ -1198,11 +1307,11 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
if (strlen(e) == len) {
match = 1;
strcpy (xstr, "");
strcpy (ystr, "");
strcpy (zstr, "");
strcpy (bstr, "");
strcpy (dstr, "");
strlcpy (xstr, "", sizeof(xstr));
strlcpy (ystr, "", sizeof(ystr));
strlcpy (zstr, "", sizeof(zstr));
strlcpy (bstr, "", sizeof(bstr));
strlcpy (dstr, "", sizeof(dstr));
for (k=0; k<len; k++) {
mc = tt_config.ttloc_ptr[ipat].pattern[k];
@ -1233,7 +1342,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
char stemp[2];
stemp[0] = e[k];
stemp[1] = '\0';
strcat (xstr, stemp);
strlcat (xstr, stemp, sizeof(xstr));
}
else {
match = 0;
@ -1245,7 +1354,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
char stemp[2];
stemp[0] = e[k];
stemp[1] = '\0';
strcat (ystr, stemp);
strlcat (ystr, stemp, sizeof(ystr));
}
else {
match = 0;
@ -1257,7 +1366,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
char stemp[2];
stemp[0] = e[k];
stemp[1] = '\0';
strcat (zstr, stemp);
strlcat (zstr, stemp, sizeof(zstr));
}
else {
match = 0;
@ -1269,7 +1378,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
char stemp[2];
stemp[0] = e[k];
stemp[1] = '\0';
strcat (bstr, stemp);
strlcat (bstr, stemp, sizeof(bstr));
}
else {
match = 0;
@ -1281,7 +1390,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
char stemp[2];
stemp[0] = e[k];
stemp[1] = '\0';
strcat (dstr, stemp);
strlcat (dstr, stemp, sizeof(dstr));
}
else {
match = 0;
@ -1324,8 +1433,10 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
*
* Description: We recognize these different formats:
*
* Cn - One digit for MIC-E position comment.
* Always / plus exactly 10 characters.
* Cn - One digit (1-9) predefined status. 0 is reserved for none.
* The defaults are derived from the MIC-E position comments
* which were always "/" plus exactly 10 characters.
* Users can override the defaults with configuration options.
*
* Cnnnnnn - Six digit frequency reformatted as nnn.nnnMHz
*
@ -1402,7 +1513,7 @@ static void raw_tt_data_to_app (int chan, char *msg)
return ;
#else
char src[10], dest[10];
char raw_tt_msg[200];
char raw_tt_msg[256];
packet_t pp;
char *c, *s;
int i;
@ -1416,10 +1527,10 @@ static void raw_tt_data_to_app (int chan, char *msg)
// Application version might be useful in case we end up using different
// message formats in later versions.
strcpy (src, "DTMF");
sprintf (dest, "%s%d%d", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION);
strlcpy (src, "DTMF", sizeof(src));
snprintf (dest, sizeof(dest), "%s%d%d", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION);
sprintf (raw_tt_msg, "%s>%s:t%s", src, dest, msg);
snprintf (raw_tt_msg, sizeof(raw_tt_msg), "%s>%s:t%s", src, dest, msg);
pp = ax25_from_text (raw_tt_msg, 1);
@ -1454,6 +1565,103 @@ static void raw_tt_data_to_app (int chan, char *msg)
/*------------------------------------------------------------------
*
* Name: dw_run_cmd
*
* Purpose: Run a command and capture the output.
*
* Inputs: cmd - The command.
*
* oneline - 0 = Keep original line separators. Caller
* must deal with operating system differences.
* 1 = Change CR, LF, TAB to space so result
* is one line of text.
* 2 = Also remove any trailing whitespace.
*
* maxresult - Amount of space available for result.
*
* Outputs: result - Output captured from running command.
*
* Returns: -1 for any sort of error.
* >0 for number of characters returned (= strlen(result))
*
* Description: This is currently used for running a user-specified
* script to generate a custom speech response.
*
* Future: There are potential other uses so it should probably
* be relocated to a file of other misc. utilities.
*
*----------------------------------------------------------------*/
int dw_run_cmd (char *cmd, int oneline, char *result, int maxresult)
{
FILE *fp;
strlcpy (result, "", sizeof(result));
fp = popen (cmd, "r");
if (fp != NULL) {
int remaining = maxresult;
char *pr = result;
int err;
while (remaining > 2 && fgets(pr, remaining, fp) != NULL) {
pr = result + strlen(result);
remaining = maxresult - strlen(result);
}
if ((err = pclose(fp)) != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR: Unable to run \"%s\"\n", cmd);
// On Windows, non-existent file produces "Operation not permitted"
// Maybe we should put in a test for whether file exists.
dw_printf ("%s\n", strerror(err));
return (-1);
}
// take out any newline characters.
if (oneline) {
for (pr = result; *pr != '\0'; pr++) {
if (*pr == '\r' || *pr == '\n' || *pr == '\t') {
*pr = ' ';
}
}
if (oneline > 1) {
pr = result + strlen(result) - 1;
while (pr >= result && *pr == ' ') {
*pr = '\0';
pr--;
}
}
}
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("%s returns \"%s\"\n", cmd, result);
return (strlen(result));
}
else {
// explain_popen() would be nice but doesn't seem to be commonly available.
// We get here only if fork or pipe fails.
// The command not existing must be caught above.
text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR: Unable to run \"%s\"\n", cmd);
dw_printf ("%s\n", strerror(errno));
return (-1);
}
} /* end dw_run_cmd */
/*------------------------------------------------------------------
*
* Name: main

142
aprs_tt.h
View File

@ -5,6 +5,7 @@
#define APRS_TT_H 1
/*
* For holding location format specifications from config file.
* Same thing is also useful for macro definitions.
@ -13,7 +14,7 @@
*/
struct ttloc_s {
enum { TTLOC_POINT, TTLOC_VECTOR, TTLOC_GRID, TTLOC_UTM, TTLOC_MGRS, TTLOC_USNG, TTLOC_MACRO, TTLOC_SATSQ } type;
enum { TTLOC_POINT, TTLOC_VECTOR, TTLOC_GRID, TTLOC_UTM, TTLOC_MGRS, TTLOC_USNG, TTLOC_MACRO, TTLOC_MHEAD, TTLOC_SATSQ } type;
char pattern[20]; /* e.g. B998, B5bbbdddd, B2xxyy, Byyyxxx, BAxxxx */
/* For macros, it should be all fixed digits, */
@ -44,6 +45,7 @@ struct ttloc_s {
double x_offset;
double y_offset;
long lzone; /* UTM zone, should be 1-60 */
char latband; /* Latitude band if specified, otherwise space or - */
char hemi; /* UTM Hemisphere, should be 'N' or 'S'. */
} utm;
@ -51,52 +53,21 @@ struct ttloc_s {
char zone[8]; /* Zone and square for USNG/MGRS */
} mgrs;
struct {
char prefix[24]; /* should be 10, 6, or 4 digits to be */
/* prepended to the received sequence. */
} mhead;
struct {
char *definition;
} macro;
};
};
/*
* Configuration options for APRStt.
*/
#define TT_MAX_XMITS 10
struct tt_config_s {
int gateway_enabled; /* Send DTMF sequences to APRStt gateway. */
int obj_recv_chan; /* Channel to listen for tones. */
int obj_xmit_chan; /* Channel to transmit object report. */
char obj_xmit_via[AX25_MAX_REPEATERS * (AX25_MAX_ADDR_LEN+1)];
/* e.g. empty or "WIDE2-1,WIDE1-1" */
int retain_time; /* Seconds to keep information about a user. */
int num_xmits; /* Number of times to transmit object report. */
int xmit_delay[TT_MAX_XMITS]; /* Delay between them. */
struct ttloc_s *ttloc_ptr; /* Pointer to variable length array of above. */
int ttloc_size; /* Number of elements allocated. */
int ttloc_len; /* Number of elements actually used. */
double corral_lat; /* The "corral" for unknown locations. */
double corral_lon;
double corral_offset;
int corral_ambiguity;
};
void aprs_tt_init (struct tt_config_s *p_config);
void aprs_tt_button (int chan, char button);
/* Error codes for sending responses to user. */
#define TT_ERROR_OK 0 /* Success. */
#define TT_ERROR_D_MSG 1 /* D was first char of field. Not implemented yet. */
#define TT_ERROR_INTERNAL 2 /* Internal error. Shouldn't be here. */
#define TT_ERROR_MACRO_NOMATCH 3 /* No definition for digit sequence. */
@ -106,7 +77,97 @@ void aprs_tt_button (int chan, char button);
#define TT_ERROR_INVALID_SYMBOL 7 /* Invalid symbol specification. */
#define TT_ERROR_INVALID_LOC 8 /* Invalid location. */
#define TT_ERROR_NO_CALL 9 /* No call or object name included. */
#define TT_ERROR_SATSQ 10 /* Satellite square must be 4 digits. */
#define TT_ERROR_INVALID_MHEAD 10 /* Invalid Maidenhead Locator. */
#define TT_ERROR_INVALID_SATSQ 11 /* Satellite square must be 4 digits. */
#define TT_ERROR_MAXP1 12 /* Number of items above. i.e. Last number plus 1. */
#if CONFIG_C /* Is this being included from config.c? */
/* Must keep in sync with above !!! */
static const char *tt_msg_id[TT_ERROR_MAXP1] = {
"OK",
"D_MSG",
"INTERNAL",
"MACRO_NOMATCH",
"BAD_CHECKSUM",
"INVALID_CALL",
"INVALID_OBJNAME",
"INVALID_SYMBOL",
"INVALID_LOC",
"NO_CALL",
"INVALID_MHEAD",
"INVALID_SATSQ"
};
#endif
/*
* Configuration options for APRStt.
*/
#define TT_MAX_XMITS 10
#define TT_MTEXT_LEN 64
struct tt_config_s {
int gateway_enabled; /* Send DTMF sequences to APRStt gateway. */
int obj_recv_chan; /* Channel to listen for tones. */
int obj_xmit_chan; /* Channel to transmit object report. */
/* -1 for none. This could happpen if we */
/* are only sending to application */
/* and/or IGate. */
int obj_send_to_app; /* send to attached application(s). */
int obj_send_to_ig; /* send to IGate. */
char obj_xmit_via[AX25_MAX_REPEATERS * (AX25_MAX_ADDR_LEN+1)];
/* e.g. empty or "WIDE2-1,WIDE1-1" */
int retain_time; /* Seconds to keep information about a user. */
int num_xmits; /* Number of times to transmit object report. */
int xmit_delay[TT_MAX_XMITS]; /* Delay between them. */
/* e.g. 3 seconds before first transmission then */
/* delays of 16, 32, seconds etc. in between repeats. */
struct ttloc_s *ttloc_ptr; /* Pointer to variable length array of above. */
int ttloc_size; /* Number of elements allocated. */
int ttloc_len; /* Number of elements actually used. */
double corral_lat; /* The "corral" for unknown locations. */
double corral_lon;
double corral_offset;
int corral_ambiguity;
char status[10][TT_MTEXT_LEN]; /* Up to 9 status messages. e.g. "/enroute" */
/* Position 0 means none and can't be changed. */
struct {
char method[AX25_MAX_ADDR_LEN]; /* SPEECH or MORSE[-n] */
char mtext[TT_MTEXT_LEN]; /* Message text. */
} response[TT_ERROR_MAXP1];
char ttcmd[80]; /* Command to generate custom audible response. */
};
void aprs_tt_init (struct tt_config_s *p_config);
void aprs_tt_button (int chan, char button);
#define APRSTT_LOC_DESC_LEN 32 /* Need at least 26 */
@ -115,6 +176,9 @@ void aprs_tt_dao_to_desc (char *dao, char *str);
void aprs_tt_sequence (int chan, char *msg);
int dw_run_cmd (char *cmd, int oneline, char *result, int maxresult);
#endif
/* end aprs_tt.h */

View File

@ -565,7 +565,7 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
int info_len;
int h;
char heard[20];
char alevel_text[32];
char alevel_text[AX25_ALEVEL_TO_TEXT_SIZE];
packets_decoded++;

126
audio.c
View File

@ -1,7 +1,4 @@
// Remove next line to eliminate annoying/useful (depending on who you ask) debug messages every 100 seconds.
#define STATISTICS 1
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
@ -95,6 +92,7 @@
#include "direwolf.h"
#include "audio.h"
#include "audio_stats.h"
#include "textcolor.h"
#include "dtime_now.h"
#include "demod.h" /* for alevel_t & demod_get_audio_level() */
@ -290,14 +288,14 @@ int audio_open (struct audio_s *pa)
if (strcasecmp(pa->adev[a].adevice_in, "stdin") == 0 || strcmp(pa->adev[a].adevice_in, "-") == 0) {
adev[a].g_audio_in_type = AUDIO_IN_TYPE_STDIN;
/* Change "-" to stdin for readability. */
strcpy (pa->adev[a].adevice_in, "stdin");
strlcpy (pa->adev[a].adevice_in, "stdin", sizeof(pa->adev[a].adevice_in));
}
if (strncasecmp(pa->adev[a].adevice_in, "udp:", 4) == 0) {
adev[a].g_audio_in_type = AUDIO_IN_TYPE_SDR_UDP;
/* Supply default port if none specified. */
if (strcasecmp(pa->adev[a].adevice_in,"udp") == 0 ||
strcasecmp(pa->adev[a].adevice_in,"udp:") == 0) {
sprintf (pa->adev[a].adevice_in, "udp:%d", DEFAULT_UDP_AUDIO_PORT);
snprintf (pa->adev[a].adevice_in, sizeof(pa->adev[a].adevice_in), "udp:%d", DEFAULT_UDP_AUDIO_PORT);
}
}
@ -305,16 +303,16 @@ int audio_open (struct audio_s *pa)
/* If not specified, the device names should be "default". */
strcpy (audio_in_name, pa->adev[a].adevice_in);
strcpy (audio_out_name, pa->adev[a].adevice_out);
strlcpy (audio_in_name, pa->adev[a].adevice_in, sizeof(audio_in_name));
strlcpy (audio_out_name, pa->adev[a].adevice_out, sizeof(audio_out_name));
char ctemp[40];
if (pa->adev[a].num_channels == 2) {
sprintf (ctemp, " (channels %d & %d)", ADEVFIRSTCHAN(a), ADEVFIRSTCHAN(a)+1);
snprintf (ctemp, sizeof(ctemp), " (channels %d & %d)", ADEVFIRSTCHAN(a), ADEVFIRSTCHAN(a)+1);
}
else {
sprintf (ctemp, " (channel %d)", ADEVFIRSTCHAN(a));
snprintf (ctemp, sizeof(ctemp), " (channel %d)", ADEVFIRSTCHAN(a));
}
text_color_set(DW_COLOR_INFO);
@ -358,7 +356,7 @@ int audio_open (struct audio_s *pa)
if (oss_audio_device_fd < 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("%s:\n", pa->adev[a].adevice_in);
// sprintf (message, "Could not open audio device %s", pa->adev[a].adevice_in);
// snprintf (message, sizeof(message), "Could not open audio device %s", pa->adev[a].adevice_in);
// perror (message);
return (-1);
}
@ -874,75 +872,6 @@ int audio_get (int a)
adev[a].inbuf_size_in_bytes / adev[a].bytes_per_frame, n);
#endif
#if STATISTICS
// TODO1.2: add audio level information to windows version. Common function?
// TODO1.2: add quiet option to suppress this.
/*
* Print information about the sample rate as a debugging aid.
* I've never seen an issue with Windows or x86 Linux but the Raspberry Pi
* has a very troublesome audio input system where many samples got lost.
* Occasional lines like this would immediately identify the issue.
*
* Past 100 seconds, 4409856 audio samples processed, 0 errors.
*
* That's a little hard to read. Maybe we'd be better off with an average
* and fewer digits like this: 44.1 k
*
* While we are at it we can also print the current audio level(s) providing
* more clues if nothing is being decoded.
*/
if (last_time[a] == 0) {
last_time[a] = time(NULL);
sample_count[a] = 0;
error_count[a] = 0;
}
else {
if (n > 0) {
sample_count[a] += n;
}
else {
error_count[a]++;
}
this_time[a] = time(NULL);
if (this_time[a] >= last_time[a] + duration) {
#if 1 /* Try this for version 1.2 and see how people react. */
float ave_rate = (sample_count[a] / 1000.0) / duration;
text_color_set(DW_COLOR_DEBUG);
if (save_audio_config_p->adev[a].num_channels > 1) {
int ch0 = ADEVFIRSTCHAN(a);
alevel_t alevel0 = demod_get_audio_level(a,ch0);
int ch1 = ADEVFIRSTCHAN(a) + 1;
alevel_t alevel1 = demod_get_audio_level(a,ch1);
dw_printf ("\nADEVICE%d: Sample rate approx. %.1f k, %d errors, receive audio levels CH%d %d, CH%d %d\n\n",
a, ave_rate, error_count[a], ch0, alevel0.rec, ch1, alevel1.rec);
}
else {
int ch0 = ADEVFIRSTCHAN(a);
alevel_t alevel0 = demod_get_audio_level(a,ch0);
dw_printf ("\nADEVICE%d: Sample rate approx. %.1f k, %d errors, receive audio level CH%d %d\n\n",
a, ave_rate, error_count[a], ch0, alevel0.rec);
}
#else
text_color_set(DW_COLOR_DEBUG);
dw_printf ("\nADEVICE%d: Past %d seconds, %d audio samples processed, %d errors.\n\n",
a, duration, sample_count[a], error_count[a]);
#endif
last_time[a] = this_time[a];
sample_count[a] = 0;
error_count[a] = 0;
}
}
#endif
if (n > 0) {
@ -950,6 +879,12 @@ int audio_get (int a)
adev[a].inbuf_len = n * adev[a].bytes_per_frame; /* convert to number of bytes */
adev[a].inbuf_next = 0;
audio_stats (a,
save_audio_config_p->adev[a].num_channels,
n,
save_audio_config_p->statistics_interval);
}
else if (n == 0) {
@ -973,6 +908,11 @@ int audio_get (int a)
text_color_set(DW_COLOR_ERROR);
dw_printf ("Audio input device %d error: %s\n", a, snd_strerror(n));
audio_stats (a,
save_audio_config_p->adev[a].num_channels,
0,
save_audio_config_p->statistics_interval);
/* Try to recover a few times and eventually give up. */
if (++retries > 10) {
adev[a].inbuf_len = 0;
@ -1015,10 +955,21 @@ int audio_get (int a)
perror("Can't read from audio device");
adev[a].inbuf_len = 0;
adev[a].inbuf_next = 0;
audio_stats (a,
save_audio_config_p->adev[a].num_channels,
0,
save_audio_config_p->statistics_interval);
return (-1);
}
adev[a].inbuf_len = n;
adev[a].inbuf_next = 0;
audio_stats (a,
save_audio_config_p->adev[a].num_channels,
n / (save_audio_config_p->adev[a].num_channels * save_audio_config_p->adev[a].bits_per_sample / 8),
save_audio_config_p->statistics_interval);
}
#endif /* USE_ALSA */
@ -1042,11 +993,23 @@ int audio_get (int a)
dw_printf ("Can't read from udp socket, res=%d", res);
adev[a].inbuf_len = 0;
adev[a].inbuf_next = 0;
audio_stats (a,
save_audio_config_p->adev[a].num_channels,
0,
save_audio_config_p->statistics_interval);
return (-1);
}
adev[a].inbuf_len = res;
adev[a].inbuf_next = 0;
audio_stats (a,
save_audio_config_p->adev[a].num_channels,
res / (save_audio_config_p->adev[a].num_channels * save_audio_config_p->adev[a].bits_per_sample / 8),
save_audio_config_p->statistics_interval);
}
break;
@ -1065,6 +1028,11 @@ int audio_get (int a)
exit (0);
}
audio_stats (a,
save_audio_config_p->adev[a].num_channels,
res / (save_audio_config_p->adev[a].num_channels * save_audio_config_p->adev[a].bits_per_sample / 8),
save_audio_config_p->statistics_interval);
adev[a].inbuf_len = res;
adev[a].inbuf_next = 0;
}

View File

@ -87,6 +87,9 @@ struct audio_s {
char tts_script[80]; /* Script for text to speech. */
int statistics_interval; /* Number of seconds between the audio */
/* statistics reports. This is set by */
/* the "-a" option. 0 to disable feature. */
/* Properties for each audio channel, common to receive and transmit. */
/* Can be different for each radio channel. */
@ -227,7 +230,7 @@ struct audio_s {
};
#if __WIN32__
#if __WIN32__ || __APPLE__
#define DEFAULT_ADEVICE "" /* Windows: Empty string = default audio device. */
#else
#if USE_ALSA

1305
audio_portaudio.c Normal file

File diff suppressed because it is too large Load Diff

178
audio_stats.c Normal file
View File

@ -0,0 +1,178 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2015 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: audio_stats.c
*
* Purpose: Print statistics for audio input stream.
*
* A common complaint is that there is no indication of
* audio input level until a packet is received correctly.
* That's true for the Windows version but the Linux version
* prints something like this each 100 seconds:
*
* ADEVICE0: Sample rate approx. 44.1 k, 0 errors, receive audio level CH0 73
*
* Some complain about the clutter but it has been a useful
* troubleshooting tool. In the earlier RPi days, the sample
* rate was quite low due to a device driver issue.
* Using a USB hub on the RPi also caused audio problems.
* One adapter, that I tried, produces samples at the
* right rate but all the samples are 0.
*
* Here we pull the code out of the Linux version of audio.c
* so we have a common function for all the platforms.
*
* We also add a command line option to adjust the time
* between reports or turn them off entirely.
*
* Revisions: This is new in version 1.3.
*
*---------------------------------------------------------------*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include "direwolf.h"
#include "audio_stats.h"
#include "textcolor.h"
#include "dtime_now.h"
#include "demod.h" /* for alevel_t & demod_get_audio_level() */
/*------------------------------------------------------------------
*
* Name: audio_stats
*
* Purpose: Add sample count from one buffer to the statistics.
* Print if specified amount of time has passed.
*
* Inputs: adev - Audio device number: 0, 1, ..., MAX_ADEVS-1
*
nchan - Number of channels for this device, 1 or 2.
*
* nsamp - How many audio samples were read.
*
* interval - How many seconds between reports.
* 0 to turn off.
*
* Returns: none
*
* Description: ...
*
*----------------------------------------------------------------*/
void audio_stats (int adev, int nchan, int nsamp, int interval)
{
/* Gather numbers for read from audio device. */
static time_t last_time[MAX_ADEVS] = { 0, 0, 0 };
time_t this_time[MAX_ADEVS];
static int sample_count[MAX_ADEVS];
static int error_count[MAX_ADEVS];
static int suppress_first[MAX_ADEVS];
if (interval <= 0) {
return;
}
assert (adev >= 0 && adev < MAX_ADEVS);
/*
* Print information about the sample rate as a troubleshooting aid.
* I've never seen an issue with Windows or x86 Linux but the Raspberry Pi
* has a very troublesome audio input system where many samples got lost.
*
* While we are at it we can also print the current audio level(s) providing
* more clues if nothing is being decoded.
*/
if (last_time[adev] == 0) {
last_time[adev] = time(NULL);
sample_count[adev] = 0;
error_count[adev] = 0;
suppress_first[adev] = 1;
/* suppressing the first one could mean a rather */
/* long wait for the first message. We make the */
/* first collection interval 3 seconds. */
last_time[adev] -= (interval - 3);
}
else {
if (nsamp > 0) {
sample_count[adev] += nsamp;
}
else {
error_count[adev]++;
}
this_time[adev] = time(NULL);
if (this_time[adev] >= last_time[adev] + interval) {
if (suppress_first[adev]) {
/* The issue we had is that the first time the rate */
/* would be off considerably because we didn't start */
/* on a second boundary. So we will suppress printing */
/* of the first one. */
suppress_first[adev] = 0;
}
else {
float ave_rate = (sample_count[adev] / 1000.0) / interval;
text_color_set(DW_COLOR_DEBUG);
if (nchan > 1) {
int ch0 = ADEVFIRSTCHAN(adev);
alevel_t alevel0 = demod_get_audio_level(ch0,0);
int ch1 = ADEVFIRSTCHAN(adev) + 1;
alevel_t alevel1 = demod_get_audio_level(ch1,0);
dw_printf ("\nADEVICE%d: Sample rate approx. %.1f k, %d errors, receive audio levels CH%d %d, CH%d %d\n\n",
adev, ave_rate, error_count[adev], ch0, alevel0.rec, ch1, alevel1.rec);
}
else {
int ch0 = ADEVFIRSTCHAN(adev);
alevel_t alevel0 = demod_get_audio_level(ch0,0);
dw_printf ("\nADEVICE%d: Sample rate approx. %.1f k, %d errors, receive audio level CH%d %d\n\n",
adev, ave_rate, error_count[adev], ch0, alevel0.rec);
}
}
last_time[adev] = this_time[adev];
sample_count[adev] = 0;
error_count[adev] = 0;
}
}
} /* end audio_stats.c */

7
audio_stats.h Normal file
View File

@ -0,0 +1,7 @@
/* audio_stats.h */
extern void audio_stats (int adev, int nchan, int nsamp, int interval);

View File

@ -1,13 +1,8 @@
//#define DEBUGUDP 1
//#define DEBUG 1
#define STATISTICS 1
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2011, 2012, 2013, 2014 John Langner, WB2OSZ
// Copyright (C) 2011, 2012, 2013, 2014, 2015 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
@ -68,6 +63,7 @@
#include "direwolf.h"
#include "audio.h"
#include "audio_stats.h"
#include "textcolor.h"
#include "ptt.h"
#include "demod.h" /* for alevel_t & demod_get_audio_level() */
@ -290,14 +286,14 @@ int audio_open (struct audio_s *pa)
if (strcasecmp(pa->adev[a].adevice_in, "stdin") == 0 || strcmp(pa->adev[a].adevice_in, "-") == 0) {
A->g_audio_in_type = AUDIO_IN_TYPE_STDIN;
/* Change - to stdin for readability. */
strcpy (pa->adev[a].adevice_in, "stdin");
strlcpy (pa->adev[a].adevice_in, "stdin", sizeof(pa->adev[a].adevice_in));
}
else if (strncasecmp(pa->adev[a].adevice_in, "udp:", 4) == 0) {
A->g_audio_in_type = AUDIO_IN_TYPE_SDR_UDP;
/* Supply default port if none specified. */
if (strcasecmp(pa->adev[a].adevice_in,"udp") == 0 ||
strcasecmp(pa->adev[a].adevice_in,"udp:") == 0) {
sprintf (pa->adev[a].adevice_in, "udp:%d", DEFAULT_UDP_AUDIO_PORT);
snprintf (pa->adev[a].adevice_in, sizeof(pa->adev[a].adevice_in), "udp:%d", DEFAULT_UDP_AUDIO_PORT);
}
}
else {
@ -754,18 +750,6 @@ int audio_get (int a)
A = &(adev[a]);
#if defined(DEBUGUDP) || defined(STATISTICS)
/* Gather numbers for read from UDP stream. */
/* Gather numbers for read from audio device. */
#define duration 100 /* report every 100 seconds. */
static time_t last_time[MAX_ADEVS];
time_t this_time[MAX_ADEVS];
static int sample_count[MAX_ADEVS];
static int error_count[MAX_ADEVS];
#endif
switch (A->g_audio_in_type) {
/*
@ -791,6 +775,12 @@ int audio_get (int a)
// TODO1.2: Need more details. Can we keep going?
dw_printf ("Timeout waiting for input from audio device %d.\n", a);
audio_stats (a,
save_audio_config_p->adev[a].num_channels,
0,
save_audio_config_p->statistics_interval);
return (-1);
}
}
@ -800,6 +790,11 @@ int audio_get (int a)
if (p->dwUser == -1) {
waveInUnprepareHeader(A->audio_in_handle, p, sizeof(WAVEHDR));
p->dwUser = 0; /* Index for next byte. */
audio_stats (a,
save_audio_config_p->adev[a].num_channels,
p->dwBytesRecorded / (save_audio_config_p->adev[a].num_channels * save_audio_config_p->adev[a].bits_per_sample / 8),
save_audio_config_p->statistics_interval);
}
if (p->dwUser < p->dwBytesRecorded) {
@ -841,36 +836,20 @@ int audio_get (int a)
dw_printf ("Can't read from udp socket, errno %d", WSAGetLastError());
A->stream_len = 0;
A->stream_next = 0;
audio_stats (a,
save_audio_config_p->adev[a].num_channels,
0,
save_audio_config_p->statistics_interval);
return (-1);
}
#if DEBUGUDP
audio_stats (a,
save_audio_config_p->adev[a].num_channels,
res / (save_audio_config_p->adev[a].num_channels * save_audio_config_p->adev[a].bits_per_sample / 8),
save_audio_config_p->statistics_interval);
// TODO: We should collect audio statistics like this for all types of input. Move to common function.
if (last_time[a] == 0) {
last_time[a] = time(NULL);
sample_count[a] = 0;
error_count[a] = 0;
}
else {
if (res > 0) {
sample_count[a] += res/2;
}
else {
error_count[a]++;
}
this_time[a] = time(NULL);
if (this_time[a] >= last_time + duration) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("\ADEVICE%d: Past %d seconds, %d UDP audio samples processed, %d errors.\n\n",
a, duration, sample_count[a], error_count[a]);
last_time[a] = this_time[a];
sample_count[a] = 0;
error_count[a] = 0;
}
}
#endif
A->stream_len = res;
A->stream_next = 0;
}
@ -892,6 +871,11 @@ int audio_get (int a)
dw_printf ("\nEnd of file on stdin. Exiting.\n");
exit (0);
}
audio_stats (a,
save_audio_config_p->adev[a].num_channels,
res / (save_audio_config_p->adev[a].num_channels * save_audio_config_p->adev[a].bits_per_sample / 8),
save_audio_config_p->statistics_interval);
A->stream_len = res;
A->stream_next = 0;

View File

@ -1,4 +1,3 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
@ -151,6 +150,7 @@
char *strtok_r(char *str, const char *delim, char **saveptr);
#endif
#include "direwolf.h"
#include "ax25_pad.h"
#include "textcolor.h"
#include "fcs_calc.h"
@ -232,10 +232,19 @@ static packet_t ax25_new (void)
}
this_p = calloc(sizeof (struct packet_s), (size_t)1);
if (this_p == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR - can't allocate memory in ax25_new.\n");
}
assert (this_p != NULL);
this_p->magic1 = MAGIC;
this_p->seq = last_seq_num;
this_p->magic2 = MAGIC;
this_p->num_addr = (-1);
return (this_p);
}
@ -258,6 +267,13 @@ void ax25_delete (packet_t this_p)
dw_printf ("ax25_delete(): before free, new=%d, delete=%d\n", new_count, delete_count);
#endif
if (this_p == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR - NULL pointer passed to ax25_delete.\n");
return;
}
delete_count++;
#if AX25MEMDEBUG
@ -345,7 +361,7 @@ packet_t ax25_from_text (char *monitor, int strict)
/* information field of an AX.25 frame? */
/* Yes, but it would be difficult in the from-text case. */
strcpy (stuff, monitor);
strlcpy (stuff, monitor, sizeof(stuff));
/*
* Translate hexadecimal values like <0xff> to non-printing characters.
@ -379,8 +395,8 @@ packet_t ax25_from_text (char *monitor, int strict)
stuff[match[0].rm_so + 5] = '\0';
n = strtol (stuff + match[0].rm_so + 3, &p, 16);
stuff[match[0].rm_so] = n;
strcpy (temp, stuff + match[0].rm_eo);
strcpy (stuff + match[0].rm_so + 1, temp);
strlcpy (temp, stuff + match[0].rm_eo, sizeof(temp));
strlcpy (stuff + match[0].rm_so + 1, temp, sizeof(stuff)-match[0].rm_so-1);
}
else {
keep_going = 0;
@ -520,10 +536,9 @@ packet_t ax25_from_text (char *monitor, int strict)
/*
* Append the info part.
*/
strcpy ((char*)(this_p->frame_data+this_p->frame_len), pinfo);
strlcpy ((char*)(this_p->frame_data+this_p->frame_len), pinfo, sizeof(this_p->frame_data)-this_p->frame_len);
this_p->frame_len += strlen(pinfo);
return (this_p);
}
@ -634,6 +649,8 @@ packet_t ax25_dup (packet_t copy_from)
this_p = ax25_new ();
assert (this_p != NULL);
save_seq = this_p->seq;
memcpy (this_p, copy_from, sizeof (struct packet_s));
@ -684,11 +701,11 @@ packet_t ax25_dup (packet_t copy_from)
int ax25_parse_addr (char *in_addr, int strict, char *out_addr, int *out_ssid, int *out_heard)
{
char *p;
char sstr[4];
char sstr[8]; /* Should be 1 or 2 digits for SSID. */
int i, j, k;
int maxlen;
strcpy (out_addr, "");
*out_addr = '\0';
*out_ssid = 0;
*out_heard = 0;
@ -712,8 +729,8 @@ int ax25_parse_addr (char *in_addr, int strict, char *out_addr, int *out_ssid, i
}
}
strcpy (sstr, "");
j = 0;
sstr[j] = '\0';
if (*p == '-') {
for (p++; isalnum(*p); p++) {
if (j >= 2) {
@ -1081,6 +1098,8 @@ int ax25_get_num_repeaters (packet_t this_p)
*
* Outputs: station - String representation of the station, including the SSID.
* e.g. "WB2OSZ-15"
* Usually variables will be AX25_MAX_ADDR_LEN bytes
* but 10 would be adequate.
*
* Bugs: No bounds checking is performed. Be careful.
*
@ -1094,7 +1113,7 @@ int ax25_get_num_repeaters (packet_t this_p)
void ax25_get_addr_with_ssid (packet_t this_p, int n, char *station)
{
int ssid;
char sstr[4];
char sstr[8]; /* Should be 1 or 2 digits for SSID. */
int i;
assert (this_p->magic1 == MAGIC);
@ -1105,7 +1124,7 @@ void ax25_get_addr_with_ssid (packet_t this_p, int n, char *station)
text_color_set(DW_COLOR_ERROR);
dw_printf ("Internal error detected in ax25_get_addr_with_ssid, %s, line %d.\n", __FILE__, __LINE__);
dw_printf ("Address index, %d, is less than zero.\n", n);
strcpy (station, "??????");
strlcpy (station, "??????", 10);
return;
}
@ -1113,7 +1132,7 @@ void ax25_get_addr_with_ssid (packet_t this_p, int n, char *station)
text_color_set(DW_COLOR_ERROR);
dw_printf ("Internal error detected in ax25_get_addr_with_ssid, %s, line %d.\n", __FILE__, __LINE__);
dw_printf ("Address index, %d, is too large for number of addresses, %d.\n", n, this_p->num_addr);
strcpy (station, "??????");
strlcpy (station, "??????", 10);
return;
}
@ -1128,12 +1147,72 @@ void ax25_get_addr_with_ssid (packet_t this_p, int n, char *station)
ssid = ax25_get_ssid (this_p, n);
if (ssid != 0) {
sprintf (sstr, "-%d", ssid);
strcat (station, sstr);
snprintf (sstr, sizeof(sstr), "-%d", ssid);
strlcat (station, sstr, 10);
}
}
/*------------------------------------------------------------------------------
*
* Name: ax25_get_addr_no_ssid
*
* Purpose: Return specified address WITHOUT any SSID.
*
* Inputs: n - Index of address. Use the symbols
* AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc.
*
* Outputs: station - String representation of the station, WITHOUT the SSID.
* e.g. "WB2OSZ"
* Usually variables will be AX25_MAX_ADDR_LEN bytes
* but 7 would be adequate.
*
* Bugs: No bounds checking is performed. Be careful.
*
* Assumption: ax25_from_text or ax25_from_frame was called first.
*
* Returns: Character string in usual human readable format,
*
*
*------------------------------------------------------------------------------*/
void ax25_get_addr_no_ssid (packet_t this_p, int n, char *station)
{
int ssid;
int i;
assert (this_p->magic1 == MAGIC);
assert (this_p->magic2 == MAGIC);
if (n < 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Internal error detected in ax25_get_addr_no_ssid, %s, line %d.\n", __FILE__, __LINE__);
dw_printf ("Address index, %d, is less than zero.\n", n);
strlcpy (station, "??????", 7);
return;
}
if (n >= this_p->num_addr) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Internal error detected in ax25_get_no_with_ssid, %s, line %d.\n", __FILE__, __LINE__);
dw_printf ("Address index, %d, is too large for number of addresses, %d.\n", n, this_p->num_addr);
strlcpy (station, "??????", 7);
return;
}
memset (station, 0, 7);
for (i=0; i<6; i++) {
unsigned char ch;
ch = (this_p->frame_data[n*7+i] >> 1) & 0x7f;
if (ch <= ' ') break;
station[i] = ch;
}
} /* end ax25_get_addr_no_ssid */
/*------------------------------------------------------------------------------
*
* Name: ax25_get_ssid
@ -1490,6 +1569,8 @@ packet_t ax25_get_nextp (packet_t this_p)
*
*------------------------------------------------------------------*/
// TODO: max len for result. buffer overflow?
void ax25_format_addrs (packet_t this_p, char *result)
{
int i;
@ -1591,7 +1672,8 @@ int ax25_pack (packet_t this_p, unsigned char result[AX25_MAX_PACKET_LEN])
*
*------------------------------------------------------------------*/
// TODO: need someway to ensure caller allocated enough space.
#define DESC_SIZ 16
ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *desc, int *pf, int *nr, int *ns)
{
@ -1600,14 +1682,14 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *
assert (this_p->magic1 == MAGIC);
assert (this_p->magic2 == MAGIC);
strcpy (desc, "????");
strlcpy (desc, "????", DESC_SIZ);
*pf = -1;
*nr = -1;
*ns = -1;
c = ax25_get_control(this_p);
if (c < 0) {
strcpy (desc, "Not AX.25");
strlcpy (desc, "Not AX.25", DESC_SIZ);
return (frame_not_AX25);
}
@ -1626,7 +1708,7 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *
*pf = (c >> 4) & 1;
*nr = (c >> 5) & 7;
}
strcpy (desc, "I frame");
strlcpy (desc, "I frame", DESC_SIZ);
return (frame_type_I);
}
else if ((c & 2) == 0) {
@ -1644,10 +1726,10 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *
}
switch ((c >> 2) & 3) {
case 0: strcpy (desc, "S frame RR"); return (frame_type_RR); break;
case 1: strcpy (desc, "S frame RNR"); return (frame_type_RNR); break;
case 2: strcpy (desc, "S frame REJ"); return (frame_type_REJ); break;
case 3: strcpy (desc, "S frame SREJ"); return (frame_type_SREJ); break;
case 0: strlcpy (desc, "S frame RR", DESC_SIZ); return (frame_type_RR); break;
case 1: strlcpy (desc, "S frame RNR", DESC_SIZ); return (frame_type_RNR); break;
case 2: strlcpy (desc, "S frame REJ", DESC_SIZ); return (frame_type_REJ); break;
case 3: strlcpy (desc, "S frame SREJ", DESC_SIZ); return (frame_type_SREJ); break;
}
}
else {
@ -1658,16 +1740,16 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *
switch (c & 0xef) {
case 0x6f: strcpy (desc, "U frame SABME"); return (frame_type_SABME); break;
case 0x2f: strcpy (desc, "U frame SABM"); return (frame_type_SABM); break;
case 0x43: strcpy (desc, "U frame DISC"); return (frame_type_DISC); break;
case 0x0f: strcpy (desc, "U frame DM"); return (frame_type_DM); break;
case 0x63: strcpy (desc, "U frame UA"); return (frame_type_UA); break;
case 0x87: strcpy (desc, "U frame FRMR"); return (frame_type_FRMR); break;
case 0x03: strcpy (desc, "U frame UI"); return (frame_type_UI); break;
case 0xaf: strcpy (desc, "U frame XID"); return (frame_type_XID); break;
case 0xe3: strcpy (desc, "U frame TEST"); return (frame_type_TEST); break;
default: strcpy (desc, "U frame ???"); return (frame_type_U); break;
case 0x6f: strlcpy (desc, "U frame SABME", DESC_SIZ); return (frame_type_SABME); break;
case 0x2f: strlcpy (desc, "U frame SABM", DESC_SIZ); return (frame_type_SABM); break;
case 0x43: strlcpy (desc, "U frame DISC", DESC_SIZ); return (frame_type_DISC); break;
case 0x0f: strlcpy (desc, "U frame DM", DESC_SIZ); return (frame_type_DM); break;
case 0x63: strlcpy (desc, "U frame UA", DESC_SIZ); return (frame_type_UA); break;
case 0x87: strlcpy (desc, "U frame FRMR", DESC_SIZ); return (frame_type_FRMR); break;
case 0x03: strlcpy (desc, "U frame UI", DESC_SIZ); return (frame_type_UI); break;
case 0xaf: strlcpy (desc, "U frame XID", DESC_SIZ); return (frame_type_XID); break;
case 0xe3: strlcpy (desc, "U frame TEST", DESC_SIZ); return (frame_type_TEST); break;
default: strlcpy (desc, "U frame ???", DESC_SIZ); return (frame_type_U); break;
}
}
@ -1740,26 +1822,28 @@ static void ctrl_to_text (int c, char *out)
/* Text description of protocol id octet. */
static void pid_to_text (int p, char *out)
#define PID_TEXT_SIZE 80
static void pid_to_text (int p, char out[PID_TEXT_SIZE])
{
if ((p & 0x30) == 0x10) { sprintf (out, "AX.25 layer 3 implemented."); }
else if ((p & 0x30) == 0x20) { sprintf (out, "AX.25 layer 3 implemented."); }
else if (p == 0x01) { sprintf (out, "ISO 8208/CCITT X.25 PLP"); }
else if (p == 0x06) { sprintf (out, "Compressed TCP/IP packet. Van Jacobson (RFC 1144)"); }
else if (p == 0x07) { sprintf (out, "Uncompressed TCP/IP packet. Van Jacobson (RFC 1144)"); }
else if (p == 0x08) { sprintf (out, "Segmentation fragment"); }
else if (p == 0xC3) { sprintf (out, "TEXNET datagram protocol"); }
else if (p == 0xC4) { sprintf (out, "Link Quality Protocol"); }
else if (p == 0xCA) { sprintf (out, "Appletalk"); }
else if (p == 0xCB) { sprintf (out, "Appletalk ARP"); }
else if (p == 0xCC) { sprintf (out, "ARPA Internet Protocol"); }
else if (p == 0xCD) { sprintf (out, "ARPA Address resolution"); }
else if (p == 0xCE) { sprintf (out, "FlexNet"); }
else if (p == 0xCF) { sprintf (out, "NET/ROM"); }
else if (p == 0xF0) { sprintf (out, "No layer 3 protocol implemented."); }
else if (p == 0xFF) { sprintf (out, "Escape character. Next octet contains more Level 3 protocol information."); }
else { sprintf (out, "Unknown protocol id = 0x%02x", p); }
if ((p & 0x30) == 0x10) { snprintf (out, PID_TEXT_SIZE, "AX.25 layer 3 implemented."); }
else if ((p & 0x30) == 0x20) { snprintf (out, PID_TEXT_SIZE, "AX.25 layer 3 implemented."); }
else if (p == 0x01) { snprintf (out, PID_TEXT_SIZE, "ISO 8208/CCITT X.25 PLP"); }
else if (p == 0x06) { snprintf (out, PID_TEXT_SIZE, "Compressed TCP/IP packet. Van Jacobson (RFC 1144)"); }
else if (p == 0x07) { snprintf (out, PID_TEXT_SIZE, "Uncompressed TCP/IP packet. Van Jacobson (RFC 1144)"); }
else if (p == 0x08) { snprintf (out, PID_TEXT_SIZE, "Segmentation fragment"); }
else if (p == 0xC3) { snprintf (out, PID_TEXT_SIZE, "TEXNET datagram protocol"); }
else if (p == 0xC4) { snprintf (out, PID_TEXT_SIZE, "Link Quality Protocol"); }
else if (p == 0xCA) { snprintf (out, PID_TEXT_SIZE, "Appletalk"); }
else if (p == 0xCB) { snprintf (out, PID_TEXT_SIZE, "Appletalk ARP"); }
else if (p == 0xCC) { snprintf (out, PID_TEXT_SIZE, "ARPA Internet Protocol"); }
else if (p == 0xCD) { snprintf (out, PID_TEXT_SIZE, "ARPA Address resolution"); }
else if (p == 0xCE) { snprintf (out, PID_TEXT_SIZE, "FlexNet"); }
else if (p == 0xCF) { snprintf (out, PID_TEXT_SIZE, "NET/ROM"); }
else if (p == 0xF0) { snprintf (out, PID_TEXT_SIZE, "No layer 3 protocol implemented."); }
else if (p == 0xFF) { snprintf (out, PID_TEXT_SIZE, "Escape character. Next octet contains more Level 3 protocol information."); }
else { snprintf (out, PID_TEXT_SIZE, "Unknown protocol id = 0x%02x", p); }
}
@ -1785,17 +1869,17 @@ void ax25_hex_dump (packet_t this_p)
if ( (c & 0x01) == 0 || /* I xxxx xxx0 */
c == 0x03 || c == 0x13) { /* UI 000x 0011 */
char p_text[100];
char pid_text[PID_TEXT_SIZE];
pid_to_text (p, p_text);
pid_to_text (p, pid_text);
strcat (cp_text, ", ");
strcat (cp_text, p_text);
strlcat (cp_text, ", ", sizeof(cp_text));
strlcat (cp_text, pid_text, sizeof(cp_text));
}
sprintf (l_text, ", length = %d", flen);
strcat (cp_text, l_text);
snprintf (l_text, sizeof(l_text), ", length = %d", flen);
strlcat (cp_text, l_text, sizeof(cp_text));
dw_printf ("%s\n", cp_text);
}
@ -2097,7 +2181,7 @@ void ax25_safe_print (char *pstr, int len, int ascii_only)
/* UTF-8 does not use fe and ff except in a possible */
/* "Byte Order Mark" (BOM) at the beginning. */
sprintf (safe_str + safe_len, "<0x%02x>", ch);
snprintf (safe_str + safe_len, sizeof(safe_str)-safe_len, "<0x%02x>", ch);
safe_len += 6;
}
else {
@ -2138,6 +2222,8 @@ void ax25_safe_print (char *pstr, int len, int ascii_only)
* Comma is to be avoided because one place this
* ends up is in a CSV format file.
*
* size should be AX25_ALEVEL_TO_TEXT_SIZE.
*
* Returns: True if something to print. (currently if alevel.original >= 0)
* False if not.
*
@ -2153,25 +2239,30 @@ void ax25_safe_print (char *pstr, int len, int ascii_only)
*------------------------------------------------------------------*/
int ax25_alevel_to_text (alevel_t alevel, char *text)
int ax25_alevel_to_text (alevel_t alevel, char text[AX25_ALEVEL_TO_TEXT_SIZE])
{
if (alevel.rec < 0) {
strcpy (text, "");
strlcpy (text, "", AX25_ALEVEL_TO_TEXT_SIZE);
return (0);
}
// TODO1.2: haven't thought much about non-AFSK cases yet.
// What should we do for 9600 baud?
// Possibility: low/high tone for DTMF???
// For DTMF omit the two extra numbers.
if (alevel.mark >= 0 && alevel.space < 0) { /* baseband */
sprintf (text, "%d(%+d/%+d)", alevel.rec, alevel.mark, alevel.space);
snprintf (text, AX25_ALEVEL_TO_TEXT_SIZE, "%d(%+d/%+d)", alevel.rec, alevel.mark, alevel.space);
}
else if (alevel.mark == -2 && alevel.space == -2) { /* DTMF */
snprintf (text, AX25_ALEVEL_TO_TEXT_SIZE, "%d", alevel.rec);
}
else { /* AFSK */
//sprintf (text, "%d:%d(%d/%d=%05.3f=)", alevel.original, alevel.rec, alevel.mark, alevel.space, alevel.ms_ratio);
sprintf (text, "%d(%d/%d)", alevel.rec, alevel.mark, alevel.space);
//snprintf (text, AX25_ALEVEL_TO_TEXT_SIZE, "%d:%d(%d/%d=%05.3f=)", alevel.original, alevel.rec, alevel.mark, alevel.space, alevel.ms_ratio);
snprintf (text, AX25_ALEVEL_TO_TEXT_SIZE, "%d(%d/%d)", alevel.rec, alevel.mark, alevel.space);
}
return (1);

View File

@ -318,7 +318,8 @@ extern void ax25_remove_addr (packet_t this_p, int n);
extern int ax25_get_num_addr (packet_t pp);
extern int ax25_get_num_repeaters (packet_t this_p);
extern void ax25_get_addr_with_ssid (packet_t pp, int n, char *);
extern void ax25_get_addr_with_ssid (packet_t pp, int n, char *station);
extern void ax25_get_addr_no_ssid (packet_t pp, int n, char *station);
extern int ax25_get_ssid (packet_t pp, int n);
extern void ax25_set_ssid (packet_t this_p, int n, int ssid);
@ -360,7 +361,8 @@ extern unsigned short ax25_m_m_crc (packet_t pp);
extern void ax25_safe_print (char *, int, int ascii_only);
extern int ax25_alevel_to_text (alevel_t alevel, char *text);
#define AX25_ALEVEL_TO_TEXT_SIZE 32 // overkill but safe.
extern int ax25_alevel_to_text (alevel_t alevel, char text[AX25_ALEVEL_TO_TEXT_SIZE]);
#endif /* AX25_PAD_H */

107
beacon.c
View File

@ -59,6 +59,7 @@
#include "dwgps.h"
#include "log.h"
#include "dlq.h"
#include "aprs_tt.h" // for dw_run_cmd - should relocate someday.
@ -205,11 +206,11 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct
case BEACON_CUSTOM:
/* INFO is required. */
/* INFO or INFOCMD is required. */
if (g_misc_config_p->beacon[j].custom_info == NULL) {
if (g_misc_config_p->beacon[j].custom_info == NULL && g_misc_config_p->beacon[j].custom_infocmd == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file, line %d: INFO is required for custom beacon.\n", g_misc_config_p->beacon[j].lineno);
dw_printf ("Config file, line %d: INFO or INFOCMD is required for custom beacon.\n", g_misc_config_p->beacon[j].lineno);
g_misc_config_p->beacon[j].btype = BEACON_IGNORE;
continue;
}
@ -578,6 +579,8 @@ static void * beacon_thread (void *arg)
char mycall[AX25_MAX_ADDR_LEN];
int alt_ft;
char super_comment[AX25_MAX_INFO_LEN]; // Fixed part + any dynamic part.
/*
* Obtain source call for the beacon.
* This could potentially be different on different channels.
@ -588,11 +591,11 @@ static void * beacon_thread (void *arg)
* Version 1.1 - channel should now be 0 for IGate.
* Type of destination is encoded separately.
*/
strcpy (mycall, "NOCALL");
strlcpy (mycall, "NOCALL", sizeof(mycall));
assert (g_misc_config_p->beacon[j].sendto_chan >= 0);
strcpy (mycall, g_modem_config_p->achan[g_misc_config_p->beacon[j].sendto_chan].mycall);
strlcpy (mycall, g_modem_config_p->achan[g_misc_config_p->beacon[j].sendto_chan].mycall, sizeof(mycall));
if (strlen(mycall) == 0 || strcmp(mycall, "NOCALL") == 0) {
text_color_set(DW_COLOR_ERROR);
@ -606,22 +609,52 @@ static void * beacon_thread (void *arg)
* src > dest [ , via ]
*/
strcpy (beacon_text, mycall);
strcat (beacon_text, ">");
strlcpy (beacon_text, mycall, sizeof(beacon_text));
strlcat (beacon_text, ">", sizeof(beacon_text));
if (g_misc_config_p->beacon[j].dest != NULL) {
strcat (beacon_text, g_misc_config_p->beacon[j].dest);
strlcat (beacon_text, g_misc_config_p->beacon[j].dest, sizeof(beacon_text));
}
else {
sprintf (stemp, "%s%1d%1d", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION);
strcat (beacon_text, stemp);
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) {
strcat (beacon_text, ",");
strcat (beacon_text, g_misc_config_p->beacon[j].via);
strlcat (beacon_text, ",", sizeof(beacon_text));
strlcat (beacon_text, g_misc_config_p->beacon[j].via, sizeof(beacon_text));
}
strcat (beacon_text, ":");
strlcat (beacon_text, ":", sizeof(beacon_text));
/*
* If the COMMENTCMD option was specified, run specified command to get variable part.
* Result is any fixed part followed by any variable part.
*/
// 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 (g_misc_config_p->beacon[j].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, (int)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);
}
}
/*
* Add the info part depending on beacon type.
@ -638,9 +671,9 @@ static void * beacon_thread (void *arg)
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,
0, 0, /* course, speed */
g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
g_misc_config_p->beacon[j].comment,
info);
strcat (beacon_text, info);
super_comment,
info, sizeof(info));
strlcat (beacon_text, info, sizeof(beacon_text));
g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
break;
@ -650,9 +683,9 @@ static void * beacon_thread (void *arg)
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,
0, 0, /* course, speed */
g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset, g_misc_config_p->beacon[j].comment,
info);
strcat (beacon_text, info);
g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset, super_comment,
info, sizeof(info));
strlcat (beacon_text, info, sizeof(beacon_text));
g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
break;
@ -673,9 +706,9 @@ static void * beacon_thread (void *arg)
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,
coarse, (int)roundf(my_speed_knots),
g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
g_misc_config_p->beacon[j].comment,
info);
strcat (beacon_text, info);
super_comment,
info, sizeof(info));
strlcat (beacon_text, info, sizeof(beacon_text));
/* Remember most recent tracker beacon. */
@ -707,7 +740,7 @@ static void * beacon_thread (void *arg)
A.g_tone = G_UNKNOWN;
A.g_dcs = G_UNKNOWN;
strcpy (A.g_src, mycall);
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_lat = my_lat;
@ -730,12 +763,32 @@ static void * beacon_thread (void *arg)
case BEACON_CUSTOM:
if (g_misc_config_p->beacon[j].custom_info != NULL) {
strcat (beacon_text, g_misc_config_p->beacon[j].custom_info);
/* Fixed handcrafted text. */
strlcat (beacon_text, g_misc_config_p->beacon[j].custom_info, sizeof(beacon_text));
}
else if (g_misc_config_p->beacon[j].custom_infocmd != NULL) {
char info_part[AX25_MAX_INFO_LEN];
char *p;
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, (int)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);
strlcpy (beacon_text, "", sizeof(beacon_text)); // abort!
}
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Internal error. custom_info is null. %s %d\n", __FILE__, __LINE__);
continue;
strlcpy (beacon_text, "", sizeof(beacon_text)); // abort!
}
g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
break;
@ -749,6 +802,10 @@ static void * beacon_thread (void *arg)
/*
* Parse monitor format into form for transmission.
*/
if (strlen(beacon_text) == 0) {
continue;
}
pp = ax25_from_text (beacon_text, strict);
if (pp != NULL) {

1207
config.c

File diff suppressed because it is too large Load Diff

View File

@ -32,8 +32,8 @@ 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 agwpe_port; /* Port number for the "AGW TCPIP Socket Interface" */
int kiss_port; /* Port number for the "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. */
@ -97,6 +97,9 @@ struct misc_config_s {
char *custom_info; /* Info part for handcrafted custom beacon. */
/* Ignore the rest below if this is set. */
char *custom_infocmd; /* Command to generate info part. */
/* Again, other options below are then ignored. */
int messaging; /* Set messaging attribute for position report. */
/* i.e. Data Type Indicator of '=' rather than '!' */
@ -119,6 +122,7 @@ struct misc_config_s {
float offset; /* MHz. */
char *comment; /* Comment or NULL. */
char *commentcmd; /* Command to append more to Comment or NULL. */
} beacon[MAX_BEACONS];

File diff suppressed because it is too large Load Diff

View File

@ -61,6 +61,8 @@ typedef struct decode_aprs_s {
char g_name[12]; /* Object or item name. Max. 9 characters. */
char g_addressee[12]; /* Addressee for a "message." Max. 9 characters. */
/* Also for Directed Station Query which is a */
/* special case of message. */
float g_speed; /* Speed in MPH. */
@ -88,7 +90,23 @@ typedef struct decode_aprs_s {
int g_dcs; /* Digital coded squelch, print as 3 octal digits. */
int g_offset; /* Transmit offset, KHz */
int g_offset; /* Transmit offset, kHz */
char g_query_type[12]; /* General Query: APRS, IGATE, WX, ... */
/* Addressee is NOT set. */
/* Directed Station Query: exactly 5 characters. */
/* APRSD, APRST, PING?, ... */
/* Addressee is set. */
double g_footprint_lat; /* A general query may contain a foot print. */
double g_footprint_lon; /* Set all to G_UNKNOWN if not used. */
float g_footprint_radius; /* Radius in miles. */
char g_query_callsign[12]; /* Directed query may contain callsign. */
/* e.g. tell me all objects from that callsign. */
char g_weather[500]; /* Weather. Can get quite long. Rethink max size. */

18
demod.c
View File

@ -191,28 +191,16 @@ int demod_init (struct audio_s *pa)
#if __arm__
/* We probably don't have a lot of CPU power available. */
/* Previously we would use F if possible otherwise fall back to A. */
#if 0
if (save_audio_config_p->achan[chan].baud == FFF_BAUD &&
save_audio_config_p->achan[chan].mark_freq == FFF_MARK_FREQ &&
save_audio_config_p->achan[chan].space_freq == FFF_SPACE_FREQ &&
save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec == FFF_SAMPLES_PER_SEC) {
just_letters[0] = FFF_PROFILE;
just_letters[1] = '\0';
}
else {
strcpy (just_letters, "A");
}
#else
/* In version 1.2, new default is E+ /3. */
strcpy (just_letters, "E"); // version 1.2 now E.
if (have_plus != -1) have_plus = 1; // Add as default for version 1.2
// If not explicitly turned off.
if (save_audio_config_p->achan[chan].decimate == 0) {
save_audio_config_p->achan[chan].decimate = 3;
if (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec > 40000) {
save_audio_config_p->achan[chan].decimate = 3;
}
}
#endif
#else
strcpy (just_letters, "E"); // version 1.2 changed C to E.
if (have_plus != -1) have_plus = 1; // Add as default for version 1.2

View File

@ -134,7 +134,6 @@ void digipeater_init (struct audio_s *p_audio_config, struct digi_config_s *p_di
void digipeater (int from_chan, packet_t pp)
{
int to_chan;
packet_t result;
// dw_printf ("digipeater()\n");
@ -156,6 +155,8 @@ void digipeater (int from_chan, packet_t pp)
for (to_chan=0; to_chan<MAX_CHANS; to_chan++) {
if (save_digi_config_p->enabled[from_chan][to_chan]) {
if (to_chan == from_chan) {
packet_t result;
result = digipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall,
save_audio_config_p->achan[to_chan].mycall,
&save_digi_config_p->alias[from_chan][to_chan], &save_digi_config_p->wide[from_chan][to_chan],
@ -179,6 +180,8 @@ void digipeater (int from_chan, packet_t pp)
for (to_chan=0; to_chan<MAX_CHANS; to_chan++) {
if (save_digi_config_p->enabled[from_chan][to_chan]) {
if (to_chan != from_chan) {
packet_t result;
result = digipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall,
save_audio_config_p->achan[to_chan].mycall,
&save_digi_config_p->alias[from_chan][to_chan], &save_digi_config_p->wide[from_chan][to_chan],
@ -227,6 +230,9 @@ void digipeater (int from_chan, packet_t pp)
* filter_str - Filter expression string or NULL.
*
* Returns: Packet object for transmission or NULL.
* The original packet is not modified. (with one exception, probably obsolete)
* We make a copy and return that modified copy!
* This is very important because we could digipeat from one channel to many.
*
* Description: The packet will be digipeated if the next unused digipeater
* field matches one of the following:
@ -262,7 +268,6 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
int ssid;
int r;
char repeater[AX25_MAX_ADDR_LEN];
packet_t result = NULL;
int err;
char err_msg[100];
@ -276,6 +281,8 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
if (pfilter(from_chan, to_chan, filter_str, pp) != 1) {
// TODO1.2: take out debug message
// Actually it turns out to be useful.
// Maybe add a quiet option to suppress it although no one has complained about it yet.
//#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("Packet was rejected for digipeating from channel %d to %d by filter: %s\n", from_chan, to_chan, filter_str);
@ -289,8 +296,8 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
*
* The SSID in the Destination Address field of all packets is coded to specify
* the APRS digipeater path.
* If the Destination Address SSID is 0, the packet follows the standard AX.25
* digipeater (VIA) path contained in the Digipeater Addresses field of the
* If the Destination Address SSID is -0, the packet follows the standard AX.25
* digipeater ("VIA") path contained in the Digipeater Addresses field of the
* AX.25 frame.
* If the Destination Address SSID is non-zero, the packet follows one of 15
* generic APRS digipeater paths.
@ -317,7 +324,7 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
r = ax25_get_first_not_repeated(pp);
if (r < AX25_REPEATER_1) {
return NULL;
return (NULL);
}
ax25_get_addr_with_ssid(pp, r, repeater);
@ -337,7 +344,11 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
*/
if (strcmp(repeater, mycall_rec) == 0) {
packet_t result;
result = ax25_dup (pp);
assert (result != NULL);
/* If using multiple radio channels, they */
/* could have different calls. */
ax25_set_addr (result, r, mycall_xmit);
@ -369,7 +380,6 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
text_color_set(DW_COLOR_INFO);
dw_printf ("Digipeater: Drop redundant packet to channel %d.\n", to_chan);
//#endif
assert (result == NULL);
return NULL;
}
@ -379,7 +389,11 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
*/
err = regexec(alias,repeater,0,NULL,0);
if (err == 0) {
packet_t result;
result = ax25_dup (pp);
assert (result != NULL);
ax25_set_addr (result, r, mycall_xmit);
ax25_set_h (result, r);
return (result);
@ -408,8 +422,11 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
if (strcmp(repeater2, mycall_rec) == 0 ||
regexec(alias,repeater2,0,NULL,0) == 0) {
packet_t result;
result = ax25_dup (pp);
assert (result != NULL);
ax25_set_addr (result, r2, mycall_xmit);
ax25_set_h (result, r2);
@ -461,14 +478,22 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
*/
if (ssid == 1) {
packet_t result;
result = ax25_dup (pp);
assert (result != NULL);
ax25_set_addr (result, r, mycall_xmit);
ax25_set_h (result, r);
return (result);
}
if (ssid >= 2 && ssid <= 7) {
packet_t result;
result = ax25_dup (pp);
assert (result != NULL);
ax25_set_ssid(result, r, ssid-1); // should be at least 1
if (ax25_get_num_repeaters(pp) < AX25_MAX_REPEATERS) {
@ -488,8 +513,8 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
/*
* Don't repeat it if we get here.
*/
assert (result == NULL);
return NULL;
return (NULL);
}
@ -597,6 +622,7 @@ static void test (char *in, char *out)
ax25_delete (pp);
pp = ax25_from_frame (frame, frame_len, 50);
assert (pp != NULL);
ax25_format_addrs (pp, rec);
info_len = ax25_get_info (pp, &pinfo);
strcat (rec, (char*)pinfo);

View File

@ -58,6 +58,7 @@
#include <sys/ioctl.h>
#ifdef __OpenBSD__
#include <soundcard.h>
#elif __APPLE__
#else
#include <sys/soundcard.h>
#endif
@ -100,6 +101,7 @@
#include "dwgps.h"
#include "log.h"
#include "recv.h"
#include "morse.h"
//static int idx_decoded = 0;
@ -112,7 +114,7 @@ static void cleanup_linux (int);
static void usage (char **argv);
#if __SSE__
#if defined(__SSE__) && !defined(__APPLE__)
static void __cpuid(int cpuinfo[4], int infotype){
__asm__ __volatile__ (
@ -120,7 +122,7 @@ static void __cpuid(int cpuinfo[4], int infotype){
"=a" (cpuinfo[0]),
"=b" (cpuinfo[1]),
"=c" (cpuinfo[2]),
"=d" (cpuinfo[3]) :
"=d" (cpuinfo[3]):
"a" (infotype)
);
}
@ -179,11 +181,12 @@ int main (int argc, char *argv[])
int d_n_opt = 0; /* "-d n" option for Network KISS. Can be repeated for more detail. */
int d_t_opt = 0; /* "-d t" option for Tracker. Can be repeated for more detail. */
int d_o_opt = 0; /* "-d o" option for output control such as PTT and DCD. */
int a_opt = 0; /* "-a n" interval, in seconds, for audio statistics report. 0 for none. */
strcpy(l_opt, "");
strcpy(P_opt, "");
strlcpy(l_opt, "", sizeof(l_opt));
strlcpy(P_opt, "", sizeof(P_opt));
#if __WIN32__
@ -226,13 +229,13 @@ int main (int argc, char *argv[])
}
}
// TODO: control development/beta/release by versio.h instead of changing here.
// TODO: control development/beta/release by version.h instead of changing here.
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, "F", __DATE__);
dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "F", __DATE__);
//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
#if __WIN32__
@ -250,10 +253,13 @@ int main (int argc, char *argv[])
* Try to warn anyone using a CPU from the previous
* century rather than just dying for no apparent reason.
*
* Apple computers with Intel processors started with P6. Since the
* cpu test code was giving Clang compiler grief it has been excluded.
*
* Now, where can I find a Pentium 2 or earlier to test this?
*/
#if __SSE__
#if defined(__SSE__) && !defined(__APPLE__)
int cpuinfo[4];
__cpuid (cpuinfo, 0);
if (cpuinfo[0] >= 1) {
@ -292,14 +298,14 @@ int main (int argc, char *argv[])
* TODO: Automatically search other places.
*/
strcpy (config_file, "direwolf.conf");
strlcpy (config_file, "direwolf.conf", sizeof(config_file));
/*
* Look at command line options.
* So far, the only one is the configuration file location.
*/
strcpy (input_file, "");
strlcpy (input_file, "", sizeof(input_file));
while (1) {
int this_option_optind = optind ? optind : 1;
int option_index = 0;
@ -314,7 +320,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:",
c = getopt_long(argc, argv, "P:B:D:c:pxr:b:n:d:q:t:Ul:Sa:",
long_options, &option_index);
if (c == -1)
break;
@ -330,10 +336,19 @@ int main (int argc, char *argv[])
dw_printf("\n");
break;
case 'a': /* -a for audio statistics interval */
a_opt = atoi(optarg);
if (a_opt < 0) a_opt = 0;
if (a_opt < 10) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Setting such a small audio statistics interval will produce inaccurate sample rate display.\n");
}
break;
case 'c': /* -c for configuration file name */
strcpy (config_file, optarg);
strlcpy (config_file, optarg, sizeof(config_file));
break;
#if __WIN32__
@ -360,7 +375,7 @@ int main (int argc, char *argv[])
case 'P': /* -P for modem profile. */
//debug: dw_printf ("Demodulator profile set to \"%s\"\n", optarg);
strcpy (P_opt, optarg);
strlcpy (P_opt, optarg, sizeof(P_opt));
break;
case 'D': /* -D decrease AFSK demodulator sample rate */
@ -476,9 +491,16 @@ int main (int argc, char *argv[])
case 'l': /* -l for log file directory name */
strncpy (l_opt, optarg, sizeof(l_opt)-1);
strlcpy (l_opt, optarg, sizeof(l_opt));
break;
case 'S': /* Print symbol tables and exit. */
symbols_init ();
symbols_list ();
exit (0);
break;
default:
/* Should not be here. */
@ -497,7 +519,7 @@ int main (int argc, char *argv[])
dw_printf ("Warning: File(s) beyond the first are ignored.\n");
}
strcpy (input_file, argv[optind]);
strlcpy (input_file, argv[optind], sizeof(input_file));
}
@ -544,10 +566,12 @@ int main (int argc, char *argv[])
}
}
audio_config.statistics_interval = a_opt;
if (strlen(P_opt) > 0) {
/* -P for modem profile. */
/* TODO: Not yet documented. Should probably since it is consistent with atest. */
strcpy (audio_config.achan[0].profiles, P_opt);
strlcpy (audio_config.achan[0].profiles, P_opt, sizeof(audio_config.achan[0].profiles));
}
if (D_opt != 0) {
@ -556,15 +580,18 @@ int main (int argc, char *argv[])
}
if (strlen(l_opt) > 0) {
strncpy (misc_config.logdir, l_opt, sizeof(misc_config.logdir)-1);
strlcpy (misc_config.logdir, l_opt, sizeof(misc_config.logdir));
}
misc_config.enable_kiss_pt = enable_pseudo_terminal;
if (strlen(input_file) > 0) {
strcpy (audio_config.adev[0].adevice_in, input_file);
strlcpy (audio_config.adev[0].adevice_in, input_file, sizeof(audio_config.adev[0].adevice_in));
}
/*
* Open the audio source
* - soundcard
@ -600,6 +627,7 @@ int main (int argc, char *argv[])
* It is the range of the digital sound representation.
*/
gen_tone_init (&audio_config, 100);
morse_init (&audio_config, 100);
assert (audio_config.adev[0].bits_per_sample == 8 || audio_config.adev[0].bits_per_sample == 16);
assert (audio_config.adev[0].num_channels == 1 || audio_config.adev[0].num_channels == 2);
@ -727,9 +755,9 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
assert (subchan >= -1 && subchan < MAX_SUBCHANS);
assert (pp != NULL); // 1.1J+
strcpy (display_retries, "");
strlcpy (display_retries, "", sizeof(display_retries));
if (audio_config.achan[chan].fix_bits != RETRY_NONE || audio_config.achan[chan].passall) {
sprintf (display_retries, " [%s] ", retry_text[(int)retries]);
snprintf (display_retries, sizeof(display_retries), " [%s] ", retry_text[(int)retries]);
}
ax25_format_addrs (pp, stemp);
@ -744,7 +772,7 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
if (ax25_get_num_addr(pp) == 0) {
/* Not AX.25. No station to display below. */
h = -1;
strcpy (heard, "");
strlcpy (heard, "", sizeof(heard));
}
else {
h = ax25_get_heard(pp);
@ -760,7 +788,7 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
dw_printf ("Digipeater ");
}
char alevel_text[32];
char alevel_text[AX25_ALEVEL_TO_TEXT_SIZE];
ax25_alevel_to_text (alevel, alevel_text);
@ -782,6 +810,10 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
dw_printf ("%s (probably %s) audio level = %s %s %s\n", heard, probably_really, alevel_text, display_retries, spectrum);
}
else if (strcmp(heard, "DTMF") == 0) {
dw_printf ("%s audio level = %s tt\n", heard, alevel_text);
}
else {
dw_printf ("%s audio level = %s %s %s\n", heard, alevel_text, display_retries, spectrum);
@ -895,6 +927,7 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
/* Send to another application if connected. */
// TODO1.3: Put a wrapper around this so we only call one function to send by all methods.
int flen;
unsigned char fbuf[AX25_MAX_PACKET_LEN];
@ -924,23 +957,20 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
if (ax25_is_aprs(pp) && retries == RETRY_NONE) {
if (digi_config.filter_str[chan][MAX_CHANS] != NULL) {
// TODO1.2: filtering - maybe it should be ig... so we don't waste time filtering if igate not used.
}
else {
igate_send_rec_packet (chan, pp);
}
igate_send_rec_packet (chan, pp);
}
/* Send out a regenerated copy. Applies to all types, not just APRS. */
/* This was an experimental feature never documented in the User Guide. */
/* Initial feedback was positive but it fell by the wayside. */
/* Should follow up with testers and either document this or clean out the clutter. */
digi_regen (chan, pp);
/*
*Note that the digipeater function can modify the packet in place so
* Note that the digipeater function can modify the packet in place so
* this is the last thing we should do with it.
* Again, use only those with correct CRC; We don't want to spread corrupted data!
* Single bit change appears to be safe from observations so far but be cautious.
@ -1000,7 +1030,7 @@ static void usage (char **argv)
dw_printf ("\n");
dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
dw_printf ("\n");
dw_printf ("Usage: direwolf [options]\n");
dw_printf ("Usage: direwolf [options] [ - | stdin | UDP:nnnn ]\n");
dw_printf ("Options:\n");
dw_printf (" -c fname Configuration file name.\n");
dw_printf (" -l logdir Directory name for log files. Use . for current.\n");
@ -1024,14 +1054,20 @@ static void usage (char **argv)
dw_printf (" h h = Heard line with the audio level.\n");
dw_printf (" d d = Decoding of APRS packets.\n");
dw_printf (" -t n Text colors. 1=normal, 0=disabled.\n");
dw_printf (" -a n Audio statistics interval in seconds. 0 to disable.\n");
#if __WIN32__
#else
dw_printf (" -p Enable pseudo terminal for KISS protocol.\n");
#endif
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 ("\n");
dw_printf ("After any options, there can be a single command line argument for the source of\n");
dw_printf ("received audio. This can overrides the audio input specified in the configuration file.\n");
dw_printf ("\n");
#if __WIN32__
#else
dw_printf ("Complete documentation can be found in /usr/local/share/doc/direwolf.\n");

View File

@ -62,7 +62,10 @@
#if __WIN32__
#define PTW32_STATIC_LIB
#include "pthreads/pthread.h"
//#include "pthreads/pthread.h"
#define gmtime_r( _clock, _result ) \
( *(_result) = *gmtime( (_clock) ), \
(_result) )
#else
#include <pthread.h>
#endif
@ -153,9 +156,50 @@ typedef pthread_mutex_t dw_mutex_t;
} \
}
#endif
/* Platform differences for string functions. */
#if __WIN32__
char *strsep(char **stringp, const char *delim);
#endif
//#if __WIN32__
char *strcasestr(const char *S, const char *FIND);
//#endif
#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__APPLE__)
// strlcpy and strlcat should be in string.h and the C library.
#else // Use our own copy
#define DEBUG_STRL 1
#if DEBUG_STRL
#define strlcpy(dst,src,siz) strlcpy_debug(dst,src,siz,__FILE__,__func__,__LINE__)
#define strlcat(dst,src,siz) strlcat_debug(dst,src,siz,__FILE__,__func__,__LINE__)
size_t strlcpy_debug(char *__restrict__ dst, const char *__restrict__ src, size_t siz, const char *file, const char *func, int line);
size_t strlcat_debug(char *__restrict__ dst, const char *__restrict__ src, size_t siz, const char *file, const char *func, int line);
#else
#define strlcpy(dst,src,siz) strlcpy_debug(dst,src,siz)
#define strlcat(dst,src,siz) strlcat_debug(dst,src,siz)
size_t strlcpy_debug(char *__restrict__ dst, const char *__restrict__ src, size_t siz);
size_t strlcat_debug(char *__restrict__ dst, const char *__restrict__ src, size_t siz);
#endif /* DEBUG_STRL */
#endif /* BSD or Apple */
#endif /* ifndef DIREWOLF_H */

8
dlq.c
View File

@ -248,6 +248,14 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
dlq_init ();
}
assert (chan >= 0 && chan < MAX_CHANS);
if (pp == NULL) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("INTERNAL ERROR: dlq_append NULL packet pointer. Please report this!\n");
return;
}
#if AX25MEMDEBUG
if (ax25memdebug_get()) {

View File

@ -62,4 +62,10 @@ and a couple things that can be done about it.
receiver de-emphasis will
cause the amplitudes of the two tones to be different.
This makes it more difficult to demodulate them accurately.
9600 baud operation is an entirely different animal. ...
9600 baud operation is an entirely different animal. ...
- [WA8LMF TNC Test CD Results a.k.a. Battle of the TNCs](WA8LMF-TNC-Test-CD-Results.pdf)
How can we compare how well the TNCs perform under real world conditions?
The de facto standard of measurement is the number of packets decoded from [WA8LMFs TNC Test CD](http://wa8lmf.net/TNCtest/index.htm).
Many have published the number of packets they have been able to decode from this test. Here they are, all gathered in one place, for your reading pleasure.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -13,6 +13,10 @@
#include <time.h>
#ifdef __APPLE__
#include <sys/time.h>
#endif
#if __WIN32__
#include <windows.h>
#endif
@ -37,7 +41,14 @@ double dtime_now (void)
struct timespec ts;
#ifdef __APPLE__
struct timeval tp;
gettimeofday(&tp, NULL);
ts.tv_nsec = tp.tv_usec * 1000;
ts.tv_sec = tp.tv_sec;
#else
clock_gettime (CLOCK_REALTIME, &ts);
#endif
result = ((double)(ts.tv_sec) + (double)(ts.tv_nsec) * 0.000000001);

8
dtmf.c
View File

@ -5,7 +5,7 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2013, 2014 John Langner, WB2OSZ
// Copyright (C) 2013, 2014, 2015 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
@ -42,6 +42,7 @@
#include "direwolf.h"
#include "dtmf.h"
#include "hdlc_rec.h" // for dcd_change
@ -269,6 +270,11 @@ char dtmf_sample (int c, float input)
if (decoded == D->prev_dec) {
D->debounced = decoded;
// Update Data Carrier Detect Indicator.
dcd_change (c, MAX_SUBCHANS, decoded != ' ');
/* Reset timeout timer. */
if (decoded != ' ') {
D->timeout = ((TIMEOUT_SEC) * D->sample_rate) / D->block_size;

View File

@ -24,7 +24,7 @@ sleep 30
# Nothing to do if it is already running.
#
a=`ps -ef | grep direwolf | grep -v grep`
a=`pgrep direwolf`
if [ "$a" != "" ]
then
#date >> /tmp/dw-start.log
@ -62,13 +62,13 @@ echo "Start up application." >> /tmp/dw-start.log
if [ -x /usr/bin/lxterminal ]
then
/usr/bin/lxterminal -t "Dire Wolf" -e "/usr/local/bin/direwolf" &
/usr/bin/lxterminal -t "Dire Wolf" -e "/usr/local/bin/direwolf -a 100" &
elif [ -x /usr/bin/xterm ]
then
/usr/bin/xterm -bg white -fg black -e /usr/local/bin/direwolf &
/usr/bin/xterm -bg white -fg black -e "/usr/local/bin/direwolf -a 100" &
elif [ -x /usr/bin/x-terminal-emulator ]
then
/usr/bin/x-terminal-emulator -e /usr/local/bin/direwolf &
/usr/bin/x-terminal-emulator -e "/usr/local/bin/direwolf -a 100" &
else
echo "Did not find an X terminal emulator."
fi

View File

@ -1,3 +1,4 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
@ -342,7 +343,7 @@ static int cse_spd_data_extension (int course, int speed, char *presult)
x = course;
if (x < 0) x = 0;
if (x > 360) x = 360;
sprintf (stemp, "%03d", x);
snprintf (stemp, sizeof(stemp), "%03d", x);
memcpy (r->cse, stemp, 3);
r->slash = '/';
@ -350,7 +351,7 @@ static int cse_spd_data_extension (int course, int speed, char *presult)
x = speed;
if (x < 0) x = 0;
if (x > 999) x = 999;
sprintf (stemp, "%03d", x);
snprintf (stemp, sizeof(stemp), "%03d", x);
memcpy (r->spd, stemp, 3);
return (sizeof(cs_t));
@ -403,10 +404,11 @@ static int frequency_spec (float freq, float tone, float offset, char *presult)
if (freq != 0) {
freq_t *f = (freq_t*)presult;
char stemp[12];
char stemp[12]; /* Frequency should be exactly 7 characters: 999.999 */
/* Offset shouldbe exactly 4 characters: +999 */
/* Should use letters for > 999.999. */
sprintf (stemp, "%07.3f", freq);
/* TODO: Should use letters for > 999.999. */
snprintf (stemp, sizeof(stemp), "%07.3f", freq);
memcpy (f->f, stemp, 7);
memcpy (f->mhz, "MHz", 3);
f->space = ' ';
@ -422,11 +424,11 @@ static int frequency_spec (float freq, float tone, float offset, char *presult)
memcpy(to->ttt, "off", 3);
}
else {
sprintf (stemp, "%03d", (int)tone);
snprintf (stemp, sizeof(stemp), "%03d", (int)tone);
memcpy (to->ttt, stemp, 3);
}
to->space1 = ' ';
sprintf (stemp, "%+04d", (int)round(offset * 100));
snprintf (stemp, sizeof(stemp), "%+04d", (int)round(offset * 100));
memcpy (to->oooo, stemp, 4);
to->space2 = ' ';
@ -466,8 +468,12 @@ static int frequency_spec (float freq, float tone, float offset, char *presult)
*
* comment - Additional comment text.
*
* result_size - Ammount of space for result, provideed by
* caller, to avoid buffer overflow.
*
* Outputs: presult - Stored here. Should be at least ??? bytes.
* Could get into hundreds of characters
* because it includes the comment.
*
* Returns: Number of characters in result.
*
@ -503,7 +509,7 @@ int encode_position (int messaging, int compressed, double lat, double lon, int
int course, int speed,
float freq, float tone, float offset,
char *comment,
char *presult)
char *presult, size_t result_size)
{
int result_len = 0;
@ -551,18 +557,23 @@ int encode_position (int messaging, int compressed, double lat, double lon, int
/* Be sure it will be converted to 6 digits. */
if (alt_ft < 0) alt_ft = 0;
if (alt_ft > 999999) alt_ft = 999999;
sprintf (salt, "/A=%06d", alt_ft);
strcat (presult, salt);
snprintf (salt, sizeof(salt), "/A=%06d", alt_ft);
strlcat (presult, salt, result_size);
result_len += strlen(salt);
}
/* Finally, comment text. */
if (comment != NULL) {
strcat (presult, comment);
strlcat (presult, comment, result_size);
result_len += strlen(comment);
}
if (result_len >= result_size) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("encode_position result of %d characters won't fit into space provided.\n", result_len);
}
return (result_len);
} /* end encode_position */
@ -596,7 +607,14 @@ int encode_position (int messaging, int compressed, double lat, double lon, int
*
* comment - Additional comment text.
*
* result_size - Ammount of space for result, provideed by
* caller, to avoid buffer overflow.
*
* Outputs: presult - Stored here. Should be at least ??? bytes.
* 36 for fixed part,
* 7 for optional extended data,
* ~20 for freq, etc.,
* comment ...
*
* Returns: Number of characters in result.
*
@ -622,7 +640,7 @@ int encode_object (char *name, int compressed, time_t thyme, double lat, double
int power, int height, int gain, char *dir,
int course, int speed,
float freq, float tone, float offset, char *comment,
char *presult)
char *presult, size_t result_size)
{
aprs_object_t *p = (aprs_object_t *) presult;
int result_len = 0;
@ -651,7 +669,7 @@ int encode_object (char *name, int compressed, time_t thyme, double lat, double
localtime_r (thyme, &tm);
#endif
sprintf (p->o.time_stamp, "%02d%02d%02d", tm.tm_mday, tm.tm_hour, tm.tm_min);
snprintf (p->o.time_stamp, sizeof(p->o.time_stamp), "%02d%02d%02d", tm.tm_mday, tm.tm_hour, tm.tm_min);
#if XMIT_UTC
p->o.time_stamp[6] = 'z';
#else
@ -695,10 +713,15 @@ int encode_object (char *name, int compressed, time_t thyme, double lat, double
/* Finally, comment text. */
if (comment != NULL) {
strcat (presult, comment);
strlcat (presult, comment, result_size);
result_len += strlen(comment);
}
if (result_len >= result_size) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("encode_object result of %d characters won't fit into space provided.\n", result_len);
}
return (result_len);
} /* end encode_object */
@ -729,42 +752,42 @@ int main (int argc, char *argv[])
/*********** Position ***********/
encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result);
0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result, sizeof(result));
dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
/* with PHG. */
encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
50, 100, 6, "N", 0, 0, 0, 0, 0, NULL, result);
50, 100, 6, "N", 0, 0, 0, 0, 0, NULL, result, sizeof(result));
dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&PHG7368") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
/* with freq. */
encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 0, 0, 146.955, 74.4, -0.6, NULL, result);
0, 0, 0, NULL, 0, 0, 146.955, 74.4, -0.6, NULL, result, sizeof(result));
dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&146.955MHz T074 -060 ") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
/* with course/speed, freq, and comment! */
encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 180, 55, 146.955, 74.4, -0.6, "River flooding", result);
0, 0, 0, NULL, 180, 55, 146.955, 74.4, -0.6, "River flooding", result, sizeof(result));
dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz T074 -060 River flooding") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
/* Course speed, no tone, + offset */
encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 180, 55, 146.955, 0, 0.6, "River flooding", result);
0, 0, 0, NULL, 180, 55, 146.955, 0, 0.6, "River flooding", result, sizeof(result));
dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz Toff +060 River flooding") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
/* Course speed, no tone, + offset + altitude */
encode_position (0, 42+34.61/60, -(71+26.47/60), 12345, 'D', '&',
0, 0, 0, NULL, 180, 55, 146.955, 0, 0.6, "River flooding", result);
0, 0, 0, NULL, 180, 55, 146.955, 0, 0.6, "River flooding", result, sizeof(result));
dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz Toff +060 /A=012345River flooding") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
@ -772,7 +795,7 @@ int main (int argc, char *argv[])
/*********** Compressed position. ***********/
encode_position (1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result);
0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result, sizeof(result));
dw_printf ("%s\n", result);
if (strcmp(result, "!D8yKC<Hn[& !") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
@ -780,14 +803,14 @@ int main (int argc, char *argv[])
/* with PHG. In this case it is converted to precomputed radio range. TODO: check on this. Is 27.4 correct? */
encode_position (1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
50, 100, 6, "N", 0, 0, 0, 0, 0, NULL, result);
50, 100, 6, "N", 0, 0, 0, 0, 0, NULL, result, sizeof(result));
dw_printf ("%s\n", result);
if (strcmp(result, "!D8yKC<Hn[&{CG") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
/* with course/speed, freq, and comment! TODO: check on this 55 knots should be 63 MPH. we get 62. */
encode_position (1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 180, 55, 146.955, 74.4, -0.6, "River flooding", result);
0, 0, 0, NULL, 180, 55, 146.955, 74.4, -0.6, "River flooding", result, sizeof(result));
dw_printf ("%s\n", result);
if (strcmp(result, "!D8yKC<Hn[& !146.955MHz T074 -060 River flooding") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
@ -803,7 +826,7 @@ int main (int argc, char *argv[])
/*********** Object. ***********/
encode_object ("WB1GOF-C", 0, 0, 42+34.61/60, -(71+26.47/60), 'D', '&',
0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result);
0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result, sizeof(result));
dw_printf ("%s\n", result);
if (strcmp(result, ";WB1GOF-C *111111z4234.61ND07126.47W&") != 0) dw_printf ("ERROR! line %d\n", __LINE__);

View File

@ -5,12 +5,12 @@ int encode_position (int messaging, int compressed, double lat, double lon, int
int course, int speed,
float freq, float tone, float offset,
char *comment,
char *presult);
char *presult, size_t result_size);
int encode_object (char *name, int compressed, time_t thyme, double lat, double lon,
char symtab, char symbol,
int power, int height, int gain, char *dir,
int course, int speed,
float freq, float tone, float offset, char *comment,
char *presult);
char *presult, size_t result_size);

View File

@ -73,6 +73,7 @@
#include "hdlc_send.h"
#include "gen_tone.h"
#include "textcolor.h"
#include "morse.h"
static void usage (char **argv);
@ -81,6 +82,8 @@ static int audio_file_close (void);
static int g_add_noise = 0;
static float g_noise_level = 0;
static int g_morse_wpm = 0; /* Send morse code at this speed. */
static struct audio_s modem;
@ -92,15 +95,22 @@ static void send_packet (char *str)
int flen;
int c;
pp = ax25_from_text (str, 1);
flen = ax25_pack (pp, fbuf);
for (c=0; c<modem.adev[0].num_channels; c++)
{
hdlc_send_flags (c, 8, 0);
hdlc_send_frame (c, fbuf, flen);
hdlc_send_flags (c, 2, 1);
if (g_morse_wpm > 0) {
morse_send (0, str, g_morse_wpm, 100, 100);
}
else {
pp = ax25_from_text (str, 1);
flen = ax25_pack (pp, fbuf);
for (c=0; c<modem.adev[0].num_channels; c++)
{
hdlc_send_flags (c, 8, 0);
hdlc_send_frame (c, fbuf, flen);
hdlc_send_flags (c, 2, 1);
}
ax25_delete (pp);
}
ax25_delete (pp);
}
@ -164,7 +174,7 @@ int main(int argc, char **argv)
/* ':' following option character means arg is required. */
c = getopt_long(argc, argv, "gm:s:a:b:B:r:n:o:z:82",
c = getopt_long(argc, argv, "gm:s:a:b:B:r:n:o:z:82M:",
long_options, &option_index);
if (c == -1)
break;
@ -328,6 +338,20 @@ int main(int argc, char **argv)
dw_printf ("Output file set to %s\n", output_file);
break;
case 'M': /* -M for morse code speed */
//TODO: document this.
g_morse_wpm = atoi(optarg);
text_color_set(DW_COLOR_INFO);
dw_printf ("Morse code speed set to %d WPM.\n", g_morse_wpm);
if (g_morse_wpm < 5 || g_morse_wpm > 50) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Morse code speed must be in range of 5 to 50 WPM.\n");
exit (EXIT_FAILURE);
}
break;
case '?':
/* Unknown option message was already printed. */
@ -365,12 +389,19 @@ int main(int argc, char **argv)
}
gen_tone_init (&modem, amplitude/2);
morse_init (&modem, amplitude/2);
assert (modem.adev[0].bits_per_sample == 8 || modem.adev[0].bits_per_sample == 16);
assert (modem.adev[0].num_channels == 1 || modem.adev[0].num_channels == 2);
assert (modem.adev[0].samples_per_sec >= MIN_SAMPLES_PER_SEC && modem.adev[0].samples_per_sec <= MAX_SAMPLES_PER_SEC);
/*
* Get user packets(s) from file or stdin if specified.
* "-n" option is ignored in this case.

View File

@ -288,7 +288,6 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp)
*
*--------------------------------------------------------------------*/
static void put_sample (int chan, int a, int sam);
void tone_gen_put_bit (int chan, int dat)
{
@ -319,7 +318,7 @@ void tone_gen_put_bit (int chan, int dat)
tone_phase[chan] += dat ? f2_change_per_sample[chan] : f1_change_per_sample[chan];
sam = sine_table[(tone_phase[chan] >> 24) & 0xff];
put_sample (chan, a, sam);
gen_tone_put_sample (chan, a, sam);
}
else {
@ -335,7 +334,7 @@ void tone_gen_put_bit (int chan, int dat)
sam = (int) convolve (raw[chan], lp_filter[chan], lp_filter_size[chan]);
resample[chan] = 0;
put_sample (chan, a, sam);
gen_tone_put_sample (chan, a, sam);
}
}
@ -349,7 +348,7 @@ void tone_gen_put_bit (int chan, int dat)
}
static void put_sample (int chan, int a, int sam) {
void gen_tone_put_sample (int chan, int a, int sam) {
/* Ship out an audio sample. */

View File

@ -14,3 +14,4 @@ int gen_tone_init (struct audio_s *pp, int amp);
void tone_gen_put_bit (int chan, int dat);
void gen_tone_put_sample (int chan, int a, int sam);

572
generic.conf Normal file
View File

@ -0,0 +1,572 @@
C#############################################################
C# #
C# Configuration file for Dire Wolf #
C# #
L# Linux version #
W# Windows version #
M# Macintosh version #
C# #
C#############################################################
R
R
R The sample config file was getting pretty messy
R with the Windows and Linux differences.
R It would be a maintenance burden to keep most of
R two different versions in sync.
R This common source is now used to generate the
R two different variations while having only a single
R copy of the common parts.
R
R The first column contains one of the following:
R
R R remark which is discarded.
R C common to both versions.
R W Windows version only.
R L Linux version only.
R M Macintosh version and possibly others (portaudio used).
R
C#
C# Consult the User Guide for more details on configuration options.
C#
C#
C# These are the most likely settings you might change:
C#
C# (1) MYCALL - call sign and SSID for your station.
C#
C# Look for lines starting with MYCALL and
C# change NOCALL to your own.
C#
C# (2) PBEACON - enable position beaconing.
C#
C# Look for lines starting with PBEACON and
C# modify for your call, location, etc.
C#
C# (3) DIGIPEATER - configure digipeating rules.
C#
C# Look for lines starting with DIGIPEATER.
C# Most people will probably use the given example.
C# Just remove the "#" from the start of the line
C# to enable it.
C#
C# (4) IGSERVER, IGLOGIN - IGate server and login
C#
C# Configure an IGate client to relay messages between
C# radio and internet servers.
C#
C#
C# The default location is "direwolf.conf" in the current working directory.
L# On Linux, the user's home directory will also be searched.
C# An alternate configuration file location can be specified with the "-c" command line option.
C#
C# As you probably guessed by now, # indicates a comment line.
C#
C# Remove the # at the beginning of a line if you want to use a sample
C# configuration that is currently commented out.
C#
C# Commands are a keyword followed by parameters.
C#
C# Command key words are case insensitive. i.e. upper and lower case are equivalent.
C#
C# Command parameters are generally case sensitive. i.e. upper and lower case are different.
C#
C
C
C#############################################################
C# #
C# FIRST AUDIO DEVICE PROPERTIES #
C# (Channel 0 + 1 if in stereo) #
C# #
C#############################################################
C
C#
C# Many people will simply use the default sound device.
C# Some might want to use an alternative device by chosing it here.
C#
W# When the Windows version starts up, it displays something like
W# this with the available sound devices and capabilities:
W#
W# Available audio input devices for receive (*=selected):
W# * 0: Microphone (C-Media USB Headpho (channel 2)
W# 1: Microphone (Bluetooth SCO Audio
W# 2: Microphone (Bluetooth AV Audio)
W# * 3: Microphone (Realtek High Defini (channels 0 & 1)
W# Available audio output devices for transmit (*=selected):
W# * 0: Speakers (C-Media USB Headphone (channel 2)
W# 1: Speakers (Bluetooth SCO Audio)
W# 2: Realtek Digital Output(Optical)
W# 3: Speakers (Bluetooth AV Audio)
W# * 4: Speakers (Realtek High Definiti (channels 0 & 1)
W# 5: Realtek Digital Output (Realtek
W#
W# Example: To use the microphone and speaker connections on the
W# system board, either of these forms can be used:
W
W#ADEVICE High
W#ADEVICE 3 4
W
W
W# Example: To use the USB Audio, use a command like this with
W# the input and output device numbers. (Remove the # comment character.)
W#ADEVICE USB
W
W# The position in the list can change when devices (e.g. USB) are added and removed.
W# You can also specify devices by using part of the name.
W# Here is an example of specifying the USB Audio device.
W# This is case-sensitive. Upper and lower case are not treated the same.
W
W#ADEVICE USB
W
W
L# Linux ALSA is complicated. See User Guide for discussion.
L# To use something other than the default, generally use plughw
L# and a card number reported by "arecord -l" command. Example:
L
L# ADEVICE plughw:1,0
L
L# Starting with version 1.0, you can also use "-" or "stdin" to
L# pipe stdout from some other application such as a software defined
L# radio. You can also specify "UDP:" and an optional port for input.
L# Something different must be specified for output.
L
M# Macintosh Operating System uses portaudio driver for audio
M# input/output. Default device selection not available. User/OP
M# must configure the sound input/output option. Note that
M# the device names can contain spaces. In this case, the names
M# must be enclosed by quotes.
M#
M# Examples:
M#
M# ADEVICE "USB Audio Codec:6" "USB Audio Codec:5"
M#
M#
W# ADEVICE - 0
W# ADEVICE UDP:7355 0
L# ADEVICE - plughw:1,0
L# ADEVICE UDP:7355 default
M# ADEVICE UDP:7355 default
M#
L
L
C
C#
C# Number of audio channels for this souncard: 1 or 2.
C#
C
CACHANNELS 1
C#ACHANNELS 2
C
C
C#############################################################
C# #
C# SECOND AUDIO DEVICE PROPERTIES #
C# (Channel 2 + 3 if in stereo) #
C# #
C#############################################################
C
C#ADEVICE1 ...
C
C
C#############################################################
C# #
C# THIRD AUDIO DEVICE PROPERTIES #
C# (Channel 4 + 5 if in stereo) #
C# #
C#############################################################
C
C#ADEVICE2 ...
C
C
C#############################################################
C# #
C# CHANNEL 0 PROPERTIES #
C# #
C#############################################################
C
CCHANNEL 0
C
C#
C# The following MYCALL, MODEM, PTT, etc. configuration items
C# apply to the most recent CHANNEL.
C#
C
C#
C# Station identifier for this channel.
C# Multiple channels can have the same or different names.
C#
C# It can be up to 6 letters and digits with an optional ssid.
C# The APRS specification requires that it be upper case.
C#
C# Example (don't use this unless you are me): MYCALL WB2OSZ-5
C#
C
CMYCALL N0CALL
C
C#
C# Pick a suitable modem speed based on your situation.
C# 1200 Most common for VHF/UHF. Default if not specified.
C# 300 Low speed for HF SSB.
C# 9600 High speed - Can't use Microphone and Speaker connections.
C#
C# In the simplest form, just specify the speed.
C#
C
CMODEM 1200
C#MODEM 300
C#MODEM 9600
C
C#
C# These are the defaults should be fine for most cases. In special situations,
C# you might want to specify different AFSK tones or the baseband mode which does
C# not use AFSK.
C#
C#MODEM 1200 1200:2200
C#MODEM 300 1600:1800
C#MODEM 9600 0:0
C#
C#
C# On HF SSB, you might want to use multiple demodulators on slightly different
C# frequencies to compensate for stations off frequency. Here we have 7 different
C# demodulators at 30 Hz intervals. This takes a lot of CPU power so you will
C# probably need to reduce the audio sampling rate with the /n option.
C
C#MODEM 300 1600:1800 7@30 /4
C
C
C#
C# Uncomment line below to enable the DTMF decoder for this channel.
C#
C
C#DTMF
C
C#
C# If not using a VOX circuit, the transmitter Push to Talk (PTT)
C# control is usually wired to a serial port with a suitable interface circuit.
C# DON'T connect it directly!
C#
C# For the PTT command, specify the device and either RTS or DTR.
C# RTS or DTR may be preceded by "-" to invert the signal.
C# Both can be used for interfaces that want them driven with opposite polarity.
C#
L# COM1 can be used instead of /dev/ttyS0, COM2 for /dev/ttyS1, and so on.
L#
C
C#PTT COM1 RTS
C#PTT COM1 RTS -DTR
L#PTT /dev/ttyUSB0 RTS
C
L#
L# On Linux, you can also use general purpose I/O pins if
L# your system is configured for user access to them.
L# This would apply mostly to microprocessor boards, not a regular PC.
L# See separate Raspberry Pi document for more details.
L# The number may be preceded by "-" to invert the signal.
L#
L
L#PTT GPIO 25
L
C# The Data Carrier Detect (DCD) signal can be sent to the same places
C# as the PTT signal. This could be used to light up an LED like a normal TNC.
C
C#DCD COM1 -DTR
L#DCD GPIO 24
C
C
C#############################################################
C# #
C# CHANNEL 1 PROPERTIES #
C# #
C#############################################################
C
C#CHANNEL 1
C
C#
C# Specify MYCALL, MODEM, PTT, etc. configuration items for
C# CHANNEL 1. Repeat for any other channels.
C
C
C#############################################################
C# #
C# TEXT TO SPEECH COMMAND FILE #
C# #
C#############################################################
C
W#SPEECH dwespeak.bat
L#SPEECH dwespeak.sh
C
C
C#############################################################
C# #
C# VIRTUAL TNC SERVER PROPERTIES #
C# #
C#############################################################
C
C#
C# Dire Wolf acts as a virtual TNC and can communicate with
C# client applications by different protocols:
C#
C# - the "AGW TCPIP Socket Interface" - default port 8000
C# - KISS protocol over TCP socket - default port 8001
W# - KISS TNC via serial port
L# - KISS TNC via pseudo terminal (-p command line option)
C#
C
CAGWPORT 8000
CKISSPORT 8001
C
W#
W# Some applications are designed to operate with only a physical
W# TNC attached to a serial port. For these, we provide a virtual serial
W# port that appears to be connected to a TNC.
W#
W# Take a look at the User Guide for instructions to set up
W# two virtual serial ports named COM3 and COM4 connected by
W# a null modem.
W#
W# Using the configuration described, Dire Wolf will connect to
W# COM3 and the client application will use COM4.
W#
W# Uncomment following line to use this feature.
W
W#NULLMODEM COM3
W
W
C#
C# It is sometimes possible to recover frames with a bad FCS.
C# This applies to all channels.
C#
C# 0 [NONE] - Don't try to repair.
C# 1 [SINGLE] - Attempt to fix single bit error. (default)
C# 2 [DOUBLE] - Also attempt to fix two adjacent bits.
C# ... see User Guide for more values and in-depth discussion.
C#
C
C#FIX_BITS 0
C
C#
C#############################################################
C# #
C# BEACONING PROPERTIES #
C# #
C#############################################################
C
C
C#
C# Beaconing is configured with these two commands:
C#
C# PBEACON - for a position report (usually yourself)
C# OBEACON - for an object report (usually some other entity)
C#
C# Each has a series of keywords and values for options.
C# See User Guide for details.
C#
C# Example:
C#
C# This results in a broadcast once every 10 minutes.
C# Every half hour, it can travel via two digipeater hops.
C# The others are kept local.
C#
C
C#PBEACON delay=1 every=30 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W power=50 height=20 gain=4 comment="Chelmsford MA" via=WIDE1-1,WIDE2-1
C#PBEACON delay=11 every=30 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W power=50 height=20 gain=4 comment="Chelmsford MA"
C#PBEACON delay=21 every=30 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W power=50 height=20 gain=4 comment="Chelmsford MA"
C
C
C# With UTM coordinates instead of latitude and longitude.
C
C#PBEACON delay=1 every=10 overlay=S symbol="digi" zone=19T easting=307477 northing=4720178
C
C
C#
C# When the destination field is set to "SPEECH" the information part is
C# converted to speech rather than transmitted as a data frame.
C#
C
C#CBEACON dest="SPEECH" info="Club meeting tonight at 7 pm."
C
C# Similar for Morse code. If SSID is specified, it is multiplied
C# by 2 to get speed in words per minute (WPM).
C
C#CBEACON dest="MORSE-6" info="de MYCALL"
C
C
C#
C# Modify for your particular situation before removing
C# the # comment character from the beginning of appropriate lines above.
C#
C
C
C#############################################################
C# #
C# DIGIPEATER PROPERTIES #
C# #
C#############################################################
C
C#
C# For most common situations, use something like this by removing
C# the "#" from the beginning of the line below.
C#
C
C#DIGIPEAT 0 0 ^WIDE[3-7]-[1-7]$|^TEST$ ^WIDE[12]-[12]$ TRACE
C
C# See User Guide for more explanation of what this means and how
C# it can be customized for your particular needs.
C
C# Filtering can be used to limit was is digipeated.
C# For example, only weather weather reports, received on channel 0,
C# will be retransmitted on channel 1.
C#
C
C#FILTER 0 1 t/wn
C
C
C#############################################################
C# #
C# INTERNET GATEWAY #
C# #
C#############################################################
C
C# First you need to specify the name of a Tier 2 server.
C# The current preferred way is to use one of these regional rotate addresses:
C
C# noam.aprs2.net - for North America
C# soam.aprs2.net - for South America
C# euro.aprs2.net - for Europe and Africa
C# asia.aprs2.net - for Asia
C# aunz.aprs2.net - for Oceania
C
C#IGSERVER noam.aprs2.net
C
C# You also need to specify your login name and passcode.
C# Contact the author if you can't figure out how to generate the passcode.
C
C#IGLOGIN WB2OSZ-5 123456
C
C# That's all you need for a receive only IGate which relays
C# messages from the local radio channel to the global servers.
C
C# Some might want to send an IGate client position directly to a server
C# without sending it over the air and relying on someone else to
C# forward it to an IGate server. This is done by using sendto=IG rather
C# than a radio channel number. Overlay R for receive only, T for two way.
C
C#PBEACON sendto=IG delay=0:30 every=60:00 symbol="igate" overlay=R lat=42^37.14N long=071^20.83W
C#PBEACON sendto=IG delay=0:30 every=60:00 symbol="igate" overlay=T lat=42^37.14N long=071^20.83W
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
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# 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
C#FILTER IG 0 t/m
C#FILTER IG 1 t/wn
C
C# Finally, we don't want to flood the radio channel.
C# The IGate function will limit the number of packets transmitted
C# during 1 minute and 5 minute intervals. If a limit would
C# be exceeded, the packet is dropped and message is displayed in red.
C
CIGTXLIMIT 6 10
C
C
C#############################################################
C# #
C# APRStt GATEWAY #
C# #
C#############################################################
C
C#
C# Dire Wolf can receive DTMF (commonly known as Touch Tone)
C# messages and convert them to packet objects.
C#
C# See separate "APRStt-Implementation-Notes" document for details.
C#
C
C#
C# Sample gateway configuration based on:
C#
C# http://www.aprs.org/aprstt/aprstt-coding24.txt
C# http://www.aprs.org/aprs-jamboree-2013.html
C#
C
C# Define specific points.
C
CTTPOINT B01 37^55.37N 81^7.86W
CTTPOINT B7495088 42.605237 -71.34456
CTTPOINT B934 42.605237 -71.34456
C
CTTPOINT B901 42.661279 -71.364452
CTTPOINT B902 42.660411 -71.364419
CTTPOINT B903 42.659046 -71.364452
CTTPOINT B904 42.657578 -71.364602
C
C
C# For location at given bearing and distance from starting point.
C
CTTVECTOR B5bbbddd 37^55.37N 81^7.86W 0.01 mi
C
C# For location specified by x, y coordinates.
C
CTTGRID Byyyxxx 37^50.00N 81^00.00W 37^59.99N 81^09.99W
C
C# UTM location for Lowell-Dracut-Tyngsborough State Forest.
C
CTTUTM B6xxxyyy 19T 10 300000 4720000
C
C
C
C# Location for the corral.
C
CTTCORRAL 37^55.50N 81^7.00W 0^0.02N
C
C# Compact messages - Fixed locations xx and object yyy where
C# Object numbers 100 - 199 = bicycle
C# Object numbers 200 - 299 = fire truck
C# Others = dog
C
CTTMACRO xx1yy B9xx*AB166*AA2B4C5B3B0A1yy
CTTMACRO xx2yy B9xx*AB170*AA3C4C7C3B0A2yy
CTTMACRO xxyyy B9xx*AB180*AA3A6C4A0Ayyy
C
CTTMACRO z Cz
C
C# Receive on channel 0, Transmit object reports on channel 1 with optional via path.
C# You probably want to put in a transmit delay on the APRStt channel so it
C# it doesn't start sending a response before the user releases PTT.
C# This is in 10 ms units so 100 means 1000 ms = 1 second.
C
C#TTOBJ 0 1 WIDE1-1
C#CHANNEL 0
C#DWAIT 100
C
C# Advertise gateway position with beacon.
C
C# OBEACON DELAY=0:15 EVERY=10:00 VIA=WIDE1-1 OBJNAME=WB2OSZ-tt SYMBOL=APRStt LAT=42^37.14N LONG=71^20.83W COMMENT="APRStt Gateway"
C
C
C# Sample speech responses.
C# Default is Morse code "R" for received OK and "?" for all errors.
C
C#TTERR OK SPEECH Message Received.
C#TTERR D_MSG SPEECH D not implemented.
C#TTERR INTERNAL SPEECH Internal error.
C#TTERR MACRO_NOMATCH SPEECH No definition for digit sequence.
C#TTERR BAD_CHECKSUM SPEECH Bad checksum on call.
C#TTERR INVALID_CALL SPEECH Invalid callsign.
C#TTERR INVALID_OBJNAME SPEECH Invalid object name.
C#TTERR INVALID_SYMBOL SPEECH Invalid symbol.
C#TTERR INVALID_LOC SPEECH Invalid location.
C#TTERR NO_CALL SPEECH No call or object name.
C#TTERR SATSQ SPEECH Satellite square must be 4 digits.
C

View File

@ -87,7 +87,7 @@ sym_user_exit = 177, /* user exit symbol */
sym_flag = 178, /* flag symbol */
sym_circle_x = 179, /* circle with x in the center */
sym_open_24hr = 180, /* open 24 hours symbol */
sym_fhs_facility = 181, /* U Fishing Hot Spots Facility */
sym_fhs_facility = 181, /* U Fishing Hot Spots(tm) Facility */
sym_bot_cond = 182, /* Bottom Conditions */
sym_tide_pred_stn = 183, /* Tide/Current Prediction Station */
sym_anchor_prohib = 184, /* U anchor prohibited symbol */

View File

@ -112,7 +112,6 @@ static int num_subchan[MAX_CHANS]; //TODO1.2 use ptr rather than copy.
static int composite_dcd[MAX_CHANS];
static void dcd_change (int chan, int subchan, int state);
/***********************************************************************************
@ -558,7 +557,10 @@ int hdlc_rec_gathering (int chan, int subchan)
* state for the channel.
*
* Inputs: chan
* subchan
*
* subchan 0 to MAX_SUBCHANS-1 for HDLC.
* MAX_SUBCHANS for DTMF decoder.
*
* state 1 for active, 0 for not.
*
* Returns: None. Use ??? to retrieve result.
@ -566,17 +568,18 @@ int hdlc_rec_gathering (int chan, int subchan)
* Description: DCD for the channel is active if ANY of the subchannels
* is active. Update the DCD indicator.
*
* Future: Roll DTMF into the final result.
* version 1.3: Add DTMF detection into the final result.
* This is now called from dtmf.c too.
*
*--------------------------------------------------------------------*/
static void dcd_change (int chan, int subchan, int state)
void dcd_change (int chan, int subchan, int state)
{
int old, new;
assert (chan >= 0 && chan < MAX_CHANS);
assert (subchan >= 0 && subchan < MAX_SUBCHANS);
assert (subchan >= 0 && subchan <= MAX_SUBCHANS);
assert (state == 0 || state == 1);
#if DEBUG3

View File

@ -2,8 +2,6 @@
#include "audio.h"
//#include "rrbb.h"
void hdlc_rec_init (struct audio_s *pa);
@ -11,7 +9,6 @@ void hdlc_rec_init (struct audio_s *pa);
void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int descram_state);
/* Provided elsewhere to process a complete frame. */
//void process_rec_frame (int chan, unsigned char *fbuf, int flen, int level);
@ -26,5 +23,6 @@ int hdlc_rec_gathering (int chan, int subchan);
/* Transmit needs to know when someone else is transmitting. */
void dcd_change (int chan, int subchan, int state);
int hdlc_rec_data_detect_any (int chan);

126
igate.c
View File

@ -85,9 +85,8 @@
#include <unistd.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include "direwolf.h"
#include "ax25_pad.h"
@ -153,7 +152,7 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S
case AF_INET:
sa4 = (struct sockaddr_in *)pAddr;
#if __WIN32__
sprintf (pStringBuf, "%d.%d.%d.%d", sa4->sin_addr.S_un.S_un_b.s_b1,
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);
@ -164,7 +163,7 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S
case AF_INET6:
sa6 = (struct sockaddr_in6 *)pAddr;
#if __WIN32__
sprintf (pStringBuf, "%x:%x:%x:%x:%x:%x:%x:%x",
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]),
@ -178,9 +177,9 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S
#endif
break;
default:
sprintf (pStringBuf, "Invalid address family!");
snprintf (pStringBuf, StringBufSize, "Invalid address family!");
}
assert (strlen(pStringBuf) < StringBufSize);
//assert (strlen(pStringBuf) < StringBufSize);
return pStringBuf;
}
@ -198,19 +197,19 @@ int main (int argc, char *argv[])
memset (&audio_config, 0, sizeof(audio_config));
audio_config.adev[0].num_chans = 2;
strcpy (audio_config.achan[0].mycall, "WB2OSZ-1");
strcpy (audio_config.achan[0].mycall, "WB2OSZ-2");
strlcpy (audio_config.achan[0].mycall, "WB2OSZ-1", sizeof(audio_config.achan[0].mycall));
strlcpy (audio_config.achan[1].mycall, "WB2OSZ-2", sizeof(audio_config.achan[0].mycall));
memset (&igate_config, 0, sizeof(igate_config));
strcpy (igate_config.t2_server_name, "localhost");
strlcpy (igate_config.t2_server_name, "localhost", sizeof(igate_config.t2_server_name));
igate_config.t2_server_port = 14580;
strcpy (igate_config.t2_login, "WB2OSZ-JL");
strcpy (igate_config.t2_passcode, "-1");
strlcpy (igate_config.t2_login, "WB2OSZ-JL", sizeof(igate_config.t2_login));
strlcpy (igate_config.t2_passcode, "-1", sizeof(igate_config.t2_passcode));
igate_config.t2_filter = strdup ("r/1/2/3");
igate_config.tx_chan = 0;
strcpy (igate_config.tx_via, ",WIDE2-1");
strlcpy (igate_config.tx_via, ",WIDE2-1", sizeof(igate_config.tx_via));
igate_config.tx_limit_1 = 3;
igate_config.tx_limit_5 = 5;
@ -314,6 +313,35 @@ static int stats_tx_igate_packets; /* Number of packets from IGate server. */
static int stats_rf_xmit_packets; /* Number of packets passed along to radio */
/* after rate limiting or other restrictions. */
/* We have some statistics. What do we do with them?
IGate stations often send packets like this:
<IGATE MSG_CNT=1238 LOC_CNT=0 FILL_CNT=0
<IGATE,MSG_CNT=1,LOC_CNT=25
<IGATE,MSG_CNT=0,LOC_CNT=46,DIR_CNT=13,RF_CNT=49,RFPORT_ID=0
What does it all mean?
Why do some have spaces instead of commas between the capabilities?
The APRS Protocol Reference ( http://www.aprs.org/doc/APRS101.PDF ),
section 15, briefly discusses station capabilities and gives the example
IGATE,MSG_CNT=n,LOC_CNT=n
IGate Design ( http://www.aprs-is.net/IGating.aspx ) barely mentions
<IGATE,MSG_CNT=n,LOC_CNT=n
This leaves many questions. Does "number of messages transmitted" mean only
the APRS "Message" (data type indicator ":") or does it mean any type of
APRS packet? What are "local" stations? Those we hear directly without
going thru a digipeater?
What are DIR_CNT, RF_CNT, and so on?
Are the counts since the system started up or are they for some interval?
*/
/*-------------------------------------------------------------------
@ -504,7 +532,7 @@ static void * connnect_thread (void *arg)
WSADATA wsadata;
#endif
sprintf (server_port_str, "%d", save_igate_config_p->t2_server_port);
snprintf (server_port_str, sizeof(server_port_str), "%d", save_igate_config_p->t2_server_port);
#if DEBUGx
text_color_set(DW_COLOR_DEBUG);
dw_printf ("DEBUG: igate connect_thread start, port = %d = '%s'\n", save_igate_config_p->t2_server_port, server_port_str);
@ -704,14 +732,14 @@ static void * connnect_thread (void *arg)
*/
SLEEP_SEC(3);
sprintf (stemp, "user %s pass %s vers Dire-Wolf %d.%d",
snprintf (stemp, sizeof(stemp), "user %s pass %s vers Dire-Wolf %d.%d",
save_igate_config_p->t2_login, save_igate_config_p->t2_passcode,
MAJOR_VERSION, MINOR_VERSION);
if (save_igate_config_p->t2_filter != NULL) {
strcat (stemp, " filter ");
strcat (stemp, save_igate_config_p->t2_filter);
strlcat (stemp, " filter ", sizeof(stemp));
strlcat (stemp, save_igate_config_p->t2_filter, sizeof(stemp));
}
strcat (stemp, "\r\n");
strlcat (stemp, "\r\n", sizeof(stemp));
send_msg_to_server (stemp);
/* Delay until it is ok to start sending packets. */
@ -739,7 +767,7 @@ static void * connnect_thread (void *arg)
char heartbeat[10];
strcpy (heartbeat, "#\r\n");
strlcpy (heartbeat, "#\r\n", sizeof(heartbeat));
/* This will close the socket if any error. */
send_msg_to_server (heartbeat);
@ -809,7 +837,8 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
}
/* Count only while connected. */
/* Gather statistics. */
stats_rf_recv_packets++;
/*
@ -817,6 +846,7 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
*/
pp = ax25_dup (recv_pp);
assert (pp != NULL);
/*
* Third party frames require special handling to unwrap payload.
@ -915,7 +945,7 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
/*
* Someone around here occasionally sends a packet with no information part.
*/
if (strlen(pinfo) == 0) {
if (strlen((char*)pinfo) == 0) {
#if DEBUGx
text_color_set(DW_COLOR_DEBUG);
@ -946,11 +976,11 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
ax25_format_addrs (pp, msg);
msg[strlen(msg)-1] = '\0'; /* Remove trailing ":" */
strcat (msg, ",qAR,");
strcat (msg, save_audio_config_p->achan[chan].mycall);
strcat (msg, ":");
strcat (msg, (char*)pinfo);
strcat (msg, "\r\n");
strlcat (msg, ",qAR,", sizeof(msg));
strlcat (msg, save_audio_config_p->achan[chan].mycall, sizeof(msg));
strlcat (msg, ":", sizeof(msg));
strlcat (msg, (char*)pinfo, sizeof(msg));
strlcat (msg, "\r\n", sizeof(msg));
send_msg_to_server (msg);
stats_rx_igate_packets++;
@ -1205,10 +1235,11 @@ static void * igate_recv_thread (void *arg)
static void xmit_packet (char *message)
{
packet_t pp3;
char payload[500]; /* what is max len? */
char payload[AX25_MAX_PACKET_LEN]; /* what is max len? */
char *pinfo = NULL;
int info_len;
int to_chan = save_igate_config_p->tx_chan; /* which could be -1 if not configured for xmit!!! */
int to_chan = save_igate_config_p->tx_chan; /* Should be -1 if not configured for xmit!!! */
/* Future: Array of boolean to allow multiple xmit channels? */
/*
* Is IGate to Radio direction enabled?
@ -1219,12 +1250,12 @@ static void xmit_packet (char *message)
stats_tx_igate_packets++;
assert (save_igate_config_p->tx_chan >= 0 && save_igate_config_p->tx_chan < MAX_CHANS);
assert (to_chan >= 0 && to_chan < MAX_CHANS);
/*
* Try to parse it into a packet object.
* Bug: Up to 8 digipeaters are allowed in radio format.
* There is a potential of finding more here.
* There is a potential of finding a larger number here.
*/
pp3 = ax25_from_text(message, 0);
if (pp3 == NULL) {
@ -1239,8 +1270,6 @@ static void xmit_packet (char *message)
* Apply our own packet filtering if configured.
*/
//TODO1.2: Should we allow IGating to RF with more than one channel?
assert (to_chan >= 0 && to_chan < MAX_CHANS);
if (save_digi_config_p->filter_str[MAX_CHANS][to_chan] != NULL) {
@ -1281,9 +1310,11 @@ static void xmit_packet (char *message)
/*
* Convert to text representation.
*/
memset (payload, 0, sizeof(payload));
ax25_format_addrs (pp3, payload);
info_len = ax25_get_info (pp3, (unsigned char **)(&pinfo));
strcat (payload, pinfo);
strlcat (payload, pinfo, sizeof(payload));
#if DEBUGx
text_color_set(DW_COLOR_DEBUG);
dw_printf ("Tx IGate: payload=%s\n", payload);
@ -1296,23 +1327,38 @@ static void xmit_packet (char *message)
char radio [500];
packet_t pradio;
sprintf (radio, "%s>%s%d%d%s:}%s",
snprintf (radio, sizeof(radio), "%s>%s%d%d%s:}%s",
save_audio_config_p->achan[save_igate_config_p->tx_chan].mycall,
APP_TOCALL, MAJOR_VERSION, MINOR_VERSION,
save_igate_config_p->tx_via,
payload);
pradio = ax25_from_text (radio, 1);
/* Oops. Didn't have a check for NULL here. */
/* Could this be the cause of rare and elusive crashes in 1.2? */
if (pradio != NULL) {
#if ITEST
text_color_set(DW_COLOR_XMIT);
dw_printf ("Xmit: %s\n", radio);
ax25_delete (pradio);
text_color_set(DW_COLOR_XMIT);
dw_printf ("Xmit: %s\n", radio);
ax25_delete (pradio);
#else
/* This consumes packet so don't reference it again! */
tq_append (save_igate_config_p->tx_chan, TQ_PRIO_1_LO, pradio);
/* This consumes packet so don't reference it again! */
tq_append (save_igate_config_p->tx_chan, TQ_PRIO_1_LO, pradio);
#endif
stats_rf_xmit_packets++;
ig_to_tx_remember (pp3);
stats_rf_xmit_packets++;
ig_to_tx_remember (pp3);
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Received invalid packet from IGate.\n");
dw_printf ("%s\n", payload);
dw_printf ("Will not attempt to transmit third party packet.\n");
dw_printf ("%s\n", radio);
}
}
ax25_delete (pp3);

4
kiss.c
View File

@ -84,7 +84,7 @@
* 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/
* "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!)
@ -955,7 +955,7 @@ static THREAD_F kiss_listen_thread (void *arg)
#if __WIN32__
return(0);
#else
return; /* Unreachable but avoids compiler warning. */
return (THREAD_F) 0; /* Unreachable but avoids compiler warning. */
#endif
}

View File

@ -131,6 +131,12 @@ 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.
*
* This seems cumbersome and confusing to have this
* one byte offset when encapsulating an AX.25 frame.
* Maybe the type/channel byte should be passed in
* as a separate argument.
*
* Note that this is "binary" data and can contain
* nul (0x00) values. Don't treat it like a text string!
*
@ -176,6 +182,7 @@ int kiss_encapsulate (unsigned char *in, int ilen, unsigned char *out)
} /* end kiss_encapsulate */
#ifndef WALK96
/*-------------------------------------------------------------------
*
@ -659,4 +666,6 @@ main ()
#endif
#endif /* WALK96 */
/* end kiss_frame.c */

View File

@ -382,6 +382,7 @@ static void * connect_listen_thread (void *arg)
socklen_t sockaddr_size = sizeof(struct sockaddr_in);
int kiss_port = (int)(long)arg;
int listen_sock;
int bcopt = 1;
listen_sock= socket(AF_INET,SOCK_STREAM,0);
if (listen_sock == -1) {
@ -390,6 +391,14 @@ static void * connect_listen_thread (void *arg)
return (NULL);
}
/* Version 1.3 - as suggested by G8BPQ. */
/* Without this, if you kill the application then try to run it */
/* again quickly the port number is unavailable for a while. */
/* Don't try doing the same thing On Windows; It has a different meaning. */
/* http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t */
setsockopt (listen_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&bcopt, 4);
sockaddr.sin_addr.s_addr = INADDR_ANY;
sockaddr.sin_port = htons(kiss_port);
sockaddr.sin_family = AF_INET;

View File

@ -559,7 +559,7 @@ double ll_distance_km (double lat1, double lon1, double lat2, double lon2)
*
* Purpose: Convert Maidenhead locator to latitude and longitude.
*
* Inputs: maidenhead - 4 or 6 character grid square.
* Inputs: maidenhead - 2, 4, 6, or 8 character grid square locator.
*
* Outputs: dlat, dlon - Latitude and longitude.
* Original values unchanged if error.
@ -591,9 +591,9 @@ int ll_from_grid_square (char *maidenhead, double *dlat, double *dlon)
char mh[16];
if (strlen(maidenhead) != 4 && strlen(maidenhead) != 6) {
if (strlen(maidenhead) != 2 && strlen(maidenhead) != 4 && strlen(maidenhead) != 6 && strlen(maidenhead) != 8) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Maidenhead locator \"%s\" must 4 or 6 characters in this context.\n", maidenhead);
dw_printf("Maidenhead locator \"%s\" must 2, 4, 6, or 8 characters.\n", maidenhead);
return (0);
}
@ -607,11 +607,6 @@ int ll_from_grid_square (char *maidenhead, double *dlat, double *dlon)
return (0);
}
if ( ! isdigit(mh[2]) || ! isdigit(mh[3]) ) {
text_color_set(DW_COLOR_ERROR);
dw_printf("The second pair of characters in Maidenhead locator \"%s\" must be digits.\n", maidenhead);
return (0);
}
/* Lon: 360 deg / 18 squares = 20 deg / square */
/* Lat: 180 deg / 18 squares = 10 deg / square */
@ -619,55 +614,68 @@ int ll_from_grid_square (char *maidenhead, double *dlat, double *dlon)
lon = (mh[0] - 'A') * 20 - 180;
lat = (mh[1] - 'A') * 10 - 90;
/* Lon: 20 deg / 10 squares = 2 deg / square */
/* Lat: 10 deg / 10 squares = 1 deg / square */
if (strlen(mh) >= 4) {
lon += (mh[2] - '0') * 2;
lat += (mh[3] - '0');
if (strlen(mh) >=6) {
if (islower(mh[4])) mh[4] = toupper(mh[4]);
if (islower(mh[5])) mh[5] = toupper(mh[5]);
if (mh[4] < 'A' || mh[4] > 'X' || mh[5] < 'A' || mh[5] > 'X') {
if ( ! isdigit(mh[2]) || ! isdigit(mh[3]) ) {
text_color_set(DW_COLOR_ERROR);
dw_printf("The third pair of characters in Maidenhead locator \"%s\" must be in range of A thru X.\n", maidenhead);
dw_printf("The second pair of characters in Maidenhead locator \"%s\" must be digits.\n", maidenhead);
return (0);
}
/* Lon: 2 deg / 24 squares = 5 minutes / square */
/* Lat: 1 deg / 24 squares = 2.5 minutes / square */
/* Lon: 20 deg / 10 squares = 2 deg / square */
/* Lat: 10 deg / 10 squares = 1 deg / square */
lon += (mh[4] - 'A') * 5.0 / 60.0;
lat += (mh[5] - 'A') * 2.5 / 60.0;
lon += (mh[2] - '0') * 2;
lat += (mh[3] - '0');
if (strlen(mh) >= 8) {
if ( ! isdigit(mh[6]) || ! isdigit(mh[7]) ) {
if (strlen(mh) >=6) {
if (islower(mh[4])) mh[4] = toupper(mh[4]);
if (islower(mh[5])) mh[5] = toupper(mh[5]);
if (mh[4] < 'A' || mh[4] > 'X' || mh[5] < 'A' || mh[5] > 'X') {
text_color_set(DW_COLOR_ERROR);
dw_printf("The fourth pair of characters in Maidenhead locator \"%s\" must be digits.\n", maidenhead);
dw_printf("The third pair of characters in Maidenhead locator \"%s\" must be in range of A thru X.\n", maidenhead);
return (0);
}
/* Lon: 5 min / 10 squares = 0.5 minutes / square */
/* Lat: 2.5 min / 10 squares = 0.25 minutes / square */
/* Lon: 2 deg / 24 squares = 5 minutes / square */
/* Lat: 1 deg / 24 squares = 2.5 minutes / square */
lon += (mh[6] - '0') * 0.50 / 60.0;
lat += (mh[7] - '0') * 0.25 / 60.0;
lon += (mh[4] - 'A') * 5.0 / 60.0;
lat += (mh[5] - 'A') * 2.5 / 60.0;
lon += 0.250 / 60.0; /* Move from corner to center of square */
lat += 0.125 / 60.0;
if (strlen(mh) >= 8) {
if ( ! isdigit(mh[6]) || ! isdigit(mh[7]) ) {
text_color_set(DW_COLOR_ERROR);
dw_printf("The fourth pair of characters in Maidenhead locator \"%s\" must be digits.\n", maidenhead);
return (0);
}
/* Lon: 5 min / 10 squares = 0.5 minutes / square */
/* Lat: 2.5 min / 10 squares = 0.25 minutes / square */
lon += (mh[6] - '0') * 0.50 / 60.0;
lat += (mh[7] - '0') * 0.25 / 60.0;
lon += 0.250 / 60.0; /* Move from corner to center of square */
lat += 0.125 / 60.0;
}
else {
lon += 2.5 / 60.0; /* Move from corner to center of square */
lat += 1.25 / 60.0;
}
}
else {
lon += 2.5 / 60.0; /* Move from corner to center of square */
lat += 1.25 / 60.0;
lon += 1.0; /* Move from corner to center of square */
lat += 0.5;
}
}
else {
lon += 1.0; /* Move from corner to center of square */
lat += 0.5;
lon += 10; /* Move from corner to center of square */
lat += 5;
}
//text_color_set(DW_COLOR_DEBUG);

View File

@ -113,6 +113,14 @@ Send Xmit level calibration tones.
.B "-U "
Print UTF-8 test string and exit.
.TP
.B "-S "
Print Symbol tables and exit.
.TP
.BI "-a " "n"
Report audio device statistics each n seconds.
.SH EXAMPLES
gqrx (2.3 and later) has the ability to send streaming audio through a UDP socket to another application for further processing.

View File

@ -5,7 +5,7 @@
*
* Waypoint icon codes for use in the $PMGNWPL sentence.
*
* Derived from Data Transmission Protocol For Magellan Products version 2.11
* Derived from Data Transmission Protocol For Magellan Products - version 2.11
*/
#define MGN_crossed_square "a"

View File

@ -1,9 +1,34 @@
This is NOT used for the Linux version.
These are part of the standard C library for Linux and Cygwin.
Files in this directory fill in the gaps missing for some operating systems.
--------------------------------------
These are part of the standard C library for Linux and similar operating systems.
For the Windows version we need to include our own copy.
They were copied from Cygwin source:
They were copied from Cygwin source.
/usr/src/cygwin-1.7.10-1/newlib/libc/string/...
/usr/src/cygwin-1.7.10-1/newlib/libc/string/strsep.c
/usr/src/cygwin-1.7.10-1/newlib/libc/string/strtok_r.c
strsep.c
strtok_r.c
--------------------------------------
This was also missing on Windows but available everywhere else.
strcasestr.c
--------------------------------------
The are used for the Linux and Windows versions.
They should be part of the standard C library for OpenBSD, FreeBSD, Mac OS X.
These are from OpenBSD.
http://ftp.netbsd.org/pub/pkgsrc/current/pkgsrc/net/tnftp/files/libnetbsd/strlcpy.c
http://ftp.netbsd.org/pub/pkgsrc/current/pkgsrc/net/tnftp/files/libnetbsd/strlcat.c
strlcpy.c
strlcat.c

127
misc/strlcat.c Normal file
View File

@ -0,0 +1,127 @@
/*------------------------------------------------------------------
*
* Module: strlcat.c
*
* Purpose: Safe string functions to guard against buffer overflow.
*
* Description: The size of character strings, especially when coming from the
* outside, can sometimes exceed a fixed size storage area.
*
* There was one case where a MIC-E format packet had an enormous
* comment that exceeded an internal buffer of 256 characters,
* resulting in a crash.
*
* We are not always meticulous about checking sizes to avoid overflow.
* Use of these functions, instead of strcpy and strcat, should
* help avoid issues.
*
* Orgin: From OpenBSD as the copyright notice indicates.
* The GNU folks didn't think it was appropriate for inclusion
* in glibc. https://lwn.net/Articles/507319/
*
* Modifications: Added extra debug output when strings are truncated.
* Not sure if I will leave this in the release version
* or just let it happen silently.
*
*---------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>
#include "direwolf.h"
#include "textcolor.h"
/* $NetBSD: strlcat.c,v 1.5 2014/10/31 18:59:32 spz Exp $ */
/* from NetBSD: strlcat.c,v 1.16 2003/10/27 00:12:42 lukem Exp */
/* from OpenBSD: strlcat.c,v 1.10 2003/04/12 21:56:39 millert Exp */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
* FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Appends src to string dst of size siz (unlike strncat, siz is the
* full size of dst, not space left). At most siz-1 characters
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
* If retval >= siz, truncation occurred.
*/
#if DEBUG_STRL
size_t strlcat_debug(char *__restrict__ dst, const char *__restrict__ src, size_t siz, const char *file, const char *func, int line)
#else
size_t strlcat_debug(char *__restrict__ dst, const char *__restrict__ src, size_t siz)
#endif
{
char *d = dst;
const char *s = src;
size_t n = siz;
size_t dlen;
size_t retval;
#if DEBUG_STRL
if (dst == NULL) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("ERROR: strlcat dst is NULL. (%s %s %d)\n", file, func, line);
return (0);
}
if (src == NULL) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("ERROR: strlcat src is NULL. (%s %s %d)\n", file, func, line);
return (0);
}
if (siz == 1 || siz == 4) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Suspicious strlcat siz. Is it using sizeof a pointer variable? (%s %s %d)\n", file, func, line);
}
#endif
/* Find the end of dst and adjust bytes left but don't go past end */
while (n-- != 0 && *d != '\0')
d++;
dlen = d - dst;
n = siz - dlen;
if (n == 0) {
retval = dlen + strlen(s);
goto the_end;
}
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
n--;
}
s++;
}
*d = '\0';
retval = dlen + (s - src); /* count does not include NUL */
the_end:
#if DEBUG_STRL
if (retval >= siz) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("WARNING: strlcat result length %d exceeds maximum length %d. (%s %s %d)\n",
(int)retval, (int)(siz-1), file, func, line);
}
#endif
return (retval);
}

119
misc/strlcpy.c Normal file
View File

@ -0,0 +1,119 @@
/*------------------------------------------------------------------
*
* Module: strlcpy.c
*
* Purpose: Safe string functions to guard against buffer overflow.
*
* Description: The size of character strings, especially when coming from the
* outside, can sometimes exceed a fixed size storage area.
*
* There was one case where a MIC-E format packet had an enormous
* comment that exceeded an internal buffer of 256 characters,
* resulting in a crash.
*
* We are not always meticulous about checking sizes to avoid overflow.
* Use of these functions, instead of strcpy and strcat, should
* help avoid issues.
*
* Orgin: From OpenBSD as the copyright notice indicates.
* The GNU folks didn't think it was appropriate for inclusion
* in glibc. https://lwn.net/Articles/507319/
*
* Modifications: Added extra debug output when strings are truncated.
* Not sure if I will leave this in the release version
* or just let it happen silently.
*
*---------------------------------------------------------------*/
/* $NetBSD: strlcpy.c,v 1.5 2014/10/31 18:59:32 spz Exp $ */
/* from NetBSD: strlcpy.c,v 1.14 2003/10/27 00:12:42 lukem Exp */
/* from OpenBSD: strlcpy.c,v 1.7 2003/04/12 21:56:39 millert Exp */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
* FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include "direwolf.h"
#include "textcolor.h"
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
#if DEBUG_STRL
size_t strlcpy_debug(char *__restrict__ dst, const char *__restrict__ src, size_t siz, const char *file, const char *func, int line)
#else
size_t strlcpy_debug(char *__restrict__ dst, const char *__restrict__ src, size_t siz)
#endif
{
char *d = dst;
const char *s = src;
size_t n = siz;
size_t retval;
#if DEBUG_STRL
if (dst == NULL) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("ERROR: strlcpy dst is NULL. (%s %s %d)\n", file, func, line);
return (0);
}
if (src == NULL) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("ERROR: strlcpy src is NULL. (%s %s %d)\n", file, func, line);
return (0);
}
if (siz == 1 || siz == 4) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Suspicious strlcpy siz. Is it using sizeof a pointer variable? (%s %s %d)\n", file, func, line);
}
#endif
/* Copy as many bytes as will fit */
if (n != 0 && --n != 0) {
do {
if ((*d++ = *s++) == 0)
break;
} while (--n != 0);
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
retval = s - src - 1; /* count does not include NUL */
#if DEBUG_STRL
if (retval >= siz) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("WARNING: strlcpy result length %d exceeds maximum length %d. (%s %s %d)\n",
(int)retval, (int)(siz-1), file, func, line);
}
#endif
return (retval);
}

252
morse.c
View File

@ -1,7 +1,7 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2013 John Langner, WB2OSZ
// Copyright (C) 2013, 2015 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
@ -35,47 +35,32 @@
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <sys/time.h>
#include <math.h>
#if __WIN32__
#include <windows.h>
#else
#include <sys/termios.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#endif
#include "direwolf.h"
#include "textcolor.h"
#include "audio.h"
#include "ptt.h"
#define WPM 10
#define TIME_UNITS_TO_MS(tu,wpm) (((tu)*1200)/(wpm))
// TODO : should be in .h file.
#include "gen_tone.h" /* for gen_tone_put_sample */
#include "morse.h"
/*
* Delay from PTT on to start of first character.
* Currently the only anticipated use for this is
* APRStt responses. In this case, we want an adequate
* delay for someone to press the # button, release
* the PTT button, and start listening for a response.
* Might get ambitious and make this adjustable some day.
* Good enough for now.
*/
#define MORSE_TXDELAY_MS 1500
/*
* Delay from end of last character to PTT off.
* Avoid chopping off the last element.
*/
#define MORSE_TXTAIL_MS 200
#define MORSE_TONE 800
#define TIME_UNITS_TO_MS(tu,wpm) (((tu)*1200.0)/(wpm))
static const struct morse_s {
char ch;
char enc[7];
char enc[8]; /* $ has 7 elements */
} morse[] = {
{ 'A', ".-" },
{ 'B', "-..." },
@ -117,19 +102,103 @@ static const struct morse_s {
{ '.', ".-.-.-" },
{ ',', "--..--" },
{ '?', "..--.." },
{ '/', "-..-." }
{ '/', "-..-." },
{ '=', "-...-" }, /* from ARRL */
{ '-', "-....-" },
{ ')', "-.--.-" }, /* does not distinguish open/close */
{ ':', "---..." },
{ ';', "-.-.-." },
{ '"', ".-..-." },
{ '\'', ".----." },
{ '$', "...-..-" },
{ '!', "-.-.--" }, /* more from wikipedia */
{ '(', "-.--." },
{ '&', ".-..." },
{ '+', ".-.-." },
{ '_', "..--.-" },
{ '@', ".--.-." },
};
#define NUM_MORSE (sizeof(morse) / sizeof(struct morse_s))
static void morse_tone (int tu);
static void morse_quiet (int tu);
static void morse_tone (int chan, int tu, int wpm);
static void morse_quiet (int chan, int tu, int wpm);
static void morse_quiet_ms (int chan, int ms);
static int morse_lookup (int ch);
static int morse_units_ch (int ch);
static int morse_units_str (char *str);
/*
* Properties of the digitized sound stream.
*/
static struct audio_s *save_audio_config_p;
/* Constants after initialization. */
#define TICKS_PER_CYCLE ( 256.0 * 256.0 * 256.0 * 256.0 )
static short sine_table[256];
/*------------------------------------------------------------------
*
* Name: morse_init
*
* Purpose: Initialize for tone generation.
*
* Inputs: audio_config_p - Pointer to audio configuration structure.
*
* The fields we care about are:
*
* samples_per_sec
*
* amp - Signal amplitude on scale of 0 .. 100.
*
* 100 will produce maximum amplitude of +-32k samples.
*
* Returns: 0 for success.
* -1 for failure.
*
* Description: Precompute a sine wave table.
*
*----------------------------------------------------------------*/
int morse_init (struct audio_s *audio_config_p, int amp)
{
int j;
/*
* Save away modem parameters for later use.
*/
save_audio_config_p = audio_config_p;
for (j=0; j<256; j++) {
double a;
int s;
a = ((double)(j) / 256.0) * (2 * M_PI);
s = (int) (sin(a) * 32767.0 * amp / 100.0);
/* 16 bit sound sample is in range of -32768 .. +32767. */
assert (s >= -32768 && s <= 32767);
sine_table[j] = s;
}
return (0);
} /* end morse_init */
/*-------------------------------------------------------------------
*
* Name: morse_send
@ -159,7 +228,11 @@ int morse_send (int chan, char *str, int wpm, int txdelay, int txtail)
int time_units;
char *p;
time_units = 0;
morse_quiet_ms (chan, txdelay);
for (p = str; *p != '\0'; p++) {
int i;
@ -169,37 +242,38 @@ int morse_send (int chan, char *str, int wpm, int txdelay, int txtail)
for (e = morse[i].enc; *e != '\0'; e++) {
if (*e == '.') {
morse_tone (1);
morse_tone (chan,1,wpm);
time_units++;
}
else {
morse_tone (3);
morse_tone (chan,3,wpm);
time_units += 3;
}
if (e[1] != '\0') {
morse_quiet (1);
morse_quiet (chan,1,wpm);
time_units++;
}
}
}
else {
morse_quiet (1);
morse_quiet (chan,1,wpm);
time_units++;
}
if (p[1] != '\0') {
morse_quiet (3);
morse_quiet (chan,3,wpm);
time_units += 3;
}
}
morse_quiet_ms (chan, txtail);
if (time_units != morse_units_str(str)) {
dw_printf ("morse: Internal error. Inconsistent length, %d vs. %d calculated.\n",
time_units, morse_units_str(str));
}
return (txdelay +
TIME_UNITS_TO_MS(time_units, wpm) +
(int) (TIME_UNITS_TO_MS(time_units, wpm) + 0.5) +
txtail);
} /* end morse_send */
@ -212,18 +286,47 @@ int morse_send (int chan, char *str, int wpm, int txdelay, int txtail)
*
* Purpose: Generate tone for specified number of time units.
*
* Inputs: tu - Number of time units.
* Inputs: chan - Radio channel.
* tu - Number of time units. Should be 1 or 3.
* wpm - Speed in WPM.
*
*--------------------------------------------------------------------*/
static void morse_tone (int tu) {
int num_cycles;
int n;
static void morse_tone (int chan, int tu, int wpm) {
#if MTEST1
int n;
for (n=0; n<tu; n++) {
dw_printf ("#");
}
#else
int a = ACHAN2ADEV(chan); /* device for channel. */
int sam;
int nsamples;
int j;
unsigned int tone_phase; // Phase accumulator for tone generation.
// Upper bits are used as index into sine table.
int f1_change_per_sample; // How much to advance phase for each audio sample.
assert (save_audio_config_p->achan[chan].valid);
tone_phase = 0;
f1_change_per_sample = (int) (((double)MORSE_TONE * TICKS_PER_CYCLE / (double)save_audio_config_p->adev[a].samples_per_sec ) + 0.5);
nsamples = (int) ((TIME_UNITS_TO_MS(tu,wpm) * (float)save_audio_config_p->adev[a].samples_per_sec / 1000.) + 0.5);
for (j=0; j<nsamples; j++) {
tone_phase += f1_change_per_sample;
sam = sine_table[(tone_phase >> 24) & 0xff];
gen_tone_put_sample (chan, a, sam);
};
#endif
} /* end morse_tone */
@ -235,20 +338,75 @@ static void morse_tone (int tu) {
*
* Purpose: Generate silence for specified number of time units.
*
* Inputs: tu - Number of time units.
* Inputs: chan - Radio channel.
* tu - Number of time units.
* wpm - Speed in WPM.
*
*--------------------------------------------------------------------*/
static void morse_quiet (int tu) {
int n;
static void morse_quiet (int chan, int tu, int wpm) {
#if MTEST1
int n;
for (n=0; n<tu; n++) {
dw_printf (".");
}
#else
int a = ACHAN2ADEV(chan); /* device for channel. */
int sam = 0;
int nsamples;
int j;
assert (save_audio_config_p->achan[chan].valid);
nsamples = (int) ((TIME_UNITS_TO_MS(tu,wpm) * (float)save_audio_config_p->adev[a].samples_per_sec / 1000.) + 0.5);
for (j=0; j<nsamples; j++) {
gen_tone_put_sample (chan, a, sam);
};
#endif
} /* end morse_quiet */
/*-------------------------------------------------------------------
*
* Name: morse_quiet
*
* Purpose: Generate silence for specified number of milliseconds.
* This is used for the txdelay and txtail times.
*
* Inputs: chan - Radio channel.
* tu - Number of time units.
*
*--------------------------------------------------------------------*/
static void morse_quiet_ms (int chan, int ms) {
#if MTEST1
#else
int a = ACHAN2ADEV(chan); /* device for channel. */
int sam = 0;
int nsamples;
int j;
assert (save_audio_config_p->achan[chan].valid);
nsamples = (int) ((ms * (float)save_audio_config_p->adev[a].samples_per_sec / 1000.) + 0.5);
for (j=0; j<nsamples; j++) {
gen_tone_put_sample (chan, a, sam);
};
#endif
} /* end morse_quiet_ms */
/*-------------------------------------------------------------------
*
@ -346,7 +504,7 @@ static int morse_units_ch (int ch)
static int morse_units_str (char *str)
{
int i;
//int i;
int len;
int k;
int units;
@ -362,6 +520,8 @@ static int morse_units_str (char *str)
}
#if MTEST1
int main (int argc, char *argv[]) {
dw_printf ("CQ DX\n");
@ -374,6 +534,8 @@ int main (int argc, char *argv[]) {
}
#endif
/* end morse.c */

8
morse.h Normal file
View File

@ -0,0 +1,8 @@
/* morse.h */
int morse_init (struct audio_s *audio_config_p, int amp) ;
int morse_send (int chan, char *str, int wpm, int txdelay, int txtail);
#define MORSE_DEFAULT_WPM 10

342
nmea.c
View File

@ -64,15 +64,7 @@ char *strsep(char **stringp, const char *delim);
#include "nmea.h"
#include "grm_sym.h" /* Garmin symbols */
#include "mgn_icon.h" /* Magellan icons */
#if __WIN32__
typedef HANDLE MYFDTYPE;
#define MYFDERROR INVALID_HANDLE_VALUE
#else
typedef int MYFDTYPE;
#define MYFDERROR (-1)
#endif
#include "serial_port.h"
// TODO: receive buffer... static kiss_frame_t kf; /* Accumulated KISS frame and state of decoder. */
@ -103,7 +95,7 @@ void nmea_set_debug (int n)
* Name: nmea_init
*
* Purpose: Initialization for NMEA communication port.
&
*
* Inputs: mc->nmea_port - name of serial port.
*
* Global output: nmea_port_fd
@ -112,15 +104,9 @@ void nmea_set_debug (int n)
* Description: (1) Open serial port device.
* (2) Start a new thread to listen for GPS receiver.
*
* Reference: http://www.robbayer.com/files/serial-win.pdf
*
*---------------------------------------------------------------*/
static MYFDTYPE nmea_open_port (char *device);
void nmea_init (struct misc_config_s *mc)
{
@ -132,24 +118,12 @@ void nmea_init (struct misc_config_s *mc)
/*
* Open serial port connection.
* 4800 baud is standard for GPS.
* Should add an option to allow changing someday.
*/
if (strlen(mc->nmea_port) > 0) {
#if ! __WIN32__
/* Translate Windows device name into Linux name. */
/* COM1 -> /dev/ttyS0, etc. */
if (strncasecmp(mc->nmea_port, "COM", 3) == 0) {
int n = atoi (mc->nmea_port + 3);
text_color_set(DW_COLOR_INFO);
dw_printf ("Converted NMEA device '%s'", mc->nmea_port);
if (n < 1) n = 1;
sprintf (mc->nmea_port, "/dev/ttyS%d", n-1);
dw_printf (" to Linux equivalent '%s'\n", mc->nmea_port);
}
#endif
nmea_port_fd = nmea_open_port (mc->nmea_port);
nmea_port_fd = serial_port_open (mc->nmea_port, 4800);
if (nmea_port_fd != MYFDERROR) {
#if __WIN32__
@ -180,138 +154,6 @@ void nmea_init (struct misc_config_s *mc)
}
/*
* Returns fd for serial port or MYFDERROR for error.
*/
static MYFDTYPE nmea_open_port (char *devicename)
{
#if __WIN32__
MYFDTYPE fd;
DCB dcb;
int ok;
char bettername[50];
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("nmea_open_port ( '%s' )\n", devicename);
#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
strcpy (bettername, devicename);
if (strncasecmp(devicename, "COM", 3) == 0) {
int n;
n = atoi(devicename+3);
if (n >= 10) {
strcpy (bettername, "\\\\.\\");
strcat (bettername, devicename);
}
}
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 open NMEA port %s.\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 ("nmea_open_port: GetCommState failed.\n");
}
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx */
dcb.DCBlength = sizeof(DCB);
dcb.BaudRate = CBR_4800;
dcb.fBinary = 1;
dcb.fParity = 0;
dcb.fOutxCtsFlow = 0;
dcb.fOutxDsrFlow = 0;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
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 ("nmea_open_port: SetCommState failed.\n");
}
text_color_set(DW_COLOR_INFO);
dw_printf("NMEA communication started on %s.\n", devicename);
#else
/* Linux version. */
int fd;
struct termios ts;
int e;
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("nmea_open_port ( '%s' )\n", devicename);
#endif
fd = open (devicename, O_RDWR);
if (fd == MYFDERROR) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR - Could not open NMEA port %s.\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. */
cfsetispeed (&ts, B4800);
cfsetospeed (&ts, B4800);
e = tcsetattr (fd, TCSANOW, &ts);
if (e != 0) { perror ("nmea tcsetattr"); }
text_color_set(DW_COLOR_INFO);
dw_printf("NMEA communication started on %s.\n", devicename);
#endif
return (fd);
}
/*-------------------------------------------------------------------
*
@ -575,7 +417,7 @@ https://freepository.com:444/50lItuLQ7fW6s-web/browser/Tracker2/trunk/sources/wa
Data Transmission Protocol For Magellan Products version 2.11
Data Transmission Protocol For Magellan Products - version 2.11
@ -637,45 +479,7 @@ static void nmea_send_sentence (char *sent)
// TODO: need to append CR LF.
#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 (nmea_port_fd, sent, len, &nwritten, &ov_wr))
{
err = GetLastError();
if (err != ERROR_IO_PENDING)
{
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError sending NMEA sentence. Error %d.\n\n", (int)GetLastError());
//CloseHandle (nmea_port_fd);
//nmea_port_fd = MYFDERROR;
}
}
else if (nwritten != len)
{
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError sending NMEA sentence. Only %d of %d written.\n\n", (int)nwritten, len);
//CloseHandle (nmea_port_fd);
//nmea_port_fd = MYFDERROR;
}
#else
err = write (nmea_port_fd, sent, (size_t)len);
if (err != len)
{
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError sending NMEA sentence. err=%d\n\n", err);
//close (nmea_port_fd);
//nmea_port_fd = MYFDERROR;
}
#endif
serial_port_write (nmea_port_fd, sent, len);
} /* nmea_send_sentence */
@ -695,109 +499,6 @@ static void nmea_send_sentence (char *sent)
*
*--------------------------------------------------------------------*/
//TODO: should pass fd by reference so it can be zapped.
//BUG: If we close it here, that fact doesn't get back
// to the main receiving thread.
/* Return one byte (value 0 - 255) or terminate thread on error. */
static int nmea_get (MYFDTYPE fd)
{
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 (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 (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 (fd);
//fd = MYFDERROR;
//pthread_exit (NULL);
}
}
} /* end while n==0 */
CloseHandle(ov_rd.hEvent);
if (n != 1) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nNMEA failed to get one byte. n=%d.\n\n", (int)n);
#if DEBUG9
fprintf (log_fp, "n=%d\n", n);
#endif
}
#else /* Linux version */
int n;
n = read(fd, &ch, (size_t)1);
if (n != 1) {
//text_color_set(DW_COLOR_ERROR);
//dw_printf ("\nError receiving kiss message from client application. Closing connection %d.\n\n", fd);
close (fd);
fd = MYFDERROR;
pthread_exit (NULL);
}
#endif
#if DEBUGx
text_color_set(DW_COLOR_DEBUG);
dw_printf ("nmea_get(%d) returns 0x%02x\n", fd, ch);
#endif
return (ch);
}
// Maximum length of message from GPS receiver.
// 82 according to some people. Larger to be safe.
@ -823,9 +524,25 @@ static void * nmea_listen_thread (void *arg)
while (1) {
unsigned char ch;
int ch;
ch = serial_port_get1(fd);
// TODO: if ch < 0, terminate thread ...
//CloseHandle (fd);
//fd = MYFDERROR;
//pthread_exit (NULL);
//text_color_set(DW_COLOR_ERROR);
//dw_printf ("\nError trying to read from GPS receiver. Closing connection %d.\n\n", fd);
//close (fd); -> serial_port_close(fd)
//fd = MYFDERROR;
//pthread_exit (NULL);
ch = nmea_get(fd);
if (ch == '$') {
// Start of new sentence.
@ -982,7 +699,14 @@ static void nmea_parse_gps (char *sentence)
text_color_set (DW_COLOR_INFO);
dw_printf("%d %.6f %.6f %.1f %.0f %.1f\n", fix, g_lat, g_lon, g_speed, g_course, g_alt);
#if WALK96
// TODO: Need to design a proper interface.
extern void walk96 (int fix, double lat, double lon, float knots, float course, float alt);
walk96 (fix, g_lat, g_lon, g_speed, g_course, g_alt);
#endif
}
// $GPGGA has altitude.

View File

@ -133,7 +133,7 @@ static int filt_s (pfstate_t *pf);
* Both are 0 .. MAX_CHANS-1 or MAX_CHANS for IGate.
* For debug/error messages only.
*
* filter - Filter specs and logical operators to combine them.
* filter - String of filter specs and logical operators to combine them.
*
* pp - Packet object handle.
*
@ -152,12 +152,28 @@ int pfilter (int from_chan, int to_chan, char *filter, packet_t pp)
char *p;
int result;
assert (from_chan >= 0 && from_chan <= MAX_CHANS);
assert (to_chan >= 0 && to_chan <= MAX_CHANS);
if (pp == NULL) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("INTERNAL ERROR in pfilter: NULL packet pointer. Please report this!\n");
return (-1);
}
if (filter == NULL) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("INTERNAL ERROR in pfilter: NULL filter string pointer. Please report this!\n");
return (-1);
}
pfstate.from_chan = from_chan;
pfstate.to_chan = to_chan;
/* Copy filter string, removing any control characters. */
memset (pfstate.filter_str, 0, sizeof(pfstate.filter_str));
strncpy (pfstate.filter_str, filter, MAX_FILTER_LEN-1);
pfstate.filter_str[MAX_FILTER_LEN-1] = '\0';
pfstate.nexti = 0;
for (p = pfstate.filter_str; *p != '\0'; p++) {
if (iscntrl(*p)) {
@ -206,11 +222,11 @@ int pfilter (int from_chan, int to_chan, char *filter, packet_t pp)
* Note that a filter-spec must be followed by space or
* end of line. This is so the magic characters can appear in one.
*
* Future: Allow words like 'OR' as alternatives to symbols like '|'.
* Future: Maybe allow words like 'OR' as alternatives to symbols like '|'.
*
* Unresolved Issue:
*
* Adding the special operattors adds a new complication.
* Adding the special operators adds a new complication.
* How do we handle the case where we want those characters in
* a filter specification? For example how do we know if the
* last character of /#& means HF gateway or AND the next part
@ -242,32 +258,32 @@ static void next_token (pfstate_t *pf)
if (pf->filter_str[pf->nexti] == '\0') {
pf->token_type = TOKEN_EOL;
strcpy (pf->token_str, "end-of-line");
strlcpy (pf->token_str, "end-of-line", sizeof(pf->token_str));
}
else if (pf->filter_str[pf->nexti] == '&') {
pf->nexti++;
pf->token_type = TOKEN_AND;
strcpy (pf->token_str, "\"&\"");
strlcpy (pf->token_str, "\"&\"", sizeof(pf->token_str));
}
else if (pf->filter_str[pf->nexti] == '|') {
pf->nexti++;
pf->token_type = TOKEN_OR;
strcpy (pf->token_str, "\"|\"");
strlcpy (pf->token_str, "\"|\"", sizeof(pf->token_str));
}
else if (pf->filter_str[pf->nexti] == '!') {
pf->nexti++;
pf->token_type = TOKEN_NOT;
strcpy (pf->token_str, "\"!\"");
strlcpy (pf->token_str, "\"!\"", sizeof(pf->token_str));
}
else if (pf->filter_str[pf->nexti] == '(') {
pf->nexti++;
pf->token_type = TOKEN_LPAREN;
strcpy (pf->token_str, "\"(\"");
strlcpy (pf->token_str, "\"(\"", sizeof(pf->token_str));
}
else if (pf->filter_str[pf->nexti] == ')') {
pf->nexti++;
pf->token_type = TOKEN_RPAREN;
strcpy (pf->token_str, "\")\"");
strlcpy (pf->token_str, "\")\"", sizeof(pf->token_str));
}
else {
char *p = pf->token_str;
@ -488,7 +504,7 @@ static int parse_filter_spec (pfstate_t *pf)
else {
char stemp[80];
sprintf (stemp, "Unrecognized filter type '%c'", pf->token_str[0]);
snprintf (stemp, sizeof(stemp), "Unrecognized filter type '%c'", pf->token_str[0]);
print_error (pf, stemp);
result = -1;
}
@ -535,7 +551,7 @@ static int filt_bodgu (pfstate_t *pf, char *arg)
char *v;
int result = 0;
strcpy (str, pf->token_str);
strlcpy (str, pf->token_str, sizeof(str));
sep[0] = str[1];
sep[1] = '\0';
cp = str + 2;
@ -590,13 +606,16 @@ static int filt_bodgu (pfstate_t *pf, char *arg)
static int filt_t (pfstate_t *pf)
{
char src[MAX_TOKEN_LEN];
char *infop;
char src[AX25_MAX_ADDR_LEN];
char *infop = NULL;
char *f;
memset (src, 0, sizeof(src));
ax25_get_addr_with_ssid (pf->pp, AX25_SOURCE, src);
(void) ax25_get_info (pf->pp, (unsigned char **)(&infop));
assert (infop != NULL);
for (f = pf->token_str + 2; *f != '\0'; f++) {
switch (*f) {
@ -678,6 +697,12 @@ static int filt_t (pfstate_t *pf)
infop[2] == src[1] &&
infop[3] == src[2]) return (1);
break;
default:
print_error (pf, "Invalid letter in t/ filter.\n");
return (-1);
break;
}
}
return (0); /* Didn't match anything. Reject */
@ -690,7 +715,7 @@ static int filt_t (pfstate_t *pf)
*
* Name: filt_r
*
* Purpose: Is it in range of given location.
* Purpose: Is it in range (kilometers) of given location.
*
* Inputs: pf - Pointer to current state information.
* token_str should contain something of format:
@ -718,7 +743,7 @@ static int filt_r (pfstate_t *pf)
double dlat, dlon, ddist, km;
strcpy (str, pf->token_str);
strlcpy (str, pf->token_str, sizeof(str));
sep[0] = str[1];
sep[1] = '\0';
cp = str + 2;
@ -781,9 +806,9 @@ static int filt_r (pfstate_t *pf)
*
* Description:
*
* pri is zero or more symbols from the primary symbol set.
* alt is one or more symbols from the alternate symbol set.
* over is overlay characters. Overlays apply only to the alternate symbol set.
* "pri" is zero or more symbols from the primary symbol set.
* "alt" is one or more symbols from the alternate symbol set.
* "over" is overlay characters. Overlays apply only to the alternate symbol set.
*
* Examples:
* s/-> Allow house and car from primary symbol table.
@ -801,7 +826,7 @@ static int filt_s (pfstate_t *pf)
char *pri, *alt, *over;
strcpy (str, pf->token_str);
strlcpy (str, pf->token_str, sizeof(str));
sep[0] = str[1];
sep[1] = '\0';
cp = str + 2;
@ -869,19 +894,19 @@ static void print_error (pfstate_t *pf, char *msg)
if (pf->from_chan == MAX_CHANS) {
if (pf->to_chan == MAX_CHANS) {
sprintf (intro, "filter[IG,IG]: ");
snprintf (intro, sizeof(intro), "filter[IG,IG]: ");
}
else {
sprintf (intro, "filter[IG,%d]: ", pf->to_chan);
snprintf (intro, sizeof(intro), "filter[IG,%d]: ", pf->to_chan);
}
}
else {
if (pf->to_chan == MAX_CHANS) {
sprintf (intro, "filter[%d,IG]: ", pf->from_chan);
snprintf (intro, sizeof(intro), "filter[%d,IG]: ", pf->from_chan);
}
else {
sprintf (intro, "filter[%d,%d]: ", pf->from_chan, pf->to_chan);
snprintf (intro, sizeof(intro), "filter[%d,%d]: ", pf->from_chan, pf->to_chan);
}
}
@ -903,7 +928,7 @@ static void print_error (pfstate_t *pf, char *msg)
*
* Purpose: Unit test for packet filtering.
*
* Usage: gcc -Wall -o pftest -DTEST pfilter.c ax25_pad.o textcolor.o fcs_calc.o decode_aprs.o latlong.o symbols.o telemetry.o misc.a regex.a && ./pftest
* Usage: gcc -Wall -o pftest -DTEST pfilter.c ax25_pad.o textcolor.o fcs_calc.o decode_aprs.o latlong.o symbols.o telemetry.o tt_text.c misc.a regex.a && ./pftest
*
*
*--------------------------------------------------------------------*/

6
ptt.c
View File

@ -35,6 +35,12 @@
*
* Version 1.1: Add parallel printer port for x86 Linux only.
*
* This is hardcoded to use the primary motherboard parallel
* printer port at I/O address 0x378. This might work with
* a PCI card configured to use the same address if the
* motherboard does not have a built in parallel port.
* It won't work with a USB-to-parallel-printer-port adapter.
*
* Version 1.2: More than two radio channels.
* Generalize for additional signals besides PTT.
*

451
serial_port.c Normal file
View File

@ -0,0 +1,451 @@
// 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
//
// 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/>.
//
//#define DEBUG 1
/*------------------------------------------------------------------
*
* Module: serial.c
*
* Purpose: Interface to serial port, hiding operating system differences.
*
*---------------------------------------------------------------*/
#include <stdio.h>
#if __WIN32__
#include <stdlib.h>
#include <windows.h>
#else
#define __USE_XOPEN2KXSI 1
#define __USE_XOPEN 1
#include <stdlib.h>
#include <ctype.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#endif
#include <assert.h>
#include <string.h>
#include "direwolf.h"
#include "textcolor.h"
#include "serial_port.h"
/*-------------------------------------------------------------------
*
* Name: serial_port_open
*
* Purpose: Open serial port.
*
* Inputs: devicename - For Windows, usually like COM5.
* For Linux, usually /dev/tty...
* "COMn" also allowed and converted to /dev/ttyS(n-1)
*
* baud - Speed. 4800, 9600, etc.
*
* Returns Handle for serial port or MYFDERROR for error.
*
*---------------------------------------------------------------*/
MYFDTYPE serial_port_open (char *devicename, int baud)
{
#if __WIN32__
MYFDTYPE fd;
DCB dcb;
int ok;
char bettername[50];
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("serial_port_open ( '%s', %d )\n", devicename, baud);
#endif
// Reference: http://www.robbayer.com/files/serial-win.pdf
// 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
strcpy (bettername, devicename);
if (strncasecmp(devicename, "COM", 3) == 0) {
int n;
n = atoi(devicename+3);
if (n >= 10) {
strcpy (bettername, "\\\\.\\");
strcat (bettername, devicename);
}
}
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 open serial port %s.\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 ("serial_port_open: GetCommState failed.\n");
}
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx */
dcb.DCBlength = sizeof(DCB);
switch (baud) {
case 1200: dcb.BaudRate = CBR_1200; break;
case 2400: dcb.BaudRate = CBR_2400; break;
case 4800: dcb.BaudRate = CBR_4800; break;
case 9600: dcb.BaudRate = CBR_9600; break;
case 19200: dcb.BaudRate = CBR_19200; break;
case 38400: dcb.BaudRate = CBR_38400; break;
case 57600: dcb.BaudRate = CBR_57600; break;
case 115200: dcb.BaudRate = CBR_115200; break;
default: text_color_set(DW_COLOR_ERROR);
dw_printf ("serial_port_open: Unsupported speed %d. Using 4800.\n", baud);
dcb.BaudRate = CBR_4800;
break;
}
dcb.fBinary = 1;
dcb.fParity = 0;
dcb.fOutxCtsFlow = 0;
dcb.fOutxDsrFlow = 0;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
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 ("serial_port_open: SetCommState failed.\n");
}
//text_color_set(DW_COLOR_INFO);
//dw_printf("Successful serial port open on %s.\n", devicename);
#else
/* Linux version. */
int fd;
struct termios ts;
int e;
char linuxname[50];
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("serial_port_open ( '%s' )\n", devicename);
#endif
/* Translate Windows device name into Linux name. */
/* COM1 -> /dev/ttyS0, etc. */
strcpy (linuxname, devicename);
if (strncasecmp(devicename, "COM", 3) == 0) {
int n = atoi (devicename + 3);
text_color_set(DW_COLOR_INFO);
dw_printf ("Converted serial port name '%s'", devicename);
if (n < 1) n = 1;
sprintf (linuxname, "/dev/ttyS%d", n-1);
dw_printf (" to Linux equivalent '%s'\n", linuxname);
}
fd = open (linuxname, O_RDWR);
if (fd == MYFDERROR) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR - Could not open serial port %s.\n", linuxname);
return (MYFDERROR);
}
e = tcgetattr (fd, &ts);
if (e != 0) { perror ("tcgetattr"); }
cfmakeraw (&ts);
ts.c_cc[VMIN] = 1; /* wait for at least one character */
ts.c_cc[VTIME] = 0; /* no fancy timing. */
switch (baud) {
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;
case 57600: cfsetispeed (&ts, B57600); cfsetospeed (&ts, B57600); break;
case 115200: cfsetispeed (&ts, B115200); cfsetospeed (&ts, B115200); break;
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);
break;
}
e = tcsetattr (fd, TCSANOW, &ts);
if (e != 0) { perror ("tcsetattr"); }
//text_color_set(DW_COLOR_INFO);
//dw_printf("Successfully opened serial port %s.\n", devicename);
#endif
return (fd);
}
/*-------------------------------------------------------------------
*
* Name: serial_port_write
*
* Purpose: Send characters to serial port.
*
* Inputs: fd - Handle from open.
* str - Pointer to array of bytes.
* len - Number of bytes to write.
*
* Returns Number of bytes written. Should be the same as len.
* -1 if error.
*
*---------------------------------------------------------------*/
int serial_port_write (MYFDTYPE fd, char *str, int len)
{
if (fd == MYFDERROR) {
return (-1);
}
#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 (fd, str, len, &nwritten, &ov_wr))
{
int err = GetLastError();
if (err != ERROR_IO_PENDING)
{
text_color_set(DW_COLOR_ERROR);
dw_printf ("Error writing to serial port. Error %d.\n\n", err);
}
}
else if (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);
}
return (nwritten);
#else
int written;
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);
return (-1);
}
return (written);
#endif
} /* serial_port_write */
/*-------------------------------------------------------------------
*
* Name: serial_port_get1
*
* Purpose: Get one byte from the serial port. Wait if not ready.
*
* Inputs: fd - Handle from open.
*
* Returns: Value of byte in range of 0 to 255.
* -1 if error.
*
*--------------------------------------------------------------------*/
int serial_port_get1 (MYFDTYPE fd)
{
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 (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 (fd, &ov_rd, &n, 1))
{
int err3 = GetLastError();
text_color_set(DW_COLOR_ERROR);
dw_printf ("Serial Port GetOverlappedResult error %d.\n\n", err3);
}
else
{
/* Success! n should be 1 */
}
}
}
else
{
text_color_set(DW_COLOR_ERROR);
dw_printf ("Serial port read error %d.\n", err1);
return (-1);
}
}
} /* end while n==0 */
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);
}
#else /* Linux version */
int n;
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);
return (-1);
}
#endif
#if DEBUGx
text_color_set(DW_COLOR_DEBUG);
if (isprint(ch)) {
dw_printf ("serial_port_get1(%d) returns 0x%02x = '%c'\n", fd, ch, ch);
}
else {
dw_printf ("serial_port_get1(%d) returns 0x%02x\n", fd, ch);
}
#endif
return (ch);
}
/*-------------------------------------------------------------------
*
* Name: serial_port_close
*
* Purpose: Close the device.
*
* Inputs: fd - Handle from open.
*
* Returns: None.
*
*--------------------------------------------------------------------*/
// TODO:
/* end serial_port.c */

27
serial_port.h Normal file
View File

@ -0,0 +1,27 @@
/* serial_port.h */
#if __WIN32__
#include <stdlib.h>
#include <windows.h>
typedef HANDLE MYFDTYPE;
#define MYFDERROR INVALID_HANDLE_VALUE
#else
typedef int MYFDTYPE;
#define MYFDERROR (-1)
#endif
extern MYFDTYPE serial_port_open (char *devicename, int baud);
extern int serial_port_write (MYFDTYPE fd, char *str, int len);
extern int serial_port_get1 (MYFDTYPE fd);
extern void serial_port_close (MYFDTYPE fd);

122
server.c
View File

@ -256,52 +256,52 @@ static void debug_print (fromto_t fromto, int client, struct agwpe_s *pmsg, int
switch (fromto) {
case FROM_CLIENT:
strcpy (direction, "from"); /* from the client application */
strlcpy (direction, "from", sizeof(direction)); /* from the client application */
switch (pmsg->kind_lo) {
case 'P': strcpy (datakind, "Application Login"); break;
case 'X': strcpy (datakind, "Register CallSign"); break;
case 'x': strcpy (datakind, "Unregister CallSign"); break;
case 'G': strcpy (datakind, "Ask Port Information"); break;
case 'm': strcpy (datakind, "Enable Reception of Monitoring Frames"); break;
case 'R': strcpy (datakind, "AGWPE Version Info"); break;
case 'g': strcpy (datakind, "Ask Port Capabilities"); break;
case 'H': strcpy (datakind, "Callsign Heard on a Port"); break;
case 'y': strcpy (datakind, "Ask Outstanding frames waiting on a Port"); break;
case 'Y': strcpy (datakind, "Ask Outstanding frames waiting for a connection"); break;
case 'M': strcpy (datakind, "Send UNPROTO Information"); break;
case 'C': strcpy (datakind, "Connect, Start an AX.25 Connection"); break;
case 'D': strcpy (datakind, "Send Connected Data"); break;
case 'd': strcpy (datakind, "Disconnect, Terminate an AX.25 Connection"); break;
case 'v': strcpy (datakind, "Connect VIA, Start an AX.25 circuit thru digipeaters"); break;
case 'V': strcpy (datakind, "Send UNPROTO VIA"); break;
case 'c': strcpy (datakind, "Non-Standard Connections, Connection with PID"); break;
case 'K': strcpy (datakind, "Send data in raw AX.25 format"); break;
case 'k': strcpy (datakind, "Activate reception of Frames in raw format"); break;
default: strcpy (datakind, "**INVALID**"); break;
case 'P': strlcpy (datakind, "Application Login", sizeof(datakind)); break;
case 'X': strlcpy (datakind, "Register CallSign", sizeof(datakind)); break;
case 'x': strlcpy (datakind, "Unregister CallSign", sizeof(datakind)); break;
case 'G': strlcpy (datakind, "Ask Port Information", sizeof(datakind)); break;
case 'm': strlcpy (datakind, "Enable Reception of Monitoring Frames", sizeof(datakind)); break;
case 'R': strlcpy (datakind, "AGWPE Version Info", sizeof(datakind)); break;
case 'g': strlcpy (datakind, "Ask Port Capabilities", sizeof(datakind)); break;
case 'H': strlcpy (datakind, "Callsign Heard on a Port", sizeof(datakind)); break;
case 'y': strlcpy (datakind, "Ask Outstanding frames waiting on a Port", sizeof(datakind)); break;
case 'Y': strlcpy (datakind, "Ask Outstanding frames waiting for a connection", sizeof(datakind)); break;
case 'M': strlcpy (datakind, "Send UNPROTO Information", sizeof(datakind)); break;
case 'C': strlcpy (datakind, "Connect, Start an AX.25 Connection", sizeof(datakind)); break;
case 'D': strlcpy (datakind, "Send Connected Data", sizeof(datakind)); break;
case 'd': strlcpy (datakind, "Disconnect, Terminate an AX.25 Connection", sizeof(datakind)); break;
case 'v': strlcpy (datakind, "Connect VIA, Start an AX.25 circuit thru digipeaters", sizeof(datakind)); break;
case 'V': strlcpy (datakind, "Send UNPROTO VIA", sizeof(datakind)); break;
case 'c': strlcpy (datakind, "Non-Standard Connections, Connection with PID", sizeof(datakind)); break;
case 'K': strlcpy (datakind, "Send data in raw AX.25 format", sizeof(datakind)); break;
case 'k': strlcpy (datakind, "Activate reception of Frames in raw format", sizeof(datakind)); break;
default: strlcpy (datakind, "**INVALID**", sizeof(datakind)); break;
}
break;
case TO_CLIENT:
default:
strcpy (direction, "to"); /* sent to the client application. */
strlcpy (direction, "to", sizeof(direction)); /* sent to the client application. */
switch (pmsg->kind_lo) {
case 'R': strcpy (datakind, "Version Number"); break;
case 'X': strcpy (datakind, "Callsign Registration"); break;
case 'G': strcpy (datakind, "Port Information"); break;
case 'g': strcpy (datakind, "Capabilities of a Port"); break;
case 'y': strcpy (datakind, "Frames Outstanding on a Port"); break;
case 'Y': strcpy (datakind, "Frames Outstanding on a Connection"); break;
case 'H': strcpy (datakind, "Heard Stations on a Port"); break;
case 'C': strcpy (datakind, "AX.25 Connection Received"); break;
case 'D': strcpy (datakind, "Connected AX.25 Data"); break;
case 'M': strcpy (datakind, "Monitored Connected Information"); break;
case 'S': strcpy (datakind, "Monitored Supervisory Information"); break;
case 'U': strcpy (datakind, "Monitored Unproto Information"); break;
case 'T': strcpy (datakind, "Monitoring Own Information"); break;
case 'K': strcpy (datakind, "Monitored Information in Raw Format"); break;
default: strcpy (datakind, "**INVALID**"); break;
case 'R': strlcpy (datakind, "Version Number", sizeof(datakind)); break;
case 'X': strlcpy (datakind, "Callsign Registration", sizeof(datakind)); break;
case 'G': strlcpy (datakind, "Port Information", sizeof(datakind)); break;
case 'g': strlcpy (datakind, "Capabilities of a Port", sizeof(datakind)); break;
case 'y': strlcpy (datakind, "Frames Outstanding on a Port", sizeof(datakind)); break;
case 'Y': strlcpy (datakind, "Frames Outstanding on a Connection", sizeof(datakind)); break;
case 'H': strlcpy (datakind, "Heard Stations on a Port", sizeof(datakind)); break;
case 'C': strlcpy (datakind, "AX.25 Connection Received", sizeof(datakind)); break;
case 'D': strlcpy (datakind, "Connected AX.25 Data", sizeof(datakind)); break;
case 'M': strlcpy (datakind, "Monitored Connected Information", sizeof(datakind)); break;
case 'S': strlcpy (datakind, "Monitored Supervisory Information", sizeof(datakind)); break;
case 'U': strlcpy (datakind, "Monitored Unproto Information", sizeof(datakind)); break;
case 'T': strlcpy (datakind, "Monitoring Own Information", sizeof(datakind)); break;
case 'K': strlcpy (datakind, "Monitored Information in Raw Format", sizeof(datakind)); break;
default: strlcpy (datakind, "**INVALID**", sizeof(datakind)); break;
}
}
@ -466,7 +466,7 @@ static THREAD_F connect_listen_thread (void *arg)
SOCKET listen_sock;
WSADATA wsadata;
sprintf (server_port_str, "%d", (int)(long)arg);
snprintf (server_port_str, sizeof(server_port_str), "%d", (int)(long)arg);
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("DEBUG: serverport = %d = '%s'\n", (int)(long)arg, server_port_str);
@ -591,6 +591,7 @@ static THREAD_F connect_listen_thread (void *arg)
socklen_t sockaddr_size = sizeof(struct sockaddr_in);
int server_port = (int)(long)arg;
int listen_sock;
int bcopt = 1;
listen_sock= socket(AF_INET,SOCK_STREAM,0);
if (listen_sock == -1) {
@ -599,6 +600,15 @@ static THREAD_F connect_listen_thread (void *arg)
return (NULL);
}
/* Version 1.3 - as suggested by G8BPQ. */
/* Without this, if you kill the application then try to run it */
/* again quickly the port number is unavailable for a while. */
/* Don't try doing the same thing On Windows; It has a different meaning. */
/* http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t */
setsockopt (listen_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&bcopt, 4);
sockaddr.sin_addr.s_addr = INADDR_ANY;
sockaddr.sin_port = htons(server_port);
sockaddr.sin_family = AF_INET;
@ -791,7 +801,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
/* The documentation example includes these 3 extra in the Len= value */
/* but actual observed data uses only the packet info length. */
sprintf (agwpe_msg.data, " %d:Fm %s To %s <UI pid=%02X Len=%d >[%02d:%02d:%02d]\r%s\r\r",
snprintf (agwpe_msg.data, sizeof(agwpe_msg.data), " %d:Fm %s To %s <UI pid=%02X Len=%d >[%02d:%02d:%02d]\r%s\r\r",
chan+1, agwpe_msg.hdr.call_from, agwpe_msg.hdr.call_to,
ax25_get_pid(pp), info_len,
tm->tm_hour, tm->tm_min, tm->tm_sec,
@ -1103,7 +1113,7 @@ static THREAD_F cmd_listen_thread (void *arg)
count++;
}
}
sprintf (reply.info, "%d;", count);
snprintf (reply.info, sizeof(reply.info), "%d;", count);
for (j=0; j<MAX_CHANS; j++) {
if (save_audio_config_p->achan[j].valid) {
@ -1113,22 +1123,22 @@ static THREAD_F cmd_listen_thread (void *arg)
static const char *names[8] = { "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth" };
if (save_audio_config_p->adev[a].num_channels == 1) {
sprintf (stemp, "Port%d %s soundcard mono;", j+1, names[a]);
strcat (reply.info, stemp);
snprintf (stemp, sizeof(stemp), "Port%d %s soundcard mono;", j+1, names[a]);
strlcat (reply.info, stemp, sizeof(reply.info));
}
else {
sprintf (stemp, "Port%d %s soundcard %s;", j+1, names[a], j&1 ? "right" : "left");
strcat (reply.info, stemp);
snprintf (stemp, sizeof(stemp), "Port%d %s soundcard %s;", j+1, names[a], j&1 ? "right" : "left");
strlcat (reply.info, stemp, sizeof(reply.info));
}
}
}
#else
if (num_channels == 1) {
sprintf (reply.info, "1;Port1 Single channel;");
snprintf (reply.info, sizeof(reply.info), "1;Port1 Single channel;");
}
else {
sprintf (reply.info, "2;Port1 Left channel;Port2 Right Channel;");
snprintf (reply.info, sizeof(reply.info), "2;Port1 Left channel;Port2 Right Channel;");
}
#endif
reply.hdr.data_len = strlen(reply.info) + 1;
@ -1200,9 +1210,9 @@ static THREAD_F cmd_listen_thread (void *arg)
reply.hdr.portx = cmd.hdr.portx
strcpy (reply.hdr.call_from, "WB2OSZ-15");
strlcpy (reply.hdr.call_from, "WB2OSZ-15", sizeof(reply.hdr.call_from));
strcpy (agwpe_msg.data, ...);
strlcpy (agwpe_msg.data, ..., sizeof(agwpe_msg.data));
reply.hdr.data_len = strlen(reply.info);
@ -1247,21 +1257,21 @@ static THREAD_F cmd_listen_thread (void *arg)
// We have already assured these do not exceed 9 characters.
strcpy (stemp, cmd.hdr.call_from);
strcat (stemp, ">");
strcat (stemp, cmd.hdr.call_to);
strlcpy (stemp, cmd.hdr.call_from, sizeof(stemp));
strlcat (stemp, ">", sizeof(stemp));
strlcat (stemp, cmd.hdr.call_to, sizeof(stemp));
cmd.data[cmd.hdr.data_len] = '\0';
ndigi = cmd.data[0];
p = cmd.data + 1;
for (k=0; k<ndigi; k++) {
strcat (stemp, ",");
strcat (stemp, p);
strlcat (stemp, ",", sizeof(stemp));
strlcat (stemp, p, sizeof(stemp));
p += 10;
}
strcat (stemp, ":");
strcat (stemp, p);
strlcat (stemp, ":", sizeof(stemp));
strlcat (stemp, p, sizeof(stemp));
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("Transmit '%s'\n", stemp);
@ -1306,7 +1316,7 @@ static THREAD_F cmd_listen_thread (void *arg)
//
// The first byte of data is described as:
//
// the “TNC” to use
// the "TNC" to use
// 00=Port 1
// 16=Port 2
//

184
symbols.c
View File

@ -1,7 +1,7 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2011,2012,2013,2014 John Langner, WB2OSZ
// Copyright (C) 2011, 2012, 2013, 2014, 2015 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
@ -31,6 +31,8 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "direwolf.h"
#include "textcolor.h"
#include "symbols.h"
@ -259,6 +261,17 @@ static const struct {
/* ~ 94 */ { "Q4", "TNC Stream Switch" } };
// Make sure the array is null terminated.
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",
#endif
(const char *) NULL
};
/*------------------------------------------------------------------
*
* Function: symbols_init
@ -306,7 +319,7 @@ static int new_sym_len = 0; /* Number of elements used. */
void symbols_init (void)
{
FILE *fp;
FILE *fp = NULL;
/*
* We only care about lines with this format:
@ -340,13 +353,13 @@ void symbols_init (void)
// If search strategy changes, be sure to keep decode_tocall in sync.
fp = NULL;
j = 0;
do {
if (search_locations[j] == NULL) break;
fp = fopen(search_locations[j++], "r");
} while (fp == NULL);
fp = fopen("symbols-new.txt", "r");
#ifndef __WIN32__
if (fp == NULL) {
fp = fopen("/usr/share/direwolf/symbols-new.txt", "r");
}
#endif
if (fp == NULL) {
text_color_set(DW_COLOR_ERROR);
@ -401,6 +414,83 @@ void symbols_init (void)
} /* end symbols_init */
/*------------------------------------------------------------------
*
* Function: symbols_list
*
* Purpose: Print a list of all the symbols.
*
* Inputs: none
*
*------------------------------------------------------------------*/
void symbols_list (void)
{
int n;
dw_printf ("\n");
dw_printf ("\tPRIMARY SYMBOL TABLE\n");
dw_printf ("\n");
dw_printf ("sym GPSxy GPSCnn APRStt Icon\n");
dw_printf ("--- ----- ------ ------ ----\n");
for (n = 1; n < SYMTAB_SIZE; n++) {
dw_printf (" /%c %s %02d AB1%02d %s\n", n + ' ', primary_symtab[n].xy, n, n, primary_symtab[n].description);
}
dw_printf ("\n");
dw_printf ("\tALTERNATE SYMBOL TABLE\n");
dw_printf ("\n");
dw_printf ("sym GPSxy GPSEnn APRStt Icon\n");
dw_printf ("--- ----- ------ ------ ----\n");
for (n = 1; n < SYMTAB_SIZE; n++) {
dw_printf (" \\%c %s %02d AB2%02d %s\n", n + ' ', alternate_symtab[n].xy, n, n, alternate_symtab[n].description);
}
dw_printf ("\n");
dw_printf ("\tNEW SYMBOLS from symbols-new.txt\n");
dw_printf ("\n");
dw_printf ("sym GPSxyz GPSxnn APRStt Icon\n");
dw_printf ("--- ------ ------ ------ ----\n");
for (n = 0; n < new_sym_len; n++) {
int overlay = new_sym_ptr[n].overlay;
int symbol = new_sym_ptr[n].symbol;
char tones[12];
symbols_to_tones (overlay, symbol, tones);
if (overlay == '/') {
dw_printf (" %c%c %s%c C%02d %-7s %s\n", overlay, symbol,
primary_symtab[symbol - ' '].xy, ' ',
symbol - ' ', tones,
new_sym_ptr[n].description);
}
else if (isupper(overlay) || isdigit(overlay)) {
dw_printf (" %c%c %s%c %-7s %s\n", overlay, symbol,
alternate_symtab[symbol - ' '].xy, overlay,
tones,
new_sym_ptr[n].description);
}
else {
dw_printf (" %c%c %s%c E%02d %-7s %s\n", overlay, symbol,
alternate_symtab[symbol - ' '].xy, ' ',
symbol - ' ', tones,
new_sym_ptr[n].description);
}
}
dw_printf ("\n");
} /* end symbols_list */
/*------------------------------------------------------------------
*
* Function: symbols_from_dest_or_src
@ -602,19 +692,19 @@ int symbols_into_dest (char symtab, char symbol, char *dest)
if (symbol >= '!' && symbol <= '~' && symtab == '/') {
/* Primary Symbol table. */
sprintf (dest, "GPSC%02d", symbol - ' ');
snprintf (dest, 7, "GPSC%02d", symbol - ' ');
return (0);
}
else if (symbol >= '!' && symbol <= '~' && symtab == '\\') {
/* Alternate Symbol table. */
sprintf (dest, "GPSE%02d", symbol - ' ');
snprintf (dest, 7, "GPSE%02d", symbol - ' ');
return (0);
}
else if (symbol >= '!' && symbol <= '~' && (isupper(symtab) || isdigit(symtab))) {
/* Alternate Symbol table with overlay. */
sprintf (dest, "GPS%s%c", alternate_symtab[symbol - ' '].xy, symtab);
snprintf (dest, 7, "GPS%s%c", alternate_symtab[symbol - ' '].xy, symtab);
return (0);
}
@ -623,7 +713,7 @@ int symbols_into_dest (char symtab, char symbol, char *dest)
dw_printf ("Could not convert symbol \"%c%c\" to GPSxyz destination format.\n",
symtab, symbol);
strcpy (dest, "GPS???"); /* Error. */
strlcpy (dest, "GPS???", sizeof(dest)); /* Error. */
return (1);
}
@ -637,16 +727,19 @@ int symbols_into_dest (char symtab, char symbol, char *dest)
* Inputs: symtab /, \, 0-9, A-Z
* symbol any printable character ! to ~
*
* desc_size Size of description provided by caller
* so we can avoid buffer overflow.
*
* Outputs: description Text description.
* "--no-symbol--" if error.
*
*
*
* Description: This is used for the monitoring and the
* decode_aprs utility.
*
*------------------------------------------------------------------*/
void symbols_get_description (char symtab, char symbol, char *description)
void symbols_get_description (char symtab, char symbol, char *description, size_t desc_size)
{
char tmp2[2];
int j;
@ -672,7 +765,7 @@ void symbols_get_description (char symtab, char symbol, char *description)
/* We do the latter. */
symbol = ' ';
strcpy (description, primary_symtab[symbol-' '].description);
strlcpy (description, primary_symtab[symbol-' '].description, desc_size);
return;
}
@ -688,7 +781,7 @@ void symbols_get_description (char symtab, char symbol, char *description)
for (j=0; j<new_sym_len; j++) {
if (symtab == new_sym_ptr[j].overlay && symbol == new_sym_ptr[j].symbol) {
strcpy (description, new_sym_ptr[j].description);
strlcpy (description, new_sym_ptr[j].description, desc_size);
return;
}
}
@ -696,15 +789,15 @@ void symbols_get_description (char symtab, char symbol, char *description)
// Otherwise use the original symbol tables.
if (symtab == '/') {
strcpy (description, primary_symtab[symbol-' '].description);
strlcpy (description, primary_symtab[symbol-' '].description, desc_size);
}
else {
strcpy (description, alternate_symtab[symbol-' '].description);
strlcpy (description, alternate_symtab[symbol-' '].description, desc_size);
if (symtab != '\\') {
strcat (description, " w/overlay ");
strlcat (description, " w/overlay ", desc_size);
tmp2[0] = symtab;
tmp2[1] = '\0';
strcat (description, tmp2);
strlcat (description, tmp2, desc_size);
}
}
@ -798,6 +891,57 @@ int symbols_code_from_description (char overlay, char *description, char *symtab
} /* end symbols_code_from_description */
/*------------------------------------------------------------------
*
* Function: symbols_to_tones
*
* Purpose: Convert symbol to APRStt tone sequence.
*
* Inputs: symtab/overlay
* symbol
*
* Output: tones - string of AB...
*
* Description:
*
* Primary: AB1nn nn = same number as GPSCnn
* Alternate: AB2nn nn = same number as GPSEnn
* with overlay: AB0nntt nn = same as with alternate
* tt = one or two tones from two key method.
*
*------------------------------------------------------------------*/
void symbols_to_tones (char symtab, char symbol, char *tones)
{
if (symtab == '/') {
// TODO: potential buffer overflow.
sprintf (tones, "AB1%02d", symbol - ' ');
}
else if (isupper(symtab) || isdigit(symtab)) {
char text[2];
char tt[8];
text[0] = symtab;
text[1] = '\0';
tt_text_to_two_key (text, 0, tt);
sprintf (tones, "AB0%02d%s", symbol - ' ', tt);
}
else {
sprintf (tones, "AB2%02d", symbol - ' ');
}
} /* end symbols_to_tones */
#if 0
/* Quick, incomplete, unit test. */

View File

@ -3,12 +3,17 @@
void symbols_init (void);
void symbols_list (void);
void symbols_from_dest_or_src (char dti, char *src, char *dest, char *symtab, char *symbol);
int symbols_into_dest (char symtab, char symbol, char *dest);
void symbols_get_description (char symtab, char symbol, char *description);
void symbols_get_description (char symtab, char symbol, char *description, size_t desc_size);
int symbols_code_from_description (char overlay, char *description, char *symtab, char *symbol);
void symbols_to_tones (char symtab, char symbol, char *tones);
/* end symbols.h */

View File

@ -0,0 +1,55 @@
# Sample configuration for demonstration of sending telemetry.
# Here we try to replicate actual data heard for a balloon.
CHANNEL 0
MYCALL M0XER-3
# These will send the beacons to the transmitter (which you disconnected, right?)
# First the metadata.
# Channel 1: Battery voltage, Volts, scaled up by 100
# Channel 2: Solar voltage, Volts, scaled up by 100
# Channel 3: Temperature, degrees C, sent as Kelvin x 10
# Channel 4: Number of satellites, no units
# Note: When using Strawberry perl, as specified in the example, Windows knows
# that the .pl file type is associated with it. When using a different implementation
# of perl, which doesn't make this association of file type to application, it might
# be necessary to use something like this instead:
#
# ... infocmd="c:\strawberry\perl\bin\perl.exe telem-parm.pl M0XER-3 Vbat Vsolar Temp Sat"
# Here we use the generic scripts to generate the messages with metadata.
# The "infocmd=..." option means use the result for the info part of the packet.
CBEACON delay=0:10 every=1:00 infocmd="telem-parm.pl M0XER-3 Vbat Vsolar Temp Sat"
CBEACON delay=0:12 every=1:00 infocmd="telem-unit.pl M0XER-3 V V C """" m"
CBEACON delay=0:14 every=1:00 infocmd="telem-eqns.pl M0XER-3 0 0.001 0 0 0.001 0 0 0.1 -273.2 0 1 0 0 1 0"
CBEACON delay=0:16 every=1:00 infocmd="telem-bits.pl M0XER-3 11111111 ""10mW research balloon"""
# Now the telemetry data.
# In a real situation, the location and telemetry data would come from sensors.
# Here we have just hardcoded 3 sets of historical data as a demonstration.
# telem-balloon.pl accumulates the data then invokes telem-data91.pl to convert
# it to the compressed format. This is inserted into the position comment with "commentcmd=..."
PBEACON compress=1 delay=0:20 every=1:00 via=WIDE2-1 symbol=Balloon lat=61^34.2876N lon=155^40.0931W alt=12953 commentcmd="telem-balloon.pl 3307 4.383 0.436 -34.6 12"
PBEACON compress=1 delay=0:22 every=1:00 via=WIDE2-1 symbol=Balloon lat=51^07.4402N lon=124^14.4472W alt=12563 commentcmd="telem-balloon.pl 6524 4.515 0.653 -1.3 7"
PBEACON compress=1 delay=0:24 every=1:00 via=WIDE2-1 symbol=Balloon lat=55^58.5558N lon=122^28.5933W alt=12680 commentcmd="telem-balloon.pl 7458 4.521 0.587 -8.3 7"
# Now we do the same thing again.
# This time, add the SENDTO=R0 option to simulate reception.
# These will be sent to any attached applications so you can see how they process the data.
CBEACON SENDTO=R0 delay=0:30 every=1:00 infocmd="telem-parm.pl M0XER-3 Vbat Vsolar Temp Sat"
CBEACON SENDTO=R0 delay=0:32 every=1:00 infocmd="telem-unit.pl M0XER-3 V V C """" m"
CBEACON SENDTO=R0 delay=0:34 every=1:00 infocmd="telem-eqns.pl M0XER-3 0 0.001 0 0 0.001 0 0 0.1 -273.2 0 1 0 0 1 0"
CBEACON SENDTO=R0 delay=0:36 every=1:00 infocmd="telem-bits.pl M0XER-3 11111111 ""10mW research balloon"""
PBEACON SENDTO=R0 compress=1 delay=0:40 every=1:00 via=WIDE2-1 symbol=Balloon lat=61^34.2876N lon=155^40.0931W alt=12953 commentcmd="telem-balloon.pl 3307 4.383 0.436 -34.6 12"
PBEACON SENDTO=R0 compress=1 delay=0:42 every=1:00 via=WIDE2-1 symbol=Balloon lat=51^07.4402N lon=124^14.4472W alt=12563 commentcmd="telem-balloon.pl 6524 4.515 0.653 -1.3 7"
PBEACON SENDTO=R0 compress=1 delay=0:44 every=1:00 via=WIDE2-1 symbol=Balloon lat=55^58.5558N lon=122^28.5933W alt=12680 commentcmd="telem-balloon.pl 7458 4.521 0.587 -8.3 7"

View File

@ -0,0 +1,43 @@
#!/usr/bin/perl
# Part of Dire Wolf APRS Telemetry Toolkit, WB2OSZ, 2015
# In a real situation this would obtain data from sensors.
# For demonstration purposes we use historical data supplied on the command line.
if ($#ARGV+1 != 5) {
print STDERR "5 command line arguments must be provided.\n";
usage();
}
($seq,$vbat,$vsolar,$temp,$sat) = @ARGV;
# Scale to integer in range of 0 to 8280.
# This must be the inverse of the mapping in the EQNS message.
$vbat = int(($vbat * 1000) + 0.5);
$vsolar = int(($vsolar * 1000) + 0.5);
$temp = int((($temp + 273.2) * 10) + 0.5);
exit system("telem-data91.pl $seq $vbat $vsolar $temp $sat");
sub usage ()
{
print STDERR "\n";
print STDERR "balloon.pl - Format data into Compressed telemetry format.\n";
print STDERR "\n";
print STDERR "In a real situation this would obtain data from sensors.\n";
print STDERR "For demonstration purposes we use historical data supplied on the command line.\n";
print STDERR "\n";
print STDERR "Usage: balloon.pl seq vbat vsolar temp sat\n";
print STDERR "\n";
print STDERR "Where,\n";
print STDERR " seq is a sequence number.\n";
print STDERR " vbat is battery voltage.\n";
print STDERR " vsolar is solar cell voltage.\n";
print STDERR " temp is temperature, degrees C.\n";
print STDERR " sat is number of GPS satellites visible.\n";
exit 1;
}

View File

@ -0,0 +1,33 @@
#!/usr/bin/perl
# Part of Dire Wolf APRS Telemetry Toolkit, WB2OSZ, 2015
if ($#ARGV+1 < 2 || $#ARGV+1 > 3) {
print STDERR "A callsign, bit sense string, and optional project title must be provided.\n";
usage();
}
# Separate out call and pad to 9 characters.
$call = shift @ARGV;
$call = substr($call . " ", 0, 9);
if ( ! ($ARGV[0] =~ m/^[01]{8}$/)) {
print STDERR "The bit-sense value must be 8 binary digits.\n";
usage();
}
print ":$call:BITS." . join (',', @ARGV) . "\n";
exit 0;
sub usage ()
{
print STDERR "\n";
print STDERR "telem-bits.pl - Generate BITS message with bit polarity and optional project title.\n";
print STDERR "\n";
print STDERR "Usage: telem-bits.pl call bit-sense [ project-title ]\n";
print STDERR "\n";
print STDERR "Bit-sense is string of 8 binary digits.\n";
print STDERR "If project title contains any spaces, enclose it in quotes.\n";
exit 1;
}

View File

@ -0,0 +1,31 @@
#!/usr/bin/perl
# Part of Dire Wolf APRS Telemetry Toolkit, WB2OSZ, 2015
if ($#ARGV+1 < 2 || $#ARGV+1 > 7) {
print STDERR "2 to 7 command line arguments must be provided.\n";
usage();
}
if ($#ARGV+1 == 7) {
if ( ! ($ARGV[6] =~ m/^[01]{8}$/)) {
print STDERR "The sixth value must be 8 binary digits.\n";
usage();
}
}
print "T#" . join (',', @ARGV) . "\n";
exit 0;
sub usage ()
{
print STDERR "\n";
print STDERR "telem-data.pl - Format data into Telemetry Report format.\n";
print STDERR "\n";
print STDERR "Usage: telem-data.pl sequence value1 [ value2 ... ]\n";
print STDERR "\n";
print STDERR "A sequence number and up to 5 analog values can be specified.\n";
print STDERR "Any sixth value must be 8 binary digits.\n";
exit 1;
}

View File

@ -0,0 +1,65 @@
#!/usr/bin/perl
# Part of Dire Wolf APRS Telemetry Toolkit, WB2OSZ, 2015
# For explanation of encoding see:
# http://he.fi/doc/aprs-base91-comment-telemetry.txt
if ($#ARGV+1 < 2 || $#ARGV+1 > 7) {
print STDERR "2 to 7 command line arguments must be provided.\n";
usage();
}
if ($#ARGV+1 == 7) {
if ( ! ($ARGV[6] =~ m/^[01]{8}$/)) {
print STDERR "The sixth value must be 8 binary digits.\n";
usage();
}
# Convert binary digits to value.
$ARGV[6] = oct("0b" . reverse($ARGV[6]));
}
$result = "|";
for ($n = 0 ; $n <= $#ARGV; $n++) {
#print $n . " = " . $ARGV[$n] . "\n";
$v = $ARGV[$n];
if ($v != int($v) || $v < 0 || $v > 8280) {
print STDERR "$v is not an integer in range of 0 to 8280.\n";
usage();
}
$result .= base91($v);
}
$result .= "|";
print "$result\n";
exit 0;
sub base91 ()
{
my $x = @_[0];
my $d1 = int ($x / 91);
my $d2 = $x % 91;
return chr($d1+33) . chr($d2+33);
}
sub usage ()
{
print STDERR "\n";
print STDERR "telem-data91.pl - Format data into compressed base 91 telemetry.\n";
print STDERR "\n";
print STDERR "Usage: telem-data91.pl sequence value1 [ value2 ... ]\n";
print STDERR "\n";
print STDERR "A sequence number and up to 5 analog values can be specified.\n";
print STDERR "Any sixth value must be 8 binary digits.\n";
print STDERR "Values must be integers in range of 0 to 8280.\n";
exit 1;
}

View File

@ -0,0 +1,28 @@
#!/usr/bin/perl
# Part of Dire Wolf APRS Telemetry Toolkit, WB2OSZ, 2015
if ($#ARGV+1 != 4 && $#ARGV+1 != 7 && $#ARGV+1 != 10 && $#ARGV+1 != 13 && $#ARGV+1 != 16) {
print STDERR "A callsign and 1 to 5 sets of 3 coefficients must be provided.\n";
usage();
}
# Separate out call and pad to 9 characters.
$call = shift @ARGV;
$call = substr($call . " ", 0, 9);
print ":$call:EQNS." . join (',', @ARGV) . "\n";
exit 0;
sub usage ()
{
print STDERR "\n";
print STDERR "telem-eqns.pl - Generate EQNS message with scaling coefficients\n";
print STDERR "\n";
print STDERR "Usage: telem-eqns.pl call a1 b1 c1 [ a2 b2 c2 ... ]\n";
print STDERR "\n";
print STDERR "Specify a callsign and 1 to 5 sets of 3 coefficients.\n";
print STDERR "See APRS protocol reference for their meaning.\n";
exit 1;
}

View File

@ -0,0 +1,7 @@
2E0TOY>APRS::M0XER-3 :BITS.11111111,10mW research balloon
2E0TOY>APRS::M0XER-3 :PARM.Vbat,Vsolar,Temp,Sat
2E0TOY>APRS::M0XER-3 :EQNS.0,0.001,0,0,0.001,0,0,0.1,-273.2,0,1,0,0,1,0
2E0TOY>APRS::M0XER-3 :UNIT.V,V,C,,m
M0XER-3>APRS63,WIDE2-1:!//Bap'.ZGO JHAE/A=042496|E@Q0%i;5!-|
M0XER-3>APRS63,WIDE2-1:!/4\;u/)K$O J]YD/A=041216|h`RY(1>q!(|
M0XER-3>APRS63,WIDE2-1:!/23*f/R$UO Jf'x/A=041600|rxR_'J>+!(|

View File

@ -0,0 +1,27 @@
#!/usr/bin/perl
# Part of Dire Wolf APRS Telemetry Toolkit, WB2OSZ, 2015
if ($#ARGV+1 < 2 || $#ARGV+1 > 14) {
print STDERR "A callsign and 1 to 13 channel names must be provided.\n";
usage();
}
# Separate out call and pad to 9 characters.
$call = shift @ARGV;
$call = substr($call . " ", 0, 9);
print ":$call:PARM." . join (',', @ARGV) . "\n";
exit 0;
sub usage ()
{
print STDERR "\n";
print STDERR "telem-parm.pl - Generate PARM message with channel names.\n";
print STDERR "\n";
print STDERR "Usage: telem-parm.pl call aname1 ... aname5 dname1 .,, dname8\n";
print STDERR "\n";
print STDERR "Specify a callsign and up to 13 names for the analog & digital channels.\n";
exit 1;
}

View File

@ -0,0 +1,27 @@
#!/usr/bin/perl
# Part of Dire Wolf APRS Telemetry Toolkit, WB2OSZ, 2015
if ($#ARGV+1 < 2 || $#ARGV+1 > 14) {
print STDERR "A callsign and 1 to 13 units/labels must be provided.\n";
usage();
}
# Separate out call and pad to 9 characters.
$call = shift @ARGV;
$call = substr($call . " ", 0, 9);
print ":$call:UNIT." . join (',', @ARGV) . "\n";
exit 0;
sub usage ()
{
print STDERR "\n";
print STDERR "telem-unit.pl - Generate UNIT message with channel units/labels.\n";
print STDERR "\n";
print STDERR "Usage: telem-unit.pl call unit1 ... unit5 label1 .,, label8\n";
print STDERR "\n";
print STDERR "Specify a callsign and up to 13 names for the units/labels.\n";
exit 1;
}

View File

@ -0,0 +1,25 @@
# Sample configuration for demonstration of sending telemetry.
# Here we take a voltage from an analog to digital converter (ADC).
ADEVICE plughw:1,0
MYCALL MYCALL-9
# For demonstration purposes, the metadata will be sent each minute and
# voltage data every 10 seconds. In actual practice, it would be much less frequent.
# First the metadata.
# The "infocmd=..." option means use the result for the info part of the packet.
CBEACON delay=0:10 every=1:00 via=WIDE2-1 infocmd="telem-parm.pl MYCALL-9 Supply"
CBEACON delay=0:11 every=1:00 via=WIDE2-1 infocmd="telem-unit.pl MYCALL-9 Volts"
# Now the telemetry data.
# First we use telem-volts.py to read a volage from the A/D converter.
# This is supplied to telem-data.pl as a command line argument.
# The result is used as the info part of a custom beacon.
CBEACON delay=0:15 every=0:10 via=WIDE2-1 infocmd="telem-data.pl 0 `PYTHONPATH=~/Adafruit-Raspberry-Pi-Python-Code/Adafruit_ADS1x15 telem-volts.py`"

View File

@ -0,0 +1,36 @@
#!/usr/bin/python
# Part of Dire Wolf APRS Telemetry Toolkit, WB2OSZ, 2015
# Derived from
# https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code/blob/master/Adafruit_ADS1x15/ads1x15_ex_singleended.py
import time, signal, sys
from Adafruit_ADS1x15 import ADS1x15
ADS1015 = 0x00 # 12-bit ADC
ADS1115 = 0x01 # 16-bit ADC
# Set ADC full scale to 2048 mV.
# Can't use original 4096 with 3.3V supply.
gain = 2048
# Select the sample time, 1/sps second.
# Longer is better to average out noise.
sps = 64
# Set this to ADS1015 or ADS1115 depending on the ADC you are using!
adc = ADS1x15(ic=ADS1115)
# Values for voltage divider on ADC input.
r1 = 1000000.
r2 = 100000.
# Read channel 0 in single-ended mode using the settings above
volts = adc.readADCSingleEnded(0, gain, sps) * 0.001 * (r1+r2) / r2
# Calibration correction specific to my hardware.
# (multiply by expected value, divide by uncalibrated result.)
#volts = volts * 4.98 / 4.889
print "%.3f" % (volts)

View File

@ -129,7 +129,7 @@ static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_A
*
* Inputs: station - Station name with optional SSID.
*
* REturns: Pointer to metadata.
* Returns: Pointer to metadata.
*
*--------------------------------------------------------------------*/
@ -145,6 +145,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);
return (p);
}
}
@ -153,6 +157,7 @@ static struct t_metadata_s * t_get_metadata (char *station)
memset (p, 0, sizeof (struct t_metadata_s));
p->magic1 = MAGIC1;
strncpy (p->station, station, sizeof(p->station)-1);
for (n = 0; n < T_NUM_ANALOG; n++) {
@ -232,6 +237,7 @@ static int t_ndp (char *str)
* quiet - suppress error messages.
*
* Outputs: output - Decoded telemetry in human readable format.
* TODO: How big does it need to be? (buffer overflow?)
* comment - Any comment after the data.
*
* Description: The first character, after the "T" data type indicator, must be "#"
@ -249,7 +255,7 @@ 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.
* We will accept these but issue a warning that others might not.
*
*--------------------------------------------------------------------*/
@ -275,6 +281,10 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output
#endif
pm = t_get_metadata(station);
assert (pm->magic1 == MAGIC1);
assert (pm->magic2 == MAGIC2);
seq = 0;
for (n = 0; n < T_NUM_ANALOG; n++) {
araw[n] = G_UNKNOWN;
@ -294,10 +304,15 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output
/*
* Make a copy of the input string because this will alter it.
* Remove any trailing CR/LF.
*/
strncpy (stemp, info+2, sizeof(stemp));
stemp[sizeof(stemp)-1] = '\0';
memset (stemp, 0, sizeof(stemp));
strncpy (stemp, info+2, sizeof(stemp)-1);
for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) {
*p = '\0';
}
next = stemp;
p = strsep(&next,",");
@ -309,13 +324,16 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output
araw[n] = atof(p);
ndp[n] = t_ndp(p);
}
if (strlen(p) != 3 || araw[n] < 0 || araw[n] > 255 || araw[n] != (int)(araw[n])) {
if ( ! quiet) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Telemetry analog values should be 3 digit integer values in range of 000 to 255.\n");
dw_printf("Some applications might not interpret \"%s\" properly.\n", p);
}
}
// Version 1.3: Suppress this message.
// No one pays attention to the original 000 to 255 range.
// BTW, this doesn't trap values like 0.0 or 1.0
//if (strlen(p) != 3 || araw[n] < 0 || araw[n] > 255 || araw[n] != (int)(araw[n])) {
// if ( ! quiet) {
// text_color_set(DW_COLOR_ERROR);
// dw_printf("Telemetry analog values should be 3 digit integer values in range of 000 to 255.\n");
// dw_printf("Some applications might not interpret \"%s\" properly.\n", p);
// }
//}
n++;
}
@ -331,8 +349,10 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output
dw_printf("Expected to find 8 binary digits after \"%s\" for the digital values.\n", p);
}
}
// TODO: test this!
if (strlen(next) > 8) {
strcpy (comment, next+8);
strlcpy (comment, next+8, sizeof(comment));
next[8] = '\0';
}
for (k = 0; k < strlen(next); k++) {
@ -393,7 +413,7 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output
* Description: We are expecting from 2 to 7 pairs of base 91 digits.
* The first pair is the sequence number.
* Next we have 1 to 5 analog values.
* If digital values are present, all 5 analog values must be present.
* If digital values are present, all 5 analog values must be present.
*
*--------------------------------------------------------------------*/
@ -447,6 +467,9 @@ void telemetry_data_base91 (char *station, char *cdata, char *output)
pm = t_get_metadata(station);
assert (pm->magic1 == MAGIC1);
assert (pm->magic2 == MAGIC2);
seq = 0;
for (n = 0; n < T_NUM_ANALOG; n++) {
araw[n] = G_UNKNOWN;
@ -513,7 +536,7 @@ void telemetry_data_base91 (char *station, char *cdata, char *output)
* Outputs: Stored for future use when data values are received.
*
* Description: The first 5 characters of the message are "PARM." and the
* rest is a variable length list of comma separated names.
* rest is a variable length list of comma separated names.
*
* The original spec has different maximum lengths for different
* fields which we will ignore.
@ -540,12 +563,19 @@ void telemetry_name_message (char *station, char *msg)
/*
* Make a copy of the input string because this will alter it.
* Remove any trailing CR LF.
*/
strncpy (stemp, msg, sizeof(stemp));
stemp[sizeof(stemp)-1] = '\0';
memset (stemp, 0, sizeof(stemp));
strncpy (stemp, msg, sizeof(stemp)-1);
for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) {
*p = '\0';
}
pm = t_get_metadata(station);
assert (pm->magic1 == MAGIC1);
assert (pm->magic2 == MAGIC2);
next = stemp;
@ -553,6 +583,7 @@ void telemetry_name_message (char *station, char *msg)
while ((p = strsep(&next,",")) != NULL) {
if (n < T_NUM_ANALOG + T_NUM_DIGITAL) {
if (strlen(p) > 0 && strcmp(p,"-") != 0) {
memset (pm->name[n], 0, T_STR_LEN);
strncpy (pm->name[n], p, T_STR_LEN-1);
}
n++;
@ -586,7 +617,7 @@ void telemetry_name_message (char *station, char *msg)
* Outputs: Stored for future use when data values are received.
*
* Description: The first 5 characters of the message are "UNIT." and the
* rest is a variable length list of comma separated units/labels.
* rest is a variable length list of comma separated units/labels.
*
* The original spec has different maximum lengths for different
* fields which we will ignore.
@ -610,12 +641,19 @@ void telemetry_unit_label_message (char *station, char *msg)
/*
* Make a copy of the input string because this will alter it.
* Remove any trailing CR LF.
*/
memset (stemp, 0, sizeof(stemp));
strncpy (stemp, msg, sizeof(stemp)-1);
strncpy (stemp, msg, sizeof(stemp));
stemp[sizeof(stemp)-1] = '\0';
for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) {
*p = '\0';
}
pm = t_get_metadata(station);
assert (pm->magic1 == MAGIC1);
assert (pm->magic2 == MAGIC2);
next = stemp;
@ -623,6 +661,7 @@ void telemetry_unit_label_message (char *station, char *msg)
while ((p = strsep(&next,",")) != NULL) {
if (n < T_NUM_ANALOG + T_NUM_DIGITAL) {
if (strlen(p) > 0) {
memset (pm->unit[n], 0, T_STR_LEN);
strncpy (pm->unit[n], p, T_STR_LEN-1);
}
n++;
@ -657,7 +696,7 @@ void telemetry_unit_label_message (char *station, char *msg)
* Outputs: Stored for future use when data values are received.
*
* Description: The first 5 characters of the message are "EQNS." and the
* rest is a comma separated list of 15 floating point values.
* rest is a comma separated list of 15 floating point values.
*
* The spec appears to require all 15 so we will issue an
* error if fewer found.
@ -681,12 +720,19 @@ void telemetry_coefficents_message (char *station, char *msg, int quiet)
/*
* Make a copy of the input string because this will alter it.
* Remove any trailing CR LF.
*/
strncpy (stemp, msg, sizeof(stemp));
stemp[sizeof(stemp)-1] = '\0';
memset (stemp, 0, sizeof(stemp));
strncpy (stemp, msg, sizeof(stemp)-1);
for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) {
*p = '\0';
}
pm = t_get_metadata(station);
assert (pm->magic1 == MAGIC1);
assert (pm->magic2 == MAGIC2);
next = stemp;
@ -765,6 +811,8 @@ 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 (strlen(msg) < 8) {
if ( ! quiet) {
@ -831,34 +879,36 @@ void telemetry_bit_sense_message (char *station, char *msg, int quiet)
* Outputs: output - Decoded telemetry in human readable format.
*
* Description: Process raw data according to any metadata available
* and put into human readable form.
* and put into human readable form.
*
*--------------------------------------------------------------------*/
static void fval_to_str (float x, int ndp, char *str)
#define VAL_STR_SIZE 64
static void fval_to_str (float x, int ndp, char str[VAL_STR_SIZE])
{
if (x == G_UNKNOWN) {
strcpy (str, "?");
strlcpy (str, "?", VAL_STR_SIZE);
}
else {
sprintf (str, "%.*f", ndp, x);
snprintf (str, VAL_STR_SIZE, "%.*f", ndp, x);
}
}
static void ival_to_str (int x, char *str)
static void ival_to_str (int x, char str[VAL_STR_SIZE])
{
if (x == G_UNKNOWN) {
strcpy (str, "?");
strlcpy (str, "?", VAL_STR_SIZE);
}
else {
sprintf (str, "%d", x);
snprintf (str, VAL_STR_SIZE, "%d", x);
}
}
static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_ANALOG], int ndp[T_NUM_ANALOG], int draw[T_NUM_DIGITAL], char *output)
{
int n;
char stemp[50];
char val_str[VAL_STR_SIZE];
assert (pm != NULL);
@ -872,9 +922,9 @@ static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_A
strcat (output, ": ");
}
ival_to_str (seq, stemp);
ival_to_str (seq, val_str);
strcat (output, "Seq=");
strcat (output, stemp);
strcat (output, val_str);
for (n = 0; n < T_NUM_ANALOG; n++) {
@ -905,8 +955,8 @@ static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_A
z = pm->coeff_ndp[n][C_A] == 0 ? 0 : pm->coeff_ndp[n][C_A] + ndp[n] + ndp[n];
fndp = MAX (z, MAX(pm->coeff_ndp[n][C_B] + ndp[n], pm->coeff_ndp[n][C_C]));
}
fval_to_str (fval, fndp, stemp);
strcat (output, stemp);
fval_to_str (fval, fndp, val_str);
strcat (output, val_str);
if (strlen(pm->unit[n]) > 0) {
strcat (output, " ");
strcat (output, pm->unit[n]);
@ -936,13 +986,13 @@ static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_A
dval = draw[n] ^ ! pm->sense[n];
}
ival_to_str (dval , stemp);
ival_to_str (dval, val_str);
if (strlen(pm->unit[T_NUM_ANALOG+n]) > 0) {
strcat (output, " ");
strcat (output, pm->unit[T_NUM_ANALOG+n]);
}
strcat (output, stemp);
strcat (output, val_str);
}
}

View File

@ -1,3 +1,4 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
@ -38,6 +39,12 @@
* http://en.wikipedia.org/wiki/ANSI_escape_code
* http://academic.evergreen.edu/projects/biophysics/technotes/program/ansi_esc.htm
*
*
>>>> READ THIS PART!!! <<<<
*
*
* Problem: The ANSI escape sequences, used on Linux, allow 8 basic colors.
* Unfortunately, white is not one of them. We only have dark
* white, also known as light gray. To get brighter colors,
@ -111,6 +118,8 @@ static const char clear_eos[] = "\e[0J";
/* expected bright/bold (1) to get bright white background. */
/* Makes no sense but I stumbled across that somewhere. */
/* If you do get blinking, remove all references to "\e[5;47m" */
static const char background_white[] = "\e[5;47m";
/* Whenever a dark color is used, the */

12
tq.c
View File

@ -226,6 +226,12 @@ void tq_append (int chan, int prio, packet_t pp)
assert (chan >= 0 && chan < MAX_CHANS);
assert (prio >= 0 && prio < TQ_NUM_PRIO);
if (pp == NULL) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("INTERNAL ERROR: tq_append NULL packet pointer. Please report this!\n");
return;
}
#if AX25MEMDEBUG
if (ax25memdebug_get()) {
@ -245,16 +251,16 @@ void tq_append (int chan, int prio, packet_t pp)
* Is transmit queue out of control?
*
* There is no technical reason to limit the transmit packet queue length, it just seemed like a good
* warning that something wasnt right.
* warning that something wasn't right.
* When this was written, I was mostly concerned about APRS where packets would only be sent
* occasionally and they can be discarded if they cant be sent out in a reasonable amount of time.
* occasionally and they can be discarded if they can't be sent out in a reasonable amount of time.
*
* If a large file is being sent, with TCP/IP, it is perfectly reasonable to have a large number
* of packets waiting for transmission.
*
* Ideally, the application should be able to throttle the transmissions so the queue doesn't get too long.
* If using the KISS interface, there is no way to get this information from the TNC back to the client app.
* The AGW network interface does have a command y to query about the number of frames waiting for transmission.
* The AGW network interface does have a command 'y' to query about the number of frames waiting for transmission.
* This was implemented in version 1.2.
*
* I'd rather not take out the queue length check because it is a useful sanity check for something going wrong.

403
tt_text.c
View File

@ -139,7 +139,7 @@ static const char call10encoding[10][4] = {
/*
* 4 digit gridsquares to cover "99.99% of the world's population."
* Special satellite 4 digit gridsquares to cover "99.99% of the world's population."
*/
static const char grid[10][10][3] =
@ -368,6 +368,77 @@ int tt_text_to_two_key (char *text, int quiet, char *buttons)
} /* end tt_text_to_two_key */
/*------------------------------------------------------------------
*
* Name: tt_letter_to_two_digits
*
* Purpose: Convert one letter to 2 digit representation.
*
* Inputs: c - One letter.
*
* quiet - True to suppress error messages.
*
* Outputs: buttons - Sequence of two buttons to press.
* "00" for error because this is probably
* being used to build up a fixed length
* string where positions are signficant.
*
* Returns: Number of errors detected.
*
*----------------------------------------------------------------*/
// TODO: need to test this.
int tt_letter_to_two_digits (char c, int quiet, char *buttons)
{
char *b = buttons;
int row, col;
int errors = 0;
int found;
*b = '\0';
if (islower(c)) {
c = toupper(c);
}
if ( ! isupper(c)) {
errors++;
if (! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Letter to two digits: \"%c\" found where a letter is required.\n", c);
}
strcpy (buttons, "00");
return (errors);
}
/* Search in the translation table. */
found = 0;
for (row=0; row<10 && ! found; row++) {
for (col=0; col<4 && ! found; col++) {
if (c == translate[row][col]) {
*b++ = '0' + row;
*b++ = '1' + col;
*b = '\0';
found = 1;
}
}
}
if (! found) {
errors++;
text_color_set (DW_COLOR_ERROR);
dw_printf ("Letter to two digits: INTERNAL ERROR. Should not be here.\n");
strcpy (buttons, "00");
}
return (errors);
} /* end tt_letter_to_two_digits */
/*------------------------------------------------------------------
*
* Name: tt_text_to_call10
@ -478,9 +549,9 @@ int tt_text_to_call10 (char *text, int quiet, char *buttons)
/*------------------------------------------------------------------
*
* Name: tt_text_to_gridsquare
* Name: tt_text_to_satsq
*
* Purpose: Convert Gridsquare to 4 digit DTMF representation.
* Purpose: Convert Special Satellite Gridsquare to 4 digit DTMF representation.
*
* Inputs: text - Input string.
* Should be two letters (A thru R) and two digits.
@ -497,7 +568,7 @@ int tt_text_to_call10 (char *text, int quiet, char *buttons)
*
*----------------------------------------------------------------*/
int tt_text_to_gridsquare (char *text, int quiet, char *buttons)
int tt_text_to_satsq (char *text, int quiet, char *buttons)
{
int row, col;
@ -514,7 +585,7 @@ int tt_text_to_gridsquare (char *text, int quiet, char *buttons)
if (! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Gridsquare to DTMF: Gridsquare \"%s\" must be 4 characters.\n", text);
dw_printf ("Satellite Gridsquare to DTMF: Gridsquare \"%s\" must be 4 characters.\n", text);
}
errors++;
return (errors);
@ -530,7 +601,7 @@ int tt_text_to_gridsquare (char *text, int quiet, char *buttons)
if (! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Gridsquare to DTMF: First two characters \"%s\" must be letters in range of A to R.\n", text);
dw_printf ("Satellite Gridsquare to DTMF: First two characters \"%s\" must be letters in range of A to R.\n", text);
}
errors++;
return (errors);
@ -540,7 +611,7 @@ int tt_text_to_gridsquare (char *text, int quiet, char *buttons)
if (! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Gridsquare to DTMF: Last two characters \"%s\" must digits.\n", text);
dw_printf ("Satellite Gridsquare to DTMF: Last two characters \"%s\" must digits.\n", text);
}
errors++;
return (errors);
@ -569,13 +640,13 @@ int tt_text_to_gridsquare (char *text, int quiet, char *buttons)
errors++;
if (! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Gridsquare to DTMF: Sorry, your location can't be converted to DTMF.\n");
dw_printf ("Satellite Gridsquare to DTMF: Sorry, your location can't be converted to DTMF.\n");
}
}
return (errors);
} /* end tt_text_to_gridsquare */
} /* end tt_text_to_satsq */
@ -765,6 +836,79 @@ int tt_two_key_to_text (char *buttons, int quiet, char *text)
} /* end tt_two_key_to_text */
/*------------------------------------------------------------------
*
* Name: tt_two_digits_to_letter
*
* Purpose: Convert the two digit representation to one letter.
*
* Inputs: buttons - Input string.
* Should contain exactly two digits.
*
* quiet - True to suppress error messages.
*
* Outputs: text - Converted to string which should contain one upper case letter.
* If error, use 'x' as a placeholder because we are probably
* dealing with fixed length strings where position matters.
*
* Returns: Number of errors detected.
*
*----------------------------------------------------------------*/
// TODO: need to test
int tt_two_digits_to_letter (char *buttons, int quiet, char *text)
{
char c1 = buttons[0];
char c2 = buttons[1];
int row, col;
int errors = 0;
strcpy (text, "x");
if (c1 >= '2' && c1 <= '9') {
if (c2 >= '1' && c2 <= '4') {
row = c1 - '0';
col = c2 - '1';
if (translate[row][col] != 0) {
text[0] = translate[row][col];
text[1] = '\0';
}
else {
errors++;
strcpy (text, "x");
if (! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Two digits to letter: Invalid combination \"%c%c\".\n", c1, c2);
}
}
}
else {
errors++;
strcpy (text, "x");
if (! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Two digits to letter: Second character \"%c\" must be in range of 1 through 4.\n", c2);
}
}
}
else {
errors++;
strcpy (text, "x");
if (! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Two digits to letter: First character \"%c\" must be in range of 2 through 9.\n", c1);
}
}
return (errors);
} /* end tt_two_digits_to_letter */
/*------------------------------------------------------------------
*
* Name: tt_call10_to_text
@ -864,9 +1008,218 @@ int tt_call10_to_text (char *buttons, int quiet, char *text)
/*------------------------------------------------------------------
*
* Name: tt_gridsquare_to_text
* Name: tt_mhead_to_text
*
* Purpose: Convert the 4 digit DTMF gridsquare to normal 2 letters and 2 digits.
* Purpose: Convert the 4, 6, 10, or 12 digit DTMF representation of Maidenhead
* Grid Square Locator to normal text representation.
*
* Inputs: buttons - Input string.
* Must contain 4, 6, 10, or 12 digits.
*
* quiet - True to suppress error messages.
*
* Outputs: text - Converted to gridsquare with upper case letters and digits.
* Length should be 2, 4, 6, or 8 with alternating letter or digit pairs.
*
* Returns: Number of errors detected.
*
*----------------------------------------------------------------*/
int tt_mhead_to_text (char *buttons, int quiet, char *text)
{
char *b;
char *t;
int errors = 0;
strcpy (text, "");
/* Validity check. */
if (strlen(buttons) != 4 && strlen(buttons) != 6 && strlen(buttons) != 10 && strlen(buttons) != 12) {
if (! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("DTMF to Maidenhead Gridsquare Locator: Input \"%s\" must be exactly 4, 6, 10, or 12 digits.\n", buttons);
}
errors++;
return (errors);
}
for (b = buttons; *b != '\0'; b++) {
if (! isdigit(*b)) {
if (! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("DTMF to Maidenhead Gridsquare Locator: Input \"%s\" can contain only digits.\n", buttons);
}
errors++;
return (errors);
}
}
b = buttons;
t = text;
errors += tt_two_digits_to_letter (b, quiet, t);
b += 2;
t++;
errors += tt_two_digits_to_letter (b, quiet, t);
b += 2;
t++;
if (strlen(buttons) > 4) {
*t++ = *b++;
*t++ = *b++;
*t = '\0';
if (strlen(buttons) > 6) {
errors += tt_two_digits_to_letter (b, quiet, t);
b += 2;
t++;
errors += tt_two_digits_to_letter (b, quiet, t);
b += 2;
t++;
if (strlen(buttons) > 10) {
*t++ = *b++;
*t++ = *b++;
*t = '\0';
}
}
}
return (errors);
} /* end tt_mhead_to_text */
/*------------------------------------------------------------------
*
* Name: tt_text_to_mhead
*
* Purpose: Convert the 2, 4, 6, or 8 character Maidenhead
* Grid Square Locator to DTMF representation.
*
* Outputs: text - Maidenhead Grid Square locator in usual format.
* Length should be 2, 4, 6, or 8 with alternating letter or digit pairs.
*
* Inputs: buttons - Result with 4, 6, 10, or 12 digits.
* Each letter is replaced by two digits.
*
* quiet - True to suppress error messages.
*
* Returns: Number of errors detected.
*
*----------------------------------------------------------------*/
int tt_text_to_mhead (char *text, int quiet, char *buttons)
{
char *b;
char *t;
int errors = 0;
strcpy (buttons, "");
if (strlen(text) != 2 && strlen(text) != 4 && strlen(text) != 6 && strlen(text) != 8) {
if (! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Maidenhead Gridsquare Locator to DTMF: Input \"%s\" must be exactly 2, 4, 6, or 8 characters.\n", text);
}
errors++;
return (errors);
}
t = text;
b = buttons;
if (toupper(t[0]) < 'A' || toupper(t[0]) > 'R' || toupper(t[1]) < 'A' || toupper(t[1]) > 'R') {
if (! quiet) {
text_color_set(DW_COLOR_ERROR);
dw_printf("The first pair of characters in Maidenhead locator \"%s\" must be in range of A thru R.\n", text);
}
errors++;
return(errors);
}
errors += tt_letter_to_two_digits (*t, quiet, b);
t++;
b += 2;
errors += tt_letter_to_two_digits (*t, quiet, b);
t++;
b += 2;
if (strlen(text) > 2) {
if ( ! isdigit(t[0]) || ! isdigit(t[1])) {
if (! quiet) {
text_color_set(DW_COLOR_ERROR);
dw_printf("The second pair of characters in Maidenhead locator \"%s\" must digits 0 thru 9.\n", text);
}
errors++;
return(errors);
}
*b++ = *t++;
*b++ = *t++;
*b = '\0';
if (strlen(text) > 4) {
if (toupper(t[0]) < 'A' || toupper(t[0]) > 'X' || toupper(t[1]) < 'A' || toupper(t[1]) > 'X') {
if (! quiet) {
text_color_set(DW_COLOR_ERROR);
dw_printf("The third pair of characters in Maidenhead locator \"%s\" must be in range of A thru X.\n", text);
}
errors++;
return(errors);
}
errors += tt_letter_to_two_digits (*t, quiet, b);
t++;
b += 2;
errors += tt_letter_to_two_digits (*t, quiet, b);
t++;
b += 2;
if (strlen(text) > 6) {
if ( ! isdigit(t[0]) || ! isdigit(t[1])) {
if (! quiet) {
text_color_set(DW_COLOR_ERROR);
dw_printf("The fourth pair of characters in Maidenhead locator \"%s\" must digits 0 thru 9.\n", text);
}
errors++;
return(errors);
}
*b++ = *t++;
*b++ = *t++;
*b = '\0';
}
}
}
return (errors);
} /* tt_text_to_mhead */
/*------------------------------------------------------------------
*
* Name: tt_satsq_to_text
*
* Purpose: Convert the 4 digit DTMF special Satellite gridsquare to normal 2 letters and 2 digits.
*
* Inputs: buttons - Input string.
* Should contain 4 digits.
@ -879,7 +1232,7 @@ int tt_call10_to_text (char *buttons, int quiet, char *text)
*
*----------------------------------------------------------------*/
int tt_gridsquare_to_text (char *buttons, int quiet, char *text)
int tt_satsq_to_text (char *buttons, int quiet, char *text)
{
char *b;
int row, col;
@ -893,7 +1246,7 @@ int tt_gridsquare_to_text (char *buttons, int quiet, char *text)
if (! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("DTMF to Gridsquare: Input \"%s\" must be exactly 4 digits.\n", buttons);
dw_printf ("DTMF to Satellite Gridsquare: Input \"%s\" must be exactly 4 digits.\n", buttons);
}
errors++;
return (errors);
@ -904,7 +1257,7 @@ int tt_gridsquare_to_text (char *buttons, int quiet, char *text)
if (! isdigit(*b)) {
if (! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("DTMF to Gridsquare: Input \"%s\" can contain only digits.\n", buttons);
dw_printf ("DTMF to Satellite Gridsquare: Input \"%s\" can contain only digits.\n", buttons);
}
errors++;
return (errors);
@ -919,7 +1272,7 @@ int tt_gridsquare_to_text (char *buttons, int quiet, char *text)
return (errors);
} /* end tt_gridsquare_to_text */
} /* end tt_satsq_to_text */
/*------------------------------------------------------------------
@ -1042,9 +1395,15 @@ int main (int argc, char *argv[])
dw_printf ("\"%s\"\n", buttons);
}
n = tt_text_to_gridsquare (text, 1, buttons);
n = tt_text_to_mhead (text, 1, buttons);
if (n == 0) {
dw_printf ("Push buttons for gridsquare:\n");
dw_printf ("Push buttons for Maidenhead Grid Square Locator:\n");
dw_printf ("\"%s\"\n", buttons);
}
n = tt_text_to_satsq (text, 1, buttons);
if (n == 0) {
dw_printf ("Push buttons for satellite gridsquare:\n");
dw_printf ("\"%s\"\n", buttons);
}
@ -1112,9 +1471,15 @@ int main (int argc, char *argv[])
dw_printf ("\"%s\"\n", text);
}
n = tt_gridsquare_to_text (buttons, 1, text);
n = tt_mhead_to_text (buttons, 1, text);
if (n == 0) {
dw_printf ("Decoded gridsquare from 4 DTMF digits:\n");
dw_printf ("Decoded Maidenhead Locator from DTMF digits:\n");
dw_printf ("\"%s\"\n", text);
}
n = tt_satsq_to_text (buttons, 1, text);
if (n == 0) {
dw_printf ("Decoded satellite gridsquare from 4 DTMF digits:\n");
dw_printf ("\"%s\"\n", text);
}

View File

@ -2,7 +2,7 @@
/* tt_text.h */
/* Encode to DTMF representation. */
/* Encode normal human readable to DTMF representation. */
int tt_text_to_multipress (char *text, int quiet, char *buttons);
@ -10,7 +10,9 @@ int tt_text_to_two_key (char *text, int quiet, char *buttons);
int tt_text_to_call10 (char *text, int quiet, char *buttons) ;
int tt_text_to_gridsquare (char *text, int quiet, char *buttons) ;
int tt_text_to_mhead (char *text, int quiet, char *buttons) ;
int tt_text_to_satsq (char *text, int quiet, char *buttons) ;
/* Decode DTMF to normal human readable form. */
@ -21,7 +23,9 @@ int tt_two_key_to_text (char *buttons, int quiet, char *text);
int tt_call10_to_text (char *buttons, int quiet, char *text);
int tt_gridsquare_to_text (char *buttons, int quiet, char *text);
int tt_mhead_to_text (char *buttons, int quiet, char *text);
int tt_satsq_to_text (char *buttons, int quiet, char *text);
/* end tt_text.h */

421
tt_user.c
View File

@ -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, 2015 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
@ -55,6 +55,11 @@
#include "encode_aprs.h"
#include "latlong.h"
#include "server.h"
#include "kiss.h"
#include "kissnet.h"
/*
* Information kept about local APRStt users.
*
@ -86,6 +91,10 @@ static struct tt_user_s {
/* Possibly other tactical call / object label. */
/* Null string indicates table position is not used. */
int count; /* Number of times we received information for this object. */
/* Value 1 means first time and could be used to send */
/* a welcome greeting. */
int ssid; /* SSID to add. */
/* Default of 12 but not always. */
@ -108,6 +117,7 @@ static struct tt_user_s {
/* 3 within 30 seconds to improve chances of */
/* being heard while using digipeater duplicate */
/* removal. */
// TODO: I think implementation is different.
time_t next_xmit; /* Time for next transmit. Meaningful only */
/* if xmits > 0. */
@ -116,23 +126,53 @@ static struct tt_user_s {
/* Otherwise, this is a display offset position */
/* from the gateway. */
char loc_text[24]; /* Text representation of location when a single */
/* lat/lon point would be deceptive. e.g. */
/* 32TPP8049 */
/* 32TPP8179549363 */
/* 32T 681795 4849363 */
/* EM29QE78 */
double latitude, longitude; /* Location either from user or generated */
/* position in the corral. */
char freq[12]; /* Frequency in format 999.999MHz */
char comment[MAX_COMMENT_LEN+1]; /* Free form comment. */
char comment[MAX_COMMENT_LEN+1]; /* Free form comment from user. */
/* Comment sent in final object report includes */
/* other information besides this. */
char mic_e; /* Position status. */
/* Should be a character in range of '1' to '9' for */
/* the predefined status strings or '0' for none. */
char dao[8]; /* Enhanced position information. */
} tt_user[MAX_TT_USERS];
static void clear_user(int i);
static void xmit_object_report (int i, double c_lat, double c_long, int ambiguity, double c_offs);
static void xmit_object_report (int i, int first_time);
static void tt_setenv (int i);
#if __WIN32__
// setenv is missing on Windows!
int setenv(const char *name, const char *value, int overwrite)
{
char etemp[1000];
snprintf (etemp, sizeof(etemp), "%s=%s", name, value);
putenv (etemp);
return (0);
}
#endif
/*------------------------------------------------------------------
@ -244,7 +284,7 @@ static void clear_user(int i)
{
assert (i >= 0 && i < MAX_TT_USERS);
memset (&tt_user[i], 0, sizeof (struct tt_user_s));
memset (&(tt_user[i]), 0, sizeof (struct tt_user_s));
} /* end clear_user */
@ -341,7 +381,7 @@ static void digit_suffix (char *callsign, char *suffix)
char *t;
strcpy (suffix, "000");
strlcpy (suffix, "000", sizeof(suffix));
tt_text_to_two_key (callsign, 0, two_key);
for (t = two_key; *t != '\0'; t++) {
if (isdigit(*t)) {
@ -365,6 +405,7 @@ static void digit_suffix (char *callsign, char *suffix)
* ssid
* overlay - or symbol table identifier
* symbol
* loc_text - Original text for non lat/lon location
* latitude
* longitude
* freq
@ -380,11 +421,15 @@ static void digit_suffix (char *callsign, char *suffix)
*
*----------------------------------------------------------------*/
int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double latitude,
int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, char *loc_text, double latitude,
double longitude, char *freq, char *comment, char mic_e, char *dao)
{
int i;
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("tt_user_heard (%s, %d, %c, %c, %s, ...)\n", callsign, ssid, overlay, symbol, loc_text);
/*
* At this time all messages are expected to contain a callsign.
* Other types of messages, not related to a particular person/object
@ -392,7 +437,7 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double l
*/
if (callsign[0] == '\0') {
text_color_set(DW_COLOR_ERROR);
printf ("APRStt message did not include callsign.\n");
printf ("APRStt tone sequence did not include callsign / object name.\n");
return (TT_ERROR_NO_CALL);
}
@ -410,10 +455,13 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double l
assert (i >= 0 && i < MAX_TT_USERS);
strncpy (tt_user[i].callsign, callsign, MAX_CALLSIGN_LEN);
tt_user[i].callsign[MAX_CALLSIGN_LEN] = '\0';
tt_user[i].count = 1;
tt_user[i].ssid = ssid;
tt_user[i].overlay = overlay;
tt_user[i].symbol = symbol;
digit_suffix(tt_user[i].callsign, tt_user[i].digit_suffix);
strlcpy (tt_user[i].loc_text, loc_text, sizeof(tt_user[i].loc_text));
if (latitude != G_UNKNOWN && longitude != G_UNKNOWN) {
/* We have specific location. */
tt_user[i].corral_slot = 0;
@ -425,7 +473,7 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double l
tt_user[i].corral_slot = corral_slot();
}
strcpy (tt_user[i].freq, freq);
strlcpy (tt_user[i].freq, freq, sizeof(tt_user[i].freq));
strncpy (tt_user[i].comment, comment, MAX_COMMENT_LEN);
tt_user[i].comment[MAX_COMMENT_LEN] = '\0';
tt_user[i].mic_e = mic_e;
@ -437,8 +485,14 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double l
*/
assert (i >= 0 && i < MAX_TT_USERS);
tt_user[i].count++;
/* Any reason to look at ssid here? */
if (strlen(loc_text) > 0) {
strlcpy (tt_user[i].loc_text, loc_text, sizeof(tt_user[i].loc_text));
}
if (latitude != G_UNKNOWN && longitude != G_UNKNOWN) {
/* We have specific location. */
tt_user[i].corral_slot = 0;
@ -447,7 +501,7 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double l
}
if (freq[0] != '\0') {
strcpy (tt_user[i].freq, freq);
strlcpy (tt_user[i].freq, freq, sizeof(tt_user[i].freq));
}
if (comment[0] != '\0') {
@ -471,6 +525,19 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double l
tt_user[i].xmits = 0;
tt_user[i].next_xmit = tt_user[i].last_heard + save_tt_config_p->xmit_delay[0];
/*
* Send to applications and IGate immediately.
*/
xmit_object_report (i, 1);
/*
* Put properties into environment variables in preparation
* for calling a user-specified script.
*/
tt_setenv (i);
return (0); /* Success! */
} /* end tt_user_heard */
@ -497,12 +564,23 @@ void tt_user_background (void)
time_t now = time(NULL);
int i;
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("tt_user_background() now = %d\n", (int)now);
for (i=0; i<MAX_TT_USERS; i++) {
assert (i >= 0 && i < MAX_TT_USERS);
if (tt_user[i].callsign[0] != '\0') {
if (tt_user[i].xmits < save_tt_config_p->num_xmits && tt_user[i].next_xmit <= now) {
xmit_object_report (i, save_tt_config_p->corral_lat, save_tt_config_p->corral_lon,
save_tt_config_p->corral_ambiguity, save_tt_config_p->corral_offset);
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("tt_user_background() now = %d\n", (int)now);
//tt_user_dump ();
xmit_object_report (i, 0);
/* Increase count of number times this one was sent. */
tt_user[i].xmits++;
@ -510,6 +588,8 @@ void tt_user_background (void)
/* Schedule next one. */
tt_user[i].next_xmit += save_tt_config_p->xmit_delay[tt_user[i].xmits];
}
//tt_user_dump ();
}
}
}
@ -521,7 +601,7 @@ void tt_user_background (void)
if (tt_user[i].callsign[0] != '\0') {
if (tt_user[i].last_heard + save_tt_config_p->retain_time < now) {
// debug - dw_printf ("debug: purging expired user %d\n", i);
//dw_printf ("debug: purging expired user %d\n", i);
clear_user (i);
}
@ -536,11 +616,13 @@ void tt_user_background (void)
*
* Purpose: Create object report packet and put into transmit queue.
*
* Inputs: i - Index into user table.
* c_lat - Corral latitude.
* c_long - Corral longitude.
* ambiguity - Number of amibiguity digits: 0, 1, 2, or 3.
* c_offs - Corral (latitude) offset.
* Inputs: i - Index into user table.
*
* first_time - Is this being called immediately after the tone sequence
* was received or after some delay?
* For the former, we send to any attached applications
* and the IGate.
* For the latter, we transmit over radio.
*
* Outputs: Append to transmit queue.
*
@ -561,30 +643,20 @@ void tt_user_background (void)
*
*----------------------------------------------------------------*/
static const char *mic_e_position_comment[10] = {
"",
"/off duty ",
"/enroute ",
"/in service",
"/returning ",
"/committed ",
"/special ",
"/priority ",
"/emergency ",
"/custom 1 " };
static void xmit_object_report (int i, double c_lat, double c_long, int ambiguity, double c_offs)
static void xmit_object_report (int i, int first_time)
{
char object_name[20];
char object_info[50];
char info_comment[100];
char object_name[20]; // xxxxxxxxx or xxxxxx-nn
char info_comment[200]; // usercomment [locationtext] /status !DAO!
char object_info[250]; // info part of Object Report packet
char stemp[300]; // src>dest,path:object_info
double olat, olong;
char stemp[200];
packet_t pp;
unsigned char fbuf[AX25_MAX_PACKET_LEN];
int flen;
char c4[4];
//text_color_set(DW_COLOR_DEBUG);
//printf ("xmit_object_report (index = %d, first_time = %d) rx = %d, tx = %d\n", i, first_time,
// save_tt_config_p->obj_recv_chan, save_tt_config_p->obj_xmit_chan);
assert (i >= 0 && i < MAX_TT_USERS);
@ -592,12 +664,12 @@ static void xmit_object_report (int i, double c_lat, double c_long, int ambiguit
* Prepare the object name.
* Tack on "-12" if it is a callsign.
*/
strcpy (object_name, tt_user[i].callsign);
strlcpy (object_name, tt_user[i].callsign, sizeof(object_name));
if (strlen(object_name) <= 6 && tt_user[i].ssid != 0) {
char stemp8[8];
sprintf (stemp8, "-%d", tt_user[i].ssid);
strcat (object_name, stemp8);
snprintf (stemp8, sizeof(stemp8), "-%d", tt_user[i].ssid);
strlcat (object_name, stemp8, sizeof(object_name));
}
if (tt_user[i].corral_slot == 0) {
@ -611,61 +683,93 @@ static void xmit_object_report (int i, double c_lat, double c_long, int ambiguit
/*
* Use made up position in the corral.
*/
double c_lat = save_tt_config_p->corral_lat; // Corral latitude.
double c_long = save_tt_config_p->corral_lon; // Corral longitude.
double c_offs = save_tt_config_p->corral_offset; // Corral (latitude) offset.
olat = c_lat - (tt_user[i].corral_slot - 1) * c_offs;
olong = c_long;
}
/*
* Build comment field from various information.
*
* usercomment [locationtext] /status !DAO!
*
* Any frequency is inserted at beginning later.
*/
strcpy (info_comment, "");
strlcpy (info_comment, "", sizeof(info_comment));
if (strlen(tt_user[i].comment) != 0) {
strcat (info_comment, tt_user[i].comment);
strlcat (info_comment, tt_user[i].comment, sizeof(info_comment));
}
if (strlen(tt_user[i].loc_text) > 0) {
if (strlen(info_comment) > 0) {
strlcat (info_comment, " ", sizeof(info_comment));
}
strlcat (info_comment, "[", sizeof(info_comment));
strlcat (info_comment, tt_user[i].loc_text, sizeof(info_comment));
strlcat (info_comment, "]", sizeof(info_comment));
}
if (tt_user[i].mic_e >= '1' && tt_user[i].mic_e <= '9') {
strcat (info_comment, mic_e_position_comment[tt_user[i].mic_e - '0']);
if (strlen(info_comment) > 0) {
strlcat (info_comment, " ", sizeof(info_comment));
}
// Insert "/" if status does not already begin with it.
if (save_tt_config_p->status[tt_user[i].mic_e - '0'][0] != '/') {
strlcat (info_comment, "/", sizeof(info_comment));
}
strlcat (info_comment, save_tt_config_p->status[tt_user[i].mic_e - '0'], sizeof(info_comment));
}
if (strlen(tt_user[i].dao) > 0) {
strcat (info_comment, tt_user[i].dao);
if (strlen(info_comment) > 0) {
strlcat (info_comment, " ", sizeof(info_comment));
}
strlcat (info_comment, tt_user[i].dao, sizeof(info_comment));
}
/* Official limit is 43 characters. */
info_comment[MAX_COMMENT_LEN] = '\0';
//info_comment[MAX_COMMENT_LEN] = '\0';
/*
* Packet header is built from mycall (of transmit channel) and software version.
*/
strcpy (stemp, save_audio_config_p->achan[save_tt_config_p->obj_xmit_chan].mycall);
strcat (stemp, ">");
strcat (stemp, APP_TOCALL);
if (save_tt_config_p->obj_xmit_chan >= 0) {
strlcpy (stemp, save_audio_config_p->achan[save_tt_config_p->obj_xmit_chan].mycall, sizeof(stemp));
}
else {
strlcpy (stemp, save_audio_config_p->achan[save_tt_config_p->obj_recv_chan].mycall, sizeof(stemp));
}
strlcat (stemp, ">", sizeof(stemp));
strlcat (stemp, APP_TOCALL, sizeof(stemp));
c4[0] = '0' + MAJOR_VERSION;
c4[1] = '0' + MINOR_VERSION;
c4[2] = '\0';
strcat (stemp, c4);
strlcat (stemp, c4, sizeof(stemp));
/*
* Append via path if specified.
* Append via path, for transmission, if specified.
*/
if (save_tt_config_p->obj_xmit_via[0] != '\0') {
strcat (stemp, ",");
strcat (stemp, save_tt_config_p->obj_xmit_via);
if ( ! first_time && save_tt_config_p->obj_xmit_via[0] != '\0') {
strlcat (stemp, ",", sizeof(stemp));
strlcat (stemp, save_tt_config_p->obj_xmit_via, sizeof(stemp));
}
strcat (stemp, ":");
strlcat (stemp, ":", sizeof(stemp));
encode_object (object_name, 0, tt_user[i].last_heard, olat, olong,
tt_user[i].overlay, tt_user[i].symbol,
0,0,0,NULL, 0,0, /* PHGD, C/S */
atof(tt_user[i].freq), 0, 0, info_comment, object_info);
strcat (stemp, object_info);
//text_color_set(DW_COLOR_ERROR);
//printf ("\nDEBUG: %s\n\n", stemp);
atof(tt_user[i].freq), 0, 0, info_comment, object_info, sizeof(object_info));
strlcat (stemp, object_info, sizeof(stemp));
#if TT_MAIN
@ -673,32 +777,201 @@ static void xmit_object_report (int i, double c_lat, double c_long, int ambiguit
#else
if (first_time) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("[APRStt] %s\n", stemp);
}
/*
* Convert to packet and append to transmit queue.
* Convert text to packet.
*/
pp = ax25_from_text (stemp, 1);
flen = ax25_pack (pp, fbuf);
if (pp == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("\"%s\"\n", stemp);
return;
}
/*
* Process as if we heard ourself.
/*
* Send to one or more of the following depending on configuration:
* Transmit queue.
* Any attached application(s).
* IGate.
*
* When transmitting over the radio, it gets sent multipe times, to help
* probablity of being heard, with increasing delays between.
*
* The other methods are reliable so we only want to send it once.
*/
// TODO: We need radio channel where this came from.
// It would make a difference if running two radios
// and they have different station identifiers.
int chan = 0;
igate_send_rec_packet (chan, pp);
/* Remember it so we don't digipeat our own. */
if (first_time && save_tt_config_p->obj_send_to_app) {
unsigned char fbuf[AX25_MAX_PACKET_LEN];
int flen;
dedupe_remember (pp, save_tt_config_p->obj_xmit_chan);
// TODO1.3: Put a wrapper around this so we only call one function to send by all methods.
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);
}
if (first_time && save_tt_config_p->obj_send_to_ig) {
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("xmit_object_report (): send to IGate\n");
igate_send_rec_packet (save_tt_config_p->obj_recv_chan, pp);
}
if ( ! first_time && save_tt_config_p->obj_xmit_chan >= 0) {
/* Remember it so we don't digipeat our own. */
dedupe_remember (pp, save_tt_config_p->obj_xmit_chan);
tq_append (save_tt_config_p->obj_xmit_chan, TQ_PRIO_1_LO, pp);
}
else {
ax25_delete (pp);
}
tq_append (save_tt_config_p->obj_xmit_chan, TQ_PRIO_1_LO, pp);
#endif
}
static const char *letters[26] = {
"Alpha",
"Bravo",
"Charlie",
"Delta",
"Echo",
"Foxtrot",
"Golf",
"Hotel",
"India",
"Juliet",
"Kilo",
"Lima",
"Mike",
"November",
"Oscar",
"Papa",
"Quebec",
"Romeo",
"Sierra",
"Tango",
"Uniform",
"Victor",
"Whiskey",
"X-ray",
"Yankee",
"Zulu"
};
static const char *digits[10] = {
"Zero",
"One",
"Two",
"Three",
"Four",
"Five",
"Six",
"Seven",
"Eight",
"Nine"
};
/*------------------------------------------------------------------
*
* Name: tt_setenv
*
* Purpose: Put information in environment variables in preparation
* for calling a user-supplied script for custom processing.
*
* Inputs: i - Index into tt_user table.
*
* Description: Timestamps displayed relative to current time.
*
*----------------------------------------------------------------*/
static void tt_setenv (int i)
{
char stemp[256];
char t2[2];
char *p;
assert (i >= 0 && i < MAX_TT_USERS);
setenv ("TTCALL", tt_user[i].callsign, 1);
strlcpy (stemp, "", sizeof(stemp));
t2[1] = '\0';
for (p = tt_user[i].callsign; *p != '\0'; p++) {
t2[0] = *p;
strlcat (stemp, t2, sizeof(stemp));
if (p[1] != '\0') strlcat (stemp, " ", sizeof(stemp));
}
setenv ("TTCALLSP", stemp, 1);
strlcpy (stemp, "", sizeof(stemp));
for (p = tt_user[i].callsign; *p != '\0'; p++) {
if (isupper(*p)) {
strlcat (stemp, letters[*p - 'A'], sizeof(stemp));
}
else if (islower(*p)) {
strlcat (stemp, letters[*p - 'a'], sizeof(stemp));
}
else if (isdigit(*p)) {
strlcat (stemp, digits[*p - '0'], sizeof(stemp));
}
else {
t2[0] = *p;
strlcat (stemp, t2, sizeof(stemp));
}
if (p[1] != '\0') strlcat (stemp, " ", sizeof(stemp));
}
setenv ("TTCALLPH", stemp, 1);
snprintf (stemp, sizeof(stemp), "%d", tt_user[i].ssid);
setenv ("TTSSID",stemp , 1);
snprintf (stemp, sizeof(stemp), "%d", tt_user[i].count);
setenv ("TTCOUNT",stemp , 1);
snprintf (stemp, sizeof(stemp), "%c%c", tt_user[i].overlay, tt_user[i].symbol);
setenv ("TTSYMBOL",stemp , 1);
snprintf (stemp, sizeof(stemp), "%.6f", tt_user[i].latitude);
setenv ("TTLAT",stemp , 1);
snprintf (stemp, sizeof(stemp), "%.6f", tt_user[i].longitude);
setenv ("TTLON",stemp , 1);
setenv ("TTFREQ", tt_user[i].freq, 1);
setenv ("TTCOMMENT", tt_user[i].comment, 1);
setenv ("TTLOC", tt_user[i].loc_text, 1);
if (tt_user[i].mic_e >= '1' && tt_user[i].mic_e <= '9') {
setenv ("TTSTATUS", save_tt_config_p->status[tt_user[i].mic_e - '0'], 1);
}
else {
setenv ("TTSTATUS", "", 1);
}
setenv ("TTDAO", tt_user[i].dao, 1);
} /* end tt_setenv */
/*------------------------------------------------------------------
@ -770,7 +1043,7 @@ int main (int argc, char *argv[])
memset (&my_audio_config, 0, sizeof(my_audio_config));
strcpy (my_audio_config.achan[0].mycall, "WB2OSZ-15");
strlcpy (my_audio_config.achan[0].mycall, "WB2OSZ-15", sizeof(my_audio_config.achan[0].mycall));
/* Fake TT gateway config. */

View File

@ -6,7 +6,8 @@
void tt_user_init (struct audio_s *p_audio_config, struct tt_config_s *p);
int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double latitude,
int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, char *loc_text, double latitude,
double longitude, char *freq, char *comment, char mic_e, char *dao);
void tt_user_background (void);
void tt_user_background (void);
void tt_user_dump (void);

View File

@ -7,7 +7,7 @@
#include <string.h>
#include <ctype.h>
#include "direwolf.h"
#include "utm.h"
#include "mgrs.h"
#include "usng.h"
@ -40,7 +40,7 @@ int main (int argc, char *argv[])
// 3 command line arguments for UTM
strcpy (szone, argv[1]);
strlcpy (szone, argv[1], sizeof(szone));
lzone = strtoul(szone, &zlet, 10);
if (*zlet == '\0') {

View File

@ -1,8 +1,8 @@
/* Dire Wolf version 1.2 */
/* Dire Wolf version 1.3 */
#define APP_TOCALL "APDW"
#define MAJOR_VERSION 1
#define MINOR_VERSION 2
#define EXTRA_VERSION "Beta Test"
#define MINOR_VERSION 3
//#define EXTRA_VERSION "Beta Test"

180
walk96.c Normal file
View File

@ -0,0 +1,180 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2015 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/>.
//
//#define DEBUG 1
/*------------------------------------------------------------------
*
* Module: walk96.c
*
* Purpose: Quick hack to read GPS location and send very frequent
* position reports frames to a KISS TNC.
*
*
*---------------------------------------------------------------*/
#include <stdio.h>
#include <unistd.h>
#if __WIN32__
#include <stdlib.h>
#include <windows.h>
#else
#define __USE_XOPEN2KXSI 1
#define __USE_XOPEN 1
#include <stdlib.h>
#include <ctype.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#endif
#include <assert.h>
#include <string.h>
#include "direwolf.h"
#include "config.h"
#include "ax25_pad.h"
#include "textcolor.h"
#include "latlong.h"
#include "nmea.h"
#include "encode_aprs.h"
#include "serial_port.h"
#define MYCALL "WB2OSZ" /************ Change this if you use it!!! ***************/
static MYFDTYPE tnc;
main (int argc, char *argv[])
{
struct misc_config_s config;
char cmd[100];
// Look for Silicon Labs CP210x
// Just happens to be same on desktop & laptop.
tnc = serial_port_open ("COM5", 9600);
if (tnc == MYFDERROR) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Can't open serial port to KISS TNC.\n");
exit (EXIT_FAILURE); // defined in stdlib.h
}
strcpy (cmd, "\r\rhbaud 9600\rkiss on\rrestart\r");
serial_port_write (tnc, cmd, strlen(cmd));
SLEEP_MS(500);
memset (&config, 0, sizeof(config));
strcpy (config.nmea_port, "COM1");
nmea_init (&config);
SLEEP_SEC(20);
// Exit out of KISS mode.
serial_port_write (tnc, "\xc0\xff\c0", 3);
SLEEP_MS(100);
}
/* Should be called once per second. */
void walk96 (int fix, double lat, double lon, float knots, float course, float alt)
{
static int sequence = 0;
char comment[50];
sequence++;
sprintf (comment, "Sequence number %04d", sequence);
/*
* Construct the packet in normal monitoring format.
*/
int messaging = 0;
int compressed = 0;
char info[AX25_MAX_INFO_LEN];
int info_len;
char position_report[AX25_MAX_PACKET_LEN];
info_len = encode_position (messaging, compressed,
lat, lon, (int)(DW_METERS_TO_FEET(alt)),
'/', '?', // TODO: look up code for person.
G_UNKNOWN, G_UNKNOWN, G_UNKNOWN, "", // PHG
(int)course, (int)knots,
445.925, 0, 0,
comment,
info, sizeof(info));
sprintf (position_report, "%s>WALK96:%s", MYCALL, info);
text_color_set (DW_COLOR_XMIT);
dw_printf ("%s\n", position_report);
/*
* Convert it into AX.25 frame.
*/
packet_t pp;
unsigned char ax25_frame[AX25_MAX_PACKET_LEN];
int frame_len;
pp = ax25_from_text (position_report, 1);
if (pp == NULL) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Unexpected error in ax25_from_text. Quitting.\n");
exit (EXIT_FAILURE); // defined in stdlib.h
}
ax25_frame[0] = 0; // Insert channel before KISS encapsulation.
frame_len = ax25_pack (pp, ax25_frame+1);
ax25_delete (pp);
/*
* Encapsulate as KISS and send to TNC.
*/
unsigned char kiss_frame[AX25_MAX_PACKET_LEN*2];
int kiss_len;
kiss_len = kiss_encapsulate (ax25_frame, frame_len+1, (unsigned char *)kiss_frame);
//text_color_set (DW_COLOR_DEBUG);
//dw_printf ("AX.25 frame length = %d, KISS frame length = %d\n", frame_len, kiss_len);
//kiss_debug_print (1, NULL, kiss_frame, kiss_len);
serial_port_write (tnc, kiss_frame, kiss_len);
}
/* end walk96.c */

82
xmit.c
View File

@ -76,6 +76,7 @@
#include "hdlc_rec.h"
#include "ptt.h"
#include "dtime_now.h"
#include "morse.h"
@ -98,7 +99,7 @@ static int xmit_persist[MAX_CHANS]; /* Sets probability for transmitting after e
static int xmit_txdelay[MAX_CHANS]; /* After turning on the transmitter, */
/* send "flags" for txdelay * 10 mS. */
static int xmit_txtail[MAX_CHANS]; /* Amount of time to keep transmitting after we */
static int xmit_txtail[MAX_CHANS]; /* Amount of time to keep transmitting after we */
/* 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. */
@ -137,6 +138,7 @@ static dw_mutex_t audio_out_dev_mutex[MAX_ADEVS];
static int wait_for_clear_channel (int channel, int nowait, int slotttime, int persist);
static void xmit_ax25_frames (int c, int p, packet_t pp);
static void xmit_speech (int c, packet_t pp);
static void xmit_morse (int c, packet_t pp, int wpm);
/*-------------------------------------------------------------------
@ -433,11 +435,18 @@ static void * xmit_thread (void *arg)
if (ok) {
/*
* Channel is clear and we have lock on output device.
*
* If destination is "SPEECH" send info part to speech synthesizer.
* If destination is "MORSE" send as morse code.
*/
char dest[AX25_MAX_ADDR_LEN];
int ssid = 0;
if (ax25_is_aprs (pp)) {
ax25_get_addr_with_ssid(pp, AX25_DESTINATION, dest);
ax25_get_addr_no_ssid(pp, AX25_DESTINATION, dest);
ssid = ax25_get_ssid(pp, AX25_DESTINATION);
}
else {
strcpy (dest, "");
@ -446,6 +455,24 @@ static void * xmit_thread (void *arg)
if (strcmp(dest, "SPEECH") == 0) {
xmit_speech (c, pp);
}
else if (strcmp(dest, "MORSE") == 0) {
int wpm = ssid * 2;
if (wpm == 0) wpm = MORSE_DEFAULT_WPM;
// This is a bit of a hack so we don't respond too quickly for APRStt.
// It will be sent in high priority queue while a beacon wouldn't.
// Add a little delay so user has time release PTT after sending #.
// This and default txdelay would give us a second.
if (p == TQ_PRIO_0_HI) {
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("APRStt morse xmit delay hack...\n");
SLEEP_MS (700);
}
xmit_morse (c, pp, wpm);
}
else {
xmit_ax25_frames (c, p, pp);
}
@ -748,12 +775,13 @@ static void xmit_ax25_frames (int c, int p, packet_t pp)
} /* end xmit_ax25_frames */
/*-------------------------------------------------------------------
*
* Name: xmit_ax25_frames
* Name: xmit_speech
*
* Purpose: After we have a clear channel, and possibly waited a random time,
* we transmit one or more frames.
* we transmit information part of frame as speech.
*
* Inputs: c - Channel number.
*
@ -765,7 +793,6 @@ static void xmit_ax25_frames (int c, int p, packet_t pp)
* Invoke the text-to-speech script.
* Turn off transmitter.
*
*
*--------------------------------------------------------------------*/
@ -861,6 +888,51 @@ int xmit_speak_it (char *script, int c, char *orig_msg)
}
/*-------------------------------------------------------------------
*
* Name: xmit_morse
*
* Purpose: After we have a clear channel, and possibly waited a random time,
* we transmit information part of frame as Morse code.
*
* Inputs: c - Channel number.
*
* pp - Packet object pointer.
* It will be deleted so caller should not try
* to reference it after this.
*
* wpm - Speed in words per minute.
*
* Description: Turn on transmitter.
* Send text as Morse code.
* Turn off transmitter.
*
*--------------------------------------------------------------------*/
static void xmit_morse (int c, packet_t pp, int wpm)
{
int info_len;
unsigned char *pinfo;
info_len = ax25_get_info (pp, &pinfo);
text_color_set(DW_COLOR_XMIT);
dw_printf ("[%d.morse] \"%s\"\n", c, pinfo);
ptt_set (OCTYPE_PTT, c, 1);
morse_send (c, (char*)pinfo, wpm, xmit_txdelay[c] * 10, xmit_txtail[c] * 10);
ptt_set (OCTYPE_PTT, c, 0);
ax25_delete (pp);
} /* end xmit_morse */
/*-------------------------------------------------------------------
*
* Name: wait_for_clear_channel