Rewrite GPS handling. Lots of other clean up.

This commit is contained in:
WB2OSZ 2015-11-07 20:57:02 -05:00
parent dd27f9960b
commit 4c60979844
77 changed files with 4577 additions and 2521 deletions

8
.gitattributes vendored
View File

@ -28,8 +28,14 @@
*.desktop text
*.conf text
*.rc text
*.spec text
*.bat text
*.1 text
*.md text
COPYING text
Makefile* text
README* text
*.ico binary
*.png binary
Makefile* text

4
.gitignore vendored
View File

@ -6,6 +6,10 @@ z*
*~
*.xlsx
*.stackdump
direwolf.conf
*.wav
fsk_fast_filter.h
# Object files
*.o

View File

@ -3,6 +3,22 @@
----------
## Version 1.3 -- Development snapshot G -- November 2015 ##
### New Feature: ###
- GPS Tracker beacons are now available for the Windows version. Previously this was only in the Linux version.
- Implemented AGW network protocol 'M' message for sending UNPROTO information without digipeater path.
### Bugs Fixed: ###
- Tracker beacons were not always updating the location properly.
- In Mac OSX version: Assertion failed: (adev[a].inbuf_size_in_bytes >= 100 && adev[a].inbuf_size_in_bytes <= 32768), function audio_get, file audio_portaudio.c, line 917.
----------
## Version 1.3 -- Development snapshot F -- September 2015 ##
### New Feature: ###

View File

@ -2,7 +2,9 @@
# Makefile for Linux version of Dire Wolf.
#
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc direwolf.desktop direwolf.conf
APPS := direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc
all : $(APPS) direwolf.desktop direwolf.conf
@echo " "
@echo "Next step - install with:"
@echo " "
@ -12,6 +14,9 @@ all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx
CC := gcc
CFLAGS := -O3 -pthread -Igeotranz
LDFLAGS := -lm -lpthread -lrt
#
# The DSP filters spend a lot of time spinning around in little
@ -184,24 +189,27 @@ endif
#
# 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
# avoid fine tuning it for your particular computer. It could
# cause compatibility issues for those with older computers.
#
#CFLAGS += -D_FORTIFY_SOURCE
# If you want to use OSS (for FreeBSD, OpenBSD) instead of
# ALSA (for Linux), comment out (or remove) the two lines below.
CFLAGS += -DUSE_ALSA
LDLIBS += -lasound
LDFLAGS += -lasound
# Uncomment following lines to enable GPS interface & tracker function.
# Enable GPS if header file is present.
# Finding libgps.so* is more difficult because it
# is in different places on different operating systems.
#CFLAGS += -DENABLE_GPS
#LDLIBS += -lgps
enable_gpsd := $(wildcard /usr/include/gps.h)
ifneq ($(enable_gpsd),)
CFLAGS += -DENABLE_GPSD
LDFLAGS += -lgps
endif
# Name of current directory.
@ -210,7 +218,10 @@ LDLIBS += -lasound
z := $(notdir ${CURDIR})
# Main application.
# -------------------------------- Main application -----------------------------------------
direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_9600.o hdlc_rec.o \
hdlc_rec2.o multi_modem.o redecode.o rdq.o rrbb.o dlq.o \
@ -218,10 +229,17 @@ direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_9600.o hd
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 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 serial_port.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 \
dwgps.o dwgpsnmea.o dwgpsd.o dtime_now.o \
misc.a geotranz.a
$(CC) $(CFLAGS) -o $@ $^ -lpthread -lrt $(LDLIBS) -lm
$(CC) -o $@ $^ $(LDFLAGS)
ifneq ($(enable_gpsd),)
@echo " "
@echo "This includes support for gpsd."
else
@echo " "
@echo "This does NOT include support for gpsd."
endif
# Optimization for slow processors.
@ -231,10 +249,100 @@ 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
$(CC) $(CFLAGS) -o gen_fff -DGEN_FFF demod_afsk.c dsp.c textcolor.c $(LDFLAGS)
./gen_fff > fsk_fast_filter.h
#
# The destination field is often used to identify the manufacturer/model.
# These are not hardcoded into Dire Wolf. Instead they are read from
# a file called tocalls.txt at application start up time.
#
# The original permanent symbols are built in but the "new" symbols,
# using overlays, are often updated. These are also read from files.
#
# You can obtain an updated copy by typing "make tocalls-symbols".
# This is not part of the normal build process. You have to do this explicitly.
#
# The locations below appear to be the most recent.
# The copy at http://www.aprs.org/tocalls.txt is out of date.
#
.PHONY: tocalls-symbols
tocalls-symbols :
cp tocalls.txt tocalls.txt~
wget http://www.aprs.org/aprs11/tocalls.txt -O tocalls.txt
-diff tocalls.txt~ tocalls.txt
cp symbols-new.txt symbols-new.txt~
wget http://www.aprs.org/symbols/symbols-new.txt -O symbols-new.txt
-diff symbols-new.txt~ symbols-new.txt
cp symbolsX.txt symbolsX.txt~
wget http://www.aprs.org/symbols/symbolsX.txt -O symbolsX.txt
-diff symbolsX.txt~ symbolsX.txt
# ---------------------------------------- Other utilities included ------------------------------
# Separate application to decode raw data.
decode_aprs : decode_aprs.c dwgpsnmea.o dwgps.o serial_port.o symbols.o ax25_pad.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o misc.a
$(CC) $(CFLAGS) -DDECAMAIN -o $@ $^ $(LDFLAGS)
# Convert between text and touch tone representation.
text2tt : tt_text.c misc.a
$(CC) $(CFLAGS) -DENC_MAIN -o $@ $^ $(LDFLAGS)
tt2text : tt_text.c misc.a
$(CC) $(CFLAGS) -DDEC_MAIN -o $@ $^ $(LDFLAGS)
# Convert between Latitude/Longitude and UTM coordinates.
ll2utm : ll2utm.c geotranz.a textcolor.o misc.a
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
utm2ll : utm2ll.c geotranz.a textcolor.o misc.a
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
# Convert from log file to GPX.
log2gpx : log2gpx.c textcolor.o misc.a
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
# 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 misc.a
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
# Unit test for AFSK demodulator
atest : atest.c fsk_fast_filter.h demod.c demod_afsk.c demod_9600.c \
dsp.o hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \
fcs_calc.c ax25_pad.c decode_aprs.c dwgpsnmea.o \
dwgps.o serial_port.o telemetry.c latlong.c symbols.c tt_text.c textcolor.c \
misc.a
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
# Multiple AGWPE network or serial port clients to test TNCs side by side.
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 misc.a
$(CC) $(CFLAGS) -g -o $@ $^
# ----------------------------------------- Libraries --------------------------------------------
# UTM, USNG, MGRS conversions.
@ -277,7 +385,19 @@ strlcat.o : misc/strlcat.c
# ------------------------------------- Installation ----------------------------------
# Generate apprpriate sample configuration file for this platform.
# Originally, there was one sample for all platforms. It got too cluttered
# and confusing saying, this is for windows, and this is for Linux, and this ...
# Trying to maintain 3 different versions in parallel is error prone.
# We now have a single generic version which can be used to generate
# the various platform specific versions.
# generic.conf should be checked into source control.
# direwolf.conf should NOT. It is generated when compiling on the target platform.
direwolf.conf : generic.conf
egrep '^C|^L' generic.conf | cut -c2-999 > direwolf.conf
@ -289,13 +409,17 @@ direwolf.conf : generic.conf
# from source, that is not a standard part of the operating system,
# should go in /usr/local/bin.
# However, if you are preparing a "binary" RPM or DEB package, the
# However, if you are preparing a "binary" DEB or RPM package, the
# installation location should be /usr/bin.
# This is a step in the right direction but not sufficient to use /usr instead.
# Eventually I'd like to have targets here to build the .DEB and .RPM packages.
INSTALLDIR := /usr/local
# Command to "install" to system directories. Use "ginstall" for Mac.
INSTALL=install
# direwolf.desktop was previously handcrafted for the Raspberry Pi.
# It was hardcoded with lxterminal, /home/pi, and so on.
@ -324,49 +448,89 @@ endif
@echo 'Keywords=Ham Radio;APRS;Soundcard TNC;KISS;AGWPE;AX.25' >> $@
# Optional installation into /usr/local/...
# 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 direwolf.desktop
install direwolf $(INSTALLDIR)/bin
install decode_aprs $(INSTALLDIR)/bin
install text2tt $(INSTALLDIR)/bin
install tt2text $(INSTALLDIR)/bin
install ll2utm $(INSTALLDIR)/bin
install utm2ll $(INSTALLDIR)/bin
install aclients $(INSTALLDIR)/bin
install log2gpx $(INSTALLDIR)/bin
install gen_packets $(INSTALLDIR)/bin
install atest $(INSTALLDIR)/bin
install ttcalc $(INSTALLDIR)/bin
install dwespeak.sh $(INSTALLDIR)/bin
install 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 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 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
install -D --mode=644 man1/direwolf.1 $(INSTALLDIR)/man/man1/direwolf.1
install -D --mode=644 man1/gen_packets.1 $(INSTALLDIR)/man/man1/gen_packets.1
install -D --mode=644 man1/ll2utm.1 $(INSTALLDIR)/man/man1/ll2utm.1
install -D --mode=644 man1/log2gpx.1 $(INSTALLDIR)/man/man1/log2gpx.1
install -D --mode=644 man1/text2tt.1 $(INSTALLDIR)/man/man1/text2tt.1
install -D --mode=644 man1/tt2text.1 $(INSTALLDIR)/man/man1/tt2text.1
install -D --mode=644 man1/utm2ll.1 $(INSTALLDIR)/man/man1/utm2ll.1
.PHONY: install
install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png direwolf.desktop
#
# Applications, not installed with package manager, normally go in /usr/local/bin.
# /usr/bin is used instead when installing from .DEB or .RPM package.
#
$(INSTALL) direwolf $(INSTALLDIR)/bin
$(INSTALL) decode_aprs $(INSTALLDIR)/bin
$(INSTALL) text2tt $(INSTALLDIR)/bin
$(INSTALL) tt2text $(INSTALLDIR)/bin
$(INSTALL) ll2utm $(INSTALLDIR)/bin
$(INSTALL) utm2ll $(INSTALLDIR)/bin
$(INSTALL) aclients $(INSTALLDIR)/bin
$(INSTALL) log2gpx $(INSTALLDIR)/bin
$(INSTALL) gen_packets $(INSTALLDIR)/bin
$(INSTALL) atest $(INSTALLDIR)/bin
$(INSTALL) ttcalc $(INSTALLDIR)/bin
$(INSTALL) dwespeak.sh $(INSTALLDIR)/bin
#
# Telemetry Toolkit executables. Other .conf and .txt files will go into doc directory.
#
$(INSTALL) telemetry-toolkit/telem-balloon.pl $(INSTALLDIR)/bin
$(INSTALL) telemetry-toolkit/telem-bits.pl $(INSTALLDIR)/bin
$(INSTALL) telemetry-toolkit/telem-data.pl $(INSTALLDIR)/bin
$(INSTALL) telemetry-toolkit/telem-data91.pl $(INSTALLDIR)/bin
$(INSTALL) telemetry-toolkit/telem-eqns.pl $(INSTALLDIR)/bin
$(INSTALL) telemetry-toolkit/telem-parm.pl $(INSTALLDIR)/bin
$(INSTALL) telemetry-toolkit/telem-unit.pl $(INSTALLDIR)/bin
$(INSTALL) telemetry-toolkit/telem-volts.py $(INSTALLDIR)/bin
#
# Misc. data such as "tocall" to system mapping.
#
$(INSTALL) -D --mode=644 tocalls.txt /usr/share/direwolf/tocalls.txt
$(INSTALL) -D --mode=644 symbols-new.txt /usr/share/direwolf/symbols-new.txt
$(INSTALL) -D --mode=644 symbolsX.txt /usr/share/direwolf/symbolsX.txt
$(INSTALL) -D --mode=644 dw-icon.png /usr/share/direwolf/dw-icon.png
$(INSTALL) -D --mode=644 direwolf.desktop /usr/share/applications/direwolf.desktop
#
# Documentation. Various plain text files and PDF.
#
$(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 doc/User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf
$(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf
$(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS-Tracker.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf
$(INSTALL) -D --mode=644 doc/Raspberry-Pi-SDR-IGate.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-SDR-IGate.pdf
$(INSTALL) -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf
$(INSTALL) -D --mode=644 doc/APRStt-interface-for-SARTrack.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-interface-for-SARTrack.pdf
$(INSTALL) -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf
$(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf
$(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf
#
# Sample config files also go into the doc directory.
# When building from source, these can be put in home directory with "make install-conf".
# When installed from .DEB or .RPM package, the user will need to copy these to
# the home directory or other desired location.
# Someone suggested that these could go into an "examples" subdirectory under doc.
#
$(INSTALL) -D --mode=644 direwolf.conf $(INSTALLDIR)/share/doc/direwolf/direwolf.conf
$(INSTALL) -D --mode=644 telemetry-toolkit/telem-m0xer-3.txt $(INSTALLDIR)/share/doc/direwolf/telem-m0xer-3.txt
$(INSTALL) -D --mode=644 telemetry-toolkit/telem-balloon.conf $(INSTALLDIR)/share/doc/direwolf/telem-balloon.conf
$(INSTALL) -D --mode=644 telemetry-toolkit/telem-volts.conf $(INSTALLDIR)/share/doc/direwolf/telem-volts.conf
#
# "man" pages
#
$(INSTALL) -D --mode=644 man1/aclients.1 $(INSTALLDIR)/man/man1/aclients.1
$(INSTALL) -D --mode=644 man1/atest.1 $(INSTALLDIR)/man/man1/atest.1
$(INSTALL) -D --mode=644 man1/decode_aprs.1 $(INSTALLDIR)/man/man1/decode_aprs.1
$(INSTALL) -D --mode=644 man1/direwolf.1 $(INSTALLDIR)/man/man1/direwolf.1
$(INSTALL) -D --mode=644 man1/gen_packets.1 $(INSTALLDIR)/man/man1/gen_packets.1
$(INSTALL) -D --mode=644 man1/ll2utm.1 $(INSTALLDIR)/man/man1/ll2utm.1
$(INSTALL) -D --mode=644 man1/log2gpx.1 $(INSTALLDIR)/man/man1/log2gpx.1
$(INSTALL) -D --mode=644 man1/text2tt.1 $(INSTALLDIR)/man/man1/text2tt.1
$(INSTALL) -D --mode=644 man1/tt2text.1 $(INSTALLDIR)/man/man1/tt2text.1
$(INSTALL) -D --mode=644 man1/utm2ll.1 $(INSTALLDIR)/man/man1/utm2ll.1
#
@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:"
@ -375,12 +539,6 @@ install : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx ge
@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.
@ -409,41 +567,123 @@ 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 tt_text.o misc.a
$(CC) $(CFLAGS) -o decode_aprs -DTEST $^ -lm
# ---------------------------------- Automated Smoke Test --------------------------------
# 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
# Combine some unit tests into a single regression sanity check.
# Convert between Latitude/Longitude and UTM coordinates.
check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest check-modem1200 check-modem300 check-modem9600
ll2utm : ll2utm.c geotranz.a textcolor.o misc.a
$(CC) $(CFLAGS) -o $@ $^ -lm
# Can we encode and decode at popular data rates?
utm2ll : utm2ll.c geotranz.a textcolor.o misc.a
$(CC) $(CFLAGS) -o $@ $^ -lm
check-modem1200 : gen_packets atest
gen_packets -n 100 -o /tmp/test1.wav
atest -F0 -PE -L70 -G71 /tmp/test1.wav
atest -F1 -PE -L73 -G75 /tmp/test1.wav
#rm /tmp/test1.wav
check-modem300 : gen_packets atest
gen_packets -B300 -n 100 -o /tmp/test3.wav
atest -B300 -F0 -L68 -G69 /tmp/test3.wav
atest -B300 -F1 -L73 -G75 /tmp/test3.wav
rm /tmp/test3.wav
check-modem9600 : gen_packets atest
gen_packets -B9600 -n 100 -o /tmp/test9.wav
atest -B9600 -F0 -L57 -G59 /tmp/test9.wav
atest -B9600 -F1 -L66 -G67 /tmp/test9.wav
rm /tmp/test9.wav
# Convert from log file to GPX.
log2gpx : log2gpx.c textcolor.o misc.a
$(CC) $(CFLAGS) -o $@ $^ -lm
# Unit test for inner digipeater algorithm
.PHONY : dtest
dtest : digipeater.c pfilter.o ax25_pad.o dedupe.o fcs_calc.o tq.o textcolor.o \
decode_aprs.o dwgpsnmea.o dwgps.o serial_port.o latlong.o telemetry.o symbols.o tt_text.o misc.a
$(CC) $(CFLAGS) -DDIGITEST -o $@ $^ $(LDFLAGS)
./dtest
rm dtest
# Test application to generate sound.
# Unit test for APRStt tone sequence parsing.
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
.PHONY : ttest
ttest : aprs_tt.c tt_text.c latlong.o textcolor.o misc.a geotranz.a misc.a
$(CC) $(CFLAGS) -DTT_MAIN -o $@ $^ $(LDFLAGS)
./ttest
rm ttest
# Unit test for APRStt tone sequence / text conversions.
.PHONY: tttexttest
tttexttest : tt_text.c textcolor.o misc.a
$(CC) $(CFLAGS) -DTTT_TEST -o $@ $^ $(LDFLAGS)
./tttexttest
rm tttexttest
# Unit test for Packet Filtering.
.PHONY: pftest
pftest : pfilter.c ax25_pad.o textcolor.o fcs_calc.o decode_aprs.o dwgpsnmea.o dwgps.o serial_port.o latlong.o symbols.o telemetry.o tt_text.o misc.a
$(CC) $(CFLAGS) -DPFTEST -o $@ $^ $(LDFLAGS)
./pftest
rm pftest
# Unit test for telemetry decoding.
.PHONY: tlmtest
tlmtest : telemetry.c ax25_pad.o fcs_calc.o textcolor.o misc.a
$(CC) $(CFLAGS) -DTEST -o $@ $^ $(LDFLAGS)
./tlmtest
rm tlmtest
# Unit test for location coordinate conversion.
.PHONY: lltest
lltest : latlong.c textcolor.o misc.a
$(CC) $(CFLAGS) -DLLTEST -o $@ $^ $(LDFLAGS)
./lltest
rm lltest
# Unit test for encoding position & object report.
.PHONY: enctest
enctest : encode_aprs.c latlong.c textcolor.c misc.a
$(CC) $(CFLAGS) -DEN_MAIN -o $@ $^ $(LDFLAGS)
./enctest
rm enctest
# Unit test for KISS encapsulation.
.PHONY: kisstest
kisstest : kiss_frame.c
$(CC) $(CFLAGS) -DKISSTEST -o $@ $^ $(LDFLAGS)
./kisstest
rm kisstest
# ----------------------------- Manual tests and experiments ---------------------------
# Unit test for IGate
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 misc.a
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
./udptest
demod.o : tune.h
demod_afsk.o : tune.h
@ -451,80 +691,19 @@ 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 misc.a
$(CC) $(CFLAGS) -o atest $^ -lm
$(CC) $(CFLAGS) -o atest $^ $(LDFLAGS)
./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 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 misc.a
$(CC) $(CFLAGS) -DTEST -o $@ $^
./dtest
# ------------------------------- Source distribution ---------------------------------
# probably obsolete and can be removed after move to github.
# Unit test for APRStt.
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 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 misc.a
$(CC) $(CFLAGS) -o $@ $^ -lm -lrt
./udptest
# Unit test for telemetry decoding.
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.o misc.a
$(CC) $(CFLAGS) -g -o $@ $^
# Touch Tone to Speech sample application.
ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a
$(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 direwolf.desktop
echo " " > tune.h
# Package it up for distribution.
.PHONY: dist-src
dist-src : README.md CHANGES.md
@ -556,22 +735,17 @@ dist-src : README.md CHANGES.md
$z/telemetry-toolkit/* )
#
# The locations below appear to be the most recent.
# The copy at http://www.aprs.org/tocalls.txt is out of date.
#
# -----------------------------------------------------------------------------------------
.PHONY: tocalls-symbols
tocalls-symbols :
cp tocalls.txt tocalls.txt~
wget http://www.aprs.org/aprs11/tocalls.txt -O tocalls.txt
diff tocalls.txt~ tocalls.txt
cp symbols-new.txt symbols-new.txt~
wget http://www.aprs.org/symbols/symbols-new.txt -O symbols-new.txt
diff symbols-new.txt~ symbols-new.txt
cp symbolsX.txt symbolsX.txt~
wget http://www.aprs.org/symbols/symbolsX.txt -O symbolsX.txt
diff symbolsX.txt~ symbolsX.txt
.PHONY: clean
clean :
rm -f $(APPS) fsk_fast_filter.h *.o *.a direwolf.desktop
echo " " > tune.h
depend : $(wildcard *.c)
makedepend -f $(lastword $(MAKEFILE_LIST)) -- $(CFLAGS) -- $^
#

View File

@ -2,6 +2,14 @@
# Makefile for Macintosh 10.8+ version of Dire Wolf.
#
# TODO: This is a modified version of Makefile.linux and it
# has fallen a little behind. For example, it is missing the check target.
# It would be more maintainable if we could use a single file for both.
# The differences are not that great.
# Maybe the most of the differences could go in to platform specific include
# files rather than cluttering it up with too many if blocks.
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc direwolf.conf
@echo " "
@echo "Next step install with: "
@ -179,7 +187,7 @@ CFLAGS += -DUSE_PORTAUDIO -I/opt/local/include
# Not available for MacOSX.
# Although MacPorts has gpsd, wonder if it's the same thing.
#CFLAGS += -DENABLE_GPS
#CFLAGS += -DENABLE_GPSD
#LDLIBS += -lgps
# Name of current directory.
@ -197,7 +205,8 @@ direwolf : direwolf.o aprs_tt.o audio_portaudio.o audio_stats.o ax25_pad.o beaco
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
symbols.o telemetry.o textcolor.o tq.o tt_text.o tt_user.o xmit.o \
dwgps.o dwgpsnmea.o dwgpsd.o
$(CC) $(CFLAGS) -o $@ $^ -lpthread $(LDLIBS) -lm
@ -264,45 +273,89 @@ INSTALLDIR := /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
# Command to "install" to system directories. "install" for Linux. "ginstall" for Mac.
INSTALL=ginstall
.PHONY: install
install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png direwolf.desktop
#
# Applications, not installed with package manager, normally go in /usr/local/bin.
# /usr/bin is used instead when installing from .DEB or .RPM package.
#
$(INSTALL) direwolf $(INSTALLDIR)/bin
$(INSTALL) decode_aprs $(INSTALLDIR)/bin
$(INSTALL) text2tt $(INSTALLDIR)/bin
$(INSTALL) tt2text $(INSTALLDIR)/bin
$(INSTALL) ll2utm $(INSTALLDIR)/bin
$(INSTALL) utm2ll $(INSTALLDIR)/bin
$(INSTALL) aclients $(INSTALLDIR)/bin
$(INSTALL) log2gpx $(INSTALLDIR)/bin
$(INSTALL) gen_packets $(INSTALLDIR)/bin
$(INSTALL) atest $(INSTALLDIR)/bin
$(INSTALL) ttcalc $(INSTALLDIR)/bin
$(INSTALL) dwespeak.sh $(INSTALLDIR)/bin
#
# Telemetry Toolkit executables. Other .conf and .txt files will go into doc directory.
#
$(INSTALL) telemetry-toolkit/telem-balloon.pl $(INSTALLDIR)/bin
$(INSTALL) telemetry-toolkit/telem-bits.pl $(INSTALLDIR)/bin
$(INSTALL) telemetry-toolkit/telem-data.pl $(INSTALLDIR)/bin
$(INSTALL) telemetry-toolkit/telem-data91.pl $(INSTALLDIR)/bin
$(INSTALL) telemetry-toolkit/telem-eqns.pl $(INSTALLDIR)/bin
$(INSTALL) telemetry-toolkit/telem-parm.pl $(INSTALLDIR)/bin
$(INSTALL) telemetry-toolkit/telem-unit.pl $(INSTALLDIR)/bin
$(INSTALL) telemetry-toolkit/telem-volts.py $(INSTALLDIR)/bin
#
# Misc. data such as "tocall" to system mapping.
#
$(INSTALL) -D --mode=644 tocalls.txt /usr/share/direwolf/tocalls.txt
$(INSTALL) -D --mode=644 symbols-new.txt /usr/share/direwolf/symbols-new.txt
$(INSTALL) -D --mode=644 symbolsX.txt /usr/share/direwolf/symbolsX.txt
$(INSTALL) -D --mode=644 dw-icon.png /usr/share/direwolf/dw-icon.png
$(INSTALL) -D --mode=644 direwolf.desktop /usr/share/applications/direwolf.desktop
#
# Documentation. Various plain text files and PDF.
#
$(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 doc/User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf
$(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf
$(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS-Tracker.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf
$(INSTALL) -D --mode=644 doc/Raspberry-Pi-SDR-IGate.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-SDR-IGate.pdf
$(INSTALL) -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf
$(INSTALL) -D --mode=644 doc/APRStt-interface-for-SARTrack.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-interface-for-SARTrack.pdf
$(INSTALL) -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf
$(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf
$(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf
#
# Sample config files also go into the doc directory.
# When building from source, these can be put in home directory with "make install-conf".
# When installed from .DEB or .RPM package, the user will need to copy these to
# the home directory or other desired location.
# Someone suggested that these could go into an "examples" subdirectory under doc.
#
$(INSTALL) -D --mode=644 direwolf.conf $(INSTALLDIR)/share/doc/direwolf/direwolf.conf
$(INSTALL) -D --mode=644 telemetry-toolkit/telem-m0xer-3.txt $(INSTALLDIR)/share/doc/direwolf/telem-m0xer-3.txt
$(INSTALL) -D --mode=644 telemetry-toolkit/telem-balloon.conf $(INSTALLDIR)/share/doc/direwolf/telem-balloon.conf
$(INSTALL) -D --mode=644 telemetry-toolkit/telem-volts.conf $(INSTALLDIR)/share/doc/direwolf/telem-volts.conf
#
# "man" pages
#
$(INSTALL) -D --mode=644 man1/aclients.1 $(INSTALLDIR)/man/man1/aclients.1
$(INSTALL) -D --mode=644 man1/atest.1 $(INSTALLDIR)/man/man1/atest.1
$(INSTALL) -D --mode=644 man1/decode_aprs.1 $(INSTALLDIR)/man/man1/decode_aprs.1
$(INSTALL) -D --mode=644 man1/direwolf.1 $(INSTALLDIR)/man/man1/direwolf.1
$(INSTALL) -D --mode=644 man1/gen_packets.1 $(INSTALLDIR)/man/man1/gen_packets.1
$(INSTALL) -D --mode=644 man1/ll2utm.1 $(INSTALLDIR)/man/man1/ll2utm.1
$(INSTALL) -D --mode=644 man1/log2gpx.1 $(INSTALLDIR)/man/man1/log2gpx.1
$(INSTALL) -D --mode=644 man1/text2tt.1 $(INSTALLDIR)/man/man1/text2tt.1
$(INSTALL) -D --mode=644 man1/tt2text.1 $(INSTALLDIR)/man/man1/tt2text.1
$(INSTALL) -D --mode=644 man1/utm2ll.1 $(INSTALLDIR)/man/man1/utm2ll.1
#
@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:"
@ -333,16 +386,16 @@ install-conf : direwolf.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
decode_aprs : decode_aprs.c dwgpsnmea.o dwgps.o serial_port.o symbols.o ax25_pad.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o
$(CC) $(CFLAGS) -DDECAMAIN -o $@ $^ -lm
# Convert between text and touch tone representation.
text2tt : tt_text.c
$(CC) $(CFLAGS) -DENC_MAIN -o text2tt tt_text.c
$(CC) $(CFLAGS) -DENC_MAIN -o $@ $^
tt2text : tt_text.c
$(CC) $(CFLAGS) -DDEC_MAIN -o tt2text tt_text.c
$(CC) $(CFLAGS) -DDEC_MAIN -o $@ $^
# Convert between Latitude/Longitude and UTM coordinates.
@ -378,21 +431,22 @@ testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o
# 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
atest : atest.c fsk_fast_filter.h 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 dwgpsnmea.o dwgps.o serial_port.o 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
dtest : digipeater.c pfilter.o ax25_pad.o dedupe.o fcs_calc.o tq.o textcolor.o \
decode_aprs.o dwgpsnmea.o dwgps.o serial_port.o latlong.o telemetry.o symbols.o tt_text.o
$(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 geotranz.a
$(CC) $(CFLAGS) -DTT_MAIN -o $@ $^
@ -414,9 +468,9 @@ 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
tlmtest : telemetry.c ax25_pad.c fcs_calc.c textcolor.c
$(CC) $(CFLAGS) -o $@ $^ -lm
./etest
./tlmtest
# Multiple AGWPE network or serial port clients to test TNCs side by side.
@ -514,3 +568,31 @@ dist-src : README.md CHANGES.md \
$z/dw-start.sh $z/direwolf.spec \
$z/dwespeak.bat $z/dwespeak.sh \
$z/telemetry-toolkit/* )
#
# The destination field is often used to identify the manufacturer/model.
# These are not hardcoded into Dire Wolf. Instead they are read from
# a file called tocalls.txt at application start up time.
#
# The original permanent symbols are built in but the "new" symbols,
# using overlays, are often updated. These are also read from files.
#
# You can obtain an updated copy by typing "make tocalls-symbols".
# This is not part of the normal build process. You have to do this explicitly.
#
# The locations below appear to be the most recent.
# The copy at http://www.aprs.org/tocalls.txt is out of date.
#
.PHONY: tocalls-symbols
tocalls-symbols :
cp tocalls.txt tocalls.txt~
wget http://www.aprs.org/aprs11/tocalls.txt -O tocalls.txt
-diff tocalls.txt~ tocalls.txt
cp symbols-new.txt symbols-new.txt~
wget http://www.aprs.org/symbols/symbols-new.txt -O symbols-new.txt
-diff symbols-new.txt~ symbols-new.txt
cp symbolsX.txt symbolsX.txt~
wget http://www.aprs.org/symbols/symbolsX.txt -O symbolsX.txt
-diff symbolsX.txt~ symbolsX.txt

View File

@ -56,13 +56,10 @@ CFLAGS += -g
# you can compile this yourself with different options.
#
# Name of zip file for distribution.
z := $(notdir ${CURDIR})
# Main application.
# -------------------------------------- Main application --------------------------------
demod.o : fsk_demod_state.h
demod_9600.o : fsk_demod_state.h
@ -75,7 +72,8 @@ direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_9600.o hd
decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
gen_tone.o morse.o audio_win.o audio_stats.o digipeater.o 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 serial_port.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 \
dwgps.o dwgpsnmea.o dtime_now.o \
dw-icon.o regex.a misc.a geotranz.a
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
@ -95,6 +93,79 @@ fsk_fast_filter.h : demod_afsk.c
./gen_fff > fsk_fast_filter.h
#
# The destination field is often used to identify the manufacturer/model.
# These are not hardcoded into Dire Wolf. Instead they are read from
# a file called tocalls.txt at application start up time.
#
# The original permanent symbols are built in but the "new" symbols,
# using overlays, are often updated. These are also read from files.
#
# You can obtain an updated copy by typing "make tocalls-symbols".
# This is not part of the normal build process. You have to do this explicitly.
#
# The locations below appear to be the most recent.
# The copy at http://www.aprs.org/tocalls.txt is out of date.
#
.PHONY: tocalls-symbols
tocalls-symbols :
cp tocalls.txt tocalls.txt~
wget http://www.aprs.org/aprs11/tocalls.txt -O tocalls.txt
-diff tocalls.txt~ tocalls.txt
cp symbols-new.txt symbols-new.txt~
wget http://www.aprs.org/symbols/symbols-new.txt -O symbols-new.txt
-diff symbols-new.txt~ symbols-new.txt
cp symbolsX.txt symbolsX.txt~
wget http://www.aprs.org/symbols/symbolsX.txt -O symbolsX.txt
-diff symbolsX.txt~ symbolsX.txt
# ---------------------------- Other utilities included with distribution -------------------------
# Separate application to decode raw data.
decode_aprs : decode_aprs.c dwgpsnmea.o dwgps.o serial_port.o symbols.o ax25_pad.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o regex.a misc.a geotranz.a
$(CC) $(CFLAGS) -DDECAMAIN -o decode_aprs $^
# Convert between text and touch tone representation.
text2tt : tt_text.c misc.a
$(CC) $(CFLAGS) -DENC_MAIN -o $@ $^
tt2text : tt_text.c misc.a
$(CC) $(CFLAGS) -DDEC_MAIN -o $@ $^
# Convert between Latitude/Longitude and UTM coordinates.
ll2utm : ll2utm.c textcolor.c geotranz.a misc.a
$(CC) $(CFLAGS) -o $@ $^
utm2ll : utm2ll.c textcolor.c geotranz.a misc.a
$(CC) $(CFLAGS) -o $@ $^
# Convert from log file to GPX.
log2gpx : log2gpx.c textcolor.o 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 morse.o textcolor.o dsp.o misc.a regex.a
$(CC) $(CFLAGS) -o $@ $^
# ------------------------------------------- Libraries --------------------------------------------
# UTM, USNG, MGRS conversions.
geotranz.a : error_string.o mgrs.o polarst.o tranmerc.o ups.o usng.o utm.o
@ -136,8 +207,8 @@ regex.o : regex/regex.c
$(CC) $(CFLAGS) -Dbool=int -Dtrue=1 -Dfalse=0 -c -o $@ $^
# There are also a couple other functions in the misc
# subdirectory that are missing on Windows.
# There are several string functios found in Linux
# but not on Windows. Need to provide our own copy.
misc.a : strsep.o strtok_r.o strcasestr.o strlcpy.o strlcat.o
ar -cr $@ $^
@ -158,41 +229,129 @@ 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 tt_text.o regex.a misc.a geotranz.a
$(CC) $(CFLAGS) -o decode_aprs -DTEST $^
# --------------------------------- Automated Smoke Test --------------------------------
# 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
# Combine some unit tests into a single regression sanity check.
# Convert between Latitude/Longitude and UTM coordinates.
check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest check-modem1200 check-modem300 check-modem9600
ll2utm : ll2utm.c textcolor.c geotranz.a misc.a
# Can we encode and decode at popular data rates?
check-modem1200 : gen_packets atest
gen_packets -n 100 -o test1.wav
atest -F0 -PE -L70 -G71 test1.wav
atest -F1 -PE -L73 -G75 test1.wav
#rm test1.wav
check-modem300 : gen_packets atest
gen_packets -B300 -n 100 -o test3.wav
atest -B300 -F0 -L68 -G69 test3.wav
atest -B300 -F1 -L73 -G75 test3.wav
rm test3.wav
check-modem9600 : gen_packets atest
gen_packets -B9600 -n 100 -o test9.wav
atest -B9600 -F0 -L57 -G59 test9.wav
atest -B9600 -F1 -L66 -G67 test9.wav
rm test9.wav
# Unit test for AFSK demodulator
atest : atest.c fsk_fast_filter.h demod.c demod_afsk.c demod_9600.c \
dsp.o hdlc_rec.o hdlc_rec2.o multi_modem.o \
rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o \
dwgpsnmea.o dwgps.o serial_port.o latlong.c \
symbols.c tt_text.c textcolor.c telemetry.c \
misc.a regex.a
echo " " > tune.h
$(CC) $(CFLAGS) -o $@ $^
#./atest ..\\direwolf-0.2\\02_Track_2.wav
#atest -B 9600 z9.wav
#atest za100.wav
utm2ll : utm2ll.c textcolor.c geotranz.a misc.a
atest9 : 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 \
fsk_fast_filter.h
echo " " > tune.h
$(CC) $(CFLAGS) -o $@ $^
./atest9 -B 9600 ../walkabout9600.wav | grep "packets decoded in" >atest.out
#./atest9 -B 9600 noise96.wav
# Convert from log file to GPX.
# Unit test for inner digipeater algorithm
#log2gpx : log2gpx.c misc/strsep.c misc/strtok_r.c
log2gpx : log2gpx.c misc.a
$(CC) $(CFLAGS) -o $@ $^
.PHONY: dtest
dtest : digipeater.c pfilter.o ax25_pad.o dedupe.o fcs_calc.o tq.o textcolor.o \
decode_aprs.o dwgpsnmea.o dwgps.o serial_port.o latlong.o telemetry.o symbols.o tt_text.o misc.a regex.a
$(CC) $(CFLAGS) -DDIGITEST -o $@ $^
./dtest
rm dtest.exe
# Unit test for APRStt tone seqence parsing.
.PHONTY: ttest
ttest : aprs_tt.c tt_text.c latlong.o textcolor.o geotranz.a misc.a
$(CC) $(CFLAGS) -Igeotranz -DTT_MAIN -o $@ $^
./ttest
rm ttest.exe
# Unit test for APRStt tone sequence / text conversions.
.PHONY: tttexttest
tttexttest : tt_text.c textcolor.o misc.a
$(CC) $(CFLAGS) -DTTT_TEST -o $@ $^
./tttexttest
rm tttexttest.exe
# Unit test for Packet Filtering.
.PHONY: pftest
pftest : pfilter.c ax25_pad.o textcolor.o fcs_calc.o decode_aprs.o dwgpsnmea.o dwgps.o serial_port.o latlong.o symbols.o telemetry.o tt_text.o misc.a regex.a
$(CC) $(CFLAGS) -DPFTEST -o $@ $^
./pftest
rm pftest.exe
# Test application to generate sound.
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 $@ $^
# Unit test for telemetry decoding.
.PHONY: tlmtest
tlmtest : telemetry.c ax25_pad.o fcs_calc.o textcolor.o misc.a regex.a
$(CC) $(CFLAGS) -DTEST -o $@ $^
./tlmtest
rm tlmtest.exe
# Unit test for location coordinate conversion.
.PHONY: lltest
lltest : latlong.c textcolor.o misc.a
$(CC) $(CFLAGS) -DLLTEST -o $@ $^
./lltest
rm lltest.exe
# Unit test for encoding position & object report.
.PHONY: enctest
enctest : encode_aprs.c latlong.c textcolor.c misc.a
$(CC) $(CFLAGS) -DEN_MAIN -o $@ $^
./enctest
rm enctest.exe
# Unit test for KISS encapsulation.
.PHONY: kisstest
kisstest : kiss_frame.c
$(CC) $(CFLAGS) -DKISSTEST -o $@ $^
./kisstest
rm kisstest.exe
# ------------------------------ Other manual testing & experimenting -------------------------------
# For tweaking the demodulator.
@ -236,54 +395,14 @@ testagc9 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.
#./atest -B 9600 noisy96.wav | grep "packets decoded in" >atest.out
echo " " > tune.h
# 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 tt_text.c textcolor.c telemetry.c misc.a regex.a \
fsk_fast_filter.h
echo " " > tune.h
$(CC) $(CFLAGS) -o $@ $^
#./atest ..\\direwolf-0.2\\02_Track_2.wav
#atest -B 9600 z9.wav
#atest za100.wav
atest9 : 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 \
fsk_fast_filter.h
echo " " > tune.h
$(CC) $(CFLAGS) -o $@ $^
./atest9 -B 9600 ../walkabout9600.wav | grep "packets decoded in" >atest.out
#./atest9 -B 9600 noise96.wav
# Unit test for inner digipeater algorithm
dtest : digipeater.c pfilter.c ax25_pad.c dedupe.c fcs_calc.c tq.c textcolor.c misc.a regex.a
$(CC) $(CFLAGS) -DTEST -o $@ $^
./dtest
rm dtest.exe
# 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 misc.a regex.a
$(CC) $(CFLAGS) -DITEST -o $@ $^ -lwinmm -lws2_32
# Unit test for telemetry decoding.
etest : telemetry.c ax25_pad.c fcs_calc.c textcolor.c misc.a regex.a
$(CC) $(CFLAGS) -DTEST -o $@ $^
./etest
# Multiple AGWPE network or serial port clients to test TNCs side by side.
@ -299,14 +418,20 @@ ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a regex.a
# Send GPS location to KISS TNC each second.
walk96 : walk96.c nmea.c kiss_frame.c \
walk96 : walk96.c dwgps.o dwgpsnmea.o kiss_frame.o \
latlong.o encode_aprs.o serial_port.o textcolor.o \
ax25_pad.o fcs_calc.o regex.a \
misc.a
ax25_pad.o fcs_calc.o \
xmit.o hdlc_send.o gen_tone.o ptt.o tq.o \
hdlc_rec.o hdlc_rec2.o rrbb.o dsp.o audio_win.o \
multi_modem.o demod.o demod_afsk.o demod_9600.o rdq.o \
server.o morse.o audio_stats.o dtime_now.o dlq.o \
regex.a misc.a
$(CC) $(CFLAGS) -DWALK96 -o $@ $^ -lwinmm -lws2_32
#--------------------------------------------------------------
.PHONY: depend
depend : $(wildcard *.c)
@ -318,7 +443,12 @@ clean :
echo " " > tune.h
# Package it up for distribution: Prebuilt Windows & source versions.
# ------------------------------- Packaging for distribution ----------------------
# Name of zip file for distribution.
z := $(notdir ${CURDIR})
# Left out RPi Tracker due to Comcast upload size limit.
@ -416,9 +546,9 @@ dist-src : README.md CHANGES.md \
unix2dos telemetry-toolkit/telem-unit.pl
unix2dos telemetry-toolkit/telem-volts.py
unix2dos telemetry-toolkit/telem-volts.conf
# Reminders if pdf files are not up to date.
doc/User-Guide.pdf : doc/User-Guide.docx
echo "***** User-Guide.pdf is out of date *****"
@ -448,22 +578,6 @@ backup :
mkdir /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"`
cp -r . /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"`
#
# The locations below appear to be the most recent.
# The copy at http://www.aprs.org/tocalls.txt is out of date.
#
.PHONY: tocalls-symbols
tocalls-symbols :
cp tocalls.txt tocalls.txt~
wget http://www.aprs.org/aprs11/tocalls.txt -O tocalls.txt
diff tocalls.txt~ tocalls.txt
cp symbols-new.txt symbols-new.txt~
wget http://www.aprs.org/symbols/symbols-new.txt -O symbols-new.txt
diff symbols-new.txt~ symbols-new.txt
cp symbolsX.txt symbolsX.txt~
wget http://www.aprs.org/symbols/symbolsX.txt -O symbolsX.txt
diff symbolsX.txt~ symbolsX.txt
#
# The following is updated by "make depend"

View File

@ -71,6 +71,7 @@
#include <assert.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include "direwolf.h"
@ -115,7 +116,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);
@ -126,7 +127,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]),
@ -140,7 +141,7 @@ 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);
return pStringBuf;
@ -222,20 +223,20 @@ int main (int argc, char *argv[])
char stemp[100];
char *p;
strcpy (stemp, argv[j+1]);
strlcpy (stemp, argv[j+1], sizeof(stemp));
p = strtok (stemp, "=");
if (p == NULL) {
printf ("Internal error 1\n");
exit (1);
}
strcpy (hostname[j], "localhost");
strcpy (port[j], p);
strlcpy (hostname[j], "localhost", sizeof(hostname[j]));
strlcpy (port[j], p, sizeof(port[j]));
p = strtok (NULL, "=");
if (p == NULL) {
printf ("Missing description after %s\n", port[j]);
exit (1);
}
strcpy (description[j], p);
strlcpy (description[j], p, sizeof(description[j]));
}
//printf ("_WIN32_WINNT = %04x\n", _WIN32_WINNT);
@ -579,14 +580,14 @@ static void * client_thread_net (void *arg)
//printf ("server %d, portx = %d\n", my_index, mon_cmd.portx);
use_chan == mon_cmd.portx;
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';
strcat (result, pinfo);
strlcat (result, pinfo, sizeof(result));
for (p=result; *p!='\0'; p++) {
if (! isprint(*p)) *p = ' ';
}

368
aprs_tt.c
View File

@ -22,9 +22,14 @@
*
* Module: aprs_tt.c
*
* Purpose: APRStt gateway.
* Purpose: First half of APRStt gateway.
*
* Description: This file contains functions to parse the tone sequences
* and extract meaning from them.
*
* tt_user.c maintains information about users and
* generates the APRS Object Reports.
*
* Description: Transfer touch tone messages into the APRS network.
*
* References: This is based upon APRStt (TM) documents with some
* artistic freedom.
@ -38,7 +43,7 @@
// TODO: clean up terminolgy.
// "Message" has a specific meaning in APRS and this is not it.
// Touch Tone sequence might be appropriate.
// Touch Tone sequence should be appropriate.
// What do we call the parts separated by * key? Entry? Field?
@ -67,9 +72,7 @@
#include "tq.h"
#if __WIN32__
char *strtok_r(char *str, const char *delim, char **saveptr);
#endif
// geotranz
@ -103,9 +106,11 @@ static int parse_location (char *e);
static int parse_comment (char *e);
static int expand_macro (char *e);
static void raw_tt_data_to_app (int chan, char *msg);
static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *bstr, char *dstr);
static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *bstr, char *dstr, size_t valstrsize);
#if TT_MAIN
static void check_result (void);
#endif
/*------------------------------------------------------------------
@ -285,8 +290,8 @@ void aprs_tt_button (int chan, char button)
*
* Returns: None
*
* Description: Process a complete message.
* It should have one or more fields separatedy by *
* Description: Process a complete tone sequence.
* It should have one or more fields separated by *
* and terminated by a final # like these:
*
* callsign #
@ -312,27 +317,22 @@ static char m_callsign[20]; /* really object name */
*/
static char m_symtab_or_overlay;
static char m_symbol_code;
static char m_symbol_code; // Default 'A'
static char m_loc_text[24];
static double m_longitude;
static double m_latitude;
static double m_longitude; // Set to G_UNKNOWN if not defined.
static double m_latitude; // Set to G_UNKNOWN if not defined.
static char m_comment[200];
static char m_freq[12];
static char m_mic_e;
static char m_dao[6];
static int m_ssid;
//#define G_UNKNOWN -999999
static int m_ssid; // Default 12 for APRStt user.
void aprs_tt_sequence (int chan, char *msg)
{
int err;
char audible_response[1000];
packet_t pp;
char script_response[1000];
#if DEBUG
@ -367,37 +367,40 @@ void aprs_tt_sequence (int chan, char *msg)
*/
err = parse_fields (msg);
#if defined(DEBUG) || defined(TT_MAIN)
#if defined(DEBUG)
text_color_set(DW_COLOR_DEBUG);
dw_printf ("callsign=\"%s\", ssid=%d, symbol=\"%c%c\", freq=\"%s\", comment=\"%s\", lat=%.4f, lon=%.4f, dao=\"%s\"\n",
m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code, m_freq, m_comment, m_latitude, m_longitude, m_dao);
#endif
#if TT_MAIN
check_result (); // for unit testing.
#else
/*
* If digested successfully. Add to our list of users and schedule transmissions.
*/
if (err == 0) {
/*
* Digested successfully. Add to our list of users and schedule transmissions.
*/
#ifndef TT_MAIN
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
}
/*
* If a command / script was supplied, run it now.
* This can do additional processing and provide a custom audible response.
* This is done even for the error case.
*/
char script_response[1000];
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));
dw_run_cmd (tt_config.ttcmd, 1, script_response, sizeof(script_response));
}
@ -407,11 +410,15 @@ void aprs_tt_sequence (int chan, char *msg)
* Use high priority queue for consistent timing.
*/
char audible_response[1000];
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);
packet_t pp;
pp = ax25_from_text (audible_response, 0);
if (pp == NULL) {
@ -422,6 +429,7 @@ void aprs_tt_sequence (int chan, char *msg)
tq_append (chan, TQ_PRIO_0_HI, pp);
#endif /* ifndef TT_MAIN */
} /* end aprs_tt_sequence */
@ -461,14 +469,14 @@ static int parse_fields (char *msg)
//text_color_set(DW_COLOR_DEBUG);
//printf ("parse_fields (%s).\n", msg);
//dw_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);
//dw_printf ("parse_fields () field = %s\n", e);
switch (*e) {
@ -539,7 +547,7 @@ static int parse_fields (char *msg)
}
//text_color_set(DW_COLOR_DEBUG);
//printf ("parse_fields () normal return\n");
//dw_printf ("parse_fields () normal return\n");
return (0);
@ -566,21 +574,23 @@ static int parse_fields (char *msg)
*
*----------------------------------------------------------------*/
#define VALSTRSIZE 20
static int expand_macro (char *e)
{
int len;
//int len;
int ipat;
char xstr[20], ystr[20], zstr[20], bstr[20], dstr[20];
char xstr[VALSTRSIZE], ystr[VALSTRSIZE], zstr[VALSTRSIZE], bstr[VALSTRSIZE], dstr[VALSTRSIZE];
char stemp[MAX_MSG_LEN+1];
char *d, *s;
char *d;
text_color_set(DW_COLOR_DEBUG);
dw_printf ("Macro tone sequence: '%s'\n", e);
len = strlen(e);
//len = strlen(e);
ipat = find_ttloc_match (e, xstr, ystr, zstr, bstr, dstr);
ipat = find_ttloc_match (e, xstr, ystr, zstr, bstr, dstr, VALSTRSIZE);
if (ipat >= 0) {
@ -723,7 +733,7 @@ static int checksum_not_ok (char *str, int len, char found)
static int parse_callsign (char *e)
{
int len;
int c_length;
//int c_length;
char tttemp[40], stemp[30];
assert (*e == 'A');
@ -838,8 +848,9 @@ static int parse_callsign (char *e)
static int parse_object_name (char *e)
{
int len;
int c_length;
char tttemp[40], stemp[30];
//int c_length;
//char tttemp[40];
//char stemp[30];
assert (e[0] == 'A');
assert (e[1] == 'A');
@ -991,19 +1002,15 @@ static int parse_symbol (char *e)
*
*----------------------------------------------------------------*/
/* Average radius of earth in meters. */
#define R 6371000.
static int parse_location (char *e)
{
int len;
//int len;
int ipat;
char xstr[20], ystr[20], zstr[20], bstr[20], dstr[20];
char xstr[VALSTRSIZE], ystr[VALSTRSIZE], zstr[VALSTRSIZE], bstr[VALSTRSIZE], dstr[VALSTRSIZE];
double x, y, dist, bearing;
double lat0, lon0;
double lat9, lon9;
@ -1022,9 +1029,9 @@ static int parse_location (char *e)
/* If this ever changes, be sure to update corresponding */
/* section in process_comment() in decode_aprs.c */
len = strlen(e);
//len = strlen(e);
ipat = find_ttloc_match (e, xstr, ystr, zstr, bstr, dstr);
ipat = find_ttloc_match (e, xstr, ystr, zstr, bstr, dstr, VALSTRSIZE);
if (ipat >= 0) {
//dw_printf ("ipat=%d, x=%s, y=%s, b=%s, d=%s\n", ipat, xstr, ystr, bstr, dstr);
@ -1140,7 +1147,7 @@ static int parse_location (char *e)
m_latitude = R2D(lat0);
m_longitude = R2D(lon0);
//printf ("DEBUG: from UTM, latitude = %.6f, longitude = %.6f\n", m_latitude, m_longitude);
//dw_printf ("DEBUG: from UTM, latitude = %.6f, longitude = %.6f\n", m_latitude, m_longitude);
}
else {
char message[300];
@ -1190,7 +1197,7 @@ static int parse_location (char *e)
m_latitude = R2D(lat0);
m_longitude = R2D(lon0);
//printf ("DEBUG: from MGRS/USNG, latitude = %.6f, longitude = %.6f\n", m_latitude, m_longitude);
//dw_printf ("DEBUG: from MGRS/USNG, latitude = %.6f, longitude = %.6f\n", m_latitude, m_longitude);
}
else {
char message[300];
@ -1219,7 +1226,7 @@ static int parse_location (char *e)
//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) {
if (tt_mhead_to_text (stemp, 0, mh, sizeof(mh)) == 0) {
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("Case MHEAD: Resulting text \"%s\".\n", mh);
@ -1277,6 +1284,8 @@ static int parse_location (char *e)
* APRStt messsage.
* In this case, it should start with "B".
*
* valstrsize - size of the outputs so we can check for buffer overflow.
*
* Outputs: xstr - All digits matching x positions in configuration.
* ystr - y
* zstr - z
@ -1290,7 +1299,7 @@ static int parse_location (char *e)
*
*----------------------------------------------------------------*/
static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *bstr, char *dstr)
static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *bstr, char *dstr, size_t valstrsize)
{
int ipat; /* Index into patterns from configuration file */
int len; /* Length of pattern we are trying to match. */
@ -1307,11 +1316,11 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
if (strlen(e) == len) {
match = 1;
strlcpy (xstr, "", sizeof(xstr));
strlcpy (ystr, "", sizeof(ystr));
strlcpy (zstr, "", sizeof(zstr));
strlcpy (bstr, "", sizeof(bstr));
strlcpy (dstr, "", sizeof(dstr));
strlcpy (xstr, "", valstrsize);
strlcpy (ystr, "", valstrsize);
strlcpy (zstr, "", valstrsize);
strlcpy (bstr, "", valstrsize);
strlcpy (dstr, "", valstrsize);
for (k=0; k<len; k++) {
mc = tt_config.ttloc_ptr[ipat].pattern[k];
@ -1342,7 +1351,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';
strlcat (xstr, stemp, sizeof(xstr));
strlcat (xstr, stemp, valstrsize);
}
else {
match = 0;
@ -1354,7 +1363,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';
strlcat (ystr, stemp, sizeof(ystr));
strlcat (ystr, stemp, valstrsize);
}
else {
match = 0;
@ -1366,7 +1375,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';
strlcat (zstr, stemp, sizeof(zstr));
strlcat (zstr, stemp, valstrsize);
}
else {
match = 0;
@ -1378,7 +1387,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';
strlcat (bstr, stemp, sizeof(bstr));
strlcat (bstr, stemp, valstrsize);
}
else {
match = 0;
@ -1390,7 +1399,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';
strlcat (dstr, stemp, sizeof(dstr));
strlcat (dstr, stemp, valstrsize);
}
else {
match = 0;
@ -1447,7 +1456,6 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
static int parse_comment (char *e)
{
int len;
int n;
assert (*e == 'C');
@ -1579,7 +1587,7 @@ static void raw_tt_data_to_app (int chan, char *msg)
* is one line of text.
* 2 = Also remove any trailing whitespace.
*
* maxresult - Amount of space available for result.
* resultsiz - Amount of space available for result.
*
* Outputs: result - Output captured from running command.
*
@ -1594,21 +1602,21 @@ static void raw_tt_data_to_app (int chan, char *msg)
*
*----------------------------------------------------------------*/
int dw_run_cmd (char *cmd, int oneline, char *result, int maxresult)
int dw_run_cmd (char *cmd, int oneline, char *result, size_t resultsiz)
{
FILE *fp;
strlcpy (result, "", sizeof(result));
strlcpy (result, "", resultsiz);
fp = popen (cmd, "r");
if (fp != NULL) {
int remaining = maxresult;
int remaining = (int)resultsiz;
char *pr = result;
int err;
while (remaining > 2 && fgets(pr, remaining, fp) != NULL) {
pr = result + strlen(result);
remaining = maxresult - strlen(result);
remaining = (int)resultsiz - strlen(result);
}
if ((err = pclose(fp)) != 0) {
@ -1670,106 +1678,182 @@ int dw_run_cmd (char *cmd, int oneline, char *result, int maxresult)
*
* Description: Run unit test like this:
*
* rm a.exe ; gcc tt_text.c -DTT_MAIN -DDEBUG aprs_tt.c latlong.c strtok_r.o utm/LatLong-UTMconversion.c ; ./a.exe
*
* Bugs: No automatic checking.
* Just eyeball it to see if things look right.
* rm a.exe ; gcc tt_text.c -DTT_MAIN -Igeotranz aprs_tt.c latlong.o textcolor.o geotranz.a misc.a ; ./a.exe
* or
* make ttest
*
*----------------------------------------------------------------*/
// TODO: add this to "make check"
#if TT_MAIN
/*
* Regression test for the parsing.
* It does not maintain any history so abbreviation will not invoke previous full call.
*/
void text_color_set (dw_color_t c) { return; }
int dw_printf (const char *fmt, ...)
{
va_list args;
int len;
static const struct {
char *toneseq; /* Tone sequence in. */
va_start (args, fmt);
len = vprintf (fmt, args);
va_end (args);
return (len);
}
char *callsign; /* Expected results... */
char *ssid;
char *symbol;
char *freq;
char *comment;
char *lat;
char *lon;
char *dao;
} testcases[] = {
/* Callsigns & abbreviations. */
{ "A9A2B42A7A7C71#", "WB4APR", "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* WB4APR/7 */
{ "A27773#", "277", "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* abbreviated form */
/* Example in http://www.aprs.org/aprstt/aprstt-coding24.txt has a bad checksum! */
/* Bad checksum for "2777". Expected 3 but received 6. */
{ "A27776#", "", "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* Expect error message. */
/* Bad checksum for "2A7A7C7". E xpected 5 but received 1. */
{ "A2A7A7C71#", "", "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* Spelled suffix, overlay, checksum */
{ "A27773#", "277", "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* Suffix digits, overlay, checksum */
{ "A9A2B26C7D9D71#", "WB2OSZ", "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* WB2OSZ/7 numeric overlay */
{ "A67979#", "679", "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* abbreviated form */
{ "A9A2B26C7D9D5A9#", "WB2OSZ", "12", "JA", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* WB2OSZ/J letter overlay */
{ "A6795A7#", "679", "12", "JA", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* abbreviated form */
{ "A277#", "277", "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* Tactical call "277" no overlay and no checksum */
/* Locations */
{ "B01*A67979#", "679", "12", "7A", "", "", "12.2500", "56.2500", "!T1 !" },
{ "B988*A67979#", "679", "12", "7A", "", "", "12.5000", "56.5000", "!T88!" },
{ "B51000125*A67979#", "679", "12", "7A", "", "", "52.7907", "0.8309", "!TB5!" }, /* expect about 52.79 +0.83 */
{ "B5206070*A67979#", "679", "12", "7A", "", "", "37.9137", "-81.1366", "!TB5!" }, /* Try to get from Hilltop Tower to Archery & Target Range. */
/* Latitude comes out ok, 37.9137 -> 55.82 min. */
/* Longitude -81.1254 -> 8.20 min */
{ "B21234*A67979#", "679", "12", "7A", "", "", "12.3400", "56.1200", "!TB2!" },
{ "B533686*A67979#", "679", "12", "7A", "", "", "37.9222", "81.1143", "!TB5!" },
/* Comments */
{ "C1", "", "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T !" },
{ "C2", "", "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T !" },
{ "C146520", "", "12", "\\A", "146.520MHz", "", "-999999.0000", "-999999.0000", "!T !" },
{ "C7788444222550227776669660333666990122223333",
"", "12", "\\A", "", "QUICK BROWN FOX 123", "-999999.0000", "-999999.0000", "!T !" },
/* Macros */
{ "88345", "BIKE 345", "0", "/b", "", "", "12.5000", "56.5000", "!T88!" },
/* 10 digit representation for callsign & satellite grid. WB4APR near 39.5, -77 */
{ "AC9242771558*BA1819", "WB4APR", "12", "\\A", "", "", "39.5000", "-77.0000", "!TBA!" },
{ "18199242771558", "WB4APR", "12", "\\A", "", "", "39.5000", "-77.0000", "!TBA!" },
};
static int test_num;
static int error_count;
static void check_result (void)
{
char stemp[32];
text_color_set(DW_COLOR_DEBUG);
dw_printf ("callsign=\"%s\", ssid=%d, symbol=\"%c%c\", freq=\"%s\", comment=\"%s\", lat=%.4f, lon=%.4f, dao=\"%s\"\n",
m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code, m_freq, m_comment, m_latitude, m_longitude, m_dao);
if (strcmp(m_callsign, testcases[test_num].callsign) != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR: Expected \"%s\" for callsign.\n", testcases[test_num].callsign);
error_count++;
}
snprintf (stemp, sizeof(stemp), "%d", m_ssid);
if (strcmp(stemp, testcases[test_num].ssid) != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR: Expected \"%s\" for SSID.\n", testcases[test_num].ssid);
error_count++;
}
stemp[0] = m_symtab_or_overlay;
stemp[1] = m_symbol_code;
stemp[2] = '\0';
if (strcmp(stemp, testcases[test_num].symbol) != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR: Expected \"%s\" for Symbol.\n", testcases[test_num].symbol);
error_count++;
}
if (strcmp(m_freq, testcases[test_num].freq) != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR: Expected \"%s\" for Freq.\n", testcases[test_num].freq);
error_count++;
}
if (strcmp(m_comment, testcases[test_num].comment) != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR: Expected \"%s\" for Comment.\n", testcases[test_num].comment);
error_count++;
}
snprintf (stemp, sizeof(stemp), "%.4f", m_latitude);
if (strcmp(stemp, testcases[test_num].lat) != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR: Expected \"%s\" for Latitude.\n", testcases[test_num].lat);
error_count++;
}
snprintf (stemp, sizeof(stemp), "%.4f", m_longitude);
if (strcmp(stemp, testcases[test_num].lon) != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR: Expected \"%s\" for Longitude.\n", testcases[test_num].lon);
error_count++;
}
if (strcmp(m_dao, testcases[test_num].dao) != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR: Expected \"%s\" for DAO.\n", testcases[test_num].dao);
error_count++;
}
}
int main (int argc, char *argv[])
{
char text[256], buttons[256];
int n;
dw_printf ("Hello, world!\n");
aprs_tt_init (NULL);
//if (argc < 2) {
//dw_printf ("Supply text string on command line.\n");
//exit (1);
//}
error_count = 0;
/* Callsigns & abbreviations. */
for (test_num = 0; test_num < sizeof(testcases) / sizeof(testcases[0]); test_num++) {
aprs_tt_sequence (0, "A9A2B42A7A7C71#"); /* WB4APR/7 */
aprs_tt_sequence (0, "A27773#"); /* abbreviated form */
/* Example in http://www.aprs.org/aprstt/aprstt-coding24.txt has a bad checksum! */
aprs_tt_sequence (0, "A27776#"); /* Expect error message. */
text_color_set(DW_COLOR_INFO);
dw_printf ("\nTest case %d: %s\n", test_num, testcases[test_num].toneseq);
aprs_tt_sequence (0, "A2A7A7C71#"); /* Spelled suffix, overlay, checksum */
aprs_tt_sequence (0, "A27773#"); /* Suffix digits, overlay, checksum */
aprs_tt_sequence (0, testcases[test_num].toneseq);
}
aprs_tt_sequence (0, "A9A2B26C7D9D71#"); /* WB2OSZ/7 numeric overlay */
aprs_tt_sequence (0, "A67979#"); /* abbreviated form */
if (error_count != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("\n\nTEST FAILED, Total of %d errors.\n", error_count);
return (EXIT_FAILURE);
}
aprs_tt_sequence (0, "A9A2B26C7D9D5A9#"); /* WB2OSZ/J letter overlay */
aprs_tt_sequence (0, "A6795A7#"); /* abbreviated form */
aprs_tt_sequence (0, "A277#"); /* Tactical call "277" no overlay and no checksum */
/* Locations */
aprs_tt_sequence (0, "B01*A67979#");
aprs_tt_sequence (0, "B988*A67979#");
/* expect about 52.79 +0.83 */
aprs_tt_sequence (0, "B51000125*A67979#");
/* Try to get from Hilltop Tower to Archery & Target Range. */
/* Latitude comes out ok, 37.9137 -> 55.82 min. */
/* Longitude -81.1254 -> 8.20 min */
aprs_tt_sequence (0, "B5206070*A67979#");
aprs_tt_sequence (0, "B21234*A67979#");
aprs_tt_sequence (0, "B533686*A67979#");
/* Comments */
aprs_tt_sequence (0, "C1");
aprs_tt_sequence (0, "C2");
aprs_tt_sequence (0, "C146520");
aprs_tt_sequence (0, "C7788444222550227776669660333666990122223333");
/* Macros */
aprs_tt_sequence (0, "88345");
/* 10 digit representation for callsign & satellite grid. WB4APR near 39.5, -77 */
aprs_tt_sequence (0, "AC9242771558*BA1819");
aprs_tt_sequence (0, "18199242771558");
return(0);
text_color_set(DW_COLOR_REC);
dw_printf ("\n\nAll tests passed.\n");
return (EXIT_SUCCESS);
} /* end main */
#endif
/* end aprs_tt.c */

View File

@ -176,7 +176,7 @@ 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);
int dw_run_cmd (char *cmd, int oneline, char *result, size_t resultsiz);
#endif

76
atest.c
View File

@ -141,7 +141,11 @@ static int decimate = 0; /* Reduce that sampling rate if set. */
static struct audio_s my_audio_config;
static int error_if_less_than = 0; /* Exit with error status if this minimum not reached. */
static int error_if_less_than = -1; /* Exit with error status if this minimum not reached. */
/* Can be used to check that performance has not decreased. */
static int error_if_greater_than = -1; /* Exit with error status if this maximum exceeded. */
/* Can be used to check that duplicate removal is not broken. */
@ -161,6 +165,8 @@ extern float space_gain[MAX_SUBCHANS];
static void usage (void);
static int decode_only = 0; /* Set to 0 or 1 to decode only one channel. 2 for both. */
int main (int argc, char *argv[])
{
@ -171,7 +177,6 @@ int main (int argc, char *argv[])
time_t start_time;
#if defined(EXPERIMENT_G) || defined(EXPERIMENT_H)
int j;
@ -245,7 +250,7 @@ int main (int argc, char *argv[])
my_audio_config.achan[channel].space_freq = DEFAULT_SPACE_FREQ;
my_audio_config.achan[channel].baud = DEFAULT_BAUD;
strcpy (my_audio_config.achan[channel].profiles, "E");
strlcpy (my_audio_config.achan[channel].profiles, "E", sizeof(my_audio_config.achan[channel].profiles));
my_audio_config.achan[channel].num_freq = 1;
my_audio_config.achan[channel].offset = 0;
@ -261,7 +266,7 @@ int main (int argc, char *argv[])
}
while (1) {
int this_option_optind = optind ? optind : 1;
//int this_option_optind = optind ? optind : 1;
int option_index = 0;
static struct option long_options[] = {
{"future1", 1, 0, 0},
@ -272,7 +277,7 @@ int main (int argc, char *argv[])
/* ':' following option character means arg is required. */
c = getopt_long(argc, argv, "B:P:D:F:e:",
c = getopt_long(argc, argv, "B:P:D:F:L:G:012",
long_options, &option_index);
if (c == -1)
break;
@ -297,12 +302,13 @@ int main (int argc, char *argv[])
my_audio_config.achan[0].modem_type = MODEM_AFSK;
my_audio_config.achan[0].mark_freq = 1600;
my_audio_config.achan[0].space_freq = 1800;
strcpy (my_audio_config.achan[0].profiles, "D"); }
strlcpy (my_audio_config.achan[0].profiles, "D", sizeof(my_audio_config.achan[0].profiles));
}
else if (my_audio_config.achan[0].baud > 2400) {
my_audio_config.achan[0].modem_type = MODEM_SCRAMBLE;
my_audio_config.achan[0].mark_freq = 0;
my_audio_config.achan[0].space_freq = 0;
strcpy (my_audio_config.achan[0].profiles, " "); // avoid getting default later.
strlcpy (my_audio_config.achan[0].profiles, " ", sizeof(my_audio_config.achan[0].profiles)); // avoid getting default later.
dw_printf ("Using scrambled baseband signal rather than AFSK.\n");
}
else {
@ -315,7 +321,7 @@ int main (int argc, char *argv[])
case 'P': /* -P for modem profile. */
dw_printf ("Demodulator profile set to \"%s\"\n", optarg);
strcpy (my_audio_config.achan[0].profiles, optarg);
strlcpy (my_audio_config.achan[0].profiles, optarg, sizeof(my_audio_config.achan[0].profiles));
break;
case 'D': /* -D reduce sampling rate for lower CPU usage. */
@ -343,11 +349,31 @@ int main (int argc, char *argv[])
}
break;
case 'e': /* -e error if less than this number decoded. */
case 'L': /* -L error if less than this number decoded. */
error_if_less_than = atoi(optarg);
break;
case 'G': /* -G error if greater than this number decoded. */
error_if_greater_than = atoi(optarg);
break;
case '0': /* channel 0, left from stereo */
decode_only = 0;
break;
case '1': /* channel 1, right from stereo */
decode_only = 1;
break;
case '2': /* decode both from stereo */
decode_only = 2;
break;
case '?':
/* Unknown option message was already printed. */
@ -363,6 +389,9 @@ int main (int argc, char *argv[])
}
}
memcpy (&my_audio_config.achan[1], &my_audio_config.achan[0], sizeof(my_audio_config.achan[0]));
if (optind >= argc) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Specify .WAV file name on command line.\n");
@ -386,6 +415,7 @@ int main (int argc, char *argv[])
*/
err= fread (&header, (size_t)12, (size_t)1, fp);
(void)(err);
if (strncmp(header.riff, "RIFF", 4) != 0 || strncmp(header.wave, "WAVE", 4) != 0) {
text_color_set(DW_COLOR_ERROR);
@ -421,6 +451,7 @@ int main (int argc, char *argv[])
exit(1);
}
// TODO: Should have proper message, not abort.
assert (format.nchannels == 1 || format.nchannels == 2);
assert (format.wbitspersample == 8 || format.wbitspersample == 16);
@ -462,13 +493,10 @@ int main (int argc, char *argv[])
audio_sample = demod_get_sample (ACHAN2ADEV(c));
if (audio_sample >= 256 * 256)
e_o_f = 1;
e_o_f = 1;
#define ONE_CHAN 1 /* only use one audio channel. */
#if ONE_CHAN
if (c != 0) continue;
#endif
if (decode_only == 0 && c != 0) continue;
if (decode_only == 1 && c != 1) continue;
multi_modem_process_sample(c,audio_sample);
}
@ -495,9 +523,14 @@ int main (int argc, char *argv[])
#endif
dw_printf ("%d packets decoded in %d seconds.\n", packets_decoded, (int)(time(NULL) - start_time));
if (packets_decoded < error_if_less_than) {
if (error_if_less_than != -1 && packets_decoded < error_if_less_than) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("\n * * * TEST FAILED to achieve minimum of %d * * * \n", error_if_less_than);
dw_printf ("\n * * * TEST FAILED: number decoded is less than %d * * * \n", error_if_less_than);
exit (1);
}
if (error_if_greater_than != -1 && packets_decoded > error_if_greater_than) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("\n * * * TEST FAILED: number decoded is greater than %d * * * \n", error_if_greater_than);
exit (1);
}
@ -564,12 +597,11 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
unsigned char *pinfo;
int info_len;
int h;
char heard[20];
char heard[AX25_MAX_ADDR_LEN];
char alevel_text[AX25_ALEVEL_TO_TEXT_SIZE];
packets_decoded++;
ax25_format_addrs (pp, stemp);
info_len = ax25_get_info (pp, &pinfo);
@ -585,7 +617,7 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
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);
@ -676,6 +708,10 @@ static void usage (void) {
dw_printf (" -P m Select the demodulator type such as A, B, C, D (default for 300 baud),\n");
dw_printf (" E (default for 1200 baud), F, A+, B+, C+, D+, E+, F+.\n");
dw_printf ("\n");
dw_printf (" -0 Use channel 0 (left) of stereo audio (default).\n");
dw_printf (" -1 use channel 1 (right) of stereo audio.\n");
dw_printf (" -1 decode both channels of stereo audio.\n");
dw_printf ("\n");
dw_printf (" wav-file-in is a WAV format audio file.\n");
dw_printf ("\n");
dw_printf ("Examples:\n");

25
audio.c
View File

@ -661,6 +661,16 @@ static int set_alsa_params (int a, snd_pcm_t *handle, struct audio_s *pa, char *
dw_printf ("audio buffer size = %d (bytes per frame) x %d (frames per period) = %d \n", adev[a].bytes_per_frame, (int)fpp, buf_size_in_bytes);
#endif
/* Version 1.3 - after a report of this situation for Mac OSX version. */
if (buf_size_in_bytes < 256 || buf_size_in_bytes > 32768) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Audio buffer has unexpected extreme size of %d bytes.\n", buf_size_in_bytes);
dw_printf ("Detected at %s, line %d.\n", __FILE__, __LINE__);
dw_printf ("This might be caused by unusual audio device configuration values.\n");
buf_size_in_bytes = 2048;
dw_printf ("Using %d to attempt recovery.\n", buf_size_in_bytes);
}
return (buf_size_in_bytes);
@ -787,9 +797,20 @@ static int set_oss_params (int fd, struct audio_s *pa)
dw_printf ("audio_open(): using block size of %d\n", ossbuf_size_in_bytes);
#endif
#if 0
/* Original - dies without good explanation. */
assert (ossbuf_size_in_bytes >= 256 && ossbuf_size_in_bytes <= 32768);
#else
/* Version 1.3 - after a report of this situation for Mac OSX version. */
if (ossbuf_size_in_bytes < 256 || ossbuf_size_in_bytes > 32768) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Audio buffer has unexpected extreme size of %d bytes.\n", ossbuf_size_in_bytes);
dw_printf ("Detected at %s, line %d.\n", __FILE__, __LINE__);
dw_printf ("This might be caused by unusual audio device configuration values.\n");
ossbuf_size_in_bytes = 2048;
dw_printf ("Using %d to attempt recovery.\n", ossbuf_size_in_bytes);
}
#endif
return (ossbuf_size_in_bytes);
} /* end set_oss_params */

View File

@ -714,6 +714,27 @@ int audio_open (struct audio_s *pa)
* Finally allocate buffer for each direction.
*/
/* Version 1.3 - Add sanity check on buffer size. */
/* There was a reported case of assert failure on buffer size in audio_get(). */
if (adev[a].inbuf_size_in_bytes < 256 || adev[a].inbuf_size_in_bytes > 32768) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Audio input buffer has unexpected extreme size of %d bytes.\n", adev[a].inbuf_size_in_bytes);
dw_printf ("Detected at %s, line %d.\n", __FILE__, __LINE__);
dw_printf ("This might be caused by unusual audio device configuration values.\n");
adev[a].inbuf_size_in_bytes = 2048;
dw_printf ("Using %d to attempt recovery.\n", adev[a].inbuf_size_in_bytes);
}
if (adev[a].outbuf_size_in_bytes < 256 || adev[a].outbuf_size_in_bytes > 32768) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Audio output buffer has unexpected extreme size of %d bytes.\n", adev[a].outbuf_size_in_bytes);
dw_printf ("Detected at %s, line %d.\n", __FILE__, __LINE__);
dw_printf ("This might be caused by unusual audio device configuration values.\n");
adev[a].outbuf_size_in_bytes = 2048;
dw_printf ("Using %d to attempt recovery.\n", adev[a].outbuf_size_in_bytes);
}
adev[a].inbuf_ptr = malloc(adev[a].inbuf_size_in_bytes);
assert (adev[a].inbuf_ptr != NULL);
adev[a].inbuf_len = 0;

View File

@ -113,6 +113,17 @@ static int calcbufsize(int rate, int chans, int bits)
dw_printf ("audio_open: calcbufsize (rate=%d, chans=%d, bits=%d) calc size=%d, round up to %d\n",
rate, chans, bits, size1, size2);
#endif
/* Version 1.3 - add a sanity check. */
if (size2 < 256 || size2 > 32768) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Audio buffer has unexpected extreme size of %d bytes.\n", size2);
dw_printf ("Detected at %s, line %d.\n", __FILE__, __LINE__);
dw_printf ("This might be caused by unusual audio device configuration values.\n");
size2 = 2048;
dw_printf ("Using %d to attempt recovery.\n", size2);
}
return (size2);
}

View File

@ -44,22 +44,27 @@
* Description:
*
*
* A UI frame starts with 2-10 addressses (14-70 octets):
* APRS uses only UI frames.
* Each starts with 2-10 addressses (14-70 octets):
*
* * Destination Address (note: opposite order in printed format)
*
* * Destination Address
* * Source Address
* * 0-8 Digipeater Addresses (Could there ever be more as a result of
* digipeaters inserting their own call
* and decrementing the remaining count in
* WIDEn-n, TRACEn-n, etc.?
* NO. The limit is 8 when transmitting AX.25 over the radio.
* However, communication with an IGate server could have
* a longer VIA path but that is only in text form, not here.)
*
* * 0-8 Digipeater Addresses (Could there ever be more as a result of
* digipeaters inserting their own call for
* the tracing feature?
* NO. The limit is 8 when transmitting AX.25 over the
* radio.
* Communication with an IGate server could
* have a longer VIA path but that is only in text form,
* not as an AX.25 frame.)
*
* Each address is composed of:
*
* * 6 upper case letters or digits, blank padded.
* These are shifted left one bit, leaving the the LSB always 0.
* These are shifted left one bit, leaving the LSB always 0.
*
* * a 7th octet containing the SSID and flags.
* The LSB is always 0 except for the last octet of the address field.
*
@ -96,8 +101,12 @@
* have the "H" bit set to 1.
* The "H" bit would be set to 1 in the repeated frame.
*
* When monitoring, an asterisk is displayed after the last digipeater with
* the "H" bit set. No asterisk means the source is being heard directly.
* In standard monitoring format, an asterisk is displayed after the last
* digipeater with the "H" bit set. That indicates who you are hearing
* over the radio.
* (That is if digipeaters update the via path properly. Some don't so
* we don't know who we are hearing. This is discussed in the User Guide.)
* No asterisk means the source is being heard directly.
*
* Example, if we can hear all stations involved,
*
@ -118,6 +127,10 @@
*
* And, of course, the 2 byte CRC.
*
* The descriptions above, for the C, H, and RR bits, are for APRS usage.
* When operating as a KISS TNC we just pass everything along and don't
* interpret or change them.
*
*
* Constructors: ax25_init - Clear everything.
* ax25_from_text - Tear apart a text string
@ -569,11 +582,11 @@ packet_t ax25_from_frame_debug (unsigned char *fbuf, int flen, alevel_t alevel,
packet_t ax25_from_frame (unsigned char *fbuf, int flen, alevel_t alevel)
#endif
{
unsigned char *pf;
//unsigned char *pf;
packet_t this_p;
int a;
int addr_bytes;
//int a;
//int addr_bytes;
/*
* First make sure we have an acceptable length:
@ -908,7 +921,6 @@ void ax25_set_addr (packet_t this_p, int n, char *ad)
void ax25_insert_addr (packet_t this_p, int n, char *ad)
{
int k;
int ssid_temp, heard_temp;
char atemp[AX25_MAX_ADDR_LEN];
int i;
@ -979,7 +991,6 @@ void ax25_insert_addr (packet_t this_p, int n, char *ad)
void ax25_remove_addr (packet_t this_p, int n)
{
int k;
int expect;
assert (this_p->magic1 == MAGIC);
@ -1025,7 +1036,7 @@ void ax25_remove_addr (packet_t this_p, int n)
int ax25_get_num_addr (packet_t this_p)
{
unsigned char *pf;
//unsigned char *pf;
int a;
int addr_bytes;
@ -1178,7 +1189,6 @@ void ax25_get_addr_with_ssid (packet_t this_p, int n, char *station)
void ax25_get_addr_no_ssid (packet_t this_p, int n, char *station)
{
int ssid;
int i;
assert (this_p->magic1 == MAGIC);
@ -1801,23 +1811,23 @@ static void hex_dump (unsigned char *p, int len)
// TODO: use ax25_frame_type() instead.
static void ctrl_to_text (int c, char *out)
static void ctrl_to_text (int c, char *out, size_t outsiz)
{
if ((c & 1) == 0) { sprintf (out, "I frame: n(r)=%d, p=%d, n(s)=%d", (c>>5)&7, (c>>4)&1, (c>>1)&7); }
else if ((c & 0xf) == 0x01) { sprintf (out, "S frame RR: n(r)=%d, p/f=%d", (c>>5)&7, (c>>4)&1); }
else if ((c & 0xf) == 0x05) { sprintf (out, "S frame RNR: n(r)=%d, p/f=%d", (c>>5)&7, (c>>4)&1); }
else if ((c & 0xf) == 0x09) { sprintf (out, "S frame REJ: n(r)=%d, p/f=%d", (c>>5)&7, (c>>4)&1); }
else if ((c & 0xf) == 0x0D) { sprintf (out, "S frame sREJ: n(r)=%d, p/f=%d", (c>>5)&7, (c>>4)&1); }
else if ((c & 0xef) == 0x6f) { sprintf (out, "U frame SABME: p=%d", (c>>4)&1); }
else if ((c & 0xef) == 0x2f) { sprintf (out, "U frame SABM: p=%d", (c>>4)&1); }
else if ((c & 0xef) == 0x43) { sprintf (out, "U frame DISC: p=%d", (c>>4)&1); }
else if ((c & 0xef) == 0x0f) { sprintf (out, "U frame DM: f=%d", (c>>4)&1); }
else if ((c & 0xef) == 0x63) { sprintf (out, "U frame UA: f=%d", (c>>4)&1); }
else if ((c & 0xef) == 0x87) { sprintf (out, "U frame FRMR: f=%d", (c>>4)&1); }
else if ((c & 0xef) == 0x03) { sprintf (out, "U frame UI: p/f=%d", (c>>4)&1); }
else if ((c & 0xef) == 0xAF) { sprintf (out, "U frame XID: p/f=%d", (c>>4)&1); }
else if ((c & 0xef) == 0xe3) { sprintf (out, "U frame TEST: p/f=%d", (c>>4)&1); }
else { sprintf (out, "Unknown frame type for control = 0x%02x", c); }
if ((c & 1) == 0) { snprintf (out, outsiz, "I frame: n(r)=%d, p=%d, n(s)=%d", (c>>5)&7, (c>>4)&1, (c>>1)&7); }
else if ((c & 0xf) == 0x01) { snprintf (out, outsiz, "S frame RR: n(r)=%d, p/f=%d", (c>>5)&7, (c>>4)&1); }
else if ((c & 0xf) == 0x05) { snprintf (out, outsiz, "S frame RNR: n(r)=%d, p/f=%d", (c>>5)&7, (c>>4)&1); }
else if ((c & 0xf) == 0x09) { snprintf (out, outsiz, "S frame REJ: n(r)=%d, p/f=%d", (c>>5)&7, (c>>4)&1); }
else if ((c & 0xf) == 0x0D) { snprintf (out, outsiz, "S frame sREJ: n(r)=%d, p/f=%d", (c>>5)&7, (c>>4)&1); }
else if ((c & 0xef) == 0x6f) { snprintf (out, outsiz, "U frame SABME: p=%d", (c>>4)&1); }
else if ((c & 0xef) == 0x2f) { snprintf (out, outsiz, "U frame SABM: p=%d", (c>>4)&1); }
else if ((c & 0xef) == 0x43) { snprintf (out, outsiz, "U frame DISC: p=%d", (c>>4)&1); }
else if ((c & 0xef) == 0x0f) { snprintf (out, outsiz, "U frame DM: f=%d", (c>>4)&1); }
else if ((c & 0xef) == 0x63) { snprintf (out, outsiz, "U frame UA: f=%d", (c>>4)&1); }
else if ((c & 0xef) == 0x87) { snprintf (out, outsiz, "U frame FRMR: f=%d", (c>>4)&1); }
else if ((c & 0xef) == 0x03) { snprintf (out, outsiz, "U frame UI: p/f=%d", (c>>4)&1); }
else if ((c & 0xef) == 0xAF) { snprintf (out, outsiz, "U frame XID: p/f=%d", (c>>4)&1); }
else if ((c & 0xef) == 0xe3) { snprintf (out, outsiz, "U frame TEST: p/f=%d", (c>>4)&1); }
else { snprintf (out, outsiz, "Unknown frame type for control = 0x%02x", c); }
}
/* Text description of protocol id octet. */
@ -1864,7 +1874,7 @@ void ax25_hex_dump (packet_t this_p)
c = fptr[this_p->num_addr*7];
p = fptr[this_p->num_addr*7+1];
ctrl_to_text (c, cp_text); // TODO: use ax25_frame_type() instead.
ctrl_to_text (c, cp_text, sizeof(cp_text)); // TODO: use ax25_frame_type() instead.
if ( (c & 0x01) == 0 || /* I xxxx xxx0 */
c == 0x03 || c == 0x13) { /* UI 000x 0011 */

566
beacon.c
View File

@ -1,7 +1,3 @@
//#define DEBUG 1
//#define DEBUG_SIM 1
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
@ -32,17 +28,17 @@
*
*---------------------------------------------------------------*/
//#define DEBUG 1
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <math.h>
#include <time.h>
#if __WIN32__
#include <windows.h>
#endif
#include "direwolf.h"
#include "ax25_pad.h"
@ -51,7 +47,6 @@
#include "tq.h"
#include "xmit.h"
#include "config.h"
#include "digipeater.h"
#include "version.h"
#include "encode_aprs.h"
#include "beacon.h"
@ -62,14 +57,25 @@
#include "aprs_tt.h" // for dw_run_cmd - should relocate someday.
#if __WIN32__
/*
* Are we using GPS data?
* Incremented if tracker beacons configured.
* Cleared if dwgps_init fails.
* Windows doesn't have localtime_r.
* It should have the equivalent localtime_s, with opposite parameter
* order, but I get undefined reference when trying to use it.
*/
static int g_using_gps = 0;
struct tm *localtime_r(time_t *clock, struct tm *res)
{
struct tm *tm;
tm = localtime (clock);
memcpy (res, tm, sizeof(struct tm));
return (res);
}
#endif
/*
* Save pointers to configuration settings.
@ -77,8 +83,6 @@ static int g_using_gps = 0;
static struct audio_s *g_modem_config_p;
static struct misc_config_s *g_misc_config_p;
static struct digi_config_s *g_digi_config_p;
#if __WIN32__
@ -97,6 +101,11 @@ void beacon_tracker_set_debug (int level)
g_tracker_debug_level = level;
}
static time_t sb_calculate_next_time (time_t now,
float current_speed_mph, float current_course,
time_t last_xmit_time, float last_xmit_course);
static void beacon_send (int j, dwgps_info_t *gpsinfo);
/*-------------------------------------------------------------------
@ -105,26 +114,24 @@ void beacon_tracker_set_debug (int level)
*
* Purpose: Initialize the beacon process.
*
* Inputs: pmodem - Aduio device and modem configuration.
* Used only to find valide channels.
* Inputs: pmodem - Audio device and modem configuration.
* Used only to find valid channels.
*
* pconfig - misc. configuration from config file.
* pdigi - digipeater configuration from config file.
* TODO: Is this needed?
*
*
* Outputs: Remember required information for future use.
*
* Description: Initialize the queue to be empty and set up other
* mechanisms for sharing it between different threads.
* Description: Do some validity checking on the beacon configuration.
*
* Start up xmit_thread to actually send the packets
* Start up beacon_thread to actually send the packets
* at the appropriate time.
*
*--------------------------------------------------------------------*/
void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct digi_config_s *pdigi)
void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig)
{
time_t now;
int j;
@ -149,7 +156,6 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct
*/
g_modem_config_p = pmodem;
g_misc_config_p = pconfig;
g_digi_config_p = pdigi;
/*
* Precompute the packet contents so any errors are
@ -164,7 +170,9 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct
if (g_modem_config_p->achan[chan].valid) {
if (strlen(g_modem_config_p->achan[chan].mycall) > 0 && strcasecmp(g_modem_config_p->achan[chan].mycall, "NOCALL") != 0) {
if (strlen(g_modem_config_p->achan[chan].mycall) > 0 &&
strcasecmp(g_modem_config_p->achan[chan].mycall, "N0CALL") != 0 &&
strcasecmp(g_modem_config_p->achan[chan].mycall, "NOCALL") != 0) {
switch (g_misc_config_p->beacon[j].btype) {
@ -194,14 +202,18 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct
case BEACON_TRACKER:
#if defined(ENABLE_GPS) || defined(DEBUG_SIM)
g_using_gps++;
#else
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file, line %d: GPS tracker feature is not enabled.\n", g_misc_config_p->beacon[j].lineno);
g_misc_config_p->beacon[j].btype = BEACON_IGNORE;
continue;
#endif
{
dwgps_info_t gpsinfo;
dwfix_t fix;
fix = dwgps_read (&gpsinfo);
if (fix == DWFIX_NOT_INIT) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file, line %d: GPS must be configured to use TBEACON.\n", g_misc_config_p->beacon[j].lineno);
g_misc_config_p->beacon[j].btype = BEACON_IGNORE;
}
}
break;
case BEACON_CUSTOM:
@ -234,7 +246,7 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct
}
/*
* Calculate next time for each beacon.
* Calculate first time for each beacon from the 'delay' value.
*/
now = time(NULL);
@ -253,37 +265,6 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct
}
/*
* Connect to GPS receiver if any tracker beacons are configured.
* If open fails, disable all tracker beacons.
*/
#if DEBUG_SIM
g_using_gps = 1;
#elif ENABLE_GPS
if (g_using_gps > 0) {
int err;
err = dwgps_init();
if (err != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("All tracker beacons disabled.\n");
g_using_gps = 0;
for (j=0; j<g_misc_config_p->num_beacons; j++) {
if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) {
g_misc_config_p->beacon[j].btype = BEACON_IGNORE;
}
}
}
}
#endif
/*
* Start up thread for processing only if at least one is valid.
*/
@ -342,18 +323,6 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct
#define MIN(x,y) ((x) < (y) ? (x) : (y))
/* Difference between two angles. */
static inline float heading_change (float a, float b)
{
float diff;
diff = fabs(a - b);
if (diff <= 180.)
return (diff);
else
return (360. - diff);
}
#if __WIN32__
@ -362,29 +331,17 @@ static unsigned __stdcall beacon_thread (void *arg)
static void * beacon_thread (void *arg)
#endif
{
int j;
int j; /* Index into array of beacons. */
time_t earliest;
time_t now;
time_t now; /* Current time. */
int number_of_tbeacons; /* Number of tracker beacons. */
/*
* Information from GPS.
*/
int fix = 0; /* 0 = none, 2 = 2D, 3 = 3D */
double my_lat = 0; /* degrees */
double my_lon = 0;
float my_course = 0; /* degrees */
float my_speed_knots = 0;
float my_speed_mph = 0;
float my_alt_m = G_UNKNOWN; /* meters */
int my_alt_ft = G_UNKNOWN;
/*
* SmartBeaconing state.
*/
time_t sb_prev_time = 0; /* Time of most recent transmission. */
float sb_prev_course = 0; /* Most recent course reported. */
//float sb_prev_speed_mph; /* Most recent speed reported. */
int sb_every; /* Calculated time between transmissions. */
#if DEBUG
@ -392,30 +349,45 @@ static void * beacon_thread (void *arg)
char hms[20];
now = time(NULL);
localtime_r (&now, &tm);
strftime (hms, sizeof(hms), "%H:%M:%S", &tm);
text_color_set(DW_COLOR_DEBUG);
dw_printf ("beacon_thread: started %s\n", hms);
#endif
/*
* See if any tracker beacons are configured.
* No need to obtain GPS data if none.
*/
number_of_tbeacons = 0;
for (j=0; j<g_misc_config_p->num_beacons; j++) {
if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) {
number_of_tbeacons++;
}
}
now = time(NULL);
while (1) {
assert (g_misc_config_p->num_beacons >= 1);
dwgps_info_t gpsinfo;
/*
* Sleep until time for the earliest scheduled or
* the soonest we could transmit due to corner pegging.
*/
earliest = g_misc_config_p->beacon[0].next;
for (j=1; j<g_misc_config_p->num_beacons; j++) {
if (g_misc_config_p->beacon[j].btype == BEACON_IGNORE)
continue;
earliest = MIN(g_misc_config_p->beacon[j].next, earliest);
earliest = now + 60 * 60;
for (j=0; j<g_misc_config_p->num_beacons; j++) {
if (g_misc_config_p->beacon[j].btype != BEACON_IGNORE) {
earliest = MIN(g_misc_config_p->beacon[j].next, earliest);
}
}
if (g_misc_config_p->sb_configured && g_using_gps) {
if (g_misc_config_p->sb_configured && number_of_tbeacons > 0) {
earliest = MIN(now + g_misc_config_p->sb_turn_time, earliest);
earliest = MIN(now + g_misc_config_p->sb_fast_rate, earliest);
}
@ -442,128 +414,57 @@ static void * beacon_thread (void *arg)
* beacon because corner pegging make it sooner.
*/
#if DEBUG_SIM
FILE *fp;
char cs[40];
if (number_of_tbeacons > 0) {
fp = fopen ("c:\\cygwin\\tmp\\cs", "r");
if (fp != NULL) {
fscanf (fp, "%f %f", &my_course, &my_speed_knots);
fclose (fp);
}
else {
fprintf (stderr, "Can't read /tmp/cs.\n");
}
fix = 3;
my_speed_mph = DW_KNOTS_TO_MPH(my_speed_knots);
my_lat = 42.99;
my_lon = 71.99;
my_alt_m = 100;
#else
if (g_using_gps) {
fix = dwgps_read (&my_lat, &my_lon, &my_speed_knots, &my_course, &my_alt_m);
my_speed_mph = DW_KNOTS_TO_MPH(my_speed_knots);
dwfix_t fix = dwgps_read (&gpsinfo);
float my_speed_mph = DW_KNOTS_TO_MPH(gpsinfo.speed_knots);
if (g_tracker_debug_level >= 1) {
struct tm tm;
char hms[20];
localtime_r (&now, &tm);
strftime (hms, sizeof(hms), "%H:%M:%S", &tm);
text_color_set(DW_COLOR_DEBUG);
if (fix == 3) {
dw_printf ("%s 3D, %.6f, %.6f, %.1f mph, %.0f\xc2\xb0, %.1f m\n", hms, my_lat, my_lon, my_speed_mph, my_course, my_alt_m);
dw_printf ("%s 3D, %.6f, %.6f, %.1f mph, %.0f\xc2\xb0, %.1f m\n", hms, gpsinfo.dlat, gpsinfo.dlon, my_speed_mph, gpsinfo.track, gpsinfo.altitude);
}
else if (fix == 2) {
dw_printf ("%s 2D, %.6f, %.6f, %.1f mph, %.0f\xc2\xb0\n", hms, my_lat, my_lon, my_speed_mph, my_course);
dw_printf ("%s 2D, %.6f, %.6f, %.1f mph, %.0f\xc2\xb0\n", hms, gpsinfo.dlat, gpsinfo.dlon, my_speed_mph, gpsinfo.track);
}
else {
dw_printf ("%s No GPS fix\n", hms);
}
}
/* Transmit altitude only if 3D fix and user asked for it. */
my_alt_ft = G_UNKNOWN;
if (fix >= 3 && my_alt_m != G_UNKNOWN && g_misc_config_p->beacon[j].alt_m != G_UNKNOWN) {
my_alt_ft = DW_METERS_TO_FEET(my_alt_m);
}
/* Don't complain here for no fix. */
/* Possibly at the point where about to transmit. */
}
#endif
/*
* Run SmartBeaconing calculation if configured and GPS data available.
*/
if (g_misc_config_p->sb_configured && g_using_gps && fix >= 2) {
if (g_misc_config_p->sb_configured && fix >= DWFIX_2D) {
if (my_speed_mph > g_misc_config_p->sb_fast_speed) {
sb_every = g_misc_config_p->sb_fast_rate;
if (g_tracker_debug_level >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("my speed %.1f > fast %d mph, interval = %d sec\n", my_speed_mph, g_misc_config_p->sb_fast_speed, sb_every);
}
}
else if (my_speed_mph < g_misc_config_p->sb_slow_speed) {
sb_every = g_misc_config_p->sb_slow_rate;
if (g_tracker_debug_level >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("my speed %.1f < slow %d mph, interval = %d sec\n", my_speed_mph, g_misc_config_p->sb_slow_speed, sb_every);
}
}
else {
/* Can't divide by 0 assuming sb_slow_speed > 0. */
sb_every = ( g_misc_config_p->sb_fast_rate * g_misc_config_p->sb_fast_speed ) / my_speed_mph;
if (g_tracker_debug_level >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("my speed %.1f mph, interval = %d sec\n", my_speed_mph, sb_every);
}
}
time_t tnext = sb_calculate_next_time (now,
DW_KNOTS_TO_MPH(gpsinfo.speed_knots), gpsinfo.track,
sb_prev_time, sb_prev_course);
#if DEBUG_SIM
text_color_set(DW_COLOR_DEBUG);
dw_printf ("SB: fast %d %d slow %d %d speed=%.1f every=%d\n",
g_misc_config_p->sb_fast_speed, g_misc_config_p->sb_fast_rate,
g_misc_config_p->sb_slow_speed, g_misc_config_p->sb_slow_rate,
my_speed_mph, sb_every);
#endif
/*
* Test for "Corner Pegging" if moving.
*/
if (my_speed_mph >= 1.0) {
int turn_threshold = g_misc_config_p->sb_turn_angle +
g_misc_config_p->sb_turn_slope / my_speed_mph;
#if DEBUG_SIM
text_color_set(DW_COLOR_DEBUG);
dw_printf ("SB-moving: course %.0f prev %.0f thresh %d\n",
my_course, sb_prev_course, turn_threshold);
#endif
if (heading_change(my_course, sb_prev_course) > turn_threshold &&
now >= sb_prev_time + g_misc_config_p->sb_turn_time) {
if (g_tracker_debug_level >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("heading change (%.0f, %.0f) > threshold %d and %d since last >= turn time %d\n",
my_course, sb_prev_course, turn_threshold,
(int)(now - sb_prev_time), g_misc_config_p->sb_turn_time);
}
/* Send it now. */
for (j=0; j<g_misc_config_p->num_beacons; j++) {
if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) {
g_misc_config_p->beacon[j].next = now;
for (j=0; j<g_misc_config_p->num_beacons; j++) {
if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) {
/* Haven't thought about the consequences of SmartBeaconing */
/* and having more than one tbeacon configured. */
if (tnext < g_misc_config_p->beacon[j].next) {
g_misc_config_p->beacon[j].next = tnext;
}
}
} /* significant change in direction */
} /* is moving */
} /* apply SmartBeaconing */
} /* Update next time if sooner. */
} /* apply SmartBeaconing */
} /* tbeacon(s) configured. */
/*
* Send if the time has arrived.
*/
for (j=0; j<g_misc_config_p->num_beacons; j++) {
if (g_misc_config_p->beacon[j].btype == BEACON_IGNORE)
@ -571,13 +472,199 @@ static void * beacon_thread (void *arg)
if (g_misc_config_p->beacon[j].next <= now) {
/* Send the beacon. */
beacon_send (j, &gpsinfo);
/* Calculate when the next one should be sent. */
/* Easy for fixed interval. SmartBeaconing takes more effort. */
if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) {
if (gpsinfo.fix < DWFIX_2D) {
/* Fix not available so beacon was not sent. */
/* Try again in a couple seconds. */
g_misc_config_p->beacon[j].next = now + 2;
}
else if (g_misc_config_p->sb_configured) {
/* Remember most recent tracker beacon. */
/* Compute next time if not turning. */
sb_prev_time = now;
sb_prev_course = gpsinfo.track;
g_misc_config_p->beacon[j].next = sb_calculate_next_time (now,
DW_KNOTS_TO_MPH(gpsinfo.speed_knots), gpsinfo.track,
sb_prev_time, sb_prev_course);
}
else {
g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
}
}
else {
/* non-tracker beacons are at fixed spacing. */
g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
}
} /* if time to send it */
} /* for each configured beacon */
} /* do forever */
#if __WIN32__
return(0); /* unreachable but warning if not here. */
#else
return(NULL);
#endif
} /* end beacon_thread */
/*-------------------------------------------------------------------
*
* Name: sb_calculate_next_time
*
* Purpose: Calculate next transmission time using the SmartBeaconing algorithm.
*
* Inputs: now - Current time.
*
* current_speed_mph - Current speed from GPS.
* Not expecting G_UNKNOWN but should check for it.
*
* current_course - Current direction of travel.
* Could be G_UNKNOWN if stationary.
*
* last_xmit_time - Time of most recent transmission.
*
* last_xmit_course - Direction included in most recent transmission.
*
* Global In: g_misc_config_p->
* sb_configured TRUE if SmartBeaconing is configured.
* sb_fast_speed MPH
* sb_fast_rate seconds
* sb_slow_speed MPH
* sb_slow_rate seconds
* sb_turn_time seconds
* sb_turn_angle degrees
* sb_turn_slope degrees * MPH
*
* Returns: Time of next transmission.
* Could vary from now to sb_slow_rate in the future.
*
* Caution: The algorithm is defined in MPH units. GPS uses knots.
* The caller must be careful about using the proper conversions.
*
*--------------------------------------------------------------------*/
/* Difference between two angles. */
static float heading_change (float a, float b)
{
float diff;
diff = fabs(a - b);
if (diff <= 180.)
return (diff);
else
return (360. - diff);
}
static time_t sb_calculate_next_time (time_t now,
float current_speed_mph, float current_course,
time_t last_xmit_time, float last_xmit_course)
{
int beacon_rate;
time_t next_time;
/*
* Compute time between beacons for travelling in a straight line.
*/
if (current_speed_mph == G_UNKNOWN) {
beacon_rate = (int)roundf((g_misc_config_p->sb_fast_rate + g_misc_config_p->sb_slow_rate) / 2.);
}
else if (current_speed_mph > g_misc_config_p->sb_fast_speed) {
beacon_rate = g_misc_config_p->sb_fast_rate;
}
else if (current_speed_mph < g_misc_config_p->sb_slow_speed) {
beacon_rate = g_misc_config_p->sb_slow_rate;
}
else {
/* Can't divide by 0 assuming sb_slow_speed > 0. */
beacon_rate = (int)roundf(( g_misc_config_p->sb_fast_rate * g_misc_config_p->sb_fast_speed ) / current_speed_mph);
}
if (g_tracker_debug_level >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("SmartBeaconing: Beacon Rate = %d seconds for %.1f MPH\n", beacon_rate, current_speed_mph);
}
next_time = last_xmit_time + beacon_rate;
/*
* Test for "Corner Pegging" if moving.
*/
if (current_speed_mph != G_UNKNOWN && current_speed_mph >= 1.0 &&
current_course != G_UNKNOWN && last_xmit_course != G_UNKNOWN) {
float change = heading_change(current_course, last_xmit_course);
float turn_threshold = g_misc_config_p->sb_turn_angle +
g_misc_config_p->sb_turn_slope / current_speed_mph;
if (change > turn_threshold &&
now >= last_xmit_time + g_misc_config_p->sb_turn_time) {
if (g_tracker_debug_level >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("SmartBeaconing: Send now for heading change of %.0f\n", change);
}
next_time = now;
}
}
return (next_time);
} /* end sb_calculate_next_time */
/*-------------------------------------------------------------------
*
* Name: beacon_send
*
* Purpose: Transmit one beacon after it was determined to be time.
*
* Inputs: j Index into beacon configuration array below.
*
* gpsinfo Information from GPS. Used only for TBEACON.
*
* Global In: g_misc_config_p->beacon Array of beacon configurations.
*
* Outputs: Destination(s) specified:
* - Transmit queue.
* - IGate.
* - Simulated reception.
*
* Description: Prepare text in monitor format.
* Convert to packet object.
* Send to desired destination(s).
*
*--------------------------------------------------------------------*/
static void beacon_send (int j, dwgps_info_t *gpsinfo)
{
int strict = 1; /* Strict packet checking because they will go over air. */
char stemp[20];
char info[AX25_MAX_INFO_LEN];
char beacon_text[AX25_MAX_PACKET_LEN];
packet_t pp = NULL;
char mycall[AX25_MAX_ADDR_LEN];
int alt_ft;
char super_comment[AX25_MAX_INFO_LEN]; // Fixed part + any dynamic part.
@ -600,7 +687,7 @@ static void * beacon_thread (void *arg)
if (strlen(mycall) == 0 || strcmp(mycall, "NOCALL") == 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("MYCALL not set for beacon in config file line %d.\n", g_misc_config_p->beacon[j].lineno);
continue;
return;
}
/*
@ -645,7 +732,7 @@ static void * beacon_thread (void *arg)
/* 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));
k = dw_run_cmd (g_misc_config_p->beacon[j].commentcmd, 2, var_comment, sizeof(var_comment));
if (k > 0) {
strlcat (super_comment, var_comment, sizeof(super_comment));
}
@ -663,18 +750,16 @@ static void * beacon_thread (void *arg)
case BEACON_POSITION:
alt_ft = DW_METERS_TO_FEET(g_misc_config_p->beacon[j].alt_m);
encode_position (g_misc_config_p->beacon[j].messaging,
g_misc_config_p->beacon[j].compress, g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon, alt_ft,
encode_position (g_misc_config_p->beacon[j].messaging, g_misc_config_p->beacon[j].compress,
g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon,
(int)roundf(DW_METERS_TO_FEET(g_misc_config_p->beacon[j].alt_m)),
g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol,
g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
0, 0, /* course, speed */
G_UNKNOWN, G_UNKNOWN, /* course, speed */
g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
super_comment,
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;
case BEACON_OBJECT:
@ -682,48 +767,43 @@ static void * beacon_thread (void *arg)
encode_object (g_misc_config_p->beacon[j].objname, g_misc_config_p->beacon[j].compress, 0, g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon,
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_UNKNOWN, G_UNKNOWN, /* course, speed */
g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset, super_comment,
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;
case BEACON_TRACKER:
if (fix >= 2) {
int coarse; /* APRS encoder wants 1 - 360. */
/* 0 means none or unknown. */
if (gpsinfo->fix >= DWFIX_2D) {
coarse = (int)roundf(my_course);
if (coarse == 0) {
coarse = 360;
}
encode_position (g_misc_config_p->beacon[j].messaging,
g_misc_config_p->beacon[j].compress,
my_lat, my_lon, my_alt_ft,
int coarse; /* Round to nearest integer. retaining unknown state. */
int my_alt_ft;
/* Transmit altitude only if user asked for it. */
/* A positive altitude in the config file enables */
/* transmission of altitude from GPS. */
my_alt_ft = G_UNKNOWN;
if (gpsinfo->fix >= 3 && gpsinfo->altitude != G_UNKNOWN && g_misc_config_p->beacon[j].alt_m > 0) {
my_alt_ft = (int)roundf(DW_METERS_TO_FEET(gpsinfo->altitude));
}
coarse = G_UNKNOWN;
if (gpsinfo->track != G_UNKNOWN) {
coarse = (int)roundf(gpsinfo->track);
}
encode_position (g_misc_config_p->beacon[j].messaging, g_misc_config_p->beacon[j].compress,
gpsinfo->dlat, gpsinfo->dlon, my_alt_ft,
g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol,
g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
coarse, (int)roundf(my_speed_knots),
coarse, (int)roundf(gpsinfo->speed_knots),
g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
super_comment,
info, sizeof(info));
strlcat (beacon_text, info, sizeof(beacon_text));
/* Remember most recent tracker beacon. */
sb_prev_time = now;
sb_prev_course = my_course;
//sb_prev_speed_mph = my_speed_mph;
/* Calculate time for next transmission. */
if (g_misc_config_p->sb_configured) {
g_misc_config_p->beacon[j].next = now + sb_every;
}
else {
g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
}
/* Write to log file for testing. */
/* The idea is to run log2gpx and map the result rather than */
/* actually transmitting and relying on someone else to receive */
@ -743,11 +823,11 @@ static void * beacon_thread (void *arg)
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;
A.g_lon = my_lon;
A.g_speed = DW_KNOTS_TO_MPH(my_speed_knots);
A.g_lat = gpsinfo->dlat;
A.g_lon = gpsinfo->dlon;
A.g_speed_mph = DW_KNOTS_TO_MPH(gpsinfo->speed_knots);
A.g_course = coarse;
A.g_altitude = my_alt_ft;
A.g_altitude_ft = DW_METERS_TO_FEET(gpsinfo->altitude);
/* Fake channel of 999 to distinguish from real data. */
memset (&alevel, 0, sizeof(alevel));
@ -755,8 +835,7 @@ static void * beacon_thread (void *arg)
}
}
else {
g_misc_config_p->beacon[j].next = now + 2;
continue; /* No fix. Try again in a couple seconds. */
return; /* No fix. Skip this time. */
}
break;
@ -770,12 +849,11 @@ static void * beacon_thread (void *arg)
}
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));
k = dw_run_cmd (g_misc_config_p->beacon[j].custom_infocmd, 2, info_part, sizeof(info_part));
if (k > 0) {
strlcat (beacon_text, info_part, sizeof(beacon_text));
}
@ -790,7 +868,6 @@ static void * beacon_thread (void *arg)
dw_printf ("Internal error. custom_info is null. %s %d\n", __FILE__, __LINE__);
strlcpy (beacon_text, "", sizeof(beacon_text)); // abort!
}
g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
break;
case BEACON_IGNORE:
@ -803,7 +880,7 @@ static void * beacon_thread (void *arg)
* Parse monitor format into form for transmission.
*/
if (strlen(beacon_text) == 0) {
continue;
return;
}
pp = ax25_from_text (beacon_text, strict);
@ -819,11 +896,9 @@ static void * beacon_thread (void *arg)
case SENDTO_IGATE:
#if 1
text_color_set(DW_COLOR_XMIT);
dw_printf ("[ig] %s\n", beacon_text);
#endif
igate_send_rec_packet (0, pp);
ax25_delete (pp);
break;
@ -849,12 +924,7 @@ static void * beacon_thread (void *arg)
dw_printf ("%s\n", beacon_text);
}
} /* if time to send it */
} /* end beacon_send */
} /* for each configured beacon */
} /* do forever */
} /* end beacon_thread */
/* end beacon.c */

View File

@ -1,6 +1,6 @@
/* beacon.h */
void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct digi_config_s *pdigi);
void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig);
void beacon_tracker_set_debug (int level);

176
config.c
View File

@ -42,10 +42,8 @@
#include <ctype.h>
#include <math.h>
#if __WIN32__
//#include "pthreads/pthread.h"
#else
#include <pthread.h>
#if ENABLE_GPSD
#include <gps.h> /* for DEFAULT_GPSD_PORT (2947) */
#endif
#include "ax25_pad.h"
@ -184,13 +182,12 @@ static int alllettersorpm(char *p)
/* Acceptable symbols to separate degrees & minutes. */
/* Degree symbol is not in ASCII so documentation says to use "^" instead. */
/* Some wise guy will try to use degree symbol. */
/* UTF-8 is more difficult because it is a two byte sequence, c2 b0. */
#define DEG1 '^'
#define DEG2 0xb0 /* ISO Latin1 */
#define DEG3 0xf8 /* Microsoft code page 437 */
// TODO: recognize UTF-8 degree symbol.
enum parse_ll_which_e { LAT, LON };
@ -484,7 +481,6 @@ static char *split (char *string, int rest_of_line)
{
static char cmd[MAXCMDLEN];
static char token[MAXCMDLEN];
static char *nextp = NULL;
static char *c; // current position in cmd.
char *s, *t;
int in_quotes;
@ -756,6 +752,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
//strlcpy (p_misc_config->nullmodem, DEFAULT_NULLMODEM, sizeof(p_misc_config->nullmodem));
strlcpy (p_misc_config->nullmodem, "", sizeof(p_misc_config->nullmodem));
strlcpy (p_misc_config->gpsnmea_port, "", sizeof(p_misc_config->gpsnmea_port));
strlcpy (p_misc_config->nmea_port, "", sizeof(p_misc_config->nmea_port));
strlcpy (p_misc_config->logdir, "", sizeof(p_misc_config->logdir));
@ -869,12 +866,12 @@ void config_init (char *fname, struct audio_s *p_audio_config,
/* First channel of device is valid. */
p_audio_config->achan[ADEVFIRSTCHAN(adevice)].valid = 1;
strncpy (p_audio_config->adev[adevice].adevice_in, t, sizeof(p_audio_config->adev[adevice].adevice_in)-1);
strncpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out)-1);
strlcpy (p_audio_config->adev[adevice].adevice_in, t, sizeof(p_audio_config->adev[adevice].adevice_in));
strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out));
t = split(NULL,0);
if (t != NULL) {
strncpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out)-1);
strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out));
}
}
@ -924,7 +921,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
/* First channel of device is valid. */
p_audio_config->achan[ADEVFIRSTCHAN(adevice)].valid = 1;
strncpy (p_audio_config->adev[adevice].adevice_in, t, sizeof(p_audio_config->adev[adevice].adevice_in)-1);
strlcpy (p_audio_config->adev[adevice].adevice_in, t, sizeof(p_audio_config->adev[adevice].adevice_in));
}
else if (strcasecmp(t, "PAODEVICE") == 0) {
adevice = 0;
@ -951,7 +948,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
/* First channel of device is valid. */
p_audio_config->achan[ADEVFIRSTCHAN(adevice)].valid = 1;
strncpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out)-1);
strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out));
}
@ -1074,7 +1071,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
char *p;
strncpy (p_audio_config->achan[c].mycall, t, sizeof(p_audio_config->achan[c].mycall)-1);
strlcpy (p_audio_config->achan[c].mycall, t, sizeof(p_audio_config->achan[c].mycall));
for (p = p_audio_config->achan[c].mycall; *p != '\0'; p++) {
if (islower(*p)) {
@ -1082,6 +1079,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
}
}
// TODO: additional checks if valid.
// Should have a function to check for valid callsign[-ssid]
}
}
}
@ -1231,7 +1229,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
}
}
strncpy (p_audio_config->achan[channel].profiles, t, sizeof(p_audio_config->achan[channel].profiles));
strlcpy (p_audio_config->achan[channel].profiles, t, sizeof(p_audio_config->achan[channel].profiles));
t = split(NULL,0);
if (strlen(p_audio_config->achan[channel].profiles) > 1 && t != NULL) {
text_color_set(DW_COLOR_ERROR);
@ -1328,7 +1326,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
else if (alllettersorpm(t)) { /* profile of letter(s) + - */
// Will be validated later.
strncpy (p_audio_config->achan[channel].profiles, t, sizeof(p_audio_config->achan[channel].profiles));
strlcpy (p_audio_config->achan[channel].profiles, t, sizeof(p_audio_config->achan[channel].profiles));
}
else if (*t == '/') { /* /div */
@ -1354,7 +1352,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
/* A later place catches disallowed combination of + and @. */
/* A later place sets /n for 300 baud if not specified by user. */
//printf ("debug: div = %d\n", p_audio_config->achan[channel].decimate);
//dw_printf ("debug: div = %d\n", p_audio_config->achan[channel].decimate);
}
}
@ -1440,7 +1438,6 @@ void config_init (char *fname, struct audio_s *p_audio_config,
*/
else if (strcasecmp(t, "PTT") == 0 || strcasecmp(t, "DCD") == 0) {
//int n;
int ot;
char otname[8];
@ -1729,7 +1726,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
*/
else if (strcasecmp(t, "SPEECH") == 0) {
int n;
t = split(NULL,0);
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
@ -1895,8 +1892,6 @@ void config_init (char *fname, struct audio_s *p_audio_config,
else if (strcasecmp(t, "regen") == 0) {
int from_chan, to_chan;
int e;
char message[100];
t = split(NULL,0);
@ -1957,8 +1952,6 @@ void config_init (char *fname, struct audio_s *p_audio_config,
else if (strcasecmp(t, "FILTER") == 0) {
int from_chan, to_chan;
int e;
char message[100];
t = split(NULL,0);
@ -2036,7 +2029,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
*/
else if (strcasecmp(t, "TTCORRAL") == 0) {
//int n;
t = split(NULL,0);
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
@ -2371,8 +2364,6 @@ void config_init (char *fname, struct audio_s *p_audio_config,
struct ttloc_s *tl;
int j;
int znum;
char *zlet;
double dlat, dlon;
long lerr;
@ -2485,8 +2476,6 @@ void config_init (char *fname, struct audio_s *p_audio_config,
struct ttloc_s *tl;
int j;
int znum;
char *zlet;
int num_x, num_y;
double lat, lon;
long lerr;
@ -2679,7 +2668,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
p_tt_config->ttloc_len--;
continue;
}
if (tt_mhead_to_text(t, 0, mh) != 0) {
if (tt_mhead_to_text(t, 0, mh, sizeof(mh)) != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: TTMHEAD prefix not a valid DTMF sequence.\n", line);
p_tt_config->ttloc_len--;
@ -2802,7 +2791,6 @@ void config_init (char *fname, struct audio_s *p_audio_config,
struct ttloc_s *tl;
int j;
//char ch;
int p_count[3], d_count[3];
int tt_error = 0;
@ -2959,8 +2947,6 @@ void config_init (char *fname, struct audio_s *p_audio_config,
if (*pi == '}') {
char symtab;
char symbol;
char overlay;
char symdest[8];
*ps = '\0';
@ -2975,7 +2961,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
// Convert symtab(overlay) & symbol to tone sequence.
symbols_to_tones (symtab, symbol, ttemp);
symbols_to_tones (symtab, symbol, ttemp, sizeof(ttemp));
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("DEBUG config file Line %d: AB{%s} -> %s\n", line, stemp, ttemp);
@ -3129,7 +3115,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
if (t != NULL) {
// TODO: Should do some validity checking on the path.
strncpy (p_tt_config->obj_xmit_via, t, sizeof(p_tt_config->obj_xmit_via));
strlcpy (p_tt_config->obj_xmit_via, t, sizeof(p_tt_config->obj_xmit_via));
}
}
@ -3246,9 +3232,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
while (*t == ' ' || *t == '\t') t++; // remove leading white space.
strncpy (p_tt_config->status[status_num], t, TT_MTEXT_LEN);
p_tt_config->status[status_num][TT_MTEXT_LEN-1] = '\0';
strlcpy (p_tt_config->status[status_num], t, sizeof(p_tt_config->status[status_num]));
}
@ -3260,7 +3244,6 @@ void config_init (char *fname, struct audio_s *p_audio_config,
*/
else if (strcasecmp(t, "TTCMD") == 0) {
int status_num;
t = split(NULL,1);
if (t == NULL) {
@ -3269,9 +3252,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
continue;
}
strncpy (p_tt_config->ttcmd, t, sizeof(p_tt_config->ttcmd));
p_tt_config->ttcmd[sizeof(p_tt_config->ttcmd)-1] = '\0';
strlcpy (p_tt_config->ttcmd, t, sizeof(p_tt_config->ttcmd));
}
@ -3294,7 +3275,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
dw_printf ("Line %d: Missing IGate server name for IGSERVER command.\n", line);
continue;
}
strncpy (p_igate_config->t2_server_name, t, sizeof(p_igate_config->t2_server_name)-1);
strlcpy (p_igate_config->t2_server_name, t, sizeof(p_igate_config->t2_server_name));
/* If there is a : in the name, split it out as the port number. */
@ -3329,7 +3310,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
line, p_igate_config->t2_server_port);
}
}
//printf ("DEBUG server=%s port=%d\n", p_igate_config->t2_server_name, p_igate_config->t2_server_port);
//dw_printf ("DEBUG server=%s port=%d\n", p_igate_config->t2_server_name, p_igate_config->t2_server_port);
//exit (0);
}
@ -3347,7 +3328,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
continue;
}
// TODO: Wouldn't hurt to do validity checking of format.
strncpy (p_igate_config->t2_login, t, sizeof(p_igate_config->t2_login)-1);
strlcpy (p_igate_config->t2_login, t, sizeof(p_igate_config->t2_login));
t = split(NULL,0);
if (t == NULL) {
@ -3355,7 +3336,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
dw_printf ("Line %d: Missing passcode for IGLOGIN command.\n", line);
continue;
}
strncpy (p_igate_config->t2_passcode, t, sizeof(p_igate_config->t2_passcode)-1);
strlcpy (p_igate_config->t2_passcode, t, sizeof(p_igate_config->t2_passcode));
}
/*
@ -3387,7 +3368,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
if (t != NULL) {
char *p;
p_igate_config->tx_via[0] = ',';
strncpy (p_igate_config->tx_via + 1, t, sizeof(p_igate_config->tx_via)-2);
strlcpy (p_igate_config->tx_via + 1, t, sizeof(p_igate_config->tx_via)-1);
for (p = p_igate_config->tx_via; *p != '\0'; p++) {
if (islower(*p)) {
*p = toupper(*p); /* silently force upper case. */
@ -3403,7 +3384,6 @@ void config_init (char *fname, struct audio_s *p_audio_config,
*/
else if (strcasecmp(t, "IGFILTER") == 0) {
//int n;
t = split(NULL,1); /* Take rest of line as one string. */
@ -3524,12 +3504,74 @@ void config_init (char *fname, struct audio_s *p_audio_config,
continue;
}
else {
strncpy (p_misc_config->nullmodem, t, sizeof(p_misc_config->nullmodem)-1);
strlcpy (p_misc_config->nullmodem, t, sizeof(p_misc_config->nullmodem));
}
}
/*
* GPSNMEA - Device name for reading from GPS receiver.
*/
else if (strcasecmp(t, "gpsnmea") == 0) {
t = split(NULL,0);
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file, line %d: Missing serial port name for GPS receiver.\n", line);
continue;
}
else {
strlcpy (p_misc_config->gpsnmea_port, t, sizeof(p_misc_config->gpsnmea_port));
}
}
/*
* GPSD - Use GPSD server.
*
* GPSD [ host [ port ] ]
*/
else if (strcasecmp(t, "gpsd") == 0) {
#if __WIN32__
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file, line %d: The GPSD interface is not available for Windows.\n", line);
continue;
#elif ENABLE_GPSD
strlcpy (p_misc_config->gpsd_host, "localhost", sizeof(p_misc_config->gpsd_host));
p_misc_config->gpsd_port = atoi(DEFAULT_GPSD_PORT);
t = split(NULL,0);
if (t != NULL) {
strlcpy (p_misc_config->gpsd_host, t, sizeof(p_misc_config->gpsd_host));
t = split(NULL,0);
if (t != NULL) {
int n = atoi(t);
if ((n >= MIN_IP_PORT_NUMBER && n <= MAX_IP_PORT_NUMBER) || n == 0) {
p_misc_config->gpsd_port = n;
}
else {
p_misc_config->gpsd_port = atoi(DEFAULT_GPSD_PORT);
text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: Invalid port number for GPSD Socket Interface. Using default of %d.\n",
line, p_misc_config->gpsd_port);
}
}
}
#else
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file, line %d: The GPSD interface has not been enabled.\n", line);
dw_printf ("Install gpsd and libgps-dev packages then rebuild direwolf.\n");
continue;
#endif
}
/*
* NMEA - Device name for communication with NMEA device.
* Wasn't documented will probably use WAYPOINT instead.
*/
else if (strcasecmp(t, "nmea") == 0) {
t = split(NULL,0);
@ -3539,7 +3581,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
continue;
}
else {
strncpy (p_misc_config->nmea_port, t, sizeof(p_misc_config->nmea_port)-1);
strlcpy (p_misc_config->nmea_port, t, sizeof(p_misc_config->nmea_port));
}
}
@ -3554,7 +3596,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
continue;
}
else {
strncpy (p_misc_config->logdir, t, sizeof(p_misc_config->logdir)-1);
strlcpy (p_misc_config->logdir, t, sizeof(p_misc_config->logdir));
}
t = split(NULL,0);
if (t != NULL) {
@ -3591,21 +3633,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
strcasecmp(t, "OBEACON") == 0 ||
strcasecmp(t, "TBEACON") == 0 ||
strcasecmp(t, "CBEACON") == 0) {
#if __WIN32__
if (strcasecmp(t, "TBEACON") == 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file, line %d: TBEACON is available only in Linux version.\n", line);
continue;
}
#endif
#ifndef ENABLE_GPS
if (strcasecmp(t, "TBEACON") == 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file, line %d: Rebuild with GPS support for TBEACON to be available.\n", line);
continue;
}
#endif
if (p_misc_config->num_beacons < MAX_BEACONS) {
memset (&(p_misc_config->beacon[p_misc_config->num_beacons]), 0, sizeof(struct beacon_s));
@ -3638,7 +3666,9 @@ void config_init (char *fname, struct audio_s *p_audio_config,
/*
* SMARTBEACONING fast_speed fast_rate slow_speed slow_rate turn_time turn_angle turn_slope
* SMARTBEACONING [ fast_speed fast_rate slow_speed slow_rate turn_time turn_angle turn_slope ]
*
* Parameters must be all or nothing.
*/
else if (strcasecmp(t, "SMARTBEACON") == 0 ||
@ -3647,8 +3677,12 @@ void config_init (char *fname, struct audio_s *p_audio_config,
int n;
#define SB_NUM(name,sbvar,minn,maxx,unit) \
t = split(NULL,0); \
t = split(NULL,0); \
if (t == NULL) { \
if (strcmp(name, "fast speed") == 0) { \
p_misc_config->sb_configured = 1; \
continue; \
} \
text_color_set(DW_COLOR_ERROR); \
dw_printf ("Line %d: Missing %s for SmartBeaconing.\n", line, name); \
continue; \
@ -3664,7 +3698,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
}
#define SB_TIME(name,sbvar,minn,maxx,unit) \
t = split(NULL,0); \
t = split(NULL,0); \
if (t == NULL) { \
text_color_set(DW_COLOR_ERROR); \
dw_printf ("Line %d: Missing %s for SmartBeaconing.\n", line, name); \
@ -3787,11 +3821,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_s *p_audio_config)
{
char options[1000];
char *o;
char *t;
char *p;
int q;
char temp_symbol[100];
int ok;
char zone[8];
@ -3901,7 +3931,7 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_
b->custom_infocmd = strdup(value);
}
else if (strcasecmp(keyword, "OBJNAME") == 0) {
strncpy(b->objname, value, 9);
strlcpy(b->objname, value, sizeof(b->objname));
}
else if (strcasecmp(keyword, "LAT") == 0) {
b->lat = parse_ll (value, LAT, line);
@ -3913,7 +3943,7 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_
b->alt_m = atof(value);
}
else if (strcasecmp(keyword, "ZONE") == 0) {
strncpy(zone, value, sizeof(zone));
strlcpy(zone, value, sizeof(zone));
}
else if (strcasecmp(keyword, "EAST") == 0 || strcasecmp(keyword, "EASTING") == 0) {
easting = atof(value);
@ -3944,7 +3974,7 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_
b->gain = atoi(value);
}
else if (strcasecmp(keyword, "DIR") == 0 || strcasecmp(keyword, "DIRECTION") == 0) {
strncpy(b->dir, value, 2);
strlcpy(b->dir, value, sizeof(b->dir));
}
else if (strcasecmp(keyword, "FREQ") == 0) {
b->freq = atof(value);

View File

@ -38,11 +38,21 @@ struct misc_config_s {
/* Want this to be off by default because it hangs */
/* after a while if nothing is reading from other end. */
char nullmodem[40]; /* Serial port name for our end of the */
char nullmodem[20]; /* Serial port name for our end of the */
/* virtual null modem for native Windows apps. */
char nmea_port[40]; /* Serial port name for NMEA communication with GPS */
/* receiver and/or mapping application. */
char gpsnmea_port[20]; /* Serial port name for reading NMEA sentences from GPS. */
/* e.g. COM22, /dev/ttyACM0 */
char gpsd_host[20]; /* Host for gpsd server. */
/* e.g. localhost, 192.168.1.2 */
int gpsd_port; /* Port number for gpsd server. */
/* Default is 2947. */
/* e.g. COM22, /dev/ttyACM0 */
char nmea_port[20]; /* Serial port name for NMEA communication with GPS */
/* receiver and/or mapping application. Change this. */
char logdir[80]; /* Directory for saving activity logs. */

View File

@ -53,7 +53,7 @@
#include "textcolor.h"
#include "symbols.h"
#include "latlong.h"
//#include "nmea.h"
#include "dwgpsnmea.h"
#include "decode_aprs.h"
#include "telemetry.h"
@ -143,7 +143,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen);
*
* Outputs: A-> g_symbol_table, g_symbol_code,
* g_lat, g_lon,
* g_speed, g_course, g_altitude,
* g_speed_mph, g_course, g_altitude_ft,
* g_comment
* ... and many others...
*
@ -174,7 +174,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet)
A->g_lat = G_UNKNOWN;
A->g_lon = G_UNKNOWN;
A->g_speed = G_UNKNOWN;
A->g_speed_mph = G_UNKNOWN;
A->g_course = G_UNKNOWN;
A->g_power = G_UNKNOWN;
@ -182,7 +182,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet)
A->g_gain = G_UNKNOWN;
A->g_range = G_UNKNOWN;
A->g_altitude = G_UNKNOWN;
A->g_altitude_ft = G_UNKNOWN;
A->g_freq = G_UNKNOWN;
A->g_tone = G_UNKNOWN;
A->g_dcs = G_UNKNOWN;
@ -416,6 +416,11 @@ void decode_aprs_print (decode_aprs_t *A) {
if (A->g_power > 0) {
char phg[100];
/* Protcol spec doesn't mention whether this is dBd or dBi. */
/* Clarified later. */
/* http://eng.usna.navy.mil/~bruninga/aprs/aprs11.html */
/* "The Antenna Gain in the PHG format on page 28 is in dBi." */
snprintf (phg, sizeof(phg), ", %d W height=%d %ddBi %s", A->g_power, A->g_height, A->g_gain, A->g_directivity);
strlcat (stemp, phg, sizeof(stemp));
}
@ -507,11 +512,11 @@ void decode_aprs_print (decode_aprs_t *A) {
strlcat (stemp, A->g_aprstt_loc, sizeof(stemp));
};
if (A->g_speed != G_UNKNOWN) {
if (A->g_speed_mph != G_UNKNOWN) {
char spd[20];
if (strlen(stemp) > 0) strlcat (stemp, ", ", sizeof(stemp));
snprintf (spd, sizeof(spd), "%.0f MPH", A->g_speed);
snprintf (spd, sizeof(spd), "%.0f MPH", A->g_speed_mph);
strlcat (stemp, spd, sizeof(stemp));
};
@ -523,11 +528,11 @@ void decode_aprs_print (decode_aprs_t *A) {
strlcat (stemp, cse, sizeof(stemp));
};
if (A->g_altitude != G_UNKNOWN) {
if (A->g_altitude_ft != G_UNKNOWN) {
char alt[20];
if (strlen(stemp) > 0) strlcat (stemp, ", ", sizeof(stemp));
snprintf (alt, sizeof(alt), "alt %.0f ft", A->g_altitude);
snprintf (alt, sizeof(alt), "alt %.0f ft", A->g_altitude_ft);
strlcat (stemp, alt, sizeof(stemp));
};
@ -662,7 +667,7 @@ void decode_aprs_print (decode_aprs_t *A) {
* Inputs: info - Pointer to Information field.
* ilen - Information field length.
*
* Outputs: A->g_lat, A->g_lon, A->g_symbol_table, A->g_symbol_code, A->g_speed, A->g_course, A->g_altitude.
* Outputs: A->g_lat, A->g_lon, A->g_symbol_table, A->g_symbol_code, A->g_speed_mph, A->g_course, A->g_altitude_ft.
*
* Description: Type identifier '=' has APRS messaging.
* Type identifier '!' does not have APRS messaging.
@ -758,7 +763,7 @@ static void aprs_ll_pos (decode_aprs_t *A, unsigned char *info, int ilen)
* Inputs: info - Pointer to Information field.
* ilen - Information field length.
*
* Outputs: A->g_lat, A->g_lon, A->g_symbol_table, A->g_symbol_code, A->g_speed, A->g_course, A->g_altitude.
* Outputs: A->g_lat, A->g_lon, A->g_symbol_table, A->g_symbol_code, A->g_speed_mph, A->g_course, A->g_altitude_ft.
*
* Description: Type identifier '@' has APRS messaging.
* Type identifier '/' does not have APRS messaging.
@ -850,6 +855,7 @@ static void aprs_ll_pos_time (decode_aprs_t *A, unsigned char *info, int ilen)
}
}
(void)(ts); // suppress 'set but not used' warning.
}
@ -862,7 +868,7 @@ static void aprs_ll_pos_time (decode_aprs_t *A, unsigned char *info, int ilen)
* Inputs: info - Pointer to Information field.
* ilen - Information field length.
*
* Outputs: ??? TBD
* Outputs: A-> ...
*
* Description: APRS recognizes raw ASCII data strings conforming to the NMEA 0183
* Version 2.0 specification, originating from navigation equipment such
@ -875,286 +881,37 @@ static void aprs_ll_pos_time (decode_aprs_t *A, unsigned char *info, int ilen)
* VTG Velocity and Track Data
* WPL Way Point Location
*
* We presently recognize only RMC and GGA.
*
* Examples: $GPGGA,102705,5157.9762,N,00029.3256,W,1,04,2.0,75.7,M,47.6,M,,*62
* $GPGLL,2554.459,N,08020.187,W,154027.281,A
* $GPRMC,063909,A,3349.4302,N,11700.3721,W,43.022,89.3,291099,13.6,E*52
* $GPVTG,318.7,T,,M,35.1,N,65.0,K*69
*
*
*------------------------------------------------------------------*/
static void nmea_checksum (decode_aprs_t *A, char *sent)
{
char *p;
char *next;
unsigned char cs;
// Do we have valid checksum?
cs = 0;
for (p = sent+1; *p != '*' && *p != '\0'; p++) {
cs ^= *p;
}
p = strchr (sent, '*');
if (p == NULL) {
if ( ! A->g_quiet) {
text_color_set (DW_COLOR_INFO);
dw_printf("Missing GPS checksum.\n");
}
return;
}
if (cs != strtoul(p+1, NULL, 16)) {
if ( ! A->g_quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf("GPS checksum error. Expected %02x but found %s.\n", cs, p+1);
}
return;
}
*p = '\0'; // Remove the checksum.
}
static void aprs_raw_nmea (decode_aprs_t *A, unsigned char *info, int ilen)
{
char stemp[256];
char *ptype;
char *next;
strlcpy (A->g_msg_type, "Raw NMEA", sizeof(A->g_msg_type));
strlcpy (stemp, (char *)info, sizeof(stemp));
nmea_checksum (A, stemp);
next = stemp;
ptype = strsep(&next, ",");
if (strcmp(ptype, "$GPGGA") == 0)
if (strncmp((char*)info, "$GPRMC,", 7) == 0)
{
char *ptime; /* Time, hhmmss[.sss] */
char *plat; /* Latitude */
char *pns; /* North/South */
char *plon; /* Longitude */
char *pew; /* East/West */
char *pquality; /* Fix Quality: 0=invalid, 1=GPS, 2=DGPS */
char *pnsat; /* Number of satellites. */
char *phdop; /* Horizontal dilution of precision. */
char *paltitude; /* Altitude, meters above mean sea level. */
char *pm; /* "M" = meters */
/* Various other stuff... */
ptime = strsep(&next, ",");
plat = strsep(&next, ",");
pns = strsep(&next, ",");
plon = strsep(&next, ",");
pew = strsep(&next, ",");
pquality = strsep(&next, ",");
pnsat = strsep(&next, ",");
phdop = strsep(&next, ",");
paltitude = strsep(&next, ",");
pm = strsep(&next, ",");
/* Process time??? */
if (plat != NULL && strlen(plat) > 0 && pns != NULL && strlen(pns) > 0) {
A->g_lat = latitude_from_nmea(plat, pns);
}
else {
text_color_set (DW_COLOR_ERROR);
dw_printf("Incomplete latitude in sentence.\n");
}
if (plon != NULL && strlen(plon) > 0 && pew != NULL && strlen(pew) > 0) {
A->g_lon = longitude_from_nmea(plon, pew);
}
else {
text_color_set (DW_COLOR_ERROR);
dw_printf("Incomplete longitude in sentence.\n");
}
if (paltitude != NULL && strlen(paltitude) > 0) {
A->g_altitude = DW_METERS_TO_FEET(atof(paltitude));
}
else {
text_color_set (DW_COLOR_ERROR);
dw_printf("Incomplete altitude in sentence.\n");
}
float speed_knots = G_UNKNOWN;
(void) dwgpsnmea_gprmc ((char*)info, A->g_quiet, &(A->g_lat), &(A->g_lon), &speed_knots, &(A->g_course));
A->g_speed_mph = DW_KNOTS_TO_MPH(speed_knots);
}
else if (strcmp(ptype, "$GPGLL") == 0)
else if (strncmp((char*)info, "$GPGGA,", 7) == 0)
{
char *plat; /* Latitude */
char *pns; /* North/South */
char *plon; /* Longitude */
char *pew; /* East/West */
/* optional Time hhmmss[.sss] */
/* optional 'A' for data valid */
plat = strsep(&next, ",");
pns = strsep(&next, ",");
plon = strsep(&next, ",");
pew = strsep(&next, ",");
if (plat != NULL && strlen(plat) > 0 && pns != NULL && strlen(pns) > 0) {
A->g_lat = latitude_from_nmea(plat, pns);
}
else {
text_color_set (DW_COLOR_ERROR);
dw_printf("Incomplete latitude in sentence.\n");
}
if (plon != NULL && strlen(plon) > 0 && pew != NULL && strlen(pew) > 0) {
A->g_lon = longitude_from_nmea(plon, pew);
}
else {
text_color_set (DW_COLOR_ERROR);
dw_printf("Incomplete longitude in sentence.\n");
}
float alt_meters = G_UNKNOWN;
int num_sat = 0;
(void) dwgpsnmea_gpgga ((char*)info, A->g_quiet, &(A->g_lat), &(A->g_lon), &alt_meters, &num_sat);
A->g_altitude_ft = DW_METERS_TO_FEET(alt_meters);
}
else if (strcmp(ptype, "$GPRMC") == 0)
{
//char *ptime, *pstatus, *plat, *pns, *plon, *pew, *pspeed, *ptrack, *pdate;
char *ptime; /* Time, hhmmss[.sss] */
char *pstatus; /* Status, A=Active (valid position), V=Void */
char *plat; /* Latitude */
char *pns; /* North/South */
char *plon; /* Longitude */
char *pew; /* East/West */
char *pknots; /* Speed over ground, knots. */
char *pcourse; /* True course, degrees. */
char *pdate; /* Date, ddmmyy */
/* Magnetic variation */
/* In version 3.00, mode is added: A D E N (see below) */
/* Checksum */
// TODO (low): add a few other sentence types.
ptime = strsep(&next, ",");
pstatus = strsep(&next, ",");
plat = strsep(&next, ",");
pns = strsep(&next, ",");
plon = strsep(&next, ",");
pew = strsep(&next, ",");
pknots = strsep(&next, ",");
pcourse = strsep(&next, ",");
pdate = strsep(&next, ",");
/* process time ??? date ??? */
if (plat != NULL && strlen(plat) > 0 && pns != NULL && strlen(pns) > 0) {
A->g_lat = latitude_from_nmea(plat, pns);
}
else {
text_color_set (DW_COLOR_ERROR);
dw_printf("Incomplete latitude in sentence.\n");
}
if (plon != NULL && strlen(plon) > 0 && pew != NULL && strlen(pew) > 0) {
A->g_lon = longitude_from_nmea(plon, pew);
}
else {
text_color_set (DW_COLOR_ERROR);
dw_printf("Incomplete longitude in sentence.\n");
}
if (pknots != NULL && strlen(pknots) > 0) {
A->g_speed = DW_KNOTS_TO_MPH(atof(pknots));
}
else {
text_color_set (DW_COLOR_ERROR);
dw_printf("Incomplete speed in sentence.\n");
}
if (pcourse != NULL && strlen(pcourse) > 0) {
A->g_course = atof(pcourse);
}
else {
text_color_set (DW_COLOR_ERROR);
dw_printf("Incomplete course in sentence.\n");
}
}
else if (strcmp(ptype, "$GPVTG") == 0)
{
/* Speed and direction but NO location! */
char *ptcourse; /* True course, degrees. */
char *pt; /* "T" */
char *pmcourse; /* Magnetic course, degrees. */
char *pm; /* "M" */
char *pknots; /* Ground speed, knots. */
char *pn; /* "N" = Knots */
char *pkmh; /* Ground speed, km/hr */
char *pk; /* "K" = Kilometers per hour */
char *pmode; /* New in NMEA 0183 version 3.0 */
/* Mode: A=Autonomous, D=Differential, */
ptcourse = strsep(&next, ",");
pt = strsep(&next, ",");
pmcourse = strsep(&next, ",");
pm = strsep(&next, ",");
pknots = strsep(&next, ",");
pn = strsep(&next, ",");
pkmh = strsep(&next, ",");
pk = strsep(&next, ",");
pmode = strsep(&next, ",");
if (pknots != NULL && strlen(pknots) > 0) {
A->g_speed = DW_KNOTS_TO_MPH(atof(pknots));
}
else {
text_color_set (DW_COLOR_ERROR);
dw_printf("Incomplete speed in sentence.\n");
}
if (ptcourse != NULL && strlen(ptcourse) > 0) {
A->g_course = atof(ptcourse);
}
else {
text_color_set (DW_COLOR_ERROR);
dw_printf("Incomplete course in sentence.\n");
}
}
else if (strcmp(ptype, "$GPWPL") == 0)
{
char *plat; /* Latitude */
char *pns; /* North/South */
char *plon; /* Longitude */
char *pew; /* East/West */
char *pident; /* Identifier for Waypoint. rules??? */
/* checksum */
plat = strsep(&next, ",");
pns = strsep(&next, ",");
plon = strsep(&next, ",");
pew = strsep(&next, ",");
pident = strsep(&next, ",");
if (plat != NULL && strlen(plat) > 0 && pns != NULL && strlen(pns) > 0) {
A->g_lat = latitude_from_nmea(plat, pns);
}
else {
text_color_set (DW_COLOR_ERROR);
dw_printf("Incomplete latitude in sentence.\n");
}
if (plon != NULL && strlen(plon) > 0 && pew != NULL && strlen(pew) > 0) {
A->g_lon = longitude_from_nmea(plon, pew);
}
else {
text_color_set (DW_COLOR_ERROR);
dw_printf("Incomplete longitude in sentence.\n");
}
/* do something with identifier? */
}
}
} /* end aprs_raw_nmea */
@ -1535,7 +1292,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
n = ((p->speed_course[0] - 28) * 10) + ((p->speed_course[1] - 28) / 10);
if (n >= 800) n -= 800;
A->g_speed = DW_KNOTS_TO_MPH(n);
A->g_speed_mph = DW_KNOTS_TO_MPH(n);
n = ((p->speed_course[1] - 28) % 10) * 100 + (p->speed_course[2] - 28);
if (n >= 400) n -= 400;
@ -1627,16 +1384,16 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
if (plast > pfirst && pfirst[3] == '}') {
A->g_altitude = DW_METERS_TO_FEET((pfirst[0]-33)*91*91 + (pfirst[1]-33)*91 + (pfirst[2]-33) - 10000);
A->g_altitude_ft = DW_METERS_TO_FEET((pfirst[0]-33)*91*91 + (pfirst[1]-33)*91 + (pfirst[2]-33) - 10000);
if ( ! isdigit91(pfirst[0]) || ! isdigit91(pfirst[1]) || ! isdigit91(pfirst[2]))
{
if ( ! A->g_quiet) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Invalid character in MIC-E altitude. Must be in range of '!' to '{'.\n");
dw_printf("Bogus altitude of %.0f changed to unknown.\n", A->g_altitude);
dw_printf("Bogus altitude of %.0f changed to unknown.\n", A->g_altitude_ft);
}
A->g_altitude = G_UNKNOWN;
A->g_altitude_ft = G_UNKNOWN;
}
pfirst += 4;
@ -1776,7 +1533,7 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q
* Inputs: info - Pointer to Information field.
* ilen - Information field length.
*
* Outputs: A->g_object_name, A->g_lat, A->g_lon, A->g_symbol_table, A->g_symbol_code, A->g_speed, A->g_course, A->g_altitude.
* Outputs: A->g_object_name, A->g_lat, A->g_lon, A->g_symbol_table, A->g_symbol_code, A->g_speed_mph, A->g_course, A->g_altitude_ft.
*
* Description: Message has a 9 character object name which could be quite different than
* the source station.
@ -1878,7 +1635,9 @@ static void aprs_object (decode_aprs_t *A, unsigned char *info, int ilen)
}
}
}
(void)(ts);
} /* end aprs_object */
/*------------------------------------------------------------------
@ -1890,7 +1649,7 @@ static void aprs_object (decode_aprs_t *A, unsigned char *info, int ilen)
* Inputs: info - Pointer to Information field.
* ilen - Information field length.
*
* Outputs: A->g_object_name, A->g_lat, A->g_lon, A->g_symbol_table, A->g_symbol_code, A->g_speed, A->g_course, A->g_altitude.
* Outputs: A->g_object_name, A->g_lat, A->g_lon, A->g_symbol_table, A->g_symbol_code, A->g_speed_mph, A->g_course, A->g_altitude_ft.
*
* Description: An "item" is very much like an "object" except
*
@ -1928,13 +1687,13 @@ static void aprs_item (decode_aprs_t *A, unsigned char *info, int ilen)
} *q;
time_t ts = 0;
int i;
char *ppos;
p = (struct aprs_item_s *)info;
q = (struct aprs_compressed_item_s *)info;
(void)(q);
memset (A->g_name, 0, sizeof(A->g_name));
i = 0;
@ -2209,10 +1968,13 @@ static void aprs_status_report (decode_aprs_t *A, char *info, int ilen)
erp = (p - '0') * (p - '0') * 10;
}
// TODO: put result somewhere.
// TODO (low): put result somewhere.
// could use A->g_directivity and need new variable for erp.
*hp = '\0';
(void)(beam);
(void)(erp);
}
}
@ -2406,10 +2168,10 @@ static void aprs_general_query (decode_aprs_t *A, char *info, int ilen, int quie
static void aprs_directed_station_query (decode_aprs_t *A, char *addressee, char *query, int quiet)
{
char query_type[20]; /* Does the query type always need to be exactly 5 characters? */
//char query_type[20]; /* Does the query type always need to be exactly 5 characters? */
/* If not, how would we know where the extra optional information starts? */
char callsign[AX25_MAX_ADDR_LEN];
//char callsign[AX25_MAX_ADDR_LEN];
//if (strlen(query) < 5) ...
@ -2428,7 +2190,8 @@ static void aprs_directed_station_query (decode_aprs_t *A, char *addressee, char
* ilen - Information field length.
* quiet - suppress error messages.
*
* Outputs: ???
* Outputs: A->g_telemetry
* A->g_comment
*
* Description: TBD.
*
@ -2444,7 +2207,7 @@ static void aprs_telemetry (decode_aprs_t *A, char *info, int ilen, int quiet)
strlcpy (A->g_msg_type, "Telemetry", sizeof(A->g_msg_type));
telemetry_data_original (A->g_src, info, quiet, A->g_telemetry, A->g_comment);
telemetry_data_original (A->g_src, info, quiet, A->g_telemetry, sizeof(A->g_telemetry), A->g_comment, sizeof(A->g_comment));
} /* end aprs_telemetry */
@ -2542,7 +2305,7 @@ static void aprs_positionless_weather_report (decode_aprs_t *A, unsigned char *i
strlcpy (A->g_msg_type, "Positionless Weather Report", sizeof(A->g_msg_type));
time_t ts = 0;
//time_t ts = 0;
p = (struct aprs_positionless_weather_s *)info;
@ -2569,7 +2332,7 @@ static void aprs_positionless_weather_report (decode_aprs_t *A, unsigned char *i
* TODO: call this context instead and have 3 enumerated values.
*
* Global In: A->g_course - Wind info for compressed location.
* A->g_speed
* A->g_speed_mph
*
* Outputs: A->g_weather
*
@ -2578,7 +2341,7 @@ static void aprs_positionless_weather_report (decode_aprs_t *A, unsigned char *i
* For human-readable locations, we expect wind direction
* and speed in a format like this: 999/999.
* For compressed location, this has already been
* processed and put in A->g_course and A->g_speed.
* processed and put in A->g_course and A->g_speed_mph.
* Otherwise, for positionless weather data, the
* wind is in the form c999s999.
*
@ -2661,11 +2424,11 @@ static void weather_data (decode_aprs_t *A, char *wdata, int wind_prefix)
}
if (sscanf (wp+4, "%3d", &n))
{
A->g_speed = DW_KNOTS_TO_MPH(n); /* yes, in knots */
A->g_speed_mph = DW_KNOTS_TO_MPH(n); /* yes, in knots */
}
wp += 7;
}
else if ( A->g_speed == G_UNKNOWN) {
else if ( A->g_speed_mph == G_UNKNOWN) {
if ( ! getwdata (&wp, 'c', 3, &A->g_course)) {
if ( ! A->g_quiet) {
@ -2673,7 +2436,7 @@ static void weather_data (decode_aprs_t *A, char *wdata, int wind_prefix)
dw_printf("Didn't find wind direction in form c999.\n");
}
}
if ( ! getwdata (&wp, 's', 3, &A->g_speed)) { /* MPH here */
if ( ! getwdata (&wp, 's', 3, &A->g_speed_mph)) { /* MPH here */
if ( ! A->g_quiet) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Didn't find wind speed in form s999.\n");
@ -2684,9 +2447,9 @@ static void weather_data (decode_aprs_t *A, char *wdata, int wind_prefix)
// At this point, we should have the wind direction and speed
// from one of three methods.
if (A->g_speed != G_UNKNOWN) {
if (A->g_speed_mph != G_UNKNOWN) {
snprintf (A->g_weather, sizeof(A->g_weather), "wind %.1f mph", A->g_speed);
snprintf (A->g_weather, sizeof(A->g_weather), "wind %.1f mph", A->g_speed_mph);
if (A->g_course != G_UNKNOWN) {
char ctemp[40];
snprintf (ctemp, sizeof(ctemp), ", direction %.0f", A->g_course);
@ -2695,7 +2458,7 @@ static void weather_data (decode_aprs_t *A, char *wdata, int wind_prefix)
}
/* We don't want this to show up on the location line. */
A->g_speed = G_UNKNOWN;
A->g_speed_mph = G_UNKNOWN;
A->g_course = G_UNKNOWN;
/*
@ -3041,7 +2804,6 @@ static void aprs_ultimeter (decode_aprs_t *A, char *info, int ilen)
static void third_party_header (decode_aprs_t *A, char *info, int ilen)
{
int n;
strlcpy (A->g_msg_type, "Third Party Header", sizeof(A->g_msg_type));
@ -3095,7 +2857,7 @@ static void decode_position (decode_aprs_t *A, position_t *ppos)
*
* One of the following:
* A->g_course & A->g_speeed
* A->g_altitude
* A->g_altitude_ft
* A->g_range
*
* Description: The compressed position provides resolution of around ???
@ -3169,7 +2931,7 @@ static void decode_compressed_position (decode_aprs_t *A, compressed_position_t
; /* ignore other two bytes */
}
else if (((pcpos->t - 33) & 0x18) == 0x10) {
A->g_altitude = pow(1.002, (pcpos->c - 33) * 91 + pcpos->s - 33);
A->g_altitude_ft = pow(1.002, (pcpos->c - 33) * 91 + pcpos->s - 33);
}
else if (pcpos->c == '{')
{
@ -3179,7 +2941,7 @@ static void decode_compressed_position (decode_aprs_t *A, compressed_position_t
{
/* For a weather station, this is wind information. */
A->g_course = (pcpos->c - 33) * 4;
A->g_speed = DW_KNOTS_TO_MPH(pow(1.08, pcpos->s - 33) - 1.0);
A->g_speed_mph = DW_KNOTS_TO_MPH(pow(1.08, pcpos->s - 33) - 1.0);
}
}
@ -3720,7 +3482,7 @@ int get_maidenhead (decode_aprs_t *A, char *p)
* Outputs: One or more of the following, depending the data found:
*
* A->g_course
* A->g_speed
* A->g_speed_mph
* A->g_power
* A->g_height
* A->g_gain
@ -3771,7 +3533,7 @@ static int data_extension_comment (decode_aprs_t *A, char *pdext)
}
if (sscanf (pdext+4, "%3d", &n))
{
A->g_speed = DW_KNOTS_TO_MPH(n);
A->g_speed_mph = DW_KNOTS_TO_MPH(n);
}
/* Bearing and Number/Range/Quality? */
@ -3878,8 +3640,11 @@ static const char *search_locations[] = {
(const char *) NULL
};
static int tocall_cmp (const struct tocalls_s *x, const struct tocalls_s *y)
static int tocall_cmp (const void *px, const void *py)
{
const struct tocalls_s *x = (struct tocalls_s *)px;
const struct tocalls_s *y = (struct tocalls_s *)py;
if (x->len != y->len) return (y->len - x->len);
return (strcmp(x->prefix, y->prefix));
}
@ -3933,7 +3698,7 @@ static void decode_tocall (decode_aprs_t *A, char *dest)
*p-- = '\0';
}
// printf("debug: %s\n", stuff);
// dw_printf("debug: %s\n", stuff);
if (stuff[0] == ' ' &&
stuff[4] == ' ' &&
@ -4065,7 +3830,7 @@ static void substr_se (char *dest, const char *src, int start, int endp1)
* clen - Length of comment or -1 to take it all.
*
* Outputs: A->g_telemetry - Base 91 telemetry |ss1122|
* A->g_altitude - from /A=123456
* A->g_altitude_ft - from /A=123456
* A->g_lat - Might be adjusted from !DAO!
* A->g_lon - Might be adjusted from !DAO!
* A->g_aprstt_loc - Private extension to !DAO!
@ -4381,7 +4146,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen)
}
else if (regexec (&std_toff_re, A->g_comment, MAXMATCH, match, 0) == 0) {
printf ("NO tone\n");
dw_printf ("NO tone\n");
A->g_tone = 0;
strlcpy (temp, A->g_comment + match[0].rm_eo, sizeof(temp));
@ -4390,8 +4155,6 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen)
else if (regexec (&std_dcs_re, A->g_comment, MAXMATCH, match, 0) == 0) {
char sttemp[10]; /* three octal digits */
int f;
int i;
substr_se (sttemp, A->g_comment, match[1].rm_so, match[1].rm_eo);
@ -4403,7 +4166,6 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen)
else if (regexec (&std_offset_re, A->g_comment, MAXMATCH, match, 0) == 0) {
char sttemp[10]; /* includes leading sign */
int f;
substr_se (sttemp, A->g_comment, match[1].rm_so, match[1].rm_eo);
@ -4416,7 +4178,6 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen)
char sttemp[10]; /* should be two digits */
char sutemp[10]; /* m for miles or k for km */
int f;
substr_se (sttemp, A->g_comment, match[1].rm_so, match[1].rm_eo);
substr_se (sutemp, A->g_comment, match[2].rm_so, match[2].rm_eo);
@ -4453,7 +4214,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen)
//dw_printf("compressed telemetry data = \"%s\"\n", tdata);
telemetry_data_base91 (A->g_src, tdata, A->g_telemetry);
telemetry_data_base91 (A->g_src, tdata, A->g_telemetry, sizeof(A->g_telemetry));
strlcpy (temp, A->g_comment + match[0].rm_eo, sizeof(temp));
strlcpy (A->g_comment + match[0].rm_so, temp, sizeof(A->g_comment)-match[0].rm_so);
@ -4558,7 +4319,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen)
strlcpy (temp, A->g_comment + match[0].rm_eo, sizeof(temp));
A->g_comment[match[0].rm_eo] = '\0';
A->g_altitude = atoi(A->g_comment + match[0].rm_so + 3);
A->g_altitude_ft = atoi(A->g_comment + match[0].rm_so + 3);
strlcpy (A->g_comment + match[0].rm_so, temp, sizeof(A->g_comment)-match[0].rm_so);
}
@ -4586,7 +4347,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen)
(x >= 902 && x <= 928)) {
if ( ! A->g_quiet) {
sprintf (good, "%07.3fMHz", x);
snprintf (good, sizeof(good), "%07.3fMHz", x);
text_color_set(DW_COLOR_ERROR);
dw_printf("\"%s\" in comment looks like a frequency in non-standard format.\n", bad);
dw_printf("For most systems to recognize it, use exactly this form \"%s\" at beginning of comment.\n", good);
@ -4680,7 +4441,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen)
*
* Description: Compile like this to make a standalone test program.
*
* gcc -o decode_aprs -DTEST decode_aprs.c ax25_pad.c
* gcc -o decode_aprs -DDECAMAIN decode_aprs.c ax25_pad.c ...
*
* ./decode_aprs < decode_aprs.txt
*
@ -4709,7 +4470,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen)
*
*------------------------------------------------------------------*/
#if TEST
#if DECAMAIN
/* Stub for stand-alone decoder. */
@ -4809,6 +4570,6 @@ int main (int argc, char *argv[])
return (0);
}
#endif /* TEST */
#endif /* DECAMAIN */
/* end decode_aprs.c */

View File

@ -64,7 +64,7 @@ typedef struct decode_aprs_s {
/* Also for Directed Station Query which is a */
/* special case of message. */
float g_speed; /* Speed in MPH. */
float g_speed_mph; /* Speed in MPH. */
float g_course; /* 0 = North, 90 = East, etc. */
@ -78,7 +78,7 @@ typedef struct decode_aprs_s {
float g_range; /* Precomputed radio range in miles. */
float g_altitude; /* Feet above median sea level. */
float g_altitude_ft; /* Feet above median sea level. */
char g_mfr[80]; /* Manufacturer or application. */

24
demod.c
View File

@ -98,7 +98,7 @@ static int sample_count[MAX_CHANS][MAX_SUBCHANS];
int demod_init (struct audio_s *pa)
{
int j;
//int j;
int chan; /* Loop index over number of radio channels. */
char profile;
@ -184,7 +184,7 @@ int demod_init (struct audio_s *pa)
/* This has been optimized for 300 baud. */
strcpy (just_letters, "D");
strlcpy (just_letters, "D", sizeof(just_letters));
}
else {
@ -193,7 +193,7 @@ int demod_init (struct audio_s *pa)
/* Previously we would use F if possible otherwise fall back to A. */
/* In version 1.2, new default is E+ /3. */
strcpy (just_letters, "E"); // version 1.2 now E.
strlcpy (just_letters, "E", sizeof(just_letters)); // 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) {
@ -202,7 +202,7 @@ int demod_init (struct audio_s *pa)
}
}
#else
strcpy (just_letters, "E"); // version 1.2 changed C to E.
strlcpy (just_letters, "E", sizeof(just_letters)); // version 1.2 changed C to E.
if (have_plus != -1) have_plus = 1; // Add as default for version 1.2
// If not explicitly turned off.
#endif
@ -223,12 +223,12 @@ int demod_init (struct audio_s *pa)
if (have_plus == -1) have_plus = 0;
strcpy (save_audio_config_p->achan[chan].profiles, just_letters);
strlcpy (save_audio_config_p->achan[chan].profiles, just_letters, sizeof(save_audio_config_p->achan[chan].profiles));
assert (strlen(save_audio_config_p->achan[chan].profiles) >= 1);
if (have_plus) {
strcat (save_audio_config_p->achan[chan].profiles, "+");
strlcat (save_audio_config_p->achan[chan].profiles, "+", sizeof(save_audio_config_p->achan[chan].profiles));
}
/* These can be increased later for the multi-frequency case. */
@ -250,7 +250,7 @@ int demod_init (struct audio_s *pa)
text_color_set(DW_COLOR_ERROR);
dw_printf ("Channel %d: Demodulator + option can't be combined with multiple letters.\n", chan);
strcpy (save_audio_config_p->achan[chan].profiles, "C+"); // Reduce to one letter.
strlcpy (save_audio_config_p->achan[chan].profiles, "C+", sizeof(save_audio_config_p->achan[chan].profiles)); // Reduce to one letter.
num_letters = 1;
save_audio_config_p->achan[chan].num_demod = 1;
save_audio_config_p->achan[chan].num_subchan = 1; // Will be set higher later.
@ -478,7 +478,7 @@ int demod_init (struct audio_s *pa)
/* Need to take a look at CPU usage and performance difference. */
#ifndef __arm__
strcpy (save_audio_config_p->achan[chan].profiles, "+");
strlcpy (save_audio_config_p->achan[chan].profiles, "+", sizeof(save_audio_config_p->achan[chan].profiles));
#endif
}
@ -641,7 +641,8 @@ int demod_get_sample (int a)
__attribute__((hot))
void demod_process_sample (int chan, int subchan, int sam)
{
float fsam, abs_fsam;
float fsam;
//float abs_fsam;
int k;
@ -650,8 +651,8 @@ void demod_process_sample (int chan, int subchan, int sam)
static int seq = 0; /* for log file name */
#endif
int j;
int demod_data;
//int j;
//int demod_data;
struct demodulator_state_s *D;
assert (chan >= 0 && chan < MAX_CHANS);
@ -786,7 +787,6 @@ alevel_t demod_get_audio_level (int chan, int subchan)
{
struct demodulator_state_s *D;
alevel_t alevel;
int pk;
assert (chan >= 0 && chan < MAX_CHANS);
assert (subchan >= 0 && subchan < MAX_SUBCHANS);

View File

@ -56,7 +56,7 @@ static float slice_point[MAX_SUBCHANS];
/* Add sample to buffer and shift the rest down. */
__attribute__((hot))
__attribute__((hot)) __attribute__((always_inline))
static inline void push_sample (float val, float *buff, int size)
{
memmove(buff+1,buff,(size-1)*sizeof(float));
@ -66,7 +66,7 @@ static inline void push_sample (float val, float *buff, int size)
/* FIR filter kernel. */
__attribute__((hot))
__attribute__((hot)) __attribute__((always_inline))
static inline float convolve (const float *__restrict__ data, const float *__restrict__ filter, int filter_size)
{
float sum = 0.0f;
@ -81,10 +81,12 @@ static inline float convolve (const float *__restrict__ data, const float *__res
float *d = __builtin_assume_aligned(data, 16);
float *f = __builtin_assume_aligned(filter, 16);
#pragma GCC ivdep
for (j=0; j<filter_size; j++) {
sum += f[j] * d[j];
}
#else
#pragma GCC ivdep // ignored until gcc 4.9
for (j=0; j<filter_size; j++) {
sum += filter[j] * data[j];
}
@ -95,7 +97,7 @@ static inline float convolve (const float *__restrict__ data, const float *__res
/* Automatic gain control. */
/* Result should settle down to 1 unit peak to peak. i.e. -0.5 to +0.5 */
__attribute__((hot))
__attribute__((hot)) __attribute__((always_inline))
static inline float agc (float in, float fast_attack, float slow_decay, float *ppeak, float *pvalley)
{
if (in >= *ppeak) {
@ -264,7 +266,7 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D
{
float fsam;
float abs_fsam;
//float abs_fsam;
float amp;
float demod_out;
@ -273,7 +275,7 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D
static int seq = 0; /* for log file name */
#endif
int j;
//int j;
int subchan = 0;
int demod_data; /* Still scrambled. */
@ -397,7 +399,6 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D
__attribute__((hot))
static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator_state_s *D)
{
int descram; /* Data bit de-scrambled. */
/*
* Next, a PLL is used to sample near the centers of the data bits.
@ -439,22 +440,12 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
*
* http://www.amsat.org/amsat/articles/g3ruh/109/fig03.gif
*/
// Warning: 'descram' set but not used.
// It's used in conditional debug code below.
// descram =
descramble (demod_data, &(D->slicer[subchan].lfsr));
//assert (modem.modem_type[chan] == MODEM_SCRAMBLE);
//if (modem.modem_type[chan] == MODEM_SCRAMBLE) {
descram = descramble (demod_data, &(D->slicer[subchan].lfsr));
hdlc_rec_bit (chan, subchan, demod_data, 1, D->slicer[subchan].lfsr);
//D->prev_descram = descram;
//}
//else {
/* Baseband signal for completeness - not in common use. */
//hdlc_rec_bit (chan, subchan, demod_data);
//}
hdlc_rec_bit (chan, subchan, demod_data, 1, D->slicer[subchan].lfsr);
}
if (demod_data != D->slicer[subchan].prev_demod_data) {
@ -480,7 +471,7 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
if (demod_log_fp == NULL) {
seq++;
sprintf (fname, "demod96/%04d.csv", seq);
snprintf (fname, sizeof(fname), "demod96/%04d.csv", seq);
if (seq == 1) mkdir ("demod96"
#ifndef __WIN32__
, 0777

View File

@ -72,7 +72,7 @@
/* Should help with microcomputer platform. */
__attribute__((hot))
__attribute__((hot)) __attribute__((always_inline))
static inline float z (float x, float y)
{
x = fabsf(x);
@ -88,7 +88,7 @@ static inline float z (float x, float y)
/* Add sample to buffer and shift the rest down. */
__attribute__((hot))
__attribute__((hot)) __attribute__((always_inline))
static inline void push_sample (float val, float *buff, int size)
{
memmove(buff+1,buff,(size-1)*sizeof(float));
@ -98,7 +98,7 @@ static inline void push_sample (float val, float *buff, int size)
/* FIR filter kernel. */
__attribute__((hot))
__attribute__((hot)) __attribute__((always_inline))
static inline float convolve (const float *__restrict__ data, const float *__restrict__ filter, int filter_size)
{
float sum = 0.0f;
@ -113,10 +113,12 @@ static inline float convolve (const float *__restrict__ data, const float *__res
float *d = __builtin_assume_aligned(data, 16);
float *f = __builtin_assume_aligned(filter, 16);
#pragma GCC ivdep
for (j=0; j<filter_size; j++) {
sum += f[j] * d[j];
}
#else
#pragma GCC ivdep // ignored until gcc 4.9
for (j=0; j<filter_size; j++) {
sum += filter[j] * data[j];
}
@ -127,7 +129,7 @@ static inline float convolve (const float *__restrict__ data, const float *__res
/* Automatic gain control. */
/* Result should settle down to 1 unit peak to peak. i.e. -0.5 to +0.5 */
__attribute__((hot))
__attribute__((hot)) __attribute__((always_inline))
static inline float agc (float in, float fast_attack, float slow_decay, float *ppeak, float *pvalley)
{
if (in >= *ppeak) {
@ -795,7 +797,8 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
__attribute__((hot))
void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulator_state_s *D)
{
float fsam, abs_fsam;
float fsam;
//float abs_fsam;
float m_sum1, m_sum2, s_sum1, s_sum2;
float m_amp, s_amp;
float m_norm, s_norm;
@ -806,7 +809,7 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat
#endif
int j;
//int j;
int demod_data;
@ -827,7 +830,7 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat
fsam = sam / 16384.0f;
abs_fsam = fsam >= 0.0f ? fsam : -fsam;
//abs_fsam = fsam >= 0.0f ? fsam : -fsam;
/*
@ -1049,7 +1052,7 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat
if (demod_log_fp == NULL) {
seq++;
sprintf (fname, "demod/%04d.csv", seq);
snprintf (fname, sizeof(fname), "demod/%04d.csv", seq);
if (seq == 1) mkdir ("demod", 0777);
demod_log_fp = fopen (fname, "w");

View File

@ -532,6 +532,9 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
* Returns: None.
*
* Description: TODO...
*
* Initial reports were favorable.
* Should document what this is all about if there is still interest...
*
*------------------------------------------------------------------------------*/
@ -569,7 +572,7 @@ void digi_regen (int from_chan, packet_t pp)
*
*------------------------------------------------------------------------*/
#if TEST
#if DIGITEST
static char mycall[] = "WB2OSZ-9";
@ -594,6 +597,7 @@ static void test (char *in, char *out)
int info_len;
unsigned char frame[AX25_MAX_PACKET_LEN];
int frame_len;
alevel_t alevel;
dw_printf ("\n");
@ -606,7 +610,7 @@ static void test (char *in, char *out)
ax25_format_addrs (pp, rec);
info_len = ax25_get_info (pp, &pinfo);
strcat (rec, (char*)pinfo);
strlcat (rec, (char*)pinfo, sizeof(rec));
if (strcmp(in, rec) != 0) {
text_color_set(DW_COLOR_ERROR);
@ -621,11 +625,15 @@ static void test (char *in, char *out)
frame_len = ax25_pack (pp, frame);
ax25_delete (pp);
pp = ax25_from_frame (frame, frame_len, 50);
alevel.rec = 50;
alevel.mark = 50;
alevel.space = 50;
pp = ax25_from_frame (frame, frame_len, alevel);
assert (pp != NULL);
ax25_format_addrs (pp, rec);
info_len = ax25_get_info (pp, &pinfo);
strcat (rec, (char*)pinfo);
strlcat (rec, (char*)pinfo, sizeof(rec));
if (strcmp(in, rec) != 0) {
text_color_set(DW_COLOR_ERROR);
@ -648,11 +656,11 @@ static void test (char *in, char *out)
dedupe_remember (result, 0);
ax25_format_addrs (result, xmit);
info_len = ax25_get_info (result, &pinfo);
strcat (xmit, (char*)pinfo);
strlcat (xmit, (char*)pinfo, sizeof(xmit));
ax25_delete (result);
}
else {
strcpy (xmit, "");
strlcpy (xmit, "", sizeof(xmit));
}
text_color_set(DW_COLOR_XMIT);
@ -894,7 +902,7 @@ int main (int argc, char *argv[])
* Filtering integrated with rest of process...
*/
strcpy (typefilter, "w");
strlcpy (typefilter, "w", sizeof(typefilter));
test ( "N8VIM>APN391,WIDE2-1:$ULTW00000000010E097D2884FFF389DC000102430002033400000000",
"N8VIM>APN391,WB2OSZ-9*:$ULTW00000000010E097D2884FFF389DC000102430002033400000000");
@ -902,7 +910,7 @@ int main (int argc, char *argv[])
test ( "AB1OC-10>APWW10,WIDE1-1,WIDE2-1:>FN42er/# Hollis, NH iGate Operational",
"");
strcpy (typefilter, "s");
strlcpy (typefilter, "s", sizeof(typefilter));
test ( "AB1OC-10>APWW10,WIDE1-1,WIDE2-1:>FN42er/# Hollis, NH iGate Operational",
"AB1OC-10>APWW10,WB2OSZ-9*,WIDE2-1:>FN42er/# Hollis, NH iGate Operational");
@ -910,12 +918,12 @@ int main (int argc, char *argv[])
test ( "K1ABC-9>TR4R8R,WIDE1-1:`c6LlIb>/`\"4K}_%",
"");
strcpy (typefilter, "up");
strlcpy (typefilter, "up", sizeof(typefilter));
test ( "K1ABC-9>TR4R8R,WIDE1-1:`c6LlIb>/`\"4K}_%",
"K1ABC-9>TR4R8R,WB2OSZ-9*:`c6LlIb>/`\"4K}_%");
strcpy (typefilter, "");
strlcpy (typefilter, "", sizeof(typefilter));
#endif
/*
@ -934,6 +942,6 @@ int main (int argc, char *argv[])
} /* end main */
#endif /* if TEST */
#endif /* if DIGITEST */
/* end digipeater.c */

View File

@ -164,7 +164,7 @@ static struct misc_config_s misc_config;
int main (int argc, char *argv[])
{
int err;
int eof;
//int eof;
int j;
char config_file[100];
int xmit_calibrate_option = 0;
@ -180,6 +180,7 @@ int main (int argc, char *argv[])
int d_k_opt = 0; /* "-d k" option for serial port KISS. Can be repeated for more detail. */
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_g_opt = 0; /* "-d g" option for GPS. 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. */
@ -199,12 +200,6 @@ int main (int argc, char *argv[])
//Restore on exit? oldcp = GetConsoleOutputCP();
SetConsoleOutputCP(CP_UTF8);
#elif __CYGWIN__
/*
* Without this, the ISO Latin 1 characters are displayed as gray boxes.
*/
//setenv ("LANG", "C.ISO-8859-1", 1);
#else
/*
@ -230,16 +225,17 @@ int main (int argc, char *argv[])
}
// TODO: control development/beta/release by version.h instead of changing here.
// Print platform. This will provide more information when people send a copy the information displayed.
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 DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "G", __DATE__);
//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
#if __WIN32__
SetConsoleCtrlHandler (cleanup_win, TRUE);
SetConsoleCtrlHandler ((PHANDLER_ROUTINE)cleanup_win, TRUE);
#else
setlinebuf (stdout);
signal (SIGINT, cleanup_linux);
@ -278,19 +274,7 @@ int main (int argc, char *argv[])
text_color_set(DW_COLOR_INFO);
#endif
/*
* This has not been very well tested in 64 bit mode.
*/
#if 0
if (sizeof(int) != 4 || sizeof(long) != 4 || sizeof(char *) != 4) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("------------------------------------------------------------------\n");
dw_printf ("This might not work properly when compiled for a 64 bit target.\n");
dw_printf ("It is recommended that you rebuild it with gcc -m32 option.\n");
dw_printf ("------------------------------------------------------------------\n");
}
#endif
/*
* Default location of configuration file is current directory.
@ -307,7 +291,7 @@ int main (int argc, char *argv[])
strlcpy (input_file, "", sizeof(input_file));
while (1) {
int this_option_optind = optind ? optind : 1;
//int this_option_optind = optind ? optind : 1;
int option_index = 0;
int c;
char *p;
@ -448,6 +432,7 @@ int main (int argc, char *argv[])
// separate out gps & waypoints.
case 'g': d_g_opt++; break;
case 't': d_t_opt++; beacon_tracker_set_debug (d_t_opt); break;
case 'w': nmea_set_debug (1); break; // not documented yet.
@ -685,7 +670,9 @@ int main (int argc, char *argv[])
/*
* Open port for communication with GPS.
*/
nmea_init (&misc_config);
dwgps_init (&misc_config, d_g_opt);
nmea_init (&misc_config); // TODO: revisit.
/*
* Create thread for trying to salvage frames with bad FCS.
@ -695,8 +682,8 @@ int main (int argc, char *argv[])
/*
* Enable beaconing.
*/
beacon_init (&audio_config, &misc_config, &digi_config);
beacon_init (&audio_config, &misc_config);
log_init(misc_config.logdir);
@ -920,7 +907,7 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
if (A.g_lat != G_UNKNOWN && A.g_lon != G_UNKNOWN) {
nmea_send_waypoint (strlen(A.g_name) > 0 ? A.g_name : A.g_src,
A.g_lat, A.g_lon, A.g_symbol_table, A.g_symbol_code,
DW_FEET_TO_METERS(A.g_altitude), A.g_course, DW_MPH_TO_KNOTS(A.g_speed),
DW_FEET_TO_METERS(A.g_altitude_ft), A.g_course, DW_MPH_TO_KNOTS(A.g_speed_mph),
A.g_comment);
}
}
@ -947,7 +934,7 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
*/
if (subchan == -1) {
if (tt_config.gateway_enabled && info_len >= 2) {
aprs_tt_sequence (chan, pinfo+1);
aprs_tt_sequence (chan, (char*)(pinfo+1));
}
}
else {
@ -1048,7 +1035,8 @@ static void usage (char **argv)
dw_printf (" n n = KISS network client.\n");
dw_printf (" u u = Display non-ASCII text in hexadecimal.\n");
dw_printf (" p p = dump Packets in hexadecimal.\n");
dw_printf (" t t = gps Tracker.\n");
dw_printf (" g g = GPS interface.\n");
dw_printf (" t t = Tracker beacon.\n");
dw_printf (" o o = output controls such as PTT and DCD.\n");
dw_printf (" -q Quiet (suppress output) options:\n");
dw_printf (" h h = Heard line with the audio level.\n");

View File

@ -66,7 +66,7 @@
# To use something other than the default, generally use plughw
# and a card number reported by "arecord -l" command. Example:
# ADEVICE plughw:1,0
ADEVICE "Realtek High"
# Starting with version 1.0, you can also use "-" or "stdin" to
# pipe stdout from some other application such as a software defined
@ -93,8 +93,7 @@ ACHANNELS 1
# #
#############################################################
#ADEVICE1 ...
#ADEVICE1 USB
#############################################################
# #
@ -129,7 +128,7 @@ CHANNEL 0
# Example (don't use this unless you are me): MYCALL WB2OSZ-5
#
MYCALL N0CALL
MYCALL WB2OSZ-14
#
# Pick a suitable modem speed based on your situation.
@ -166,7 +165,7 @@ MODEM 1200
# Uncomment line below to enable the DTMF decoder for this channel.
#
#DTMF
DTMF
#
# If not using a VOX circuit, the transmitter Push to Talk (PTT)
@ -181,7 +180,7 @@ MODEM 1200
#
#PTT COM1 RTS
#PTT COM1 RTS -DTR
PTT COM1 RTS -DTR
#PTT /dev/ttyUSB0 RTS
#
@ -213,6 +212,8 @@ MODEM 1200
# Specify MYCALL, MODEM, PTT, etc. configuration items for
# CHANNEL 1. Repeat for any other channels.
#CHANNEL 2
#DTMF
#############################################################
# #
@ -260,6 +261,11 @@ KISSPORT 8001
# #
#############################################################
GPSNMEA COM22
TBEACON delay=0:30 every=0:20 SYMBOL=car FREQ=146.955 OFFSET=-0.600 TONE=74.4
#
# Beaconing is configured with these two commands:

View File

@ -3,7 +3,6 @@
#define DIREWOLF_H 1
/*
* Previously, we could handle only a single audio device.
* This meant we could have only two radio channels.
@ -75,6 +74,10 @@
/* Prefix with DW_ because /usr/include/gps.h uses a couple of these names. */
#ifndef G_UNKNOWN
#include "latlong.h"
#endif
#define DW_METERS_TO_FEET(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 3.2808399)
#define DW_FEET_TO_METERS(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.3048)
@ -163,8 +166,10 @@ typedef pthread_mutex_t dw_mutex_t;
/* Platform differences for string functions. */
#if __WIN32__
char *strsep(char **stringp, const char *delim);
char *strtok_r(char *str, const char *delim, char **saveptr);
#endif
//#if __WIN32__

10
dlq.c
View File

@ -276,9 +276,9 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
pnew->alevel = alevel;
pnew->retries = retries;
if (spectrum == NULL)
strcpy(pnew->spectrum, "");
strlcpy(pnew->spectrum, "", sizeof(pnew->spectrum));
else
strcpy(pnew->spectrum, spectrum);
strlcpy(pnew->spectrum, spectrum, sizeof(pnew->spectrum));
#if DEBUG1
text_color_set(DW_COLOR_DEBUG);
@ -524,7 +524,7 @@ void dlq_wait_while_empty (void)
*
*--------------------------------------------------------------------*/
int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum)
int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum, size_t spectrumsize)
{
struct dlq_item_s *phead;
@ -562,7 +562,7 @@ int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_
memset (alevel, 0xff, sizeof(*alevel));
*retries = -1;
strcpy(spectrum,"");
strlcpy(spectrum, "", spectrumsize);
result = 0;
}
else {
@ -576,7 +576,7 @@ int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_
*pp = phead->pp;
*alevel = phead->alevel;
*retries = phead->retries;
strcpy (spectrum, phead->spectrum);
strlcpy (spectrum, phead->spectrum, spectrumsize);
result = 1;
}

2
dlq.h
View File

@ -22,7 +22,7 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
void dlq_wait_while_empty (void);
int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum);
int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum, size_t spectrumsize);
#endif

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,5 @@
#include "direwolf.h"
#include "textcolor.h"
#include "dtime_now.h"
@ -17,9 +17,7 @@
#include <sys/time.h>
#endif
#if __WIN32__
#include <windows.h>
#endif

423
dwgps.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) 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
@ -22,69 +22,67 @@
*
* Module: dwgps.c
*
* Purpose: Interface to location data, i.e. GPS receiver.
* Purpose: Interface for obtaining location from GPS.
*
* Description: Tracker beacons need to know the current location.
* At this time, I can't think of any other reason why
* we would need this information.
* Description: This is a wrapper for two different implementations:
*
* For Linux, we use gpsd and libgps.
* This has the extra benefit that the system clock can
* be set from the GPS signal.
* (1) Read NMEA sentences from a serial port (or USB
* that looks line one). Available for all platforms.
*
* Not yet implemented for Windows. Not sure how yet.
* The Windows location API is new in Windows 7.
* At the end of 2013, about 1/3 of Windows users are
* still using XP so that still needs to be supported.
* (2) Read from gpsd. Not available for Windows.
* Including this is optional because it depends
* on another external software component.
*
* Reference:
*
* API: dwgps_init Connect to data stream at start up time.
*
* dwgps_read Return most recent location to application.
*
* dwgps_print Print contents of structure for debugging.
*
* dwgps_term Shutdown on exit.
*
*
* from below: dwgps_set_data Called from other two implementations to
* save data until it is needed.
*
*---------------------------------------------------------------*/
#if TEST
#define ENABLE_GPS 1
#endif
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#if __WIN32__
#include <windows.h>
#else
#if ENABLE_GPS
#include <gps.h>
#if GPSD_API_MAJOR_VERSION != 5
#error libgps API version might be incompatible.
#endif
#endif
#endif
#include <time.h>
#include "direwolf.h"
#include "textcolor.h"
#include "dwgps.h"
#include "dwgpsnmea.h"
#include "dwgpsd.h"
/* Was init successful? */
static int s_dwgps_debug = 0; /* Enable debug output. */
/* >= 2 show updates from GPS. */
/* >= 1 show results from dwgps_read. */
static enum { INIT_NOT_YET, INIT_SUCCESS, INIT_FAILED } init_status = INIT_NOT_YET;
/*
* The GPS reader threads deposit current data here when it becomes available.
* dwgps_read returns it to the requesting application.
*
* A critical region to avoid inconsistency between fields.
*/
#if __WIN32__
#include <windows.h>
#else
#if ENABLE_GPS
static dwgps_info_t s_dwgps_info = {
.timestamp = 0,
.fix = DWFIX_NOT_INIT, /* to detect read without init. */
.dlat = G_UNKNOWN,
.dlon = G_UNKNOWN,
.speed_knots = G_UNKNOWN,
.track = G_UNKNOWN,
.altitude = G_UNKNOWN
};
static struct gps_data_t gpsdata;
#endif
#endif
static dw_mutex_t s_gps_mutex;
/*-------------------------------------------------------------------
@ -93,223 +91,125 @@ static struct gps_data_t gpsdata;
*
* Purpose: Intialize the GPS interface.
*
* Inputs: none.
*
* Returns: 0 = success
* -1 = failure
* Inputs: pconfig Configuration settings. This might include
* serial port name for direct connect and host
* name or address for network connection.
*
* Description: For Linux, this maps into gps_open.
* Not yet implemented for Windows.
* debug - If >= 1, print results when dwgps_read is called.
* (In this file.)
*
* If >= 2, location updates are also printed.
* (In other two related files.)
*
* Returns: none
*
* Description: Call corresponding functions for implementations.
* Normally we would expect someone to use either GPSNMEA or
* GPSD but there is nothing to prevent use of both at the
* same time.
*
*--------------------------------------------------------------------*/
int dwgps_init (void)
void dwgps_init (struct misc_config_s *pconfig, int debug)
{
#if __WIN32__
s_dwgps_debug = debug;
/*
* Windows version. Not implemented yet.
*/
dw_mutex_init (&s_gps_mutex);
text_color_set(DW_COLOR_ERROR);
dw_printf ("GPS interface not yet available in Windows version.\n");
init_status = INIT_FAILED;
return (-1);
dwgpsnmea_init (pconfig, debug);
#elif ENABLE_GPS
#if ENABLE_GPSD
int err;
#if USE_GPS_SHM
/*
* Linux - Shared memory interface to gpsd.
*
* I wanted to use this method because it is simpler and more efficient.
*
* The current version of gpsd, supplied with Raspian, is 3.6 from back in
* May 2012, is missing support for the shared memory interface.
* https://github.com/raspberrypi/linux/issues/523
*
* I tried to download a newer source and build with shared memory support
* but ran into a couple other issues.
*
* sudo apt-get install libncurses5-dev
* sudo apt-get install scons
* cd ~
* wget http://download-mirror.savannah.gnu.org/releases/gpsd/gpsd-3.11.tar.gz
* tar xfz gpsd-3.11.tar.gz
* cd gpsd-3.11
* scons prefix=/usr libdir=lib/arm-linux-gnueabihf shm_export=True python=False
* sudo scons udev-install
*
* For now, we will use the socket interface.
* Maybe get back to this again someday.
*/
err = gps_open (GPSD_SHARED_MEMORY, NULL, &gpsdata);
if (err != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Unable to connect to GPSD shared memory interface, status=%d.\n", err);
if (err == NL_NOHOST) {
// I don't think this is right but we are not using it anyhow.
dw_printf ("Shared memory interface is not enabled in libgps.\n");
dw_printf ("Download the gpsd source and build with 'shm_export=True' option.\n");
}
else {
dw_printf ("%s\n", gps_errstr(errno));
}
init_status = INIT_FAILED;
return (-1);
}
init_status = INIT_SUCCESS;
return (0);
#else
/*
* Linux - Socket interface to gpsd.
*/
err = gps_open ("localhost", DEFAULT_GPSD_PORT, &gpsdata);
if (err != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Unable to connect to GPSD stream, status%d.\n", err);
dw_printf ("%s\n", gps_errstr(errno));
init_status = INIT_FAILED;
return (-1);
}
gps_stream(&gpsdata, WATCH_ENABLE | WATCH_JSON, NULL);
init_status = INIT_SUCCESS;
return (0);
#endif
#else /* end ENABLE_GPS */
text_color_set(DW_COLOR_ERROR);
dw_printf ("GPS interface not enabled in this version.\n");
dw_printf ("See documentation on how to rebuild with ENABLE_GPS.\n");
init_status = INIT_FAILED;
return (-1);
dwgpsd_init (pconfig, debug);
#endif
} /* end dwgps_init */
SLEEP_MS(500); /* So receive thread(s) can clear the */
/* not init status before it gets checked. */
} /* end dwgps_init */
/*-------------------------------------------------------------------
*
* Name: dwgps_clear
*
* Purpose: Clear the gps info structure.
*
*--------------------------------------------------------------------*/
void dwgps_clear (dwgps_info_t *gpsinfo)
{
gpsinfo->timestamp = 0;
gpsinfo->fix = DWFIX_NOT_SEEN;
gpsinfo->dlat = G_UNKNOWN;
gpsinfo->dlon = G_UNKNOWN;
gpsinfo->speed_knots = G_UNKNOWN;
gpsinfo->track = G_UNKNOWN;
gpsinfo->altitude = G_UNKNOWN;
}
/*-------------------------------------------------------------------
*
* Name: dwgps_read
*
* Purpose: Obtain current location from GPS receiver.
* Purpose: Return most recent location data available.
*
* Outputs: *plat - Latitude.
* *plon - Longitude.
* *pspeed - Speed, knots.
* *pcourse - Course over ground, degrees.
* *palt - Altitude, meters.
* Outputs: gpsinfo - Structure with latitude, longitude, etc.
*
* Returns: Position fix quality. Same as in structure.
*
* Returns: -1 = error
* 0 = location currently not available (no fix)
* 2 = 2D fix, lat/lon, speed, and course are set.
* 3 - 3D fix, altitude is also set.
*
*--------------------------------------------------------------------*/
int dwgps_read (double *plat, double *plon, float *pspeed, float *pcourse, float *palt)
dwfix_t dwgps_read (dwgps_info_t *gpsinfo)
{
#if __WIN32__
text_color_set(DW_COLOR_ERROR);
dw_printf ("Internal error, dwgps_read, shouldn't be here.\n");
return (-1);
dw_mutex_lock (&s_gps_mutex);
#elif ENABLE_GPS
memcpy (gpsinfo, &s_dwgps_info, sizeof(*gpsinfo));
int err;
dw_mutex_unlock (&s_gps_mutex);
if (init_status != INIT_SUCCESS) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Internal error, dwgps_read without successful init.\n");
return (-1);
if (s_dwgps_debug >= 1) {
text_color_set (DW_COLOR_DEBUG);
dwgps_print ("gps_read: ", gpsinfo);
}
#if USE_GPS_SHM
// TODO: Should we check timestamp and complain if very stale?
// or should we leave that up to the caller?
/*
* Shared memory version.
*/
return (s_dwgps_info.fix);
}
err = gps_read (&gpsdata);
#if DEBUG
dw_printf ("gps_read returns %d bytes\n", err);
#endif
/*-------------------------------------------------------------------
*
* Name: dwgps_print
*
* Purpose: Print gps information for debugging.
*
* Inputs: msg - Message for prefix on line.
* gpsinfo - Structure with latitude, longitude, etc.
*
* Description: Caller is responsible for setting text color.
*
*--------------------------------------------------------------------*/
#else
void dwgps_print (char *msg, dwgps_info_t *gpsinfo)
{
/*
* Socket version.
*/
dw_printf ("%stime=%d fix=%d lat=%.6f lon=%.6f trk=%.0f spd=%.1f alt=%.0f\n",
msg,
(int)gpsinfo->timestamp, (int)gpsinfo->fix,
gpsinfo->dlat, gpsinfo->dlon,
gpsinfo->track, gpsinfo->speed_knots,
gpsinfo->altitude);
// Wait for up to 1000 milliseconds.
// This should only happen in the beaconing thread so
// I'm not worried about other functions hanging.
if (gps_waiting(&gpsdata, 1000)) {
err = gps_read (&gpsdata);
}
else {
gps_stream(&gpsdata, WATCH_ENABLE | WATCH_JSON, NULL);
sleep (1);
}
#endif
if (err > 0) {
/* Data is available. */
if (gpsdata.status >= STATUS_FIX && gpsdata.fix.mode >= MODE_2D) {
*plat = gpsdata.fix.latitude;
*plon = gpsdata.fix.longitude;
*pcourse = gpsdata.fix.track;
*pspeed = MPS_TO_KNOTS * gpsdata.fix.speed; /* libgps uses meters/sec */
if (gpsdata.fix.mode >= MODE_3D) {
*palt = gpsdata.fix.altitude;
return (3);
}
return (2);
}
/* No fix. Probably temporary condition. */
return (0);
}
else if (err == 0) {
/* No data available at the present time. */
return (0);
}
else {
/* More serious error. */
return (-1);
}
#else
text_color_set(DW_COLOR_ERROR);
dw_printf ("Internal error, dwgps_read, shouldn't be here.\n");
return (-1);
#endif
} /* end dwgps_read */
} /* end dwgps_set_data */
/*-------------------------------------------------------------------
@ -326,19 +226,10 @@ int dwgps_read (double *plat, double *plon, float *pspeed, float *pcourse, float
void dwgps_term (void) {
#if __WIN32__
#elif ENABLE_GPS
if (init_status == INIT_SUCCESS) {
#ifndef USE_GPS_SHM
gps_stream(&gpsdata, WATCH_DISABLE, NULL);
#endif
gps_close (&gpsdata);
}
#else
dwgpsnmea_term ();
#if ENABLE_GPSD
dwgpsd_term ();
#endif
} /* end dwgps_term */
@ -348,69 +239,27 @@ void dwgps_term (void) {
/*-------------------------------------------------------------------
*
* Name: main
* Name: dwgps_set_data
*
* Purpose: Simple unit test for other functions in this file.
* Purpose: Called by the GPS interfaces when new data is available.
*
* Description: Compile with -DTEST option.
*
* gcc -DTEST dwgps.c textcolor.c -lgps
* ./a.out
* Inputs: gpsinfo - Structure with latitude, longitude, etc.
*
*--------------------------------------------------------------------*/
#if TEST
int main (int argc, char *argv[])
void dwgps_set_data (dwgps_info_t *gpsinfo)
{
#if __WIN32__
/* Debug print is handled by the two callers so */
/* we can distinguish the source. */
printf ("Not in win32 version yet.\n");
dw_mutex_lock (&s_gps_mutex);
#elif ENABLE_GPS
int err;
int fix;
double lat;
double lon;
float speed;
float course;
float alt;
memcpy (&s_dwgps_info, gpsinfo, sizeof(s_dwgps_info));
err = dwgps_init ();
if (err != 0) exit(1);
while (1) {
fix = dwgps_read (&lat, &lon, &speed, &course, &alt) ;
switch (fix) {
case 3:
case 2:
dw_printf ("%.6f %.6f", lat, lon);
dw_printf (" %.1f knots %.0f degrees", speed, course);
if (fix==3) dw_printf (" altitude = %.1f meters", alt);
dw_printf ("\n");
break;
case 0:
dw_printf ("location currently not available.\n");
break;
default:
dw_printf ("ERROR getting GPS information.\n");
}
sleep (3);
}
#else
printf ("Test: Shouldn't be here.\n");
#endif
} /* end main */
#endif
dw_mutex_unlock (&s_gps_mutex);
} /* end dwgps_set_data */
/* end dwgps.c */

50
dwgps.h
View File

@ -1,13 +1,59 @@
/* dwgps.h */
#ifndef DWGPS_H
#define DWGPS_H 1
int dwgps_init (void);
int dwgps_read (double *plat, double *plon, float *pspeed, float *pcourse, float *palt);
#include <time.h>
#include "config.h" /* for struct misc_config_s */
/*
* Values for fix, equivalent to values from libgps.
* -2 = not initialized.
* -1 = error communicating with GPS receiver.
* 0 = nothing heard yet.
* 1 = had signal but lost it.
* 2 = 2D.
* 3 = 3D.
*
* Undefined float & double values are set to G_UNKNOWN.
*
*/
enum dwfix_e { DWFIX_NOT_INIT= -2, DWFIX_ERROR= -1, DWFIX_NOT_SEEN=0, DWFIX_NO_FIX=1, DWFIX_2D=2, DWFIX_3D=3 };
typedef enum dwfix_e dwfix_t;
typedef struct dwgps_info_s {
time_t timestamp; /* When last updated. System time. */
dwfix_t fix; /* Quality of position fix. */
double dlat; /* Latitude. Valid if fix >= 2. */
double dlon; /* Longitude. Valid if fix >= 2. */
float speed_knots; /* libgps uses meters/sec but we use GPS usual knots. */
float track; /* What is difference between track and course? */
float altitude; /* meters above mean sea level. Valid if fix == 3. */
} dwgps_info_t;
void dwgps_init (struct misc_config_s *pconfig, int debug);
void dwgps_clear (dwgps_info_t *gpsinfo);
dwfix_t dwgps_read (dwgps_info_t *gpsinfo);
void dwgps_print (char *msg, dwgps_info_t *gpsinfo);
void dwgps_term (void);
void dwgps_set_data (dwgps_info_t *gpsinfo);
#endif /* DWGPS_H 1 */
/* end dwgps.h */

434
dwgpsd.c Normal file
View File

@ -0,0 +1,434 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// 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
// 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: dwgps.c
*
* Purpose: Interface to location data, i.e. GPS receiver.
*
* Description: For Linux, we would normally want to use gpsd and libgps.
* This allows multiple applications to access the GPS data,
* without fighting over the same serial port, and has the
* extra benefit that the system clock can be set from the GPS signal.
*
* Reference: http://www.catb.org/gpsd/
*
*---------------------------------------------------------------*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#if __WIN32__
#error Not for Windows
#endif
#if ENABLE_GPSD
#include <gps.h>
// Debian bug report: direwolf (1.2-1) FTBFS with libgps22 as part of the gpsd transition (#803605):
// dwgps.c claims to only support GPSD_API_MAJOR_VERSION 5, but also builds successfully with
// GPSD_API_MAJOR_VERSION 6 provided by libgps22 when the attached patch is applied.
#if GPSD_API_MAJOR_VERSION < 5 || GPSD_API_MAJOR_VERSION > 6
#error libgps API version might be incompatible.
#endif
#endif
#include "direwolf.h"
#include "textcolor.h"
#include "dwgps.h"
#include "dwgpsd.h"
static int s_debug = 0; /* Enable debug output. */
/* >= 1 show results from dwgps_read. */
/* >= 2 show updates from GPS. */
static void * read_gpsd_thread (void *arg);
/*
* Information for interface to gpsd daemon.
*/
static struct gps_data_t gpsdata;
/*-------------------------------------------------------------------
*
* Name: dwgpsd_init
*
* Purpose: Intialize the GPS interface.
*
* Inputs: pconfig Configuration settings. This includes
* host name or address for network connection.
*
* debug - If >= 1, print results when dwgps_read is called.
* (In different file.)
*
* If >= 2, location updates are also printed.
* (In this file.)
*
* Returns: 1 = success
* 0 = nothing to do (no host specified in config)
* -1 = failure
*
* Description: - Establish socket connection with gpsd.
* - Start up thread to process incoming data.
* It reads from the daemon and deposits into
* shared region via dwgps_put_data.
*
* The application calls dwgps_read to get the most
8 recent information.
*
*--------------------------------------------------------------------*/
/*
* Historical notes:
*
* Originally, I wanted to use the shared memory interface to gpsd
* because it is simpler and more efficient. Just access it when we
* actually need the data and we don't have a lot of extra unnecessary
* busy work going on.
*
* The current version of gpsd, supplied with Raspian, is 3.6 from back in
* May 2012, is missing support for the shared memory interface.
*
* I tried to download a newer source and build with shared memory support
* but ran into a couple other issues.
*
* sudo apt-get install libncurses5-dev
* sudo apt-get install scons
* cd ~
* wget http://download-mirror.savannah.gnu.org/releases/gpsd/gpsd-3.11.tar.gz
* tar xfz gpsd-3.11.tar.gz
* cd gpsd-3.11
* scons prefix=/usr libdir=lib/arm-linux-gnueabihf shm_export=True python=False
* sudo scons udev-install
*
* For now, we will use the socket interface. Maybe get back to this again someday.
*/
int dwgpsd_init (struct misc_config_s *pconfig, int debug)
{
#if ENABLE_GPSD
pthread_t read_gps_tid;
int e;
int err;
int arg = 0;
char sport[12];
dwgps_info_t info;
s_debug = debug;
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("dwgpsd_init()\n");
}
/*
* Socket interface to gpsd.
*/
if (strlen(pconfig->gpsd_host) == 0) {
/* Nothing to do. Leave initial fix value of errror. */
return (0);
}
snprintf (sport, sizeof(sport), "%d", pconfig->gpsd_port);
err = gps_open (pconfig->gpsd_host, sport, &gpsdata);
if (err != 0) {
dwgps_info_t info;
text_color_set(DW_COLOR_ERROR);
dw_printf ("Unable to connect to GPSD stream.\n");
dw_printf ("%s\n", gps_errstr(errno));
return (-1);
}
gps_stream(&gpsdata, WATCH_ENABLE | WATCH_JSON, NULL);
e = pthread_create (&read_gps_tid, NULL, read_gpsd_thread, (void *)(long)arg);
if (e != 0) {
text_color_set(DW_COLOR_ERROR);
perror("Could not create GPS reader thread");
return (-1);
}
/* success */
return (1);
#else /* end ENABLE_GPSD */
// Shouldn't be here.
text_color_set(DW_COLOR_ERROR);
dw_printf ("GPSD interface not enabled in this version.\n");
dw_printf ("See documentation on how to rebuild with ENABLE_GPSD.\n");
return (-1);
#endif
} /* end dwgps_init */
/*-------------------------------------------------------------------
*
* Name: read_gpsd_thread
*
* Purpose: Read information from GPSD, as it becomes available, and
* store in memory shared with dwgps_read.
*
* Inputs: arg - not used
*
*--------------------------------------------------------------------*/
#define TIMEOUT 30
#if ENABLE_GPSD
static void * read_gpsd_thread (void *arg)
{
dwgps_info_t info;
if (s_debug >= 1) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("starting read_gpsd_thread (%d)\n", (int)(long)arg);
}
dwgps_clear (&info);
info.fix = DWFIX_NOT_SEEN; /* clear not init state. */
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dwgps_print ("GPSD: ", &info);
}
dwgps_set_data (&info);
while (1) {
if ( ! gps_waiting(&gpsdata, TIMEOUT * 1000000)) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("GPSD: Timeout waiting for GPS data.\n");
/* Fall thru to read which should get error and bail out. */
}
if (gps_read (&gpsdata) == -1) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("------------------------------------------\n");
dw_printf ("GPSD: Lost communication with gpsd server.\n");
dw_printf ("------------------------------------------\n");
info.fix = DWFIX_ERROR;
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dwgps_print ("GPSD: ", &info);
}
dwgps_set_data (&info);
break; // Jump out of loop and terminate thread.
}
switch (gpsdata.fix.mode) {
default:
case MODE_NOT_SEEN:
if (info.fix >= DWFIX_2D) {
text_color_set(DW_COLOR_INFO);
dw_printf ("GPSD: Lost location fix.\n");
}
info.fix = DWFIX_NOT_SEEN;
break;
case MODE_NO_FIX:
if (info.fix >= DWFIX_2D) {
text_color_set(DW_COLOR_INFO);
dw_printf ("GPSD: Lost location fix.\n");
}
info.fix = DWFIX_NO_FIX;
break;
case MODE_2D:
if (info.fix != DWFIX_2D) {
text_color_set(DW_COLOR_INFO);
dw_printf ("GPSD: Location fix is now 2D.\n");
}
info.fix = DWFIX_2D;
break;
case MODE_3D:
if (info.fix != DWFIX_3D) {
text_color_set(DW_COLOR_INFO);
dw_printf ("GPSD: Location fix is now 3D.\n");
}
info.fix = DWFIX_3D;
break;
}
/* Data is available. */
// TODO: what is gpsdata.status?
if (gpsdata.status >= STATUS_FIX && gpsdata.fix.mode >= MODE_2D) {
info.dlat = isnan(gpsdata.fix.latitude) ? G_UNKNOWN : gpsdata.fix.latitude;
info.dlon = isnan(gpsdata.fix.longitude) ? G_UNKNOWN : gpsdata.fix.longitude;
info.track = isnan(gpsdata.fix.track) ? G_UNKNOWN : gpsdata.fix.track;
info.speed_knots = isnan(gpsdata.fix.speed) ? G_UNKNOWN : (MPS_TO_KNOTS * gpsdata.fix.speed);
if (gpsdata.fix.mode >= MODE_3D) {
info.altitude = isnan(gpsdata.fix.altitude) ? G_UNKNOWN : gpsdata.fix.altitude;
}
}
info.timestamp = time(NULL);
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dwgps_print ("GPSD: ", &info);
}
dwgps_set_data (&info);
}
return(0); // Terminate thread on serious error.
} /* end read_gps_thread */
#endif
/*-------------------------------------------------------------------
*
* Name: dwgpsd_term
*
* Purpose: Shut down GPS interface before exiting from application.
*
* Inputs: none.
*
* Returns: none.
*
*--------------------------------------------------------------------*/
void dwgpsd_term (void) {
#if ENABLE_GPSD
gps_close (&gpsdata);
#endif
} /* end dwgpsd_term */
/*-------------------------------------------------------------------
*
* Name: main
*
* Purpose: Simple unit test for other functions in this file.
*
* Description: Compile with -DGPSTEST option.
*
* gcc -DGPSTEST -DENABLE_GPSD dwgpsd.c dwgps.c textcolor.o latlong.o misc.a -lm -lpthread -lgps
* ./a.out
*
*--------------------------------------------------------------------*/
#if GPSTEST
int dwgpsnmea_init (struct misc_config_s *pconfig, int debug)
{
return (0);
}
void dwgpsnmea_term (void)
{
return;
}
int main (int argc, char *argv[])
{
struct misc_config_s config;
dwgps_info_t info;
memset (&config, 0, sizeof(config));
strlcpy (config.gpsd_host, "localhost", sizeof(config.gpsd_host));
config.gpsd_port = atoi(DEFAULT_GPSD_PORT);
dwgps_init (&config, 3);
while (1) {
dwfix_t fix;
fix = dwgps_read (&info) ;
text_color_set (DW_COLOR_INFO);
switch (fix) {
case DWFIX_2D:
case DWFIX_3D:
dw_printf ("%.6f %.6f", info.dlat, info.dlon);
dw_printf (" %.1f knots %.0f degrees", info.speed_knots, info.track);
if (fix==3) dw_printf (" altitude = %.1f meters", info.altitude);
dw_printf ("\n");
break;
case DWFIX_NOT_SEEN:
case DWFIX_NO_FIX:
dw_printf ("Location currently not available.\n");
break;
case DWFIX_NOT_INIT:
dw_printf ("GPS Init failed.\n");
exit (1);
case DWFIX_ERROR:
default:
dw_printf ("ERROR getting GPS information.\n");
break;
}
SLEEP_SEC (3);
}
} /* end main */
#endif
/* end dwgpsd.c */

22
dwgpsd.h Normal file
View File

@ -0,0 +1,22 @@
/* dwgpsd.h - For communicating with daemon */
#ifndef DWGPSD_H
#define DWGPSD_H 1
#include "config.h"
int dwgpsd_init (struct misc_config_s *pconfig, int debug);
void dwgpsd_term (void);
#endif
/* end dwgpsd.h */

800
dwgpsnmea.c Normal file
View File

@ -0,0 +1,800 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// 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
// 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: dwgpsnmea.c
*
* Purpose: process NMEA sentences from a GPS receiver.
*
* Description: This version is available for all operating systems.
*
*---------------------------------------------------------------*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include "direwolf.h"
#include "textcolor.h"
#include "dwgps.h"
#include "dwgpsnmea.h"
#include "serial_port.h"
static int s_debug = 0; /* Enable debug output. */
/* See dwgpsnmea_init description for values. */
#if __WIN32__
static unsigned __stdcall read_gpsnmea_thread (void *arg);
#else
static void * read_gpsnmea_thread (void *arg);
#endif
/*-------------------------------------------------------------------
*
* Name: dwgpsnmea_init
*
* Purpose: Open serial port for the GPS receiver.
*
* Inputs: pconfig Configuration settings. This includes
* serial port name for direct connect.
*
* debug - If >= 1, print results when dwgps_read is called.
* (In different file.)
*
* If >= 2, location updates are also printed.
* (In this file.)
* Why not do it in dwgps_set_data() ?
* Here, we can prefix it with GPSNMEA to
* distinguish it from GPSD.
*
* If >= 3, Also the NMEA sentences.
* (In this file.)
*
* Returns: 1 = success
* 0 = nothing to do (no serial port specified in config)
* -1 = failure
*
* Description: When talking directly to GPS receiver (any operating system):
*
* - Open the appropriate serial port.
* - Start up thread to process incoming data.
* It reads from the serial port and deposits into
* dwgps_info, above.
*
* The application calls dwgps_read to get the most recent information.
*
*--------------------------------------------------------------------*/
/* Make this static and available to all functions so term function can access it. */
static MYFDTYPE s_gpsnmea_port_fd = MYFDERROR; /* Handle for serial port. */
int dwgpsnmea_init (struct misc_config_s *pconfig, int debug)
{
//dwgps_info_t info;
#if __WIN32__
HANDLE read_gps_th;
#else
pthread_t read_gps_tid;
int e;
#endif
s_debug = debug;
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("dwgpsnmea_init()\n");
}
if (strlen(pconfig->gpsnmea_port) == 0) {
/* Nothing to do. Leave initial fix value for not init. */
return (0);
}
/*
* Open serial port connection.
* 4800 baud is standard for GPS.
* Should add an option to allow changing someday.
*/
s_gpsnmea_port_fd = serial_port_open (pconfig->gpsnmea_port, 4800);
if (s_gpsnmea_port_fd != MYFDERROR) {
#if __WIN32__
read_gps_th = (HANDLE)_beginthreadex (NULL, 0, read_gpsnmea_thread, (void*)(long)s_gpsnmea_port_fd, 0, NULL);
if (read_gps_th == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Could not create GPS NMEA listening thread.\n");
return (-1);
}
#else
int e;
e = pthread_create (&read_gps_tid, NULL, read_gpsnmea_thread, (void*)(long)s_gpsnmea_port_fd);
if (e != 0) {
text_color_set(DW_COLOR_ERROR);
perror("Could not create GPS NMEA listening thread.");
return (-1);
}
#endif
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Could not open serial port %s for GPS receiver.\n", pconfig->gpsnmea_port);
return (-1);
}
/* success */
return (1);
} /* end dwgpsnmea_init */
/*-------------------------------------------------------------------
*
* Name: read_gpsnmea_thread
*
* Purpose: Read information from GPS, as it becomes available, and
* store it for later retrieval by dwgps_read.
*
* Inputs: fd - File descriptor for serial port.
*
* Description: This version reads from serial port and parses the
* NMEA sentences.
*
*--------------------------------------------------------------------*/
#define TIMEOUT 5
#if __WIN32__
static unsigned __stdcall read_gpsnmea_thread (void *arg)
#else
static void * read_gpsnmea_thread (void *arg)
#endif
{
MYFDTYPE fd = (MYFDTYPE)(long)arg;
// Maximum length of message from GPS receiver is 82 according to some people.
// Make buffer considerably larger to be safe.
#define NMEA_MAX_LEN 160
char gps_msg[NMEA_MAX_LEN];
int gps_msg_len = 0;
dwgps_info_t info;
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("read_gpsnmea_thread (%d)\n", (int)(long)arg);
}
dwgps_clear (&info);
info.fix = DWFIX_NOT_SEEN; /* clear not init state. */
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dwgps_print ("GPSNMEA: ", &info);
}
dwgps_set_data (&info);
while (1) {
int ch;
ch = serial_port_get1(fd);
if (ch < 0) {
/* This might happen if a USB device is unplugged. */
/* I can't imagine anything that would cause it with */
/* a normal serial port. */
text_color_set(DW_COLOR_ERROR);
dw_printf ("----------------------------------------------\n");
dw_printf ("GPSNMEA: Lost communication with GPS receiver.\n");
dw_printf ("----------------------------------------------\n");
info.fix = DWFIX_ERROR;
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dwgps_print ("GPSNMEA: ", &info);
}
dwgps_set_data (&info);
// TODO: doesn't exist yet - serial_port_close(fd);
s_gpsnmea_port_fd = MYFDERROR;
break; /* terminate thread. */
}
if (ch == '$') {
// Start of new sentence.
gps_msg_len = 0;
gps_msg[gps_msg_len++] = ch;
gps_msg[gps_msg_len] = '\0';
}
else if (ch == '\r' || ch == '\n') {
if (gps_msg_len >= 6 && gps_msg[0] == '$') {
dwfix_t f;
if (s_debug >= 3) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("%s\n", gps_msg);
}
/* Process sentence. */
if (strncmp(gps_msg, "$GPRMC", 6) == 0) {
f = dwgpsnmea_gprmc (gps_msg, 0, &info.dlat, &info.dlon, &info.speed_knots, &info.track);
if (f == DWFIX_ERROR) {
/* Parse error. Shouldn't happen. Better luck next time. */
text_color_set(DW_COLOR_INFO);
dw_printf ("GPSNMEA: Error parsing $GPRMC sentence.\n");
dw_printf ("%s\n", gps_msg);
}
else if (f == DWFIX_2D) {
if (info.fix != DWFIX_2D && info.fix != DWFIX_3D) {
text_color_set(DW_COLOR_INFO);
dw_printf ("GPSNMEA: Location fix is now available.\n");
info.fix = DWFIX_2D; // Don't know if 2D or 3D. Take minimum.
}
info.timestamp = time(NULL);
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dwgps_print ("GPSNMEA: ", &info);
}
dwgps_set_data (&info);
}
else {
if (info.fix == DWFIX_2D || info.fix == DWFIX_3D) {
text_color_set(DW_COLOR_INFO);
dw_printf ("GPSNMEA: Lost location fix.\n");
}
info.fix = f; /* lost it. */
info.timestamp = time(NULL);
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dwgps_print ("GPSNMEA: ", &info);
}
dwgps_set_data (&info);
}
}
else if (strncmp(gps_msg, "$GPGGA", 6) == 0) {
int nsat;
f = dwgpsnmea_gpgga (gps_msg, 0, &info.dlat, &info.dlon, &info.altitude, &nsat);
/* Only switch between 2D & 3D. */
/* Let GPRMC handle other changes in fix state and data transfer. */
if (f == DWFIX_ERROR) {
/* Parse error. Shouldn't happen. Better luck next time. */
text_color_set(DW_COLOR_INFO);
dw_printf ("GPSNMEA: Error parsing $GPGGA sentence.\n");
dw_printf ("%s\n", gps_msg);
}