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 *.desktop text
*.conf text *.conf text
*.rc text *.rc text
*.spec text
*.bat text
*.1 text
*.md text
COPYING text
Makefile* text
README* text
*.ico binary *.ico binary
*.png binary *.png binary
Makefile* text

4
.gitignore vendored
View File

@ -6,6 +6,10 @@ z*
*~ *~
*.xlsx *.xlsx
*.stackdump *.stackdump
direwolf.conf
*.wav
fsk_fast_filter.h
# Object files # Object files
*.o *.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 ## ## Version 1.3 -- Development snapshot F -- September 2015 ##
### New Feature: ### ### New Feature: ###

View File

@ -2,7 +2,9 @@
# Makefile for Linux version of Dire Wolf. # 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 " "
@echo "Next step - install with:" @echo "Next step - install with:"
@echo " " @echo " "
@ -12,6 +14,9 @@ all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx
CC := gcc CC := gcc
CFLAGS := -O3 -pthread -Igeotranz CFLAGS := -O3 -pthread -Igeotranz
LDFLAGS := -lm -lpthread -lrt
# #
# The DSP filters spend a lot of time spinning around in little # 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 # If you are planning to distribute the binary version to other
# people (in some ham radio software collection, RPM, or DEB package), # 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. # cause compatibility issues for those with older computers.
# #
#CFLAGS += -D_FORTIFY_SOURCE
# If you want to use OSS (for FreeBSD, OpenBSD) instead of # If you want to use OSS (for FreeBSD, OpenBSD) instead of
# ALSA (for Linux), comment out (or remove) the two lines below. # ALSA (for Linux), comment out (or remove) the two lines below.
CFLAGS += -DUSE_ALSA 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 enable_gpsd := $(wildcard /usr/include/gps.h)
#LDLIBS += -lgps ifneq ($(enable_gpsd),)
CFLAGS += -DENABLE_GPSD
LDFLAGS += -lgps
endif
# Name of current directory. # Name of current directory.
@ -210,7 +218,10 @@ LDLIBS += -lasound
z := $(notdir ${CURDIR}) 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 \ 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 \ 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 \ 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 \ 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 \ 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 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. # Optimization for slow processors.
@ -231,10 +249,100 @@ demod_afsk.o : fsk_fast_filter.h
fsk_fast_filter.h : demod_afsk.c 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 ./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. # UTM, USNG, MGRS conversions.
@ -277,7 +385,19 @@ strlcat.o : misc/strlcat.c
# ------------------------------------- Installation ----------------------------------
# Generate apprpriate sample configuration file for this platform. # 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 direwolf.conf : generic.conf
egrep '^C|^L' generic.conf | cut -c2-999 > direwolf.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, # from source, that is not a standard part of the operating system,
# should go in /usr/local/bin. # 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. # installation location should be /usr/bin.
# This is a step in the right direction but not sufficient to use /usr instead. # 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 INSTALLDIR := /usr/local
# Command to "install" to system directories. Use "ginstall" for Mac.
INSTALL=install
# direwolf.desktop was previously handcrafted for the Raspberry Pi. # direwolf.desktop was previously handcrafted for the Raspberry Pi.
# It was hardcoded with lxterminal, /home/pi, and so on. # 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' >> $@ @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. # 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 .PHONY: install
install direwolf $(INSTALLDIR)/bin install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png direwolf.desktop
install decode_aprs $(INSTALLDIR)/bin #
install text2tt $(INSTALLDIR)/bin # Applications, not installed with package manager, normally go in /usr/local/bin.
install tt2text $(INSTALLDIR)/bin # /usr/bin is used instead when installing from .DEB or .RPM package.
install ll2utm $(INSTALLDIR)/bin #
install utm2ll $(INSTALLDIR)/bin $(INSTALL) direwolf $(INSTALLDIR)/bin
install aclients $(INSTALLDIR)/bin $(INSTALL) decode_aprs $(INSTALLDIR)/bin
install log2gpx $(INSTALLDIR)/bin $(INSTALL) text2tt $(INSTALLDIR)/bin
install gen_packets $(INSTALLDIR)/bin $(INSTALL) tt2text $(INSTALLDIR)/bin
install atest $(INSTALLDIR)/bin $(INSTALL) ll2utm $(INSTALLDIR)/bin
install ttcalc $(INSTALLDIR)/bin $(INSTALL) utm2ll $(INSTALLDIR)/bin
install dwespeak.sh $(INSTALLDIR)/bin $(INSTALL) aclients $(INSTALLDIR)/bin
install telemetry-toolkit/*.p[ly] $(INSTALLDIR)/bin $(INSTALL) log2gpx $(INSTALLDIR)/bin
install -D --mode=644 tocalls.txt /usr/share/direwolf/tocalls.txt $(INSTALL) gen_packets $(INSTALLDIR)/bin
install -D --mode=644 symbols-new.txt /usr/share/direwolf/symbols-new.txt $(INSTALL) atest $(INSTALLDIR)/bin
install -D --mode=644 symbolsX.txt /usr/share/direwolf/symbolsX.txt $(INSTALL) ttcalc $(INSTALLDIR)/bin
install -D --mode=644 dw-icon.png /usr/share/direwolf/dw-icon.png $(INSTALL) dwespeak.sh $(INSTALLDIR)/bin
install -D --mode=644 direwolf.desktop /usr/share/applications/direwolf.desktop #
install -D --mode=644 README.md $(INSTALLDIR)/share/doc/direwolf/README.md # Telemetry Toolkit executables. Other .conf and .txt files will go into doc directory.
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) telemetry-toolkit/telem-balloon.pl $(INSTALLDIR)/bin
install -D --mode=644 LICENSE-other.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-other.txt $(INSTALL) telemetry-toolkit/telem-bits.pl $(INSTALLDIR)/bin
install -D --mode=644 doc/User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf $(INSTALL) telemetry-toolkit/telem-data.pl $(INSTALLDIR)/bin
install -D --mode=644 doc/Raspberry-Pi-APRS.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf $(INSTALL) telemetry-toolkit/telem-data91.pl $(INSTALLDIR)/bin
install -D --mode=644 doc/Raspberry-Pi-APRS-Tracker.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf $(INSTALL) telemetry-toolkit/telem-eqns.pl $(INSTALLDIR)/bin
install -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf $(INSTALL) telemetry-toolkit/telem-parm.pl $(INSTALLDIR)/bin
install -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf $(INSTALL) telemetry-toolkit/telem-unit.pl $(INSTALLDIR)/bin
install -D --mode=644 man1/aclients.1 $(INSTALLDIR)/man/man1/aclients.1 $(INSTALL) telemetry-toolkit/telem-volts.py $(INSTALLDIR)/bin
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 # Misc. data such as "tocall" to system mapping.
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 tocalls.txt /usr/share/direwolf/tocalls.txt
install -D --mode=644 man1/ll2utm.1 $(INSTALLDIR)/man/man1/ll2utm.1 $(INSTALL) -D --mode=644 symbols-new.txt /usr/share/direwolf/symbols-new.txt
install -D --mode=644 man1/log2gpx.1 $(INSTALLDIR)/man/man1/log2gpx.1 $(INSTALL) -D --mode=644 symbolsX.txt /usr/share/direwolf/symbolsX.txt
install -D --mode=644 man1/text2tt.1 $(INSTALLDIR)/man/man1/text2tt.1 $(INSTALL) -D --mode=644 dw-icon.png /usr/share/direwolf/dw-icon.png
install -D --mode=644 man1/tt2text.1 $(INSTALLDIR)/man/man1/tt2text.1 $(INSTALL) -D --mode=644 direwolf.desktop /usr/share/applications/direwolf.desktop
install -D --mode=644 man1/utm2ll.1 $(INSTALLDIR)/man/man1/utm2ll.1 #
# 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 " "
@echo "If this is your first install, not an upgrade, type this to put a copy" @echo "If this is your first install, not an upgrade, type this to put a copy"
@echo "of the sample configuration file (direwolf.conf) in your home directory:" @echo "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 " " @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. # These would be done as ordinary user.
# The Raspberry Pi has ~/Desktop but Ubuntu does not. # The Raspberry Pi has ~/Desktop but Ubuntu does not.
@ -409,41 +567,123 @@ install-rpi : dw-start.sh
# Separate application to decode raw data. # ---------------------------------- Automated Smoke Test --------------------------------
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
# Convert between text and touch tone representation. # Combine some unit tests into a single regression sanity check.
text2tt : tt_text.c
$(CC) $(CFLAGS) -DENC_MAIN -o text2tt tt_text.c
tt2text : tt_text.c
$(CC) $(CFLAGS) -DDEC_MAIN -o tt2text tt_text.c
# Convert between Latitude/Longitude and UTM coordinates. check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest check-modem1200 check-modem300 check-modem9600
ll2utm : ll2utm.c geotranz.a textcolor.o misc.a # Can we encode and decode at popular data rates?
$(CC) $(CFLAGS) -o $@ $^ -lm
utm2ll : utm2ll.c geotranz.a textcolor.o misc.a check-modem1200 : gen_packets atest
$(CC) $(CFLAGS) -o $@ $^ -lm 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 # Unit test for inner digipeater algorithm
$(CC) $(CFLAGS) -o $@ $^ -lm
.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 .PHONY : ttest
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -lm 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.o : tune.h
demod_afsk.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 \ 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 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 ./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 # ------------------------------- Source distribution ---------------------------------
$(CC) $(CFLAGS) -DTEST -o $@ $^
./dtest # 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 .PHONY: dist-src
dist-src : README.md CHANGES.md dist-src : README.md CHANGES.md
@ -556,22 +735,17 @@ dist-src : README.md CHANGES.md
$z/telemetry-toolkit/* ) $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 : .PHONY: clean
cp tocalls.txt tocalls.txt~ clean :
wget http://www.aprs.org/aprs11/tocalls.txt -O tocalls.txt rm -f $(APPS) fsk_fast_filter.h *.o *.a direwolf.desktop
diff tocalls.txt~ tocalls.txt echo " " > tune.h
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 depend : $(wildcard *.c)
cp symbolsX.txt symbolsX.txt~ makedepend -f $(lastword $(MAKEFILE_LIST)) -- $(CFLAGS) -- $^
wget http://www.aprs.org/symbols/symbolsX.txt -O symbolsX.txt
diff symbolsX.txt~ symbolsX.txt
# #

View File

@ -2,6 +2,14 @@
# Makefile for Macintosh 10.8+ version of Dire Wolf. # 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 all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc direwolf.conf
@echo " " @echo " "
@echo "Next step install with: " @echo "Next step install with: "
@ -179,7 +187,7 @@ CFLAGS += -DUSE_PORTAUDIO -I/opt/local/include
# Not available for MacOSX. # Not available for MacOSX.
# Although MacPorts has gpsd, wonder if it's the same thing. # Although MacPorts has gpsd, wonder if it's the same thing.
#CFLAGS += -DENABLE_GPS #CFLAGS += -DENABLE_GPSD
#LDLIBS += -lgps #LDLIBS += -lgps
# Name of current directory. # 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 \ geotranz.a hdlc_rec.o hdlc_rec2.o hdlc_send.o igate.o kiss_frame.o \
kiss.o kissnet.o latlong.o latlong.o log.o morse.o multi_modem.o \ kiss.o 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 \ 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 $(CC) $(CFLAGS) -o $@ $^ -lpthread $(LDLIBS) -lm
@ -264,45 +273,89 @@ INSTALLDIR := /usr/local
# Needs to be run as root or with sudo. # Needs to be run as root or with sudo.
# TODO: Review file locations. # TODO: Review file locations.
install : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets \ # Command to "install" to system directories. "install" for Linux. "ginstall" for Mac.
tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png
ginstall direwolf $(INSTALLDIR)/bin INSTALL=ginstall
ginstall decode_aprs $(INSTALLDIR)/bin
ginstall text2tt $(INSTALLDIR)/bin .PHONY: install
ginstall tt2text $(INSTALLDIR)/bin install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png direwolf.desktop
ginstall ll2utm $(INSTALLDIR)/bin #
ginstall utm2ll $(INSTALLDIR)/bin # Applications, not installed with package manager, normally go in /usr/local/bin.
ginstall aclients $(INSTALLDIR)/bin # /usr/bin is used instead when installing from .DEB or .RPM package.
ginstall log2gpx $(INSTALLDIR)/bin #
ginstall gen_packets $(INSTALLDIR)/bin $(INSTALL) direwolf $(INSTALLDIR)/bin
ginstall atest $(INSTALLDIR)/bin $(INSTALL) decode_aprs $(INSTALLDIR)/bin
ginstall ttcalc $(INSTALLDIR)/bin $(INSTALL) text2tt $(INSTALLDIR)/bin
ginstall dwespeak.sh $(INSTALLDIR)/bin $(INSTALL) tt2text $(INSTALLDIR)/bin
ginstall telemetry-toolkit/*.p[ly] $(INSTALLDIR)/bin $(INSTALL) ll2utm $(INSTALLDIR)/bin
ginstall -D --mode=644 tocalls.txt $(INSTALLDIR)/share/direwolf/tocalls.txt $(INSTALL) utm2ll $(INSTALLDIR)/bin
ginstall -D --mode=644 symbols-new.txt $(INSTALLDIR)/share/direwolf/symbols-new.txt $(INSTALL) aclients $(INSTALLDIR)/bin
ginstall -D --mode=644 symbolsX.txt $(INSTALLDIR)/share/direwolf/symbolsX.txt $(INSTALL) log2gpx $(INSTALLDIR)/bin
ginstall -D --mode=644 dw-icon.png $(INSTALLDIR)/share/direwolf/dw-icon.png $(INSTALL) gen_packets $(INSTALLDIR)/bin
ginstall -D --mode=644 README.md $(INSTALLDIR)/share/doc/direwolf/README.md $(INSTALL) atest $(INSTALLDIR)/bin
ginstall -D --mode=644 CHANGES.md $(INSTALLDIR)/share/doc/direwolf/CHANGES.md $(INSTALL) ttcalc $(INSTALLDIR)/bin
ginstall -D --mode=644 direwolf.conf $(INSTALLDIR)/share/direwolf/config/direwolf.conf $(INSTALL) dwespeak.sh $(INSTALLDIR)/bin
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 # Telemetry Toolkit executables. Other .conf and .txt files will go into doc directory.
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 $(INSTALL) telemetry-toolkit/telem-balloon.pl $(INSTALLDIR)/bin
ginstall -D --mode=644 doc/Raspberry-Pi-APRS-Tracker.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf $(INSTALL) telemetry-toolkit/telem-bits.pl $(INSTALLDIR)/bin
ginstall -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf $(INSTALL) telemetry-toolkit/telem-data.pl $(INSTALLDIR)/bin
ginstall -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf $(INSTALL) telemetry-toolkit/telem-data91.pl $(INSTALLDIR)/bin
ginstall -D --mode=644 man1/aclients.1 $(INSTALLDIR)/man/man1/aclients.1 $(INSTALL) telemetry-toolkit/telem-eqns.pl $(INSTALLDIR)/bin
ginstall -D --mode=644 man1/atest.1 $(INSTALLDIR)/man/man1/atest.1 $(INSTALL) telemetry-toolkit/telem-parm.pl $(INSTALLDIR)/bin
ginstall -D --mode=644 man1/decode_aprs.1 $(INSTALLDIR)/man/man1/decode_aprs.1 $(INSTALL) telemetry-toolkit/telem-unit.pl $(INSTALLDIR)/bin
ginstall -D --mode=644 man1/direwolf.1 $(INSTALLDIR)/man/man1/direwolf.1 $(INSTALL) telemetry-toolkit/telem-volts.py $(INSTALLDIR)/bin
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 # Misc. data such as "tocall" to system mapping.
ginstall -D --mode=644 man1/log2gpx.1 $(INSTALLDIR)/man/man1/log2gpx.1 #
ginstall -D --mode=644 man1/text2tt.1 $(INSTALLDIR)/man/man1/text2tt.1 $(INSTALL) -D --mode=644 tocalls.txt /usr/share/direwolf/tocalls.txt
ginstall -D --mode=644 man1/tt2text.1 $(INSTALLDIR)/man/man1/tt2text.1 $(INSTALL) -D --mode=644 symbols-new.txt /usr/share/direwolf/symbols-new.txt
ginstall -D --mode=644 man1/utm2ll.1 $(INSTALLDIR)/man/man1/utm2ll.1 $(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 " "
@echo "If this is your first install, not an upgrade, type this to put a copy" @echo "If this is your first install, not an upgrade, type this to put a copy"
@echo "of the sample configuration file (direwolf.conf) in your home directory:" @echo "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. # 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 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) -o decode_aprs -DTEST $^ -lm $(CC) $(CFLAGS) -DDECAMAIN -o $@ $^ -lm
# Convert between text and touch tone representation. # Convert between text and touch tone representation.
text2tt : tt_text.c text2tt : tt_text.c
$(CC) $(CFLAGS) -DENC_MAIN -o text2tt tt_text.c $(CC) $(CFLAGS) -DENC_MAIN -o $@ $^
tt2text : tt_text.c 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. # 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 # 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 \ 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 telemetry.c latlong.c symbols.c textcolor.c tt_text.c fcs_calc.c ax25_pad.c decode_aprs.c dwgpsnmea.o dwgps.o serial_port.o telemetry.c latlong.c symbols.c textcolor.c tt_text.c
$(CC) $(CFLAGS) -o $@ $^ -lm $(CC) $(CFLAGS) -o $@ $^ -lm
# Unit test for inner digipeater algorithm # 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 $@ $^ $(CC) $(CFLAGS) -DTEST -o $@ $^
./dtest ./dtest
# Unit test for APRStt. # 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 $@ $^ $(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. # 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 $(CC) $(CFLAGS) -o $@ $^ -lm
./etest ./tlmtest
# Multiple AGWPE network or serial port clients to test TNCs side by side. # 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/dw-start.sh $z/direwolf.spec \
$z/dwespeak.bat $z/dwespeak.sh \ $z/dwespeak.bat $z/dwespeak.sh \
$z/telemetry-toolkit/* ) $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. # 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.o : fsk_demod_state.h
demod_9600.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 \ 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 \ 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 \ 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 dw-icon.o regex.a misc.a geotranz.a
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32 $(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
@ -95,6 +93,79 @@ fsk_fast_filter.h : demod_afsk.c
./gen_fff > fsk_fast_filter.h ./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. # UTM, USNG, MGRS conversions.
geotranz.a : error_string.o mgrs.o polarst.o tranmerc.o ups.o usng.o utm.o 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 $@ $^ $(CC) $(CFLAGS) -Dbool=int -Dtrue=1 -Dfalse=0 -c -o $@ $^
# There are also a couple other functions in the misc # There are several string functios found in Linux
# subdirectory that are missing on Windows. # but not on Windows. Need to provide our own copy.
misc.a : strsep.o strtok_r.o strcasestr.o strlcpy.o strlcat.o misc.a : strsep.o strtok_r.o strcasestr.o strlcpy.o strlcat.o
ar -cr $@ $^ ar -cr $@ $^
@ -158,41 +229,129 @@ strlcat.o : misc/strlcat.c
$(CC) $(CFLAGS) -I. -c -o $@ $^ $(CC) $(CFLAGS) -I. -c -o $@ $^
# Separate application to decode raw data. # --------------------------------- Automated Smoke Test --------------------------------
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 $^
# Convert between text and touch tone representation. # Combine some unit tests into a single regression sanity check.
text2tt : tt_text.c
$(CC) $(CFLAGS) -DENC_MAIN -o text2tt tt_text.c
tt2text : tt_text.c
$(CC) $(CFLAGS) -DDEC_MAIN -o tt2text tt_text.c
# Convert between Latitude/Longitude and UTM coordinates. 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 $@ $^ $(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 $@ $^ $(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 .PHONY: dtest
log2gpx : log2gpx.c misc.a dtest : digipeater.c pfilter.o ax25_pad.o dedupe.o fcs_calc.o tq.o textcolor.o \
$(CC) $(CFLAGS) -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 # Unit test for telemetry decoding.
$(CC) $(CFLAGS) -o $@ $^
.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. # 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 #./atest -B 9600 noisy96.wav | grep "packets decoded in" >atest.out
echo " " > tune.h 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 # Unit test for IGate
itest : igate.c textcolor.c ax25_pad.c fcs_calc.c misc.a regex.a itest : igate.c textcolor.c ax25_pad.c fcs_calc.c misc.a regex.a
$(CC) $(CFLAGS) -DITEST -o $@ $^ -lwinmm -lws2_32 $(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. # 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. # 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 \ latlong.o encode_aprs.o serial_port.o textcolor.o \
ax25_pad.o fcs_calc.o regex.a \ ax25_pad.o fcs_calc.o \
misc.a 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 $(CC) $(CFLAGS) -DWALK96 -o $@ $^ -lwinmm -lws2_32
#--------------------------------------------------------------
.PHONY: depend .PHONY: depend
depend : $(wildcard *.c) depend : $(wildcard *.c)
@ -318,7 +443,12 @@ clean :
echo " " > tune.h 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. # Left out RPi Tracker due to Comcast upload size limit.
@ -418,7 +548,7 @@ dist-src : README.md CHANGES.md \
unix2dos telemetry-toolkit/telem-volts.conf unix2dos telemetry-toolkit/telem-volts.conf
# Reminders if pdf files are not up to date.
doc/User-Guide.pdf : doc/User-Guide.docx doc/User-Guide.pdf : doc/User-Guide.docx
echo "***** User-Guide.pdf is out of date *****" echo "***** User-Guide.pdf is out of date *****"
@ -448,22 +578,6 @@ backup :
mkdir /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"` mkdir /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"`
cp -r . /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" # The following is updated by "make depend"

View File

@ -71,6 +71,7 @@
#include <assert.h> #include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <string.h> #include <string.h>
#include <time.h>
#include "direwolf.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: case AF_INET:
sa4 = (struct sockaddr_in *)pAddr; sa4 = (struct sockaddr_in *)pAddr;
#if __WIN32__ #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_b2,
sa4->sin_addr.S_un.S_un_b.s_b3, sa4->sin_addr.S_un.S_un_b.s_b3,
sa4->sin_addr.S_un.S_un_b.s_b4); 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: case AF_INET6:
sa6 = (struct sockaddr_in6 *)pAddr; sa6 = (struct sockaddr_in6 *)pAddr;
#if __WIN32__ #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)))[0]),
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[1]), ntohs(((unsigned short *)(&(sa6->sin6_addr)))[1]),
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[2]), 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 #endif
break; break;
default: default:
sprintf (pStringBuf, "Invalid address family!"); snprintf (pStringBuf, StringBufSize, "Invalid address family!");
} }
assert (strlen(pStringBuf) < StringBufSize); assert (strlen(pStringBuf) < StringBufSize);
return pStringBuf; return pStringBuf;
@ -222,20 +223,20 @@ int main (int argc, char *argv[])
char stemp[100]; char stemp[100];
char *p; char *p;
strcpy (stemp, argv[j+1]); strlcpy (stemp, argv[j+1], sizeof(stemp));
p = strtok (stemp, "="); p = strtok (stemp, "=");
if (p == NULL) { if (p == NULL) {
printf ("Internal error 1\n"); printf ("Internal error 1\n");
exit (1); exit (1);
} }
strcpy (hostname[j], "localhost"); strlcpy (hostname[j], "localhost", sizeof(hostname[j]));
strcpy (port[j], p); strlcpy (port[j], p, sizeof(port[j]));
p = strtok (NULL, "="); p = strtok (NULL, "=");
if (p == NULL) { if (p == NULL) {
printf ("Missing description after %s\n", port[j]); printf ("Missing description after %s\n", port[j]);
exit (1); exit (1);
} }
strcpy (description[j], p); strlcpy (description[j], p, sizeof(description[j]));
} }
//printf ("_WIN32_WINNT = %04x\n", _WIN32_WINNT); //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); //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)); memset (&alevel, 0xff, sizeof(alevel));
pp = ax25_from_frame ((unsigned char *)(data+1), mon_cmd.data_len-1, alevel); pp = ax25_from_frame ((unsigned char *)(data+1), mon_cmd.data_len-1, alevel);
assert (pp != NULL); assert (pp != NULL);
ax25_format_addrs (pp, result); ax25_format_addrs (pp, result);
info_len = ax25_get_info (pp, (unsigned char **)(&pinfo)); info_len = ax25_get_info (pp, (unsigned char **)(&pinfo));
pinfo[info_len] = '\0'; pinfo[info_len] = '\0';
strcat (result, pinfo); strlcat (result, pinfo, sizeof(result));
for (p=result; *p!='\0'; p++) { for (p=result; *p!='\0'; p++) {
if (! isprint(*p)) *p = ' '; if (! isprint(*p)) *p = ' ';
} }

362
aprs_tt.c
View File

@ -22,9 +22,14 @@
* *
* Module: aprs_tt.c * 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 * References: This is based upon APRStt (TM) documents with some
* artistic freedom. * artistic freedom.
@ -38,7 +43,7 @@
// TODO: clean up terminolgy. // TODO: clean up terminolgy.
// "Message" has a specific meaning in APRS and this is not it. // "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? // What do we call the parts separated by * key? Entry? Field?
@ -67,9 +72,7 @@
#include "tq.h" #include "tq.h"
#if __WIN32__
char *strtok_r(char *str, const char *delim, char **saveptr);
#endif
// geotranz // geotranz
@ -103,9 +106,11 @@ static int parse_location (char *e);
static int parse_comment (char *e); static int parse_comment (char *e);
static int expand_macro (char *e); static int expand_macro (char *e);
static void raw_tt_data_to_app (int chan, char *msg); 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 * Returns: None
* *
* Description: Process a complete message. * Description: Process a complete tone sequence.
* It should have one or more fields separatedy by * * It should have one or more fields separated by *
* and terminated by a final # like these: * and terminated by a final # like these:
* *
* callsign # * callsign #
@ -312,27 +317,22 @@ static char m_callsign[20]; /* really object name */
*/ */
static char m_symtab_or_overlay; 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 char m_loc_text[24];
static double m_longitude; static double m_longitude; // Set to G_UNKNOWN if not defined.
static double m_latitude; static double m_latitude; // Set to G_UNKNOWN if not defined.
static char m_comment[200]; static char m_comment[200];
static char m_freq[12]; static char m_freq[12];
static char m_mic_e; static char m_mic_e;
static char m_dao[6]; static char m_dao[6];
static int m_ssid; static int m_ssid; // Default 12 for APRStt user.
//#define G_UNKNOWN -999999
void aprs_tt_sequence (int chan, char *msg) void aprs_tt_sequence (int chan, char *msg)
{ {
int err; int err;
char audible_response[1000];
packet_t pp;
char script_response[1000];
#if DEBUG #if DEBUG
@ -367,37 +367,40 @@ void aprs_tt_sequence (int chan, char *msg)
*/ */
err = parse_fields (msg); err = parse_fields (msg);
#if defined(DEBUG) || defined(TT_MAIN) #if defined(DEBUG)
text_color_set(DW_COLOR_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", 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); m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code, m_freq, m_comment, m_latitude, m_longitude, m_dao);
#endif #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) { 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, err = tt_user_heard (m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code,
m_loc_text, m_latitude, m_longitude, m_loc_text, m_latitude, m_longitude,
m_freq, m_comment, m_mic_e, m_dao); m_freq, m_comment, m_mic_e, m_dao);
#endif
} }
/* /*
* If a command / script was supplied, run it now. * If a command / script was supplied, run it now.
* This can do additional processing and provide a custom audible response. * 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)); strlcpy (script_response, "", sizeof(script_response));
if (strlen(tt_config.ttcmd) > 0) { 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. * Use high priority queue for consistent timing.
*/ */
char audible_response[1000];
snprintf (audible_response, sizeof(audible_response), snprintf (audible_response, sizeof(audible_response),
"APRSTT>%s:%s", "APRSTT>%s:%s",
tt_config.response[err].method, tt_config.response[err].method,
(strlen(script_response) > 0) ? script_response : tt_config.response[err].mtext); (strlen(script_response) > 0) ? script_response : tt_config.response[err].mtext);
packet_t pp;
pp = ax25_from_text (audible_response, 0); pp = ax25_from_text (audible_response, 0);
if (pp == NULL) { if (pp == NULL) {
@ -422,6 +429,7 @@ void aprs_tt_sequence (int chan, char *msg)
tq_append (chan, TQ_PRIO_0_HI, pp); tq_append (chan, TQ_PRIO_0_HI, pp);
#endif /* ifndef TT_MAIN */
} /* end aprs_tt_sequence */ } /* end aprs_tt_sequence */
@ -461,14 +469,14 @@ static int parse_fields (char *msg)
//text_color_set(DW_COLOR_DEBUG); //text_color_set(DW_COLOR_DEBUG);
//printf ("parse_fields (%s).\n", msg); //dw_printf ("parse_fields (%s).\n", msg);
strlcpy (stemp, msg, sizeof(stemp)); strlcpy (stemp, msg, sizeof(stemp));
e = strtok_r (stemp, "*#", &save); e = strtok_r (stemp, "*#", &save);
while (e != NULL) { while (e != NULL) {
//text_color_set(DW_COLOR_DEBUG); //text_color_set(DW_COLOR_DEBUG);
//printf ("parse_fields () field = %s\n", e); //dw_printf ("parse_fields () field = %s\n", e);
switch (*e) { switch (*e) {
@ -539,7 +547,7 @@ static int parse_fields (char *msg)
} }
//text_color_set(DW_COLOR_DEBUG); //text_color_set(DW_COLOR_DEBUG);
//printf ("parse_fields () normal return\n"); //dw_printf ("parse_fields () normal return\n");
return (0); return (0);
@ -566,21 +574,23 @@ static int parse_fields (char *msg)
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
#define VALSTRSIZE 20
static int expand_macro (char *e) static int expand_macro (char *e)
{ {
int len; //int len;
int ipat; 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 stemp[MAX_MSG_LEN+1];
char *d, *s; char *d;
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("Macro tone sequence: '%s'\n", e); 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) { if (ipat >= 0) {
@ -723,7 +733,7 @@ static int checksum_not_ok (char *str, int len, char found)
static int parse_callsign (char *e) static int parse_callsign (char *e)
{ {
int len; int len;
int c_length; //int c_length;
char tttemp[40], stemp[30]; char tttemp[40], stemp[30];
assert (*e == 'A'); assert (*e == 'A');
@ -838,8 +848,9 @@ static int parse_callsign (char *e)
static int parse_object_name (char *e) static int parse_object_name (char *e)
{ {
int len; int len;
int c_length; //int c_length;
char tttemp[40], stemp[30]; //char tttemp[40];
//char stemp[30];
assert (e[0] == 'A'); assert (e[0] == 'A');
assert (e[1] == 'A'); assert (e[1] == 'A');
@ -991,19 +1002,15 @@ static int parse_symbol (char *e)
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
/* Average radius of earth in meters. */ /* Average radius of earth in meters. */
#define R 6371000. #define R 6371000.
static int parse_location (char *e) static int parse_location (char *e)
{ {
int len; //int len;
int ipat; 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 x, y, dist, bearing;
double lat0, lon0; double lat0, lon0;
double lat9, lon9; double lat9, lon9;
@ -1022,9 +1029,9 @@ static int parse_location (char *e)
/* If this ever changes, be sure to update corresponding */ /* If this ever changes, be sure to update corresponding */
/* section in process_comment() in decode_aprs.c */ /* 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) { if (ipat >= 0) {
//dw_printf ("ipat=%d, x=%s, y=%s, b=%s, d=%s\n", ipat, xstr, ystr, bstr, dstr); //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_latitude = R2D(lat0);
m_longitude = R2D(lon0); 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 { else {
char message[300]; char message[300];
@ -1190,7 +1197,7 @@ static int parse_location (char *e)
m_latitude = R2D(lat0); m_latitude = R2D(lat0);
m_longitude = R2D(lon0); 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 { else {
char message[300]; char message[300];
@ -1219,7 +1226,7 @@ static int parse_location (char *e)
//text_color_set(DW_COLOR_DEBUG); //text_color_set(DW_COLOR_DEBUG);
//dw_printf ("Case MHEAD: Convert to text \"%s\".\n", stemp); //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); //text_color_set(DW_COLOR_DEBUG);
//dw_printf ("Case MHEAD: Resulting text \"%s\".\n", mh); //dw_printf ("Case MHEAD: Resulting text \"%s\".\n", mh);
@ -1277,6 +1284,8 @@ static int parse_location (char *e)
* APRStt messsage. * APRStt messsage.
* In this case, it should start with "B". * 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. * Outputs: xstr - All digits matching x positions in configuration.
* ystr - y * ystr - y
* zstr - z * 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 ipat; /* Index into patterns from configuration file */
int len; /* Length of pattern we are trying to match. */ 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) { if (strlen(e) == len) {
match = 1; match = 1;
strlcpy (xstr, "", sizeof(xstr)); strlcpy (xstr, "", valstrsize);
strlcpy (ystr, "", sizeof(ystr)); strlcpy (ystr, "", valstrsize);
strlcpy (zstr, "", sizeof(zstr)); strlcpy (zstr, "", valstrsize);
strlcpy (bstr, "", sizeof(bstr)); strlcpy (bstr, "", valstrsize);
strlcpy (dstr, "", sizeof(dstr)); strlcpy (dstr, "", valstrsize);
for (k=0; k<len; k++) { for (k=0; k<len; k++) {
mc = tt_config.ttloc_ptr[ipat].pattern[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]; char stemp[2];
stemp[0] = e[k]; stemp[0] = e[k];
stemp[1] = '\0'; stemp[1] = '\0';
strlcat (xstr, stemp, sizeof(xstr)); strlcat (xstr, stemp, valstrsize);
} }
else { else {
match = 0; match = 0;
@ -1354,7 +1363,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
char stemp[2]; char stemp[2];
stemp[0] = e[k]; stemp[0] = e[k];
stemp[1] = '\0'; stemp[1] = '\0';
strlcat (ystr, stemp, sizeof(ystr)); strlcat (ystr, stemp, valstrsize);
} }
else { else {
match = 0; match = 0;
@ -1366,7 +1375,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
char stemp[2]; char stemp[2];
stemp[0] = e[k]; stemp[0] = e[k];
stemp[1] = '\0'; stemp[1] = '\0';
strlcat (zstr, stemp, sizeof(zstr)); strlcat (zstr, stemp, valstrsize);
} }
else { else {
match = 0; match = 0;
@ -1378,7 +1387,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
char stemp[2]; char stemp[2];
stemp[0] = e[k]; stemp[0] = e[k];
stemp[1] = '\0'; stemp[1] = '\0';
strlcat (bstr, stemp, sizeof(bstr)); strlcat (bstr, stemp, valstrsize);
} }
else { else {
match = 0; match = 0;
@ -1390,7 +1399,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
char stemp[2]; char stemp[2];
stemp[0] = e[k]; stemp[0] = e[k];
stemp[1] = '\0'; stemp[1] = '\0';
strlcat (dstr, stemp, sizeof(dstr)); strlcat (dstr, stemp, valstrsize);
} }
else { else {
match = 0; 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) static int parse_comment (char *e)
{ {
int len; int len;
int n;
assert (*e == 'C'); assert (*e == 'C');
@ -1579,7 +1587,7 @@ static void raw_tt_data_to_app (int chan, char *msg)
* is one line of text. * is one line of text.
* 2 = Also remove any trailing whitespace. * 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. * 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; FILE *fp;
strlcpy (result, "", sizeof(result)); strlcpy (result, "", resultsiz);
fp = popen (cmd, "r"); fp = popen (cmd, "r");
if (fp != NULL) { if (fp != NULL) {
int remaining = maxresult; int remaining = (int)resultsiz;
char *pr = result; char *pr = result;
int err; int err;
while (remaining > 2 && fgets(pr, remaining, fp) != NULL) { while (remaining > 2 && fgets(pr, remaining, fp) != NULL) {
pr = result + strlen(result); pr = result + strlen(result);
remaining = maxresult - strlen(result); remaining = (int)resultsiz - strlen(result);
} }
if ((err = pclose(fp)) != 0) { 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: * 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 * rm a.exe ; gcc tt_text.c -DTT_MAIN -Igeotranz aprs_tt.c latlong.o textcolor.o geotranz.a misc.a ; ./a.exe
* * or
* Bugs: No automatic checking. * make ttest
* Just eyeball it to see if things look right.
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
// TODO: add this to "make check"
#if TT_MAIN #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; } static const struct {
char *toneseq; /* Tone sequence in. */
int dw_printf (const char *fmt, ...) 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)
{ {
va_list args; char stemp[32];
int len;
va_start (args, fmt); text_color_set(DW_COLOR_DEBUG);
len = vprintf (fmt, args); dw_printf ("callsign=\"%s\", ssid=%d, symbol=\"%c%c\", freq=\"%s\", comment=\"%s\", lat=%.4f, lon=%.4f, dao=\"%s\"\n",
va_end (args); m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code, m_freq, m_comment, m_latitude, m_longitude, m_dao);
return (len);
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[]) int main (int argc, char *argv[])
{ {
char text[256], buttons[256];
int n;
dw_printf ("Hello, world!\n");
aprs_tt_init (NULL); aprs_tt_init (NULL);
//if (argc < 2) { error_count = 0;
//dw_printf ("Supply text string on command line.\n");
//exit (1);
//}
/* Callsigns & abbreviations. */ for (test_num = 0; test_num < sizeof(testcases) / sizeof(testcases[0]); test_num++) {
aprs_tt_sequence (0, "A9A2B42A7A7C71#"); /* WB4APR/7 */ text_color_set(DW_COLOR_INFO);
aprs_tt_sequence (0, "A27773#"); /* abbreviated form */ dw_printf ("\nTest case %d: %s\n", test_num, testcases[test_num].toneseq);
/* Example in http://www.aprs.org/aprstt/aprstt-coding24.txt has a bad checksum! */
aprs_tt_sequence (0, "A27776#"); /* Expect error message. */
aprs_tt_sequence (0, "A2A7A7C71#"); /* Spelled suffix, overlay, checksum */ aprs_tt_sequence (0, testcases[test_num].toneseq);
aprs_tt_sequence (0, "A27773#"); /* Suffix digits, overlay, checksum */ }
aprs_tt_sequence (0, "A9A2B26C7D9D71#"); /* WB2OSZ/7 numeric overlay */ if (error_count != 0) {
aprs_tt_sequence (0, "A67979#"); /* abbreviated form */ 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 */ text_color_set(DW_COLOR_REC);
aprs_tt_sequence (0, "A6795A7#"); /* abbreviated form */ dw_printf ("\n\nAll tests passed.\n");
return (EXIT_SUCCESS);
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);
} /* end main */ } /* end main */
#endif #endif
/* end aprs_tt.c */ /* 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); 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 #endif

74
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 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 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[]) int main (int argc, char *argv[])
{ {
@ -171,7 +177,6 @@ int main (int argc, char *argv[])
time_t start_time; time_t start_time;
#if defined(EXPERIMENT_G) || defined(EXPERIMENT_H) #if defined(EXPERIMENT_G) || defined(EXPERIMENT_H)
int j; 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].space_freq = DEFAULT_SPACE_FREQ;
my_audio_config.achan[channel].baud = DEFAULT_BAUD; 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].num_freq = 1;
my_audio_config.achan[channel].offset = 0; my_audio_config.achan[channel].offset = 0;
@ -261,7 +266,7 @@ int main (int argc, char *argv[])
} }
while (1) { while (1) {
int this_option_optind = optind ? optind : 1; //int this_option_optind = optind ? optind : 1;
int option_index = 0; int option_index = 0;
static struct option long_options[] = { static struct option long_options[] = {
{"future1", 1, 0, 0}, {"future1", 1, 0, 0},
@ -272,7 +277,7 @@ int main (int argc, char *argv[])
/* ':' following option character means arg is required. */ /* ':' 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); long_options, &option_index);
if (c == -1) if (c == -1)
break; 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].modem_type = MODEM_AFSK;
my_audio_config.achan[0].mark_freq = 1600; my_audio_config.achan[0].mark_freq = 1600;
my_audio_config.achan[0].space_freq = 1800; 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) { else if (my_audio_config.achan[0].baud > 2400) {
my_audio_config.achan[0].modem_type = MODEM_SCRAMBLE; my_audio_config.achan[0].modem_type = MODEM_SCRAMBLE;
my_audio_config.achan[0].mark_freq = 0; my_audio_config.achan[0].mark_freq = 0;
my_audio_config.achan[0].space_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"); dw_printf ("Using scrambled baseband signal rather than AFSK.\n");
} }
else { else {
@ -315,7 +321,7 @@ int main (int argc, char *argv[])
case 'P': /* -P for modem profile. */ case 'P': /* -P for modem profile. */
dw_printf ("Demodulator profile set to \"%s\"\n", optarg); 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; break;
case 'D': /* -D reduce sampling rate for lower CPU usage. */ case 'D': /* -D reduce sampling rate for lower CPU usage. */
@ -343,11 +349,31 @@ int main (int argc, char *argv[])
} }
break; 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); error_if_less_than = atoi(optarg);
break; 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 '?': case '?':
/* Unknown option message was already printed. */ /* 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) { if (optind >= argc) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Specify .WAV file name on command line.\n"); 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); 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) { if (strncmp(header.riff, "RIFF", 4) != 0 || strncmp(header.wave, "WAVE", 4) != 0) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
@ -421,6 +451,7 @@ int main (int argc, char *argv[])
exit(1); exit(1);
} }
// TODO: Should have proper message, not abort.
assert (format.nchannels == 1 || format.nchannels == 2); assert (format.nchannels == 1 || format.nchannels == 2);
assert (format.wbitspersample == 8 || format.wbitspersample == 16); assert (format.wbitspersample == 8 || format.wbitspersample == 16);
@ -464,11 +495,8 @@ int main (int argc, char *argv[])
if (audio_sample >= 256 * 256) if (audio_sample >= 256 * 256)
e_o_f = 1; e_o_f = 1;
#define ONE_CHAN 1 /* only use one audio channel. */ if (decode_only == 0 && c != 0) continue;
if (decode_only == 1 && c != 1) continue;
#if ONE_CHAN
if (c != 0) continue;
#endif
multi_modem_process_sample(c,audio_sample); multi_modem_process_sample(c,audio_sample);
} }
@ -495,9 +523,14 @@ int main (int argc, char *argv[])
#endif #endif
dw_printf ("%d packets decoded in %d seconds.\n", packets_decoded, (int)(time(NULL) - start_time)); 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); 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); 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; unsigned char *pinfo;
int info_len; int info_len;
int h; int h;
char heard[20]; char heard[AX25_MAX_ADDR_LEN];
char alevel_text[AX25_ALEVEL_TO_TEXT_SIZE]; char alevel_text[AX25_ALEVEL_TO_TEXT_SIZE];
packets_decoded++; packets_decoded++;
ax25_format_addrs (pp, stemp); ax25_format_addrs (pp, stemp);
info_len = ax25_get_info (pp, &pinfo); 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) { if (ax25_get_num_addr(pp) == 0) {
/* Not AX.25. No station to display below. */ /* Not AX.25. No station to display below. */
h = -1; h = -1;
strcpy (heard, ""); strlcpy (heard, "", sizeof(heard));
} }
else { else {
h = ax25_get_heard(pp); 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 (" -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 (" E (default for 1200 baud), F, A+, B+, C+, D+, E+, F+.\n");
dw_printf ("\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 (" wav-file-in is a WAV format audio file.\n");
dw_printf ("\n"); dw_printf ("\n");
dw_printf ("Examples:\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); 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 #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); 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); dw_printf ("audio_open(): using block size of %d\n", ossbuf_size_in_bytes);
#endif #endif
#if 0
/* Original - dies without good explanation. */
assert (ossbuf_size_in_bytes >= 256 && ossbuf_size_in_bytes <= 32768); 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); return (ossbuf_size_in_bytes);
} /* end set_oss_params */ } /* end set_oss_params */

View File

@ -714,6 +714,27 @@ int audio_open (struct audio_s *pa)
* Finally allocate buffer for each direction. * 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); adev[a].inbuf_ptr = malloc(adev[a].inbuf_size_in_bytes);
assert (adev[a].inbuf_ptr != NULL); assert (adev[a].inbuf_ptr != NULL);
adev[a].inbuf_len = 0; 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", dw_printf ("audio_open: calcbufsize (rate=%d, chans=%d, bits=%d) calc size=%d, round up to %d\n",
rate, chans, bits, size1, size2); rate, chans, bits, size1, size2);
#endif #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); return (size2);
} }

View File

@ -44,22 +44,27 @@
* Description: * 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 * * Source Address
*
* * 0-8 Digipeater Addresses (Could there ever be more as a result of * * 0-8 Digipeater Addresses (Could there ever be more as a result of
* digipeaters inserting their own call * digipeaters inserting their own call for
* and decrementing the remaining count in * the tracing feature?
* WIDEn-n, TRACEn-n, etc.? * NO. The limit is 8 when transmitting AX.25 over the
* NO. The limit is 8 when transmitting AX.25 over the radio. * radio.
* However, communication with an IGate server could have * Communication with an IGate server could
* a longer VIA path but that is only in text form, not here.) * have a longer VIA path but that is only in text form,
* not as an AX.25 frame.)
* *
* Each address is composed of: * Each address is composed of:
* *
* * 6 upper case letters or digits, blank padded. * * 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. * * a 7th octet containing the SSID and flags.
* The LSB is always 0 except for the last octet of the address field. * 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. * have the "H" bit set to 1.
* The "H" bit would be set to 1 in the repeated frame. * The "H" bit would be set to 1 in the repeated frame.
* *
* When monitoring, an asterisk is displayed after the last digipeater with * In standard monitoring format, an asterisk is displayed after the last
* the "H" bit set. No asterisk means the source is being heard directly. * 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, * Example, if we can hear all stations involved,
* *
@ -118,6 +127,10 @@
* *
* And, of course, the 2 byte CRC. * 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. * Constructors: ax25_init - Clear everything.
* ax25_from_text - Tear apart a text string * 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) packet_t ax25_from_frame (unsigned char *fbuf, int flen, alevel_t alevel)
#endif #endif
{ {
unsigned char *pf; //unsigned char *pf;
packet_t this_p; packet_t this_p;
int a; //int a;
int addr_bytes; //int addr_bytes;
/* /*
* First make sure we have an acceptable length: * 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) void ax25_insert_addr (packet_t this_p, int n, char *ad)
{ {
int k;
int ssid_temp, heard_temp; int ssid_temp, heard_temp;
char atemp[AX25_MAX_ADDR_LEN]; char atemp[AX25_MAX_ADDR_LEN];
int i; 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) void ax25_remove_addr (packet_t this_p, int n)
{ {
int k;
int expect; int expect;
assert (this_p->magic1 == MAGIC); 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) int ax25_get_num_addr (packet_t this_p)
{ {
unsigned char *pf; //unsigned char *pf;
int a; int a;
int addr_bytes; 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) void ax25_get_addr_no_ssid (packet_t this_p, int n, char *station)
{ {
int ssid;
int i; int i;
assert (this_p->magic1 == MAGIC); assert (this_p->magic1 == MAGIC);
@ -1801,23 +1811,23 @@ static void hex_dump (unsigned char *p, int len)
// TODO: use ax25_frame_type() instead. // 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); } 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) { sprintf (out, "S frame RR: n(r)=%d, p/f=%d", (c>>5)&7, (c>>4)&1); } 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) { sprintf (out, "S frame RNR: 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) { sprintf (out, "S frame REJ: 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) { sprintf (out, "S frame sREJ: 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) { sprintf (out, "U frame SABME: p=%d", (c>>4)&1); } else if ((c & 0xef) == 0x6f) { snprintf (out, outsiz, "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) == 0x2f) { snprintf (out, outsiz, "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) == 0x43) { snprintf (out, outsiz, "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) == 0x0f) { snprintf (out, outsiz, "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) == 0x63) { snprintf (out, outsiz, "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) == 0x87) { snprintf (out, outsiz, "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) == 0x03) { snprintf (out, outsiz, "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) == 0xAF) { snprintf (out, outsiz, "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 if ((c & 0xef) == 0xe3) { snprintf (out, outsiz, "U frame TEST: p/f=%d", (c>>4)&1); }
else { sprintf (out, "Unknown frame type for control = 0x%02x", c); } else { snprintf (out, outsiz, "Unknown frame type for control = 0x%02x", c); }
} }
/* Text description of protocol id octet. */ /* 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]; c = fptr[this_p->num_addr*7];
p = fptr[this_p->num_addr*7+1]; 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 */ if ( (c & 0x01) == 0 || /* I xxxx xxx0 */
c == 0x03 || c == 0x13) { /* UI 000x 0011 */ c == 0x03 || c == 0x13) { /* UI 000x 0011 */

550
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. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
@ -32,17 +28,17 @@
* *
*---------------------------------------------------------------*/ *---------------------------------------------------------------*/
//#define DEBUG 1
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include <time.h> #include <time.h>
#if __WIN32__
#include <windows.h>
#endif
#include "direwolf.h" #include "direwolf.h"
#include "ax25_pad.h" #include "ax25_pad.h"
@ -51,7 +47,6 @@
#include "tq.h" #include "tq.h"
#include "xmit.h" #include "xmit.h"
#include "config.h" #include "config.h"
#include "digipeater.h"
#include "version.h" #include "version.h"
#include "encode_aprs.h" #include "encode_aprs.h"
#include "beacon.h" #include "beacon.h"
@ -62,14 +57,25 @@
#include "aprs_tt.h" // for dw_run_cmd - should relocate someday. #include "aprs_tt.h" // for dw_run_cmd - should relocate someday.
#if __WIN32__
/* /*
* Are we using GPS data? * Windows doesn't have localtime_r.
* Incremented if tracker beacons configured. * It should have the equivalent localtime_s, with opposite parameter
* Cleared if dwgps_init fails. * 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. * 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 audio_s *g_modem_config_p;
static struct misc_config_s *g_misc_config_p; static struct misc_config_s *g_misc_config_p;
static struct digi_config_s *g_digi_config_p;
#if __WIN32__ #if __WIN32__
@ -97,6 +101,11 @@ void beacon_tracker_set_debug (int level)
g_tracker_debug_level = 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. * Purpose: Initialize the beacon process.
* *
* Inputs: pmodem - Aduio device and modem configuration. * Inputs: pmodem - Audio device and modem configuration.
* Used only to find valide channels. * Used only to find valid channels.
*
* pconfig - misc. configuration from config file. * pconfig - misc. configuration from config file.
* pdigi - digipeater configuration from config file.
* TODO: Is this needed?
* *
* *
* Outputs: Remember required information for future use. * Outputs: Remember required information for future use.
* *
* Description: Initialize the queue to be empty and set up other * Description: Do some validity checking on the beacon configuration.
* mechanisms for sharing it between different threads.
* *
* Start up xmit_thread to actually send the packets * Start up beacon_thread to actually send the packets
* at the appropriate time. * 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; time_t now;
int j; 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_modem_config_p = pmodem;
g_misc_config_p = pconfig; g_misc_config_p = pconfig;
g_digi_config_p = pdigi;
/* /*
* Precompute the packet contents so any errors are * 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 (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) { 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: case BEACON_TRACKER:
#if defined(ENABLE_GPS) || defined(DEBUG_SIM) {
g_using_gps++; dwgps_info_t gpsinfo;
#else dwfix_t fix;
fix = dwgps_read (&gpsinfo);
if (fix == DWFIX_NOT_INIT) {
text_color_set(DW_COLOR_ERROR); 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); 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; g_misc_config_p->beacon[j].btype = BEACON_IGNORE;
continue; }
#endif }
break; break;
case BEACON_CUSTOM: 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); 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. * 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)) #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__ #if __WIN32__
@ -362,29 +331,17 @@ static unsigned __stdcall beacon_thread (void *arg)
static void * beacon_thread (void *arg) static void * beacon_thread (void *arg)
#endif #endif
{ {
int j; int j; /* Index into array of beacons. */
time_t earliest; 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. * SmartBeaconing state.
*/ */
time_t sb_prev_time = 0; /* Time of most recent transmission. */ time_t sb_prev_time = 0; /* Time of most recent transmission. */
float sb_prev_course = 0; /* Most recent course reported. */ 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 #if DEBUG
@ -392,30 +349,45 @@ static void * beacon_thread (void *arg)
char hms[20]; char hms[20];
now = time(NULL); now = time(NULL);
localtime_r (&now, &tm); localtime_r (&now, &tm);
strftime (hms, sizeof(hms), "%H:%M:%S", &tm); strftime (hms, sizeof(hms), "%H:%M:%S", &tm);
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("beacon_thread: started %s\n", hms); dw_printf ("beacon_thread: started %s\n", hms);
#endif #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); now = time(NULL);
while (1) { while (1) {
assert (g_misc_config_p->num_beacons >= 1); dwgps_info_t gpsinfo;
/* /*
* Sleep until time for the earliest scheduled or * Sleep until time for the earliest scheduled or
* the soonest we could transmit due to corner pegging. * the soonest we could transmit due to corner pegging.
*/ */
earliest = g_misc_config_p->beacon[0].next; earliest = now + 60 * 60;
for (j=1; j<g_misc_config_p->num_beacons; j++) { for (j=0; j<g_misc_config_p->num_beacons; j++) {
if (g_misc_config_p->beacon[j].btype == BEACON_IGNORE) if (g_misc_config_p->beacon[j].btype != BEACON_IGNORE) {
continue;
earliest = MIN(g_misc_config_p->beacon[j].next, earliest); 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_turn_time, earliest);
earliest = MIN(now + g_misc_config_p->sb_fast_rate, 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. * beacon because corner pegging make it sooner.
*/ */
#if DEBUG_SIM if (number_of_tbeacons > 0) {
FILE *fp;
char cs[40];
fp = fopen ("c:\\cygwin\\tmp\\cs", "r"); dwfix_t fix = dwgps_read (&gpsinfo);
if (fp != NULL) { float my_speed_mph = DW_KNOTS_TO_MPH(gpsinfo.speed_knots);
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);
if (g_tracker_debug_level >= 1) { if (g_tracker_debug_level >= 1) {
struct tm tm; struct tm tm;
char hms[20]; char hms[20];
localtime_r (&now, &tm); localtime_r (&now, &tm);
strftime (hms, sizeof(hms), "%H:%M:%S", &tm); strftime (hms, sizeof(hms), "%H:%M:%S", &tm);
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
if (fix == 3) { 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) { 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 { else {
dw_printf ("%s No GPS fix\n", hms); 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. */ /* Don't complain here for no fix. */
/* Possibly at the point where about to transmit. */ /* Possibly at the point where about to transmit. */
}
#endif
/* /*
* Run SmartBeaconing calculation if configured and GPS data available. * 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) { time_t tnext = sb_calculate_next_time (now,
sb_every = g_misc_config_p->sb_fast_rate; DW_KNOTS_TO_MPH(gpsinfo.speed_knots), gpsinfo.track,
if (g_tracker_debug_level >= 2) { sb_prev_time, sb_prev_course);
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);
}
}
#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++) { for (j=0; j<g_misc_config_p->num_beacons; j++) {
if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) { if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) {
g_misc_config_p->beacon[j].next = now; /* 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 */ } /* Update next time if sooner. */
} /* is moving */
} /* apply SmartBeaconing */ } /* apply SmartBeaconing */
} /* tbeacon(s) configured. */
/*
* Send if the time has arrived.
*/
for (j=0; j<g_misc_config_p->num_beacons; j++) { for (j=0; j<g_misc_config_p->num_beacons; j++) {
if (g_misc_config_p->beacon[j].btype == BEACON_IGNORE) 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) { 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. */ int strict = 1; /* Strict packet checking because they will go over air. */
char stemp[20]; char stemp[20];
char info[AX25_MAX_INFO_LEN]; char info[AX25_MAX_INFO_LEN];
char beacon_text[AX25_MAX_PACKET_LEN]; char beacon_text[AX25_MAX_PACKET_LEN];
packet_t pp = NULL; packet_t pp = NULL;
char mycall[AX25_MAX_ADDR_LEN]; char mycall[AX25_MAX_ADDR_LEN];
int alt_ft;
char super_comment[AX25_MAX_INFO_LEN]; // Fixed part + any dynamic part. 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) { if (strlen(mycall) == 0 || strcmp(mycall, "NOCALL") == 0) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("MYCALL not set for beacon in config file line %d.\n", g_misc_config_p->beacon[j].lineno); dw_printf ("MYCALL not set for beacon in config file line %d.\n", 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. */ /* 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) { if (k > 0) {
strlcat (super_comment, var_comment, sizeof(super_comment)); strlcat (super_comment, var_comment, sizeof(super_comment));
} }
@ -663,18 +750,16 @@ static void * beacon_thread (void *arg)
case BEACON_POSITION: 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,
encode_position (g_misc_config_p->beacon[j].messaging, (int)roundf(DW_METERS_TO_FEET(g_misc_config_p->beacon[j].alt_m)),
g_misc_config_p->beacon[j].compress, g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon, alt_ft,
g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, 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, 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, g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
super_comment, super_comment,
info, sizeof(info)); info, sizeof(info));
strlcat (beacon_text, info, sizeof(beacon_text)); strlcat (beacon_text, info, sizeof(beacon_text));
g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
break; break;
case BEACON_OBJECT: 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, 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].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, 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, 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)); info, sizeof(info));
strlcat (beacon_text, info, sizeof(beacon_text)); strlcat (beacon_text, info, sizeof(beacon_text));
g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
break; break;
case BEACON_TRACKER: case BEACON_TRACKER:
if (fix >= 2) { if (gpsinfo->fix >= DWFIX_2D) {
int coarse; /* APRS encoder wants 1 - 360. */
/* 0 means none or unknown. */
coarse = (int)roundf(my_course); int coarse; /* Round to nearest integer. retaining unknown state. */
if (coarse == 0) { int my_alt_ft;
coarse = 360;
/* 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));
} }
encode_position (g_misc_config_p->beacon[j].messaging,
g_misc_config_p->beacon[j].compress, coarse = G_UNKNOWN;
my_lat, my_lon, my_alt_ft, 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].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, 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, g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
super_comment, super_comment,
info, sizeof(info)); info, sizeof(info));
strlcat (beacon_text, info, sizeof(beacon_text)); 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. */ /* Write to log file for testing. */
/* The idea is to run log2gpx and map the result rather than */ /* The idea is to run log2gpx and map the result rather than */
/* actually transmitting and relying on someone else to receive */ /* 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)); strlcpy (A.g_src, mycall, sizeof(A.g_src));
A.g_symbol_table = g_misc_config_p->beacon[j].symtab; A.g_symbol_table = g_misc_config_p->beacon[j].symtab;
A.g_symbol_code = g_misc_config_p->beacon[j].symbol; A.g_symbol_code = g_misc_config_p->beacon[j].symbol;
A.g_lat = my_lat; A.g_lat = gpsinfo->dlat;
A.g_lon = my_lon; A.g_lon = gpsinfo->dlon;
A.g_speed = DW_KNOTS_TO_MPH(my_speed_knots); A.g_speed_mph = DW_KNOTS_TO_MPH(gpsinfo->speed_knots);
A.g_course = coarse; 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. */ /* Fake channel of 999 to distinguish from real data. */
memset (&alevel, 0, sizeof(alevel)); memset (&alevel, 0, sizeof(alevel));
@ -755,8 +835,7 @@ static void * beacon_thread (void *arg)
} }
} }
else { else {
g_misc_config_p->beacon[j].next = now + 2; return; /* No fix. Skip this time. */
continue; /* No fix. Try again in a couple seconds. */
} }
break; break;
@ -770,12 +849,11 @@ static void * beacon_thread (void *arg)
} }
else if (g_misc_config_p->beacon[j].custom_infocmd != NULL) { else if (g_misc_config_p->beacon[j].custom_infocmd != NULL) {
char info_part[AX25_MAX_INFO_LEN]; char info_part[AX25_MAX_INFO_LEN];
char *p;
int k; int k;
/* Run given command to obtain the info part for packet. */ /* 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) { if (k > 0) {
strlcat (beacon_text, info_part, sizeof(beacon_text)); 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__); dw_printf ("Internal error. custom_info is null. %s %d\n", __FILE__, __LINE__);
strlcpy (beacon_text, "", sizeof(beacon_text)); // abort! strlcpy (beacon_text, "", sizeof(beacon_text)); // abort!
} }
g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
break; break;
case BEACON_IGNORE: case BEACON_IGNORE:
@ -803,7 +880,7 @@ static void * beacon_thread (void *arg)
* Parse monitor format into form for transmission. * Parse monitor format into form for transmission.
*/ */
if (strlen(beacon_text) == 0) { if (strlen(beacon_text) == 0) {
continue; return;
} }
pp = ax25_from_text (beacon_text, strict); pp = ax25_from_text (beacon_text, strict);
@ -819,11 +896,9 @@ static void * beacon_thread (void *arg)
case SENDTO_IGATE: case SENDTO_IGATE:
#if 1
text_color_set(DW_COLOR_XMIT); text_color_set(DW_COLOR_XMIT);
dw_printf ("[ig] %s\n", beacon_text); dw_printf ("[ig] %s\n", beacon_text);
#endif
igate_send_rec_packet (0, pp); igate_send_rec_packet (0, pp);
ax25_delete (pp); ax25_delete (pp);
break; break;
@ -849,12 +924,7 @@ static void * beacon_thread (void *arg)
dw_printf ("%s\n", beacon_text); 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 */ /* end beacon.c */

View File

@ -1,6 +1,6 @@
/* beacon.h */ /* 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); void beacon_tracker_set_debug (int level);

172
config.c
View File

@ -42,10 +42,8 @@
#include <ctype.h> #include <ctype.h>
#include <math.h> #include <math.h>
#if __WIN32__ #if ENABLE_GPSD
//#include "pthreads/pthread.h" #include <gps.h> /* for DEFAULT_GPSD_PORT (2947) */
#else
#include <pthread.h>
#endif #endif
#include "ax25_pad.h" #include "ax25_pad.h"
@ -184,13 +182,12 @@ static int alllettersorpm(char *p)
/* Acceptable symbols to separate degrees & minutes. */ /* Acceptable symbols to separate degrees & minutes. */
/* Degree symbol is not in ASCII so documentation says to use "^" instead. */ /* Degree symbol is not in ASCII so documentation says to use "^" instead. */
/* Some wise guy will try to use degree symbol. */ /* 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 DEG1 '^'
#define DEG2 0xb0 /* ISO Latin1 */ #define DEG2 0xb0 /* ISO Latin1 */
#define DEG3 0xf8 /* Microsoft code page 437 */ #define DEG3 0xf8 /* Microsoft code page 437 */
// TODO: recognize UTF-8 degree symbol.
enum parse_ll_which_e { LAT, LON }; 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 cmd[MAXCMDLEN];
static char token[MAXCMDLEN]; static char token[MAXCMDLEN];
static char *nextp = NULL;
static char *c; // current position in cmd. static char *c; // current position in cmd.
char *s, *t; char *s, *t;
int in_quotes; 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, DEFAULT_NULLMODEM, sizeof(p_misc_config->nullmodem));
strlcpy (p_misc_config->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->nmea_port, "", sizeof(p_misc_config->nmea_port));
strlcpy (p_misc_config->logdir, "", sizeof(p_misc_config->logdir)); 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. */ /* First channel of device is valid. */
p_audio_config->achan[ADEVFIRSTCHAN(adevice)].valid = 1; 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));
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));
t = split(NULL,0); t = split(NULL,0);
if (t != NULL) { 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. */ /* First channel of device is valid. */
p_audio_config->achan[ADEVFIRSTCHAN(adevice)].valid = 1; 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) { else if (strcasecmp(t, "PAODEVICE") == 0) {
adevice = 0; adevice = 0;
@ -951,7 +948,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
/* First channel of device is valid. */ /* First channel of device is valid. */
p_audio_config->achan[ADEVFIRSTCHAN(adevice)].valid = 1; 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; 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++) { for (p = p_audio_config->achan[c].mycall; *p != '\0'; p++) {
if (islower(*p)) { if (islower(*p)) {
@ -1082,6 +1079,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
} }
} }
// TODO: additional checks if valid. // 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); t = split(NULL,0);
if (strlen(p_audio_config->achan[channel].profiles) > 1 && t != NULL) { if (strlen(p_audio_config->achan[channel].profiles) > 1 && t != NULL) {
text_color_set(DW_COLOR_ERROR); 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) + - */ else if (alllettersorpm(t)) { /* profile of letter(s) + - */
// Will be validated later. // 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 */ 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 catches disallowed combination of + and @. */
/* A later place sets /n for 300 baud if not specified by user. */ /* 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) { else if (strcasecmp(t, "PTT") == 0 || strcasecmp(t, "DCD") == 0) {
//int n;
int ot; int ot;
char otname[8]; char otname[8];
@ -1729,7 +1726,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
*/ */
else if (strcasecmp(t, "SPEECH") == 0) { else if (strcasecmp(t, "SPEECH") == 0) {
int n;
t = split(NULL,0); t = split(NULL,0);
if (t == NULL) { if (t == NULL) {
text_color_set(DW_COLOR_ERROR); 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) { else if (strcasecmp(t, "regen") == 0) {
int from_chan, to_chan; int from_chan, to_chan;
int e;
char message[100];
t = split(NULL,0); 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) { else if (strcasecmp(t, "FILTER") == 0) {
int from_chan, to_chan; int from_chan, to_chan;
int e;
char message[100];
t = split(NULL,0); 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) { else if (strcasecmp(t, "TTCORRAL") == 0) {
//int n;
t = split(NULL,0); t = split(NULL,0);
if (t == NULL) { if (t == NULL) {
text_color_set(DW_COLOR_ERROR); 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; struct ttloc_s *tl;
int j; int j;
int znum;
char *zlet;
double dlat, dlon; double dlat, dlon;
long lerr; long lerr;
@ -2485,8 +2476,6 @@ void config_init (char *fname, struct audio_s *p_audio_config,
struct ttloc_s *tl; struct ttloc_s *tl;
int j; int j;
int znum;
char *zlet;
int num_x, num_y; int num_x, num_y;
double lat, lon; double lat, lon;
long lerr; long lerr;
@ -2679,7 +2668,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
p_tt_config->ttloc_len--; p_tt_config->ttloc_len--;
continue; 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); text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: TTMHEAD prefix not a valid DTMF sequence.\n", line); dw_printf ("Line %d: TTMHEAD prefix not a valid DTMF sequence.\n", line);
p_tt_config->ttloc_len--; p_tt_config->ttloc_len--;
@ -2802,7 +2791,6 @@ void config_init (char *fname, struct audio_s *p_audio_config,
struct ttloc_s *tl; struct ttloc_s *tl;
int j; int j;
//char ch;
int p_count[3], d_count[3]; int p_count[3], d_count[3];
int tt_error = 0; int tt_error = 0;
@ -2959,8 +2947,6 @@ void config_init (char *fname, struct audio_s *p_audio_config,
if (*pi == '}') { if (*pi == '}') {
char symtab; char symtab;
char symbol; char symbol;
char overlay;
char symdest[8];
*ps = '\0'; *ps = '\0';
@ -2975,7 +2961,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
// Convert symtab(overlay) & symbol to tone sequence. // 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); //text_color_set(DW_COLOR_DEBUG);
//dw_printf ("DEBUG config file Line %d: AB{%s} -> %s\n", line, stemp, ttemp); //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) { if (t != NULL) {
// TODO: Should do some validity checking on the path. // 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. while (*t == ' ' || *t == '\t') t++; // remove leading white space.
strncpy (p_tt_config->status[status_num], t, TT_MTEXT_LEN); strlcpy (p_tt_config->status[status_num], t, sizeof(p_tt_config->status[status_num]));
p_tt_config->status[status_num][TT_MTEXT_LEN-1] = '\0';
} }
@ -3260,7 +3244,6 @@ void config_init (char *fname, struct audio_s *p_audio_config,
*/ */
else if (strcasecmp(t, "TTCMD") == 0) { else if (strcasecmp(t, "TTCMD") == 0) {
int status_num;
t = split(NULL,1); t = split(NULL,1);
if (t == NULL) { if (t == NULL) {
@ -3269,9 +3252,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
continue; continue;
} }
strncpy (p_tt_config->ttcmd, t, sizeof(p_tt_config->ttcmd)); strlcpy (p_tt_config->ttcmd, t, sizeof(p_tt_config->ttcmd));
p_tt_config->ttcmd[sizeof(p_tt_config->ttcmd)-1] = '\0';
} }
@ -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); dw_printf ("Line %d: Missing IGate server name for IGSERVER command.\n", line);
continue; 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. */ /* 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); 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); //exit (0);
} }
@ -3347,7 +3328,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
continue; continue;
} }
// TODO: Wouldn't hurt to do validity checking of format. // 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); t = split(NULL,0);
if (t == NULL) { 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); dw_printf ("Line %d: Missing passcode for IGLOGIN command.\n", line);
continue; 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) { if (t != NULL) {
char *p; char *p;
p_igate_config->tx_via[0] = ','; 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++) { for (p = p_igate_config->tx_via; *p != '\0'; p++) {
if (islower(*p)) { if (islower(*p)) {
*p = toupper(*p); /* silently force upper case. */ *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) { else if (strcasecmp(t, "IGFILTER") == 0) {
//int n;
t = split(NULL,1); /* Take rest of line as one string. */ 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; continue;
} }
else { 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. * NMEA - Device name for communication with NMEA device.
* Wasn't documented will probably use WAYPOINT instead.
*/ */
else if (strcasecmp(t, "nmea") == 0) { else if (strcasecmp(t, "nmea") == 0) {
t = split(NULL,0); t = split(NULL,0);
@ -3539,7 +3581,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
continue; continue;
} }
else { 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; continue;
} }
else { 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); t = split(NULL,0);
if (t != NULL) { if (t != NULL) {
@ -3591,21 +3633,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
strcasecmp(t, "OBEACON") == 0 || strcasecmp(t, "OBEACON") == 0 ||
strcasecmp(t, "TBEACON") == 0 || strcasecmp(t, "TBEACON") == 0 ||
strcasecmp(t, "CBEACON") == 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) { if (p_misc_config->num_beacons < MAX_BEACONS) {
memset (&(p_misc_config->beacon[p_misc_config->num_beacons]), 0, sizeof(struct beacon_s)); 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 || else if (strcasecmp(t, "SMARTBEACON") == 0 ||
@ -3649,6 +3679,10 @@ void config_init (char *fname, struct audio_s *p_audio_config,
#define SB_NUM(name,sbvar,minn,maxx,unit) \ #define SB_NUM(name,sbvar,minn,maxx,unit) \
t = split(NULL,0); \ t = split(NULL,0); \
if (t == NULL) { \ if (t == NULL) { \
if (strcmp(name, "fast speed") == 0) { \
p_misc_config->sb_configured = 1; \
continue; \
} \
text_color_set(DW_COLOR_ERROR); \ text_color_set(DW_COLOR_ERROR); \
dw_printf ("Line %d: Missing %s for SmartBeaconing.\n", line, name); \ dw_printf ("Line %d: Missing %s for SmartBeaconing.\n", line, name); \
continue; \ continue; \
@ -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) 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 *t;
char *p;
int q;
char temp_symbol[100]; char temp_symbol[100];
int ok; int ok;
char zone[8]; 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); b->custom_infocmd = strdup(value);
} }
else if (strcasecmp(keyword, "OBJNAME") == 0) { else if (strcasecmp(keyword, "OBJNAME") == 0) {
strncpy(b->objname, value, 9); strlcpy(b->objname, value, sizeof(b->objname));
} }
else if (strcasecmp(keyword, "LAT") == 0) { else if (strcasecmp(keyword, "LAT") == 0) {
b->lat = parse_ll (value, LAT, line); 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); b->alt_m = atof(value);
} }
else if (strcasecmp(keyword, "ZONE") == 0) { 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) { else if (strcasecmp(keyword, "EAST") == 0 || strcasecmp(keyword, "EASTING") == 0) {
easting = atof(value); 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); b->gain = atoi(value);
} }
else if (strcasecmp(keyword, "DIR") == 0 || strcasecmp(keyword, "DIRECTION") == 0) { 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) { else if (strcasecmp(keyword, "FREQ") == 0) {
b->freq = atof(value); b->freq = atof(value);

View File

@ -38,11 +38,21 @@ struct misc_config_s {
/* Want this to be off by default because it hangs */ /* Want this to be off by default because it hangs */
/* after a while if nothing is reading from other end. */ /* 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. */ /* virtual null modem for native Windows apps. */
char nmea_port[40]; /* Serial port name for NMEA communication with GPS */ char gpsnmea_port[20]; /* Serial port name for reading NMEA sentences from GPS. */
/* receiver and/or mapping application. */ /* 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. */ char logdir[80]; /* Directory for saving activity logs. */

View File

@ -53,7 +53,7 @@
#include "textcolor.h" #include "textcolor.h"
#include "symbols.h" #include "symbols.h"
#include "latlong.h" #include "latlong.h"
//#include "nmea.h" #include "dwgpsnmea.h"
#include "decode_aprs.h" #include "decode_aprs.h"
#include "telemetry.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, * Outputs: A-> g_symbol_table, g_symbol_code,
* g_lat, g_lon, * g_lat, g_lon,
* g_speed, g_course, g_altitude, * g_speed_mph, g_course, g_altitude_ft,
* g_comment * g_comment
* ... and many others... * ... 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_lat = G_UNKNOWN;
A->g_lon = 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_course = G_UNKNOWN;
A->g_power = 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_gain = G_UNKNOWN;
A->g_range = 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_freq = G_UNKNOWN;
A->g_tone = G_UNKNOWN; A->g_tone = G_UNKNOWN;
A->g_dcs = G_UNKNOWN; A->g_dcs = G_UNKNOWN;
@ -416,6 +416,11 @@ void decode_aprs_print (decode_aprs_t *A) {
if (A->g_power > 0) { if (A->g_power > 0) {
char phg[100]; 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); 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)); 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)); strlcat (stemp, A->g_aprstt_loc, sizeof(stemp));
}; };
if (A->g_speed != G_UNKNOWN) { if (A->g_speed_mph != G_UNKNOWN) {
char spd[20]; char spd[20];
if (strlen(stemp) > 0) strlcat (stemp, ", ", sizeof(stemp)); 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)); strlcat (stemp, spd, sizeof(stemp));
}; };
@ -523,11 +528,11 @@ void decode_aprs_print (decode_aprs_t *A) {
strlcat (stemp, cse, sizeof(stemp)); strlcat (stemp, cse, sizeof(stemp));
}; };
if (A->g_altitude != G_UNKNOWN) { if (A->g_altitude_ft != G_UNKNOWN) {
char alt[20]; char alt[20];
if (strlen(stemp) > 0) strlcat (stemp, ", ", sizeof(stemp)); 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)); strlcat (stemp, alt, sizeof(stemp));
}; };
@ -662,7 +667,7 @@ void decode_aprs_print (decode_aprs_t *A) {
* Inputs: info - Pointer to Information field. * Inputs: info - Pointer to Information field.
* ilen - Information field length. * 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. * Description: Type identifier '=' has APRS messaging.
* Type identifier '!' does not have 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. * Inputs: info - Pointer to Information field.
* ilen - Information field length. * 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. * Description: Type identifier '@' has APRS messaging.
* Type identifier '/' does not have 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. * Inputs: info - Pointer to Information field.
* ilen - Information field length. * ilen - Information field length.
* *
* Outputs: ??? TBD * Outputs: A-> ...
* *
* Description: APRS recognizes raw ASCII data strings conforming to the NMEA 0183 * Description: APRS recognizes raw ASCII data strings conforming to the NMEA 0183
* Version 2.0 specification, originating from navigation equipment such * 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 * VTG Velocity and Track Data
* WPL Way Point Location * 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 * 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 * $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 * $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 * $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) static void aprs_raw_nmea (decode_aprs_t *A, unsigned char *info, int ilen)
{ {
char stemp[256]; if (strncmp((char*)info, "$GPRMC,", 7) == 0)
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)
{ {
char *ptime; /* Time, hhmmss[.sss] */ float speed_knots = G_UNKNOWN;
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... */
(void) dwgpsnmea_gprmc ((char*)info, A->g_quiet, &(A->g_lat), &(A->g_lon), &speed_knots, &(A->g_course));
ptime = strsep(&next, ","); A->g_speed_mph = DW_KNOTS_TO_MPH(speed_knots);
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 { else if (strncmp((char*)info, "$GPGGA,", 7) == 0)
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");
}
}
else if (strcmp(ptype, "$GPGLL") == 0)
{ {
char *plat; /* Latitude */ float alt_meters = G_UNKNOWN;
char *pns; /* North/South */ int num_sat = 0;
char *plon; /* Longitude */
char *pew; /* East/West */
/* optional Time hhmmss[.sss] */
/* optional 'A' for data valid */
plat = strsep(&next, ","); (void) dwgpsnmea_gpgga ((char*)info, A->g_quiet, &(A->g_lat), &(A->g_lon), &alt_meters, &num_sat);
pns = strsep(&next, ","); A->g_altitude_ft = DW_METERS_TO_FEET(alt_meters);
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) { // TODO (low): add a few other sentence types.
A->g_lon = longitude_from_nmea(plon, pew);
}
else {
text_color_set (DW_COLOR_ERROR);
dw_printf("Incomplete longitude in sentence.\n");
}
} } /* end aprs_raw_nmea */
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 */
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? */
}
}
@ -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); n = ((p->speed_course[0] - 28) * 10) + ((p->speed_course[1] - 28) / 10);
if (n >= 800) n -= 800; 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); n = ((p->speed_course[1] - 28) % 10) * 100 + (p->speed_course[2] - 28);
if (n >= 400) n -= 400; 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] == '}') { 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 ( ! isdigit91(pfirst[0]) || ! isdigit91(pfirst[1]) || ! isdigit91(pfirst[2]))
{ {
if ( ! A->g_quiet) { if ( ! A->g_quiet) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf("Invalid character in MIC-E altitude. Must be in range of '!' to '{'.\n"); 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; 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. * Inputs: info - Pointer to Information field.
* ilen - Information field length. * 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 * Description: Message has a 9 character object name which could be quite different than
* the source station. * 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. * Inputs: info - Pointer to Information field.
* ilen - Information field length. * 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 * 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; } *q;
time_t ts = 0;
int i; int i;
char *ppos; char *ppos;
p = (struct aprs_item_s *)info; p = (struct aprs_item_s *)info;
q = (struct aprs_compressed_item_s *)info; q = (struct aprs_compressed_item_s *)info;
(void)(q);
memset (A->g_name, 0, sizeof(A->g_name)); memset (A->g_name, 0, sizeof(A->g_name));
i = 0; 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; 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. // could use A->g_directivity and need new variable for erp.
*hp = '\0'; *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) 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? */ /* 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) ... //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. * ilen - Information field length.
* quiet - suppress error messages. * quiet - suppress error messages.
* *
* Outputs: ??? * Outputs: A->g_telemetry
* A->g_comment
* *
* Description: TBD. * 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)); 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 */ } /* 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)); 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; 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. * TODO: call this context instead and have 3 enumerated values.
* *
* Global In: A->g_course - Wind info for compressed location. * Global In: A->g_course - Wind info for compressed location.
* A->g_speed * A->g_speed_mph
* *
* Outputs: A->g_weather * 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 * For human-readable locations, we expect wind direction
* and speed in a format like this: 999/999. * and speed in a format like this: 999/999.
* For compressed location, this has already been * 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 * Otherwise, for positionless weather data, the
* wind is in the form c999s999. * 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)) 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; 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 ( ! getwdata (&wp, 'c', 3, &A->g_course)) {
if ( ! A->g_quiet) { 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"); 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) { if ( ! A->g_quiet) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf("Didn't find wind speed in form s999.\n"); 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 // At this point, we should have the wind direction and speed
// from one of three methods. // 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) { if (A->g_course != G_UNKNOWN) {
char ctemp[40]; char ctemp[40];
snprintf (ctemp, sizeof(ctemp), ", direction %.0f", A->g_course); 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. */ /* 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; 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) 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)); 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: * One of the following:
* A->g_course & A->g_speeed * A->g_course & A->g_speeed
* A->g_altitude * A->g_altitude_ft
* A->g_range * A->g_range
* *
* Description: The compressed position provides resolution of around ??? * 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 */ ; /* ignore other two bytes */
} }
else if (((pcpos->t - 33) & 0x18) == 0x10) { 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 == '{') 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. */ /* For a weather station, this is wind information. */
A->g_course = (pcpos->c - 33) * 4; 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: * Outputs: One or more of the following, depending the data found:
* *
* A->g_course * A->g_course
* A->g_speed * A->g_speed_mph
* A->g_power * A->g_power
* A->g_height * A->g_height
* A->g_gain * A->g_gain
@ -3771,7 +3533,7 @@ static int data_extension_comment (decode_aprs_t *A, char *pdext)
} }
if (sscanf (pdext+4, "%3d", &n)) 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? */ /* Bearing and Number/Range/Quality? */
@ -3878,8 +3640,11 @@ static const char *search_locations[] = {
(const char *) NULL (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); if (x->len != y->len) return (y->len - x->len);
return (strcmp(x->prefix, y->prefix)); return (strcmp(x->prefix, y->prefix));
} }
@ -3933,7 +3698,7 @@ static void decode_tocall (decode_aprs_t *A, char *dest)
*p-- = '\0'; *p-- = '\0';
} }
// printf("debug: %s\n", stuff); // dw_printf("debug: %s\n", stuff);
if (stuff[0] == ' ' && if (stuff[0] == ' ' &&
stuff[4] == ' ' && 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. * clen - Length of comment or -1 to take it all.
* *
* Outputs: A->g_telemetry - Base 91 telemetry |ss1122| * 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_lat - Might be adjusted from !DAO!
* A->g_lon - Might be adjusted from !DAO! * A->g_lon - Might be adjusted from !DAO!
* A->g_aprstt_loc - Private extension to !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) { 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; A->g_tone = 0;
strlcpy (temp, A->g_comment + match[0].rm_eo, sizeof(temp)); 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) { else if (regexec (&std_dcs_re, A->g_comment, MAXMATCH, match, 0) == 0) {
char sttemp[10]; /* three octal digits */ char sttemp[10]; /* three octal digits */
int f;
int i;
substr_se (sttemp, A->g_comment, match[1].rm_so, match[1].rm_eo); 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) { else if (regexec (&std_offset_re, A->g_comment, MAXMATCH, match, 0) == 0) {
char sttemp[10]; /* includes leading sign */ char sttemp[10]; /* includes leading sign */
int f;
substr_se (sttemp, A->g_comment, match[1].rm_so, match[1].rm_eo); 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 sttemp[10]; /* should be two digits */
char sutemp[10]; /* m for miles or k for km */ 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 (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); 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); //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 (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); 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)); strlcpy (temp, A->g_comment + match[0].rm_eo, sizeof(temp));
A->g_comment[match[0].rm_eo] = '\0'; 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); 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)) { (x >= 902 && x <= 928)) {
if ( ! A->g_quiet) { if ( ! A->g_quiet) {
sprintf (good, "%07.3fMHz", x); snprintf (good, sizeof(good), "%07.3fMHz", x);
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf("\"%s\" in comment looks like a frequency in non-standard format.\n", bad); 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); 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. * 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 * ./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. */ /* Stub for stand-alone decoder. */
@ -4809,6 +4570,6 @@ int main (int argc, char *argv[])
return (0); return (0);
} }
#endif /* TEST */ #endif /* DECAMAIN */
/* end decode_aprs.c */ /* end decode_aprs.c */

View File

@ -64,7 +64,7 @@ typedef struct decode_aprs_s {
/* Also for Directed Station Query which is a */ /* Also for Directed Station Query which is a */
/* special case of message. */ /* 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. */ 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_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. */ 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 demod_init (struct audio_s *pa)
{ {
int j; //int j;
int chan; /* Loop index over number of radio channels. */ int chan; /* Loop index over number of radio channels. */
char profile; char profile;
@ -184,7 +184,7 @@ int demod_init (struct audio_s *pa)
/* This has been optimized for 300 baud. */ /* This has been optimized for 300 baud. */
strcpy (just_letters, "D"); strlcpy (just_letters, "D", sizeof(just_letters));
} }
else { else {
@ -193,7 +193,7 @@ int demod_init (struct audio_s *pa)
/* Previously we would use F if possible otherwise fall back to A. */ /* Previously we would use F if possible otherwise fall back to A. */
/* In version 1.2, new default is E+ /3. */ /* 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 (have_plus != -1) have_plus = 1; // Add as default for version 1.2
// If not explicitly turned off. // If not explicitly turned off.
if (save_audio_config_p->achan[chan].decimate == 0) { if (save_audio_config_p->achan[chan].decimate == 0) {
@ -202,7 +202,7 @@ int demod_init (struct audio_s *pa)
} }
} }
#else #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 (have_plus != -1) have_plus = 1; // Add as default for version 1.2
// If not explicitly turned off. // If not explicitly turned off.
#endif #endif
@ -223,12 +223,12 @@ int demod_init (struct audio_s *pa)
if (have_plus == -1) have_plus = 0; 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); assert (strlen(save_audio_config_p->achan[chan].profiles) >= 1);
if (have_plus) { 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. */ /* 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); text_color_set(DW_COLOR_ERROR);
dw_printf ("Channel %d: Demodulator + option can't be combined with multiple letters.\n", chan); 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; num_letters = 1;
save_audio_config_p->achan[chan].num_demod = 1; save_audio_config_p->achan[chan].num_demod = 1;
save_audio_config_p->achan[chan].num_subchan = 1; // Will be set higher later. 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. */ /* Need to take a look at CPU usage and performance difference. */
#ifndef __arm__ #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 #endif
} }
@ -641,7 +641,8 @@ int demod_get_sample (int a)
__attribute__((hot)) __attribute__((hot))
void demod_process_sample (int chan, int subchan, int sam) void demod_process_sample (int chan, int subchan, int sam)
{ {
float fsam, abs_fsam; float fsam;
//float abs_fsam;
int k; int k;
@ -650,8 +651,8 @@ void demod_process_sample (int chan, int subchan, int sam)
static int seq = 0; /* for log file name */ static int seq = 0; /* for log file name */
#endif #endif
int j; //int j;
int demod_data; //int demod_data;
struct demodulator_state_s *D; struct demodulator_state_s *D;
assert (chan >= 0 && chan < MAX_CHANS); 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; struct demodulator_state_s *D;
alevel_t alevel; alevel_t alevel;
int pk;
assert (chan >= 0 && chan < MAX_CHANS); assert (chan >= 0 && chan < MAX_CHANS);
assert (subchan >= 0 && subchan < MAX_SUBCHANS); 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. */ /* 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) static inline void push_sample (float val, float *buff, int size)
{ {
memmove(buff+1,buff,(size-1)*sizeof(float)); 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. */ /* 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) static inline float convolve (const float *__restrict__ data, const float *__restrict__ filter, int filter_size)
{ {
float sum = 0.0f; 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 *d = __builtin_assume_aligned(data, 16);
float *f = __builtin_assume_aligned(filter, 16); float *f = __builtin_assume_aligned(filter, 16);
#pragma GCC ivdep
for (j=0; j<filter_size; j++) { for (j=0; j<filter_size; j++) {
sum += f[j] * d[j]; sum += f[j] * d[j];
} }
#else #else
#pragma GCC ivdep // ignored until gcc 4.9
for (j=0; j<filter_size; j++) { for (j=0; j<filter_size; j++) {
sum += filter[j] * data[j]; sum += filter[j] * data[j];
} }
@ -95,7 +97,7 @@ static inline float convolve (const float *__restrict__ data, const float *__res
/* Automatic gain control. */ /* Automatic gain control. */
/* Result should settle down to 1 unit peak to peak. i.e. -0.5 to +0.5 */ /* 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) static inline float agc (float in, float fast_attack, float slow_decay, float *ppeak, float *pvalley)
{ {
if (in >= *ppeak) { if (in >= *ppeak) {
@ -264,7 +266,7 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D
{ {
float fsam; float fsam;
float abs_fsam; //float abs_fsam;
float amp; float amp;
float demod_out; 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 */ static int seq = 0; /* for log file name */
#endif #endif
int j; //int j;
int subchan = 0; int subchan = 0;
int demod_data; /* Still scrambled. */ 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)) __attribute__((hot))
static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator_state_s *D) 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. * 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 * http://www.amsat.org/amsat/articles/g3ruh/109/fig03.gif
*/ */
// Warning: 'descram' set but not used.
//assert (modem.modem_type[chan] == MODEM_SCRAMBLE); // It's used in conditional debug code below.
// descram =
//if (modem.modem_type[chan] == MODEM_SCRAMBLE) { descramble (demod_data, &(D->slicer[subchan].lfsr));
descram = descramble (demod_data, &(D->slicer[subchan].lfsr));
hdlc_rec_bit (chan, subchan, demod_data, 1, 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);
//}
} }
if (demod_data != D->slicer[subchan].prev_demod_data) { 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) { if (demod_log_fp == NULL) {
seq++; seq++;
sprintf (fname, "demod96/%04d.csv", seq); snprintf (fname, sizeof(fname), "demod96/%04d.csv", seq);
if (seq == 1) mkdir ("demod96" if (seq == 1) mkdir ("demod96"
#ifndef __WIN32__ #ifndef __WIN32__
, 0777 , 0777

View File

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

View File

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

View File

@ -164,7 +164,7 @@ static struct misc_config_s misc_config;
int main (int argc, char *argv[]) int main (int argc, char *argv[])
{ {
int err; int err;
int eof; //int eof;
int j; int j;
char config_file[100]; char config_file[100];
int xmit_calibrate_option = 0; 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_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_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_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 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. */ 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(); //Restore on exit? oldcp = GetConsoleOutputCP();
SetConsoleOutputCP(CP_UTF8); 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 #else
/* /*
@ -230,16 +225,17 @@ int main (int argc, char *argv[])
} }
// TODO: control development/beta/release by version.h instead of changing here. // 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_init(t_opt);
text_color_set(DW_COLOR_INFO); 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 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); //dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
#if __WIN32__ #if __WIN32__
SetConsoleCtrlHandler (cleanup_win, TRUE); SetConsoleCtrlHandler ((PHANDLER_ROUTINE)cleanup_win, TRUE);
#else #else
setlinebuf (stdout); setlinebuf (stdout);
signal (SIGINT, cleanup_linux); signal (SIGINT, cleanup_linux);
@ -278,19 +274,7 @@ int main (int argc, char *argv[])
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
#endif #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. * Default location of configuration file is current directory.
@ -307,7 +291,7 @@ int main (int argc, char *argv[])
strlcpy (input_file, "", sizeof(input_file)); strlcpy (input_file, "", sizeof(input_file));
while (1) { while (1) {
int this_option_optind = optind ? optind : 1; //int this_option_optind = optind ? optind : 1;
int option_index = 0; int option_index = 0;
int c; int c;
char *p; char *p;
@ -448,6 +432,7 @@ int main (int argc, char *argv[])
// separate out gps & waypoints. // separate out gps & waypoints.
case 'g': d_g_opt++; break;
case 't': d_t_opt++; beacon_tracker_set_debug (d_t_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. 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. * 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. * Create thread for trying to salvage frames with bad FCS.
@ -695,8 +682,8 @@ int main (int argc, char *argv[])
/* /*
* Enable beaconing. * Enable beaconing.
*/ */
beacon_init (&audio_config, &misc_config, &digi_config);
beacon_init (&audio_config, &misc_config);
log_init(misc_config.logdir); 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) { 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, 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, 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); 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 (subchan == -1) {
if (tt_config.gateway_enabled && info_len >= 2) { if (tt_config.gateway_enabled && info_len >= 2) {
aprs_tt_sequence (chan, pinfo+1); aprs_tt_sequence (chan, (char*)(pinfo+1));
} }
} }
else { else {
@ -1048,7 +1035,8 @@ static void usage (char **argv)
dw_printf (" n n = KISS network client.\n"); dw_printf (" n n = KISS network client.\n");
dw_printf (" u u = Display non-ASCII text in hexadecimal.\n"); dw_printf (" u u = Display non-ASCII text in hexadecimal.\n");
dw_printf (" p p = dump Packets 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 (" o o = output controls such as PTT and DCD.\n");
dw_printf (" -q Quiet (suppress output) options:\n"); dw_printf (" -q Quiet (suppress output) options:\n");
dw_printf (" h h = Heard line with the audio level.\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 # To use something other than the default, generally use plughw
# and a card number reported by "arecord -l" command. Example: # 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 # Starting with version 1.0, you can also use "-" or "stdin" to
# pipe stdout from some other application such as a software defined # 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 # 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. # 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. # 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) # If not using a VOX circuit, the transmitter Push to Talk (PTT)
@ -181,7 +180,7 @@ MODEM 1200
# #
#PTT COM1 RTS #PTT COM1 RTS
#PTT COM1 RTS -DTR PTT COM1 RTS -DTR
#PTT /dev/ttyUSB0 RTS #PTT /dev/ttyUSB0 RTS
# #
@ -213,6 +212,8 @@ MODEM 1200
# Specify MYCALL, MODEM, PTT, etc. configuration items for # Specify MYCALL, MODEM, PTT, etc. configuration items for
# CHANNEL 1. Repeat for any other channels. # 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: # Beaconing is configured with these two commands:

View File

@ -3,7 +3,6 @@
#define DIREWOLF_H 1 #define DIREWOLF_H 1
/* /*
* Previously, we could handle only a single audio device. * Previously, we could handle only a single audio device.
* This meant we could have only two radio channels. * 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. */ /* 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_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) #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. */ /* Platform differences for string functions. */
#if __WIN32__ #if __WIN32__
char *strsep(char **stringp, const char *delim); char *strsep(char **stringp, const char *delim);
char *strtok_r(char *str, const char *delim, char **saveptr);
#endif #endif
//#if __WIN32__ //#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->alevel = alevel;
pnew->retries = retries; pnew->retries = retries;
if (spectrum == NULL) if (spectrum == NULL)
strcpy(pnew->spectrum, ""); strlcpy(pnew->spectrum, "", sizeof(pnew->spectrum));
else else
strcpy(pnew->spectrum, spectrum); strlcpy(pnew->spectrum, spectrum, sizeof(pnew->spectrum));
#if DEBUG1 #if DEBUG1
text_color_set(DW_COLOR_DEBUG); 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; 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)); memset (alevel, 0xff, sizeof(*alevel));
*retries = -1; *retries = -1;
strcpy(spectrum,""); strlcpy(spectrum, "", spectrumsize);
result = 0; result = 0;
} }
else { else {
@ -576,7 +576,7 @@ int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_
*pp = phead->pp; *pp = phead->pp;
*alevel = phead->alevel; *alevel = phead->alevel;
*retries = phead->retries; *retries = phead->retries;
strcpy (spectrum, phead->spectrum); strlcpy (spectrum, phead->spectrum, spectrumsize);
result = 1; 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); 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 #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 "textcolor.h"
#include "dtime_now.h" #include "dtime_now.h"
@ -17,9 +17,7 @@
#include <sys/time.h> #include <sys/time.h>
#endif #endif
#if __WIN32__
#include <windows.h>
#endif

415
dwgps.c
View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // 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 // 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 // it under the terms of the GNU General Public License as published by
@ -22,69 +22,67 @@
* *
* Module: dwgps.c * 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. * Description: This is a wrapper for two different implementations:
* At this time, I can't think of any other reason why
* we would need this information.
* *
* For Linux, we use gpsd and libgps. * (1) Read NMEA sentences from a serial port (or USB
* This has the extra benefit that the system clock can * that looks line one). Available for all platforms.
* be set from the GPS signal.
* *
* Not yet implemented for Windows. Not sure how yet. * (2) Read from gpsd. Not available for Windows.
* The Windows location API is new in Windows 7. * Including this is optional because it depends
* At the end of 2013, about 1/3 of Windows users are * on another external software component.
* still using XP so that still needs to be supported.
* *
* 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 <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <assert.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <time.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 "direwolf.h" #include "direwolf.h"
#include "textcolor.h" #include "textcolor.h"
#include "dwgps.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__ static dwgps_info_t s_dwgps_info = {
#include <windows.h> .timestamp = 0,
#else .fix = DWFIX_NOT_INIT, /* to detect read without init. */
#if ENABLE_GPS .dlat = G_UNKNOWN,
.dlon = G_UNKNOWN,
.speed_knots = G_UNKNOWN,
.track = G_UNKNOWN,
.altitude = G_UNKNOWN
};
static struct gps_data_t gpsdata; static dw_mutex_t s_gps_mutex;
#endif
#endif
/*------------------------------------------------------------------- /*-------------------------------------------------------------------
@ -93,223 +91,125 @@ static struct gps_data_t gpsdata;
* *
* Purpose: Intialize the GPS interface. * Purpose: Intialize the GPS interface.
* *
* Inputs: none. * Inputs: pconfig Configuration settings. This might include
* serial port name for direct connect and host
* name or address for network connection.
* *
* Returns: 0 = success * debug - If >= 1, print results when dwgps_read is called.
* -1 = failure * (In this file.)
* *
* Description: For Linux, this maps into gps_open. * If >= 2, location updates are also printed.
* Not yet implemented for Windows. * (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;
/* dw_mutex_init (&s_gps_mutex);
* Windows version. Not implemented yet.
*/
text_color_set(DW_COLOR_ERROR); dwgpsnmea_init (pconfig, debug);
dw_printf ("GPS interface not yet available in Windows version.\n");
init_status = INIT_FAILED;
return (-1);
#elif ENABLE_GPS #if ENABLE_GPSD
int err; dwgpsd_init (pconfig, debug);
#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 #endif
#else /* end ENABLE_GPS */ SLEEP_MS(500); /* So receive thread(s) can clear the */
/* not init status before it gets checked. */
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);
#endif
} /* end dwgps_init */ } /* 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 * Name: dwgps_read
* *
* Purpose: Obtain current location from GPS receiver. * Purpose: Return most recent location data available.
* *
* Outputs: *plat - Latitude. * Outputs: gpsinfo - Structure with latitude, longitude, etc.
* *plon - Longitude. *
* *pspeed - Speed, knots. * Returns: Position fix quality. Same as in structure.
* *pcourse - Course over ground, degrees.
* *palt - Altitude, meters.
* *
* 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_mutex_lock (&s_gps_mutex);
dw_printf ("Internal error, dwgps_read, shouldn't be here.\n");
return (-1);
#elif ENABLE_GPS memcpy (gpsinfo, &s_dwgps_info, sizeof(*gpsinfo));
int err; dw_mutex_unlock (&s_gps_mutex);
if (init_status != INIT_SUCCESS) { if (s_dwgps_debug >= 1) {
text_color_set(DW_COLOR_ERROR); text_color_set (DW_COLOR_DEBUG);
dw_printf ("Internal error, dwgps_read without successful init.\n"); dwgps_print ("gps_read: ", gpsinfo);
return (-1);
} }
#if USE_GPS_SHM // TODO: Should we check timestamp and complain if very stale?
// or should we leave that up to the caller?
/* return (s_dwgps_info.fix);
* Shared memory version.
*/
err = gps_read (&gpsdata);
#if DEBUG
dw_printf ("gps_read returns %d bytes\n", err);
#endif
#else
/*
* Socket version.
*/
// 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. */ *
* 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.
*
*--------------------------------------------------------------------*/
if (gpsdata.status >= STATUS_FIX && gpsdata.fix.mode >= MODE_2D) { void dwgps_print (char *msg, dwgps_info_t *gpsinfo)
{
*plat = gpsdata.fix.latitude; dw_printf ("%stime=%d fix=%d lat=%.6f lon=%.6f trk=%.0f spd=%.1f alt=%.0f\n",
*plon = gpsdata.fix.longitude; msg,
*pcourse = gpsdata.fix.track; (int)gpsinfo->timestamp, (int)gpsinfo->fix,
*pspeed = MPS_TO_KNOTS * gpsdata.fix.speed; /* libgps uses meters/sec */ gpsinfo->dlat, gpsinfo->dlon,
gpsinfo->track, gpsinfo->speed_knots,
gpsinfo->altitude);
if (gpsdata.fix.mode >= MODE_3D) { } /* end dwgps_set_data */
*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 */
/*------------------------------------------------------------------- /*-------------------------------------------------------------------
@ -326,19 +226,10 @@ int dwgps_read (double *plat, double *plon, float *pspeed, float *pcourse, float
void dwgps_term (void) { void dwgps_term (void) {
#if __WIN32__ dwgpsnmea_term ();
#elif ENABLE_GPS
if (init_status == INIT_SUCCESS) {
#ifndef USE_GPS_SHM
gps_stream(&gpsdata, WATCH_DISABLE, NULL);
#endif
gps_close (&gpsdata);
}
#else
#if ENABLE_GPSD
dwgpsd_term ();
#endif #endif
} /* end dwgps_term */ } /* 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. * Inputs: gpsinfo - Structure with latitude, longitude, etc.
*
* gcc -DTEST dwgps.c textcolor.c -lgps
* ./a.out
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
#if TEST void dwgps_set_data (dwgps_info_t *gpsinfo)
int main (int argc, char *argv[])
{ {
#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 memcpy (&s_dwgps_info, gpsinfo, sizeof(s_dwgps_info));
int err;
int fix;
double lat;
double lon;
float speed;
float course;
float alt;
err = dwgps_init (); dw_mutex_unlock (&s_gps_mutex);
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
} /* end dwgps_set_data */
/* end dwgps.c */ /* end dwgps.c */

50
dwgps.h
View File

@ -1,13 +1,59 @@
/* dwgps.h */ /* 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_term (void);
void dwgps_set_data (dwgps_info_t *gpsinfo);
#endif /* DWGPS_H 1 */
/* end dwgps.h */ /* 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);
}
else if ((f == DWFIX_3D && info.fix == DWFIX_2D) ||
(f == DWFIX_2D && info.fix == DWFIX_3D)) {
text_color_set(DW_COLOR_INFO);
dw_printf ("GPSNMEA: Location fix is now %dD.\n", (int)f);
info.fix = f;
}
}
}
gps_msg_len = 0;
gps_msg[gps_msg_len] = '\0';
}
else {
if (gps_msg_len < NMEA_MAX_LEN-1) {
gps_msg[gps_msg_len++] = ch;
gps_msg[gps_msg_len] = '\0';
}
}
} /* while (1) */
#if __WIN32__
return (0);
#else
return (NULL);
#endif
} /* end read_gpsnmea_thread */
/*-------------------------------------------------------------------
*
* Name: remove_checksum
*
* Purpose: Validate checksum and remove before further processing.
*
* Inputs: sentence
* quiet suppress printing of error messages.
*
* Outputs: sentence modified in place.
*
* Returns: 0 = good checksum.
* -1 = error. missing or wrong.
*
*--------------------------------------------------------------------*/
static int remove_checksum (char *sent, int quiet)
{
char *p;
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 ( ! quiet) {
text_color_set (DW_COLOR_INFO);
dw_printf("Missing GPS checksum.\n");
}
return (-1);
}
if (cs != strtoul(p+1, NULL, 16)) {
if ( ! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf("GPS checksum error. Expected %02x but found %s.\n", cs, p+1);
}
return (-1);
}
*p = '\0'; // Remove the checksum.
return (0);
}
/*-------------------------------------------------------------------
*
* Name: dwgpsnmea_gprmc
*
* Purpose: Parse $GPRMC sentence and extract interesing parts.
*
* Inputs: sentence NMEA sentence.
*
* quiet suppress printing of error messages.
*
* Outputs: odlat latitude
* odlon longitude
* oknots speed
* ocourse direction of travel.
*
* Left undefined if not valid.
*
* Returns: DWFIX_ERROR Parse error.
* DWFIX_NO_FIX GPS is there but Position unknown. Could be temporary.
* DWFIX_2D Valid position. We don't know if it is really 2D or 3D.
*
* Examples: $GPRMC,001431.00,V,,,,,,,121015,,,N*7C
* $GPRMC,212404.000,V,4237.1505,N,07120.8602,W,,,150614,,*0B
* $GPRMC,000029.020,V,,,,,,,080810,,,N*45
* $GPRMC,003413.710,A,4237.1240,N,07120.8333,W,5.07,291.42,160614,,,A*7F
*
*--------------------------------------------------------------------*/
dwfix_t dwgpsnmea_gprmc (char *sentence, int quiet, double *odlat, double *odlon, float *oknots, float *ocourse)
{
char stemp[NMEA_MAX_LEN]; /* Make copy because parsing is destructive. */
char *next;
char *ptype; /* Should be $GPRMC */
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 */
strlcpy (stemp, sentence, sizeof(stemp));
if (remove_checksum (stemp, quiet) < 0) {
return (DWFIX_ERROR);
}
next = stemp;
ptype = strsep(&next, ",");
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, ",");
/* Suppress the 'set but not used' warnings. */
/* Alternatively, we might use __attribute__((unused)) */
(void)(ptype);
(void)(ptime);
(void)(pdate);
if (pstatus != NULL && strlen(pstatus) == 1) {
if (*pstatus != 'A') {
return (DWFIX_NO_FIX); /* Not "Active." Don't parse. */
}
}
else {
if ( ! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf("No status in GPRMC sentence.\n");
}
return (DWFIX_ERROR);
}
if (plat != NULL && strlen(plat) > 0 && pns != NULL && strlen(pns) > 0) {
*odlat = latitude_from_nmea(plat, pns);
}
else {
if ( ! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf("Can't get latitude from GPRMC sentence.\n");
}
return (DWFIX_ERROR);
}
if (plon != NULL && strlen(plon) > 0 && pew != NULL && strlen(pew) > 0) {
*odlon = longitude_from_nmea(plon, pew);
}
else {
if ( ! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf("Can't get longitude from GPRMC sentence.\n");
}
return (DWFIX_ERROR);
}
if (pknots != NULL && strlen(pknots) > 0) {
*oknots = atof(pknots);
}
else {
if ( ! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf("Can't get speed from GPRMC sentence.\n");
}
return (DWFIX_ERROR);
}
if (pcourse != NULL) {
if (strlen(pcourse) > 0) {
*ocourse = atof(pcourse);
}
else {
/* When stationary, this field might be empty. */
*ocourse = G_UNKNOWN;
}
}
else {
if ( ! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf("Can't get course from GPRMC sentence.\n");
}
return (DWFIX_ERROR);
}
//text_color_set (DW_COLOR_INFO);
//dw_printf("%.6f %.6f %.1f %.0f\n", *odlat, *odlon, *oknots, *ocourse);
return (DWFIX_2D);
} /* end dwgpsnmea_gprmc */
/*-------------------------------------------------------------------
*
* Name: dwgpsnmea_gpgga
*
* Purpose: Parse $GPGGA sentence and extract interesing parts.
*
* Inputs: sentence NMEA sentence.
*
* quiet suppress printing of error messages.
*
* Outputs: odlat latitude
* odlon longitude
* oalt altitude in meters
* onsat number of satellites.
*
* Left undefined if not valid.
*
* Returns: DWFIX_ERROR Parse error.
* DWFIX_NO_FIX GPS is there but Position unknown. Could be temporary.
* DWFIX_2D Valid position. We don't know if it is really 2D or 3D.
* Take more cautious value so we don't try using altitude.
*
* Examples: $GPGGA,001429.00,,,,,0,00,99.99,,,,,,*68
* $GPGGA,212407.000,4237.1505,N,07120.8602,W,0,00,,,M,,M,,*58
* $GPGGA,000409.392,,,,,0,00,,,M,0.0,M,,0000*53
* $GPGGA,003518.710,4237.1250,N,07120.8327,W,1,03,5.9,33.5,M,-33.5,M,,0000*5B
*
*--------------------------------------------------------------------*/
// TODO: in progress...
dwfix_t dwgpsnmea_gpgga (char *sentence, int quiet, double *odlat, double *odlon, float *oalt, int *onsat)
{
char stemp[NMEA_MAX_LEN]; /* Make copy because parsing is destructive. */
char *next;
char *ptype; /* Should be $GPGGA */
char *ptime; /* Time, hhmmss[.sss] */
char *plat; /* Latitude */
char *pns; /* North/South */
char *plon; /* Longitude */
char *pew; /* East/West */
char *pfix; /* 0=invalid, 1=GPS fix, 2=DGPS fix */
char *pnum_sat; /* Number of satellites */
char *phdop; /* Horiz. Dilution fo Precision */
char *paltitude; /* Altitude, above mean sea level */
char *palt_u; /* Units for Altitude, typically M for meters. */
char *pheight; /* Height above ellipsoid */
char *pheight_u; /* Units for height, typically M for meters. */
char *psince; /* Time since last DGPS update. */
char *pdsta; /* DGPS reference station id. */
strlcpy (stemp, sentence, sizeof(stemp));
if (remove_checksum (stemp, quiet) < 0) {
return (DWFIX_ERROR);
}
next = stemp;
ptype = strsep(&next, ",");
ptime = strsep(&next, ",");
plat = strsep(&next, ",");
pns = strsep(&next, ",");
plon = strsep(&next, ",");
pew = strsep(&next, ",");
pfix = strsep(&next, ",");
pnum_sat = strsep(&next, ",");
phdop = strsep(&next, ",");
paltitude = strsep(&next, ",");
palt_u = strsep(&next, ",");
pheight = strsep(&next, ",");
pheight_u = strsep(&next, ",");
psince = strsep(&next, ",");
pdsta = strsep(&next, ",");
/* Suppress the 'set but not used' warnings. */
/* Alternatively, we might use __attribute__((unused)) */
(void)(ptype);
(void)(ptime);
(void)(pnum_sat);
(void)(phdop);
(void)(palt_u);
(void)(pheight);
(void)(pheight_u);
(void)(psince);
(void)(pdsta);
if (pfix != NULL && strlen(pfix) == 1) {
if (*pfix == '0') {
return (DWFIX_NO_FIX); /* No Fix. Don't parse the rest. */
}
}
else {
if ( ! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf("No fix in GPGGA sentence.\n");
}
return (DWFIX_ERROR);
}
if (plat != NULL && strlen(plat) > 0 && pns != NULL && strlen(pns) > 0) {
*odlat = latitude_from_nmea(plat, pns);
}
else {
if ( ! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf("Can't get latitude from GPGGA sentence.\n");
}
return (DWFIX_ERROR);
}
if (plon != NULL && strlen(plon) > 0 && pew != NULL && strlen(pew) > 0) {
*odlon = longitude_from_nmea(plon, pew);
}
else {
if ( ! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf("Can't get longitude from GPGGA sentence.\n");
}
return (DWFIX_ERROR);
}
// TODO: num sat...
/*
* We can distinguish between 2D & 3D fix by presence
* of altitude or an empty field.
*/
if (paltitude != NULL) {
if (strlen(paltitude) > 0) {
*oalt = atof(paltitude);
return (DWFIX_3D);
}
else {
return (DWFIX_2D);
}
}
else {
if ( ! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf("Can't get altitude from GPGGA sentence.\n");
}
return (DWFIX_ERROR);
}
} /* end dwgpsnmea_gpgga */
/*-------------------------------------------------------------------
*
* Name: dwgpsnmea_term
*
* Purpose: Shut down GPS interface before exiting from application.
*
* Inputs: none.
*
* Returns: none.
*
*--------------------------------------------------------------------*/
void dwgpsnmea_term (void) {
// Should probably kill reader thread before closing device to avoid
// message about read error.
// serial_port_close (s_gpsnmea_port_fd);
} /* end dwgps_term */
/*-------------------------------------------------------------------
*
* Name: main
*
* Purpose: Simple unit test for other functions in this file.
*
* Description: Compile with -DGPSTEST option.
*
* Windows:
* gcc -DGPSTEST -Iregex dwgpsnmea.c dwgps.c textcolor.o serial_port.o latlong.o misc.a
* a.exe
*
* Linux:
* gcc -DGPSTEST dwgpsnmea.c dwgps.c textcolor.o serial_port.o latlong.o misc.a -lm -lpthread
* ./a.out
*
*--------------------------------------------------------------------*/
#if GPSTEST
int main (int argc, char *argv[])
{
struct misc_config_s config;
dwgps_info_t info;
memset (&config, 0, sizeof(config));
strlcpy (config.gpsnmea_port, "COM22", sizeof(config.gpsnmea_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 dwgpsnmea.c */

28
dwgpsnmea.h Normal file
View File

@ -0,0 +1,28 @@
/* dwgpsnmea.h - For NMEA sentences over serial port */
#ifndef DWGPSNMEA_H
#define DWGPSNMEA_H 1
#include "dwgps.h" /* for dwfix_t */
#include "config.h"
int dwgpsnmea_init (struct misc_config_s *pconfig, int debug);
void dwgpsnmea_term (void);
dwfix_t dwgpsnmea_gprmc (char *sentence, int quiet, double *odlat, double *odlon, float *oknots, float *ocourse);
dwfix_t dwgpsnmea_gpgga (char *sentence, int quiet, double *odlat, double *odlon, float *oalt, int *onsat);
#endif
/* end dwgpsnmea.h */

View File

@ -116,7 +116,8 @@ static int set_norm_position (char symtab, char symbol, double dlat, double dlon
* height - Feet. * height - Feet.
* gain - dBi. * gain - dBi.
* *
* course - Degress, 1 - 360. 0 means none or unknown. * course - Degress, 0 - 360 (360 equiv. to 0).
* Use G_UNKNOWN for none or unknown.
* speed - knots. * speed - knots.
* *
* *
@ -130,6 +131,9 @@ static int set_norm_position (char symtab, char symbol, double dlat, double dlon
* radio range - calculated from PHG * radio range - calculated from PHG
* altitude - not implemented yet. * altitude - not implemented yet.
* *
* Some conversion must be performed for course from
* the API definition to what is sent over the air.
*
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
/* Compressed position & symbol fields common to several message formats. */ /* Compressed position & symbol fields common to several message formats. */
@ -198,13 +202,18 @@ static int set_comp_position (char symtab, char symbol, double dlat, double dlon
* When c is '{', s is range ... * When c is '{', s is range ...
*/ */
if (course || speed) { if (speed > 0) {
int c; int c;
int s; int s;
c = (course + 1) / 4; if (course != G_UNKNOWN) {
c = (course + 2) / 4;
if (c < 0) c += 90; if (c < 0) c += 90;
if (c >= 90) c -= 90; if (c >= 90) c -= 90;
}
else {
c = 0;
}
presult->c = c + '!'; presult->c = c + '!';
s = (int)round(log(speed+1.0) / log(1.08)); s = (int)round(log(speed+1.0) / log(1.08));
@ -251,7 +260,10 @@ static int set_comp_position (char symtab, char symbol, double dlat, double dlon
* *
* Inputs: power - Watts. * Inputs: power - Watts.
* height - Feet. * height - Feet.
* gain - dB. Not clear if it is dBi or dBd. * gain - dB. Protocol spec doesn't mention whether it is dBi or dBd.
* This says dBi:
* http://www.tapr.org/pipermail/aprssig/2008-September/027034.html
* dir - Directivity: N, NE, etc., omni. * dir - Directivity: N, NE, etc., omni.
* *
* Outputs: presult - Stored here. * Outputs: presult - Stored here.
@ -260,6 +272,11 @@ static int set_comp_position (char symtab, char symbol, double dlat, double dlon
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
// TODO (bug): Doesn't check for G_UNKNOWN.
// could have a case where some, but not all, values were specified.
// Callers originally checked for any not zero.
// now they check for any > 0.
typedef struct phg_s { typedef struct phg_s {
char P; char P;
@ -317,13 +334,19 @@ static int phg_data_extension (int power, int height, int gain, char *dir, char
* *
* Purpose: Fill in parts of the course & speed data extension. * Purpose: Fill in parts of the course & speed data extension.
* *
* Inputs: course - Degress, 1 - 360. * Inputs: course - Degress, 0 - 360 (360 equiv. to 0).
* Use G_UNKNOWN for none or unknown.
*
* speed - knots. * speed - knots.
* *
* Outputs: presult - Stored here. * Outputs: presult - Stored here.
* *
* Returns: Number of characters in result. * Returns: Number of characters in result.
* *
* Description: Over the air we use:
* 0 for unknown or not relevant.
* 1 - 360 for valid course. (360 for north)
*
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
@ -340,16 +363,23 @@ static int cse_spd_data_extension (int course, int speed, char *presult)
char stemp[8]; char stemp[8];
int x; int x;
if (course != G_UNKNOWN) {
x = course; x = course;
if (x < 0) x = 0; while (x < 1) x += 360;
if (x > 360) x = 360; while (x > 360) x -= 360;
// Should now be in range of 1 - 360. */
// Original value of 0 for north is transmitted as 360. */
}
else {
x = 0;
}
snprintf (stemp, sizeof(stemp), "%03d", x); snprintf (stemp, sizeof(stemp), "%03d", x);
memcpy (r->cse, stemp, 3); memcpy (r->cse, stemp, 3);
r->slash = '/'; r->slash = '/';
x = speed; x = speed;
if (x < 0) x = 0; if (x < 0) x = 0; // would include G_UNKNOWN
if (x > 999) x = 999; if (x > 999) x = 999;
snprintf (stemp, sizeof(stemp), "%03d", x); snprintf (stemp, sizeof(stemp), "%03d", x);
memcpy (r->spd, stemp, 3); memcpy (r->spd, stemp, 3);
@ -459,8 +489,9 @@ static int frequency_spec (float freq, float tone, float offset, char *presult)
* gain - dB. Not clear if it is dBi or dBd. * gain - dB. Not clear if it is dBi or dBd.
* dir - Directivity: N, NE, etc., omni. * dir - Directivity: N, NE, etc., omni.
* *
* course - Degress, 1 - 360. 0 means none or unknown. * course - Degress, 0 - 360 (360 equiv. to 0).
* speed - knots. * Use G_UNKNOWN for none or unknown.
* speed - knots. // TODO: should distinguish unknown(not revevant) vs. known zero.
* *
* freq - MHz. * freq - MHz.
* tone - Hz. * tone - Hz.
@ -487,6 +518,7 @@ static int frequency_spec (float freq, float tone, float offset, char *presult)
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
typedef struct aprs_ll_pos_s { typedef struct aprs_ll_pos_s {
char dti; /* ! or = */ char dti; /* ! or = */
position_t pos; position_t pos;
@ -533,10 +565,10 @@ int encode_position (int messaging, int compressed, double lat, double lon, int
/* Optional data extension. (singular) */ /* Optional data extension. (singular) */
/* Can't have both course/speed and PHG. Former gets priority. */ /* Can't have both course/speed and PHG. Former gets priority. */
if (course || speed) { if (course != G_UNKNOWN || speed > 0) {
result_len += cse_spd_data_extension (course, speed, presult + result_len); result_len += cse_spd_data_extension (course, speed, presult + result_len);
} }
else if (power || height || gain) { else if (power > 0 || height > 0 || gain > 0) {
result_len += phg_data_extension (power, height, gain, dir, presult + result_len); result_len += phg_data_extension (power, height, gain, dir, presult + result_len);
} }
} }
@ -598,7 +630,8 @@ int encode_position (int messaging, int compressed, double lat, double lon, int
* gain - dB. Not clear if it is dBi or dBd. * gain - dB. Not clear if it is dBi or dBd.
* dir - Direction: N, NE, etc., omni. * dir - Direction: N, NE, etc., omni.
* *
* course - Degress, 1 - 360. 0 means none or unknown. * course - Degress, 0 - 360 (360 equiv. to 0).
* Use G_UNKNOWN for none or unknown.
* speed - knots. * speed - knots.
* *
* freq - MHz. * freq - MHz.
@ -614,7 +647,7 @@ int encode_position (int messaging, int compressed, double lat, double lon, int
* 36 for fixed part, * 36 for fixed part,
* 7 for optional extended data, * 7 for optional extended data,
* ~20 for freq, etc., * ~20 for freq, etc.,
* comment ... * comment could be very long...
* *
* Returns: Number of characters in result. * Returns: Number of characters in result.
* *
@ -694,10 +727,10 @@ int encode_object (char *name, int compressed, time_t thyme, double lat, double
/* Optional data extension. (singular) */ /* Optional data extension. (singular) */
/* Can't have both course/speed and PHG. Former gets priority. */ /* Can't have both course/speed and PHG. Former gets priority. */
if (course || speed) { if (course != G_UNKNOWN || speed > 0) {
result_len += cse_spd_data_extension (course, speed, presult + result_len); result_len += cse_spd_data_extension (course, speed, presult + result_len);
} }
else if (power || height || gain) { else if (power > 0 || height > 0 || gain > 0) {
result_len += phg_data_extension (power, height, gain, dir, presult + result_len); result_len += phg_data_extension (power, height, gain, dir, presult + result_len);
} }
} }
@ -746,93 +779,97 @@ int encode_object (char *name, int compressed, time_t thyme, double lat, double
int main (int argc, char *argv[]) int main (int argc, char *argv[])
{ {
char result[100]; char result[100];
int errors = 0;
/*********** Position ***********/ /*********** Position ***********/
encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&', encode_position (0, 0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result, sizeof(result)); 0, 0, 0, NULL, G_UNKNOWN, 0, 0, 0, 0, NULL, result, sizeof(result));
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&") != 0) dw_printf ("ERROR! line %d\n", __LINE__); if (strcmp(result, "!4234.61ND07126.47W&") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; }
/* with PHG. */ /* with PHG. */
// TODO: Need to test specifying some but not all.
encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&', encode_position (0, 0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
50, 100, 6, "N", 0, 0, 0, 0, 0, NULL, result, sizeof(result)); 50, 100, 6, "N", G_UNKNOWN, 0, 0, 0, 0, NULL, result, sizeof(result));
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&PHG7368") != 0) dw_printf ("ERROR! line %d\n", __LINE__); if (strcmp(result, "!4234.61ND07126.47W&PHG7368") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; }
/* with freq. */ /* with freq. */
encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&', encode_position (0, 0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 0, 0, 146.955, 74.4, -0.6, NULL, result, sizeof(result)); 0, 0, 0, NULL, G_UNKNOWN, 0, 146.955, 74.4, -0.6, NULL, result, sizeof(result));
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&146.955MHz T074 -060 ") != 0) dw_printf ("ERROR! line %d\n", __LINE__); if (strcmp(result, "!4234.61ND07126.47W&146.955MHz T074 -060 ") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; }
/* with course/speed, freq, and comment! */ /* with course/speed, freq, and comment! */
encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&', encode_position (0, 0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 180, 55, 146.955, 74.4, -0.6, "River flooding", result, sizeof(result)); 0, 0, 0, NULL, 180, 55, 146.955, 74.4, -0.6, "River flooding", result, sizeof(result));
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz T074 -060 River flooding") != 0) dw_printf ("ERROR! line %d\n", __LINE__); if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz T074 -060 River flooding") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; }
/* Course speed, no tone, + offset */ /* Course speed, no tone, + offset */
encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&', encode_position (0, 0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 180, 55, 146.955, 0, 0.6, "River flooding", result, sizeof(result)); 0, 0, 0, NULL, 180, 55, 146.955, 0, 0.6, "River flooding", result, sizeof(result));
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz Toff +060 River flooding") != 0) dw_printf ("ERROR! line %d\n", __LINE__); if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz Toff +060 River flooding") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; }
/* Course speed, no tone, + offset + altitude */ /* Course speed, no tone, + offset + altitude */
encode_position (0, 42+34.61/60, -(71+26.47/60), 12345, 'D', '&', encode_position (0, 0, 42+34.61/60, -(71+26.47/60), 12345, 'D', '&',
0, 0, 0, NULL, 180, 55, 146.955, 0, 0.6, "River flooding", result, sizeof(result)); 0, 0, 0, NULL, 180, 55, 146.955, 0, 0.6, "River flooding", result, sizeof(result));
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz Toff +060 /A=012345River flooding") != 0) dw_printf ("ERROR! line %d\n", __LINE__); if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz Toff +060 /A=012345River flooding") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; }
// TODO: try boundary conditions of course = 0, 359, 360
/*********** Compressed position. ***********/ /*********** Compressed position. ***********/
encode_position (1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&', encode_position (0, 1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result, sizeof(result)); 0, 0, 0, NULL, G_UNKNOWN, 0, 0, 0, 0, NULL, result, sizeof(result));
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, "!D8yKC<Hn[& !") != 0) dw_printf ("ERROR! line %d\n", __LINE__); if (strcmp(result, "!D8yKC<Hn[& !") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; }
/* with PHG. In this case it is converted to precomputed radio range. TODO: check on this. Is 27.4 correct? */ /* with PHG. In this case it is converted to precomputed radio range. TODO: check on this. Is 27.4 correct? */
encode_position (1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&', encode_position (0, 1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
50, 100, 6, "N", 0, 0, 0, 0, 0, NULL, result, sizeof(result)); 50, 100, 6, "N", G_UNKNOWN, 0, 0, 0, 0, NULL, result, sizeof(result));
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, "!D8yKC<Hn[&{CG") != 0) dw_printf ("ERROR! line %d\n", __LINE__); if (strcmp(result, "!D8yKC<Hn[&{CG") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; }
/* with course/speed, freq, and comment! TODO: check on this 55 knots should be 63 MPH. we get 62. */ /* with course/speed, freq, and comment! Roundoff. 55 knots should be 63 MPH. we get 62. */
encode_position (1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&', encode_position (0, 1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 180, 55, 146.955, 74.4, -0.6, "River flooding", result, sizeof(result)); 0, 0, 0, NULL, 180, 55, 146.955, 74.4, -0.6, "River flooding", result, sizeof(result));
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, "!D8yKC<Hn[& !146.955MHz T074 -060 River flooding") != 0) dw_printf ("ERROR! line %d\n", __LINE__); if (strcmp(result, "!D8yKC<Hn[&NUG146.955MHz T074 -060 River flooding") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; }
//$ echo 'A>B:!D8yKC<Hn[&NUG146.955MHz T074 -060 River flooding' | decode_aprs
//A>B:!D8yKC<Hn[&NUG146.955MHz T074 -060 River flooding
//Position, I=Igte IGate R=RX T=1hopTX 2=2hopTX w/overlay D
//N 42 34.6100, W 071 26.4700, 62 MPH, course 180, 146.955 MHz, -600k, PL 74.4
// River flooding
// TODO: test alt; cs+alt // TODO: test alt; cs+alt
/*********** Object. ***********/ /*********** Object. ***********/
encode_object ("WB1GOF-C", 0, 0, 42+34.61/60, -(71+26.47/60), 'D', '&', encode_object ("WB1GOF-C", 0, 0, 42+34.61/60, -(71+26.47/60), 'D', '&',
0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result, sizeof(result)); 0, 0, 0, NULL, G_UNKNOWN, 0, 0, 0, 0, NULL, result, sizeof(result));
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, ";WB1GOF-C *111111z4234.61ND07126.47W&") != 0) dw_printf ("ERROR! line %d\n", __LINE__); if (strcmp(result, ";WB1GOF-C *111111z4234.61ND07126.47W&") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; }
// TODO: need more tests. // TODO: need more tests.
return(0); if (errors > 0) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Encode APRS test FAILED with %d errors.\n", errors);
exit (EXIT_FAILURE);
}
text_color_set (DW_COLOR_REC);
dw_printf ("Encode APRS test PASSED with no errors.\n");
exit (EXIT_SUCCESS);
} /* end main */ } /* end main */

View File

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

View File

@ -118,7 +118,7 @@ static void send_packet (char *str)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int c; int c;
int digit_optind = 0; //int digit_optind = 0;
int err; int err;
int packet_count = 0; int packet_count = 0;
int i; int i;
@ -156,14 +156,14 @@ int main(int argc, char **argv)
char output_file[256]; /* -o option */ char output_file[256]; /* -o option */
FILE *input_fp = NULL; /* File or NULL for built-in message */ FILE *input_fp = NULL; /* File or NULL for built-in message */
strcpy (output_file, ""); strlcpy (output_file, "", sizeof(output_file));
/* /*
* Parse the command line options. * Parse the command line options.
*/ */
while (1) { while (1) {
int this_option_optind = optind ? optind : 1; //int this_option_optind = optind ? optind : 1;
int option_index = 0; int option_index = 0;
static struct option long_options[] = { static struct option long_options[] = {
{"future1", 1, 0, 0}, {"future1", 1, 0, 0},
@ -333,7 +333,7 @@ int main(int argc, char **argv)
case 'o': /* -o for Output file */ case 'o': /* -o for Output file */
strcpy (output_file, optarg); strlcpy (output_file, optarg, sizeof(output_file));
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("Output file set to %s\n", output_file); dw_printf ("Output file set to %s\n", output_file);
break; break;
@ -399,9 +399,6 @@ int main(int argc, char **argv)
assert (modem.adev[0].num_channels == 1 || modem.adev[0].num_channels == 2); assert (modem.adev[0].num_channels == 1 || modem.adev[0].num_channels == 2);
assert (modem.adev[0].samples_per_sec >= MIN_SAMPLES_PER_SEC && modem.adev[0].samples_per_sec <= MAX_SAMPLES_PER_SEC); assert (modem.adev[0].samples_per_sec >= MIN_SAMPLES_PER_SEC && modem.adev[0].samples_per_sec <= MAX_SAMPLES_PER_SEC);
/* /*
* Get user packets(s) from file or stdin if specified. * Get user packets(s) from file or stdin if specified.
* "-n" option is ignored in this case. * "-n" option is ignored in this case.
@ -472,12 +469,16 @@ int main(int argc, char **argv)
if (modem.achan[0].modem_type == MODEM_SCRAMBLE) { if (modem.achan[0].modem_type == MODEM_SCRAMBLE) {
g_noise_level = 0.33 * (amplitude / 200.0) * ((float)i / packet_count); g_noise_level = 0.33 * (amplitude / 200.0) * ((float)i / packet_count);
} }
else if (modem.achan[0].baud < 600) {
/* About 2/3 should be decoded properly. */
g_noise_level = amplitude *.0048 * ((float)i / packet_count);
}
else { else {
/* About 2/3 should be decoded properly. */ /* About 2/3 should be decoded properly. */
g_noise_level = amplitude *.0023 * ((float)i / packet_count); g_noise_level = amplitude *.0023 * ((float)i / packet_count);
} }
sprintf (stemp, "WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog! %04d of %04d", i, packet_count); snprintf (stemp, sizeof(stemp), "WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog! %04d of %04d", i, packet_count);
send_packet (stemp); send_packet (stemp);
@ -689,6 +690,14 @@ static int audio_file_open (char *fname, struct audio_s *pa)
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
#define MY_RAND_MAX 0x7fffffff
static int seed = 1;
static int my_rand (void) {
seed = ((seed * 1103515245) + 12345) & MY_RAND_MAX;
return (seed);
}
int audio_put (int a, int c) int audio_put (int a, int c)
{ {
@ -712,7 +721,12 @@ int audio_put (int a, int c)
/* Add random noise to the signal. */ /* Add random noise to the signal. */
/* r should be in range of -1 .. +1. */ /* r should be in range of -1 .. +1. */
r = (rand() - RAND_MAX/2.0) / (RAND_MAX/2.0); /* Use own function instead of rand() from the C library. */
/* Windows and Linux have different results, messing up my self test procedure. */
/* No idea what Mac OSX and BSD might do. */
r = (my_rand() - MY_RAND_MAX/2.0) / (MY_RAND_MAX/2.0);
s += 5 * r * g_noise_level * 32767; s += 5 * r * g_noise_level * 32767;

View File

@ -426,8 +426,8 @@ int main ()
/* one channel. 2 times: one second of each tone. */ /* one channel. 2 times: one second of each tone. */
memset (&my_audio_config, 0, sizeof(my_audio_config)); memset (&my_audio_config, 0, sizeof(my_audio_config));
strcpy (my_audio_config.adev[0].adevice_in, DEFAULT_ADEVICE); strlcpy (my_audio_config.adev[0].adevice_in, DEFAULT_ADEVICE, sizeof(my_audio_config.adev[0].adevice_in));
strcpy (my_audio_config.adev[0].adevice_out, DEFAULT_ADEVICE); strlcpy (my_audio_config.adev[0].adevice_out, DEFAULT_ADEVICE, sizeof(my_audio_config.adev[0].adevice_out));
audio_open (&my_audio_config); audio_open (&my_audio_config);
gen_tone_init (&my_audio_config, 100); gen_tone_init (&my_audio_config, 100);
@ -448,8 +448,8 @@ int main ()
/* Now try stereo. */ /* Now try stereo. */
memset (&my_audio_config, 0, sizeof(my_audio_config)); memset (&my_audio_config, 0, sizeof(my_audio_config));
strcpy (my_audio_config.adev[0].adevice_in, DEFAULT_ADEVICE); strlcpy (my_audio_config.adev[0].adevice_in, DEFAULT_ADEVICE, sizeof(my_audio_config.adev[0].adevice_in));
strcpy (my_audio_config.adev[0].adevice_out, DEFAULT_ADEVICE); strlcpy (my_audio_config.adev[0].adevice_out, DEFAULT_ADEVICE, , sizeof(my_audio_config.adev[0].adevice_out));
my_audio_config.adev[0].num_channels = 2; my_audio_config.adev[0].num_channels = 2;
audio_open (&my_audio_config); audio_open (&my_audio_config);

View File

@ -178,7 +178,7 @@ long Set_Polar_Stereographic_Parameters (double a,
double essin; double essin;
double one_PLUS_es, one_MINUS_es; double one_PLUS_es, one_MINUS_es;
double pow_es; double pow_es;
double temp, temp_northing; double temp, temp_northing = 0;
double inv_f = 1 / f; double inv_f = 1 / f;
double mc; double mc;
// const double epsilon = 1.0e-2; // const double epsilon = 1.0e-2;

View File

@ -634,8 +634,6 @@ void dcd_change (int chan, int subchan, int state)
int hdlc_rec_data_detect_any (int chan) int hdlc_rec_data_detect_any (int chan)
{ {
int subchan;
assert (chan >= 0 && chan < MAX_CHANS); assert (chan >= 0 && chan < MAX_CHANS);
return (composite_dcd[chan] != 0); return (composite_dcd[chan] != 0);

View File

@ -226,7 +226,6 @@ void hdlc_rec2_block (rrbb_t block)
retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits; retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits;
int passall = save_audio_config_p->achan[chan].passall; int passall = save_audio_config_p->achan[chan].passall;
int ok; int ok;
int n;
#if DEBUGx #if DEBUGx
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
@ -332,7 +331,7 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t a
int ok; int ok;
int len, i,j; int len, i,j;
retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits; retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits;
int passall = save_audio_config_p->achan[chan].passall; //int passall = save_audio_config_p->achan[chan].passall;
len = rrbb_get_len(block); len = rrbb_get_len(block);
@ -881,7 +880,9 @@ static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, ret
int blen; /* Block length in bits. */ int blen; /* Block length in bits. */
int i; int i;
unsigned int raw; /* From demodulator. */ unsigned int raw; /* From demodulator. */
#if DEBUGx
int crc_failed = 1; int crc_failed = 1;
#endif
int retry_conf_mode = retry_conf.mode; int retry_conf_mode = retry_conf.mode;
int retry_conf_type = retry_conf.type; int retry_conf_type = retry_conf.type;
int retry_conf_retry = retry_conf.retry; int retry_conf_retry = retry_conf.retry;
@ -1119,7 +1120,9 @@ static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, ret
goto failure; goto failure;
} }
} else { } else {
#if DEBUGx
crc_failed = 0; crc_failed = 0;
#endif
goto failure; goto failure;
} }
failure: failure:
@ -1151,8 +1154,8 @@ failure:
} }
dw_printf ("\n"); dw_printf ("\n");
} }
#endif
end: end:
#endif
return 0; /* failure. */ return 0; /* failure. */
} /* end try_decode */ } /* end try_decode */

View File

@ -377,7 +377,6 @@ void igate_init (struct audio_s *p_audio_config, struct igate_config_s *p_igate_
pthread_t cmd_listen_tid; pthread_t cmd_listen_tid;
int e; int e;
#endif #endif
int j;
#if DEBUGx #if DEBUGx
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
@ -924,6 +923,7 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
*/ */
info_len = ax25_get_info (pp, &pinfo); info_len = ax25_get_info (pp, &pinfo);
(void)(info_len);
if ((p = strchr ((char*)pinfo, '\r')) != NULL) { if ((p = strchr ((char*)pinfo, '\r')) != NULL) {
#if DEBUGx #if DEBUGx
@ -1314,6 +1314,7 @@ static void xmit_packet (char *message)
ax25_format_addrs (pp3, payload); ax25_format_addrs (pp3, payload);
info_len = ax25_get_info (pp3, (unsigned char **)(&pinfo)); info_len = ax25_get_info (pp3, (unsigned char **)(&pinfo));
(void)(info_len);
strlcat (payload, pinfo, sizeof(payload)); strlcat (payload, pinfo, sizeof(payload));
#if DEBUGx #if DEBUGx
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);

12
kiss.c
View File

@ -303,7 +303,7 @@ void kiss_init (struct misc_config_s *mc)
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("Converted nullmodem device '%s'", mc->nullmodem); dw_printf ("Converted nullmodem device '%s'", mc->nullmodem);
if (n < 1) n = 1; if (n < 1) n = 1;
sprintf (mc->nullmodem, "/dev/ttyS%d", n-1); snprintf (mc->nullmodem, sizeof(mc->nullmodem), "/dev/ttyS%d", n-1);
dw_printf (" to Linux equivalent '%s'\n", mc->nullmodem); dw_printf (" to Linux equivalent '%s'\n", mc->nullmodem);
} }
#endif #endif
@ -374,7 +374,7 @@ static MYFDTYPE kiss_open_pt (void)
return (MYFDERROR); return (MYFDERROR);
} }
strcpy (pt_slave_name, pts); strlcpy (pt_slave_name, pts, sizeof(pt_slave_name));
e = tcgetattr (fd, &ts); e = tcgetattr (fd, &ts);
if (e != 0) { if (e != 0) {
@ -512,13 +512,13 @@ static MYFDTYPE kiss_open_nullmodem (char *devicename)
// Bug fix in release 1.1 - Need to munge name for COM10 and up. // Bug fix in release 1.1 - Need to munge name for COM10 and up.
// http://support.microsoft.com/kb/115831 // http://support.microsoft.com/kb/115831
strcpy (bettername, devicename); strlcpy (bettername, devicename, sizeof(bettername));
if (strncasecmp(devicename, "COM", 3) == 0) { if (strncasecmp(devicename, "COM", 3) == 0) {
int n; int n;
n = atoi(devicename+3); n = atoi(devicename+3);
if (n >= 10) { if (n >= 10) {
strcpy (bettername, "\\\\.\\"); strlcpy (bettername, "\\\\.\\", sizeof(bettername));
strcat (bettername, devicename); strlcat (bettername, devicename, sizeof(bettername));
} }
} }
@ -667,7 +667,7 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen)
if (kiss_debug) { if (kiss_debug) {
kiss_debug_print (TO_CLIENT, "Fake command prompt", fbuf, flen); kiss_debug_print (TO_CLIENT, "Fake command prompt", fbuf, flen);
} }
strcpy ((char *)kiss_buff, (char *)fbuf); strlcpy ((char *)kiss_buff, (char *)fbuf, sizeof(kiss_buff));
kiss_len = strlen((char *)kiss_buff); kiss_len = strlen((char *)kiss_buff);
} }
else { else {

View File

@ -70,10 +70,8 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <ctype.h> #include <ctype.h>
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
@ -91,7 +89,7 @@ void hex_dump (unsigned char *p, int len);
static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug); static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug);
#if TEST #if KISSTEST
#define dw_printf printf #define dw_printf printf
@ -275,7 +273,7 @@ static int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out)
} /* end kiss_unwrap */ } /* end kiss_unwrap */
#ifndef TEST #ifndef KISSTEST
@ -326,7 +324,7 @@ static int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out)
void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(int,unsigned char*,int)) void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(int,unsigned char*,int))
{ {
//printf ("kiss_frame ( %c %02x ) \n", ch, ch); //dw_printf ("kiss_frame ( %c %02x ) \n", ch, ch);
switch (kf->state) { switch (kf->state) {
@ -625,11 +623,11 @@ void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int
/* Quick unit test for encapsulate & unwrap */ /* Quick unit test for encapsulate & unwrap */
// $ gcc -DTEST kiss_frame.c ; ./a // $ gcc -DKISSTEST kiss_frame.c ; ./a
// Quick KISS test passed OK. // Quick KISS test passed OK.
#if TEST #if KISSTEST
main () main ()
@ -661,7 +659,8 @@ main ()
assert (dlen == 512); assert (dlen == 512);
assert (memcmp(din, dout, 512) == 0); assert (memcmp(din, dout, 512) == 0);
printf ("Quick KISS test passed OK.\n"); dw_printf ("Quick KISS test passed OK.\n");
exit (EXIT_SUCCESS);
} }
#endif #endif

View File

@ -275,7 +275,7 @@ static void * connect_listen_thread (void *arg)
SOCKET listen_sock; SOCKET listen_sock;
WSADATA wsadata; WSADATA wsadata;
sprintf (kiss_port_str, "%d", (int)(long)arg); snprintf (kiss_port_str, sizeof(kiss_port_str), "%d", (int)(long)arg);
#if DEBUG #if DEBUG
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("DEBUG: kissnet port = %d = '%s'\n", (int)(long)arg, kiss_port_str); dw_printf ("DEBUG: kissnet port = %d = '%s'\n", (int)(long)arg, kiss_port_str);
@ -493,7 +493,7 @@ void kissnet_send_rec_packet (int chan, unsigned char *fbuf, int flen)
if (kiss_debug) { if (kiss_debug) {
kiss_debug_print (TO_CLIENT, "Fake command prompt", fbuf, flen); kiss_debug_print (TO_CLIENT, "Fake command prompt", fbuf, flen);
} }
strcpy ((char *)kiss_buff, (char *)fbuf); strlcpy ((char *)kiss_buff, (char *)fbuf, sizeof(kiss_buff));
kiss_len = strlen((char *)kiss_buff); kiss_len = strlen((char *)kiss_buff);
} }
else { else {

229
latlong.c
View File

@ -55,6 +55,7 @@
* ambiguity - If 1, 2, 3, or 4, blank out that many trailing digits. * ambiguity - If 1, 2, 3, or 4, blank out that many trailing digits.
* *
* Outputs: slat - String in format ddmm.mm[NS] * Outputs: slat - String in format ddmm.mm[NS]
* Should always be exactly 8 characters + NUL.
* *
* Returns: None * Returns: None
* *
@ -89,7 +90,7 @@ void latitude_to_str (double dlat, int ambiguity, char *slat)
ideg = (int)dlat; ideg = (int)dlat;
dmin = (dlat - ideg) * 60.; dmin = (dlat - ideg) * 60.;
sprintf (smin, "%05.2f", dmin); snprintf (smin, sizeof(smin), "%05.2f", dmin);
/* Due to roundoff, 59.9999 could come out as "60.00" */ /* Due to roundoff, 59.9999 could come out as "60.00" */
if (smin[0] == '6') { if (smin[0] == '6') {
smin[0] = '0'; smin[0] = '0';
@ -124,6 +125,7 @@ void latitude_to_str (double dlat, int ambiguity, char *slat)
* ambiguity - If 1, 2, 3, or 4, blank out that many trailing digits. * ambiguity - If 1, 2, 3, or 4, blank out that many trailing digits.
* *
* Outputs: slat - String in format dddmm.mm[NS] * Outputs: slat - String in format dddmm.mm[NS]
* Should always be exactly 9 characters + NUL.
* *
* Returns: None * Returns: None
* *
@ -158,7 +160,7 @@ void longitude_to_str (double dlong, int ambiguity, char *slong)
ideg = (int)dlong; ideg = (int)dlong;
dmin = (dlong - ideg) * 60.; dmin = (dlong - ideg) * 60.;
sprintf (smin, "%05.2f", dmin); snprintf (smin, sizeof(smin), "%05.2f", dmin);
/* Due to roundoff, 59.9999 could come out as "60.00" */ /* Due to roundoff, 59.9999 could come out as "60.00" */
if (smin[0] == '6') { if (smin[0] == '6') {
smin[0] = '0'; smin[0] = '0';
@ -197,6 +199,7 @@ void longitude_to_str (double dlong, int ambiguity, char *slong)
* Inputs: dlat - Floating point degrees. * Inputs: dlat - Floating point degrees.
* *
* Outputs: slat - String in format yyyy. * Outputs: slat - String in format yyyy.
* Exactly 4 bytes, no nul terminator.
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
@ -243,6 +246,7 @@ void latitude_to_comp_str (double dlat, char *clat)
* Inputs: dlong - Floating point degrees. * Inputs: dlong - Floating point degrees.
* *
* Outputs: slat - String in format xxxx. * Outputs: slat - String in format xxxx.
* Exactly 4 bytes, no nul terminator.
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
@ -330,7 +334,7 @@ void latitude_to_nmea (double dlat, char *slat, char *hemi)
ideg = (int)dlat; ideg = (int)dlat;
dmin = (dlat - ideg) * 60.; dmin = (dlat - ideg) * 60.;
sprintf (smin, "%07.4f", dmin); snprintf (smin, sizeof(smin), "%07.4f", dmin);
/* Due to roundoff, 59.99999 could come out as "60.0000" */ /* Due to roundoff, 59.99999 could come out as "60.0000" */
if (smin[0] == '6') { if (smin[0] == '6') {
smin[0] = '0'; smin[0] = '0';
@ -391,7 +395,7 @@ void longitude_to_nmea (double dlong, char *slong, char *hemi)
ideg = (int)dlong; ideg = (int)dlong;
dmin = (dlong - ideg) * 60.; dmin = (dlong - ideg) * 60.;
sprintf (smin, "%07.4f", dmin); snprintf (smin, sizeof(smin), "%07.4f", dmin);
/* Due to roundoff, 59.99999 could come out as "60.0000" */ /* Due to roundoff, 59.99999 could come out as "60.0000" */
if (smin[0] == '6') { if (smin[0] == '6') {
smin[0] = '0'; smin[0] = '0';
@ -426,7 +430,6 @@ void longitude_to_nmea (double dlong, char *slong, char *hemi)
* Bugs: Very little validation of data. * Bugs: Very little validation of data.
* *
* Errors: Return constant G_UNKNOWN for any type of error. * Errors: Return constant G_UNKNOWN for any type of error.
* Could we use special "NaN" code?
* *
*------------------------------------------------------------------*/ *------------------------------------------------------------------*/
@ -490,7 +493,6 @@ double latitude_from_nmea (char *pstr, char *phemi)
* Bugs: Very little validation of data. * Bugs: Very little validation of data.
* *
* Errors: Return constant G_UNKNOWN for any type of error. * Errors: Return constant G_UNKNOWN for any type of error.
* Could we use special "NaN" code?
* *
*------------------------------------------------------------------*/ *------------------------------------------------------------------*/
@ -559,16 +561,14 @@ double ll_distance_km (double lat1, double lon1, double lat2, double lon2)
* *
* Purpose: Convert Maidenhead locator to latitude and longitude. * Purpose: Convert Maidenhead locator to latitude and longitude.
* *
* Inputs: maidenhead - 2, 4, 6, or 8 character grid square locator. * Inputs: maidenhead - 2, 4, 6, 8, 10, or 12 character grid square locator.
* *
* Outputs: dlat, dlon - Latitude and longitude. * Outputs: dlat, dlon - Latitude and longitude.
* Original values unchanged if error. * Original values unchanged if error.
* *
* Returns: 1 for success, 0 if error. * Returns: 1 for success, 0 if error.
* *
* Bug: This does not check for invalid values. * Reference: A good converter for spot checking. Only handles 4 or 6 characters :-(
*
* Reference: A good converter for spot checking:
* http://home.arcor.de/waldemar.kebsch/The_Makrothen_Contest/fmaidenhead.html * http://home.arcor.de/waldemar.kebsch/The_Makrothen_Contest/fmaidenhead.html
* *
* Rambling: What sort of resolution does this provide? * Rambling: What sort of resolution does this provide?
@ -577,14 +577,87 @@ double ll_distance_km (double lat1, double lon1, double lat2, double lon2)
* 6371 km * 2 * pi * 0.25 / 60 / 360 = 0.463 km. Is that right? * 6371 km * 2 * pi * 0.25 / 60 / 360 = 0.463 km. Is that right?
* *
* Using this calculator, http://www.earthpoint.us/Convert.aspx * Using this calculator, http://www.earthpoint.us/Convert.aspx
* It gives lower left corner of square rather than the middle. :-(
* *
* FN42MA00 --> 19T 334361mE 4651711mN * FN42MA00 --> 19T 334361mE 4651711mN
* FN42MA11 --> 19T 335062mE 4652157mN * FN42MA11 --> 19T 335062mE 4652157mN
* ------ ------- * ------ -------
* 701 446 meters difference. * 701 446 meters difference.
* *
* With another two pairs, we are down around 2 meters for latitude.
*
*------------------------------------------------------------------*/ *------------------------------------------------------------------*/
#define MH_MIN_PAIR 1
#define MH_MAX_PAIR 6
#define MH_UNITS ( 18 * 10 * 24 * 10 * 24 * 10 * 2 )
static const struct {
char *position;
char min_ch;
char max_ch;
int value;
} mh_pair[MH_MAX_PAIR] = {
{ "first", 'A', 'R', 10 * 24 * 10 * 24 * 10 * 2 },
{ "second", '0', '9', 24 * 10 * 24 * 10 * 2 },
{ "third", 'A', 'X', 10 * 24 * 10 * 2 },
{ "fourth", '0', '9', 24 * 10 * 2 },
{ "fifth", 'A', 'X', 10 * 2 },
{ "sixth", '0', '9', 2 } }; // Even so we can get center of square.
#if 1
int ll_from_grid_square (char *maidenhead, double *dlat, double *dlon)
{
char mh[16]; /* Local copy, changed to upper case. */
int ilat = 0, ilon = 0; /* In units in table above. */
char *p;
int n;
int np = strlen(maidenhead) / 2; /* Number of pairs of characters. */
if (strlen(maidenhead) %2 != 0 || np < MH_MIN_PAIR || np > MH_MAX_PAIR) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Maidenhead locator \"%s\" must from 1 to %d pairs of characters.\n", maidenhead, MH_MAX_PAIR);
return (0);
}
strlcpy (mh, maidenhead, sizeof(mh));
for (p = mh; *p != '\0'; p++) {
if (islower(*p)) *p = toupper(*p);
}
for (n = 0; n < np; n++) {
if (mh[2*n] < mh_pair[n].min_ch || mh[2*n] > mh_pair[n].max_ch ||
mh[2*n+1] < mh_pair[n].min_ch || mh[2*n+1] > mh_pair[n].max_ch) {
text_color_set(DW_COLOR_ERROR);
dw_printf("The %s pair of characters in Maidenhead locator \"%s\" must be in range of %c thru %c.\n",
mh_pair[n].position, maidenhead, mh_pair[n].min_ch, mh_pair[n].max_ch);
return (0);
}
ilon += ( mh[2*n] - mh_pair[n].min_ch ) * mh_pair[n].value;
ilat += ( mh[2*n+1] - mh_pair[n].min_ch ) * mh_pair[n].value;
if (n == np-1) { // If last pair, take center of square.
ilon += mh_pair[n].value / 2;
ilat += mh_pair[n].value / 2;
}
}
*dlat = (double)ilat / MH_UNITS * 180. - 90.;
*dlon = (double)ilon / MH_UNITS * 360. - 180.;
//text_color_set(DW_COLOR_DEBUG);
//dw_printf("DEBUG: Maidenhead conversion \"%s\" -> %.6f %.6f\n", maidenhead, *dlat, *dlon);
return (1);
}
#else
int ll_from_grid_square (char *maidenhead, double *dlat, double *dlon) int ll_from_grid_square (char *maidenhead, double *dlat, double *dlon)
{ {
double lat, lon; double lat, lon;
@ -687,6 +760,142 @@ int ll_from_grid_square (char *maidenhead, double *dlat, double *dlon)
return (1); return (1);
} }
#endif
/* end ll_from_grid_square */ /* end ll_from_grid_square */
#if LLTEST
/* gcc -o lltest -DLLTEST latlong.c textcolor.o misc.a && lltest */
int main (int argc, char *argv[])
{
char result[20];
int errors = 0;
int ok;
double dlat, dlon;
/* Latitude to APRS format. */
latitude_to_str (45.25, 0, result);
if (strcmp(result, "4515.00N") != 0) { errors++; dw_printf ("Error 1.1: Did not expect \"%s\"\n", result); }
latitude_to_str (-45.25, 0, result);
if (strcmp(result, "4515.00S") != 0) { errors++; dw_printf ("Error 1.2: Did not expect \"%s\"\n", result); }
latitude_to_str (45.999830, 0, result);
if (strcmp(result, "4559.99N") != 0) { errors++; dw_printf ("Error 1.3: Did not expect \"%s\"\n", result); }
latitude_to_str (45.99999, 0, result);
if (strcmp(result, "4600.00N") != 0) { errors++; dw_printf ("Error 1.4: Did not expect \"%s\"\n", result); }
latitude_to_str (45.999830, 1, result);
if (strcmp(result, "4559.9 N") != 0) { errors++; dw_printf ("Error 1.5: Did not expect \"%s\"\n", result); }
latitude_to_str (45.999830, 2, result);
if (strcmp(result, "4559. N") != 0) { errors++; dw_printf ("Error 1.6: Did not expect \"%s\"\n", result); }
latitude_to_str (45.999830, 3, result);
if (strcmp(result, "455 . N") != 0) { errors++; dw_printf ("Error 1.7: Did not expect \"%s\"\n", result); }
latitude_to_str (45.999830, 4, result);
if (strcmp(result, "45 . N") != 0) { errors++; dw_printf ("Error 1.8: Did not expect \"%s\"\n", result); }
/* Longitude to APRS format. */
longitude_to_str (45.25, 0, result);
if (strcmp(result, "04515.00E") != 0) { errors++; dw_printf ("Error 2.1: Did not expect \"%s\"\n", result); }
longitude_to_str (-45.25, 0, result);
if (strcmp(result, "04515.00W") != 0) { errors++; dw_printf ("Error 2.2: Did not expect \"%s\"\n", result); }
longitude_to_str (45.999830, 0, result);
if (strcmp(result, "04559.99E") != 0) { errors++; dw_printf ("Error 2.3: Did not expect \"%s\"\n", result); }
longitude_to_str (45.99999, 0, result);
if (strcmp(result, "04600.00E") != 0) { errors++; dw_printf ("Error 2.4: Did not expect \"%s\"\n", result); }
longitude_to_str (45.999830, 1, result);
if (strcmp(result, "04559.9 E") != 0) { errors++; dw_printf ("Error 2.5: Did not expect \"%s\"\n", result); }
longitude_to_str (45.999830, 2, result);
if (strcmp(result, "04559. E") != 0) { errors++; dw_printf ("Error 2.6: Did not expect \"%s\"\n", result); }
longitude_to_str (45.999830, 3, result);
if (strcmp(result, "0455 . E") != 0) { errors++; dw_printf ("Error 2.7: Did not expect \"%s\"\n", result); }
longitude_to_str (45.999830, 4, result);
if (strcmp(result, "045 . E") != 0) { errors++; dw_printf ("Error 2.8: Did not expect \"%s\"\n", result); }
/* Compressed format. */
/* Protocol spec example has <*e7 but I got <*e8 due to rounding rather than truncation to integer. */
memset(result, 0, sizeof(result));
latitude_to_comp_str (-90.0, result);
if (strcmp(result, "{{!!") != 0) { errors++; dw_printf ("Error 3.1: Did not expect \"%s\"\n", result); }
latitude_to_comp_str (49.5, result);
if (strcmp(result, "5L!!") != 0) { errors++; dw_printf ("Error 3.2: Did not expect \"%s\"\n", result); }
latitude_to_comp_str (90.0, result);
if (strcmp(result, "!!!!") != 0) { errors++; dw_printf ("Error 3.3: Did not expect \"%s\"\n", result); }
longitude_to_comp_str (-180.0, result);
if (strcmp(result, "!!!!") != 0) { errors++; dw_printf ("Error 3.4: Did not expect \"%s\"\n", result); }
longitude_to_comp_str (-72.75, result);
if (strcmp(result, "<*e8") != 0) { errors++; dw_printf ("Error 3.5: Did not expect \"%s\"\n", result); }
longitude_to_comp_str (180.0, result);
if (strcmp(result, "{{!!") != 0) { errors++; dw_printf ("Error 3.6: Did not expect \"%s\"\n", result); }
// to be continued for others... NMEA...
/* Maidenhead locator to lat/long. */
ok = ll_from_grid_square ("BL11", &dlat, &dlon);
if (!ok || dlat < 20.4999999 || dlat > 21.5000001 || dlon < -157.0000001 || dlon > -156.9999999) { errors++; dw_printf ("Error 7.1: Did not expect %.6f %.6f\n", dlat, dlon); }
ok = ll_from_grid_square ("BL11BH", &dlat, &dlon);
if (!ok || dlat < 21.31249 || dlat > 21.31251 || dlon < -157.87501 || dlon > -157.87499) { errors++; dw_printf ("Error 7.2: Did not expect %.6f %.6f\n", dlat, dlon); }
#if 0 // TODO: add more test cases after comparing results with other cconverters.
// Many other converters are limited to smaller number of characters,
// or return corner rather than center of square, or return 3 decimal places for degrees.
ok = ll_from_grid_square ("BL11BH16", &dlat, &dlon);
if (!ok || dlat < 21.? || dlat > 21.? || dlon < -157.? || dlon > -157.?) { errors++; dw_printf ("Error 7.3: Did not expect %.6f %.6f\n", dlat, dlon); }
ok = ll_from_grid_square ("BL11BH16oo", &dlat, &dlon);
if (!ok || dlat < 21.? || dlat > 21.? || dlon < -157.? || dlon > -157.?) { errors++; dw_printf ("Error 7.4: Did not expect %.6f %.6f\n", dlat, dlon); }
ok = ll_from_grid_square ("BL11BH16oo66", &dlat, &dlon);
if (!ok || dlat < 21.? || dlat > 21.? || dlon < -157.? || dlon > -157.?) { errors++; dw_printf ("Error 7.5: Did not expect %.6f %.6f\n", dlat, dlon); }
#endif
if (errors > 0) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("\nLocation Coordinate Conversion Test - FAILED!\n");
exit (EXIT_FAILURE);
}
text_color_set (DW_COLOR_REC);
dw_printf ("\nLocation Coordinate Conversion Test - SUCCESS!\n");
exit (EXIT_SUCCESS);
}
#endif
/* end latlong.c */ /* end latlong.c */

74
log.c
View File

@ -54,7 +54,7 @@
* CSV format needs quotes if value contains comma or quote. * CSV format needs quotes if value contains comma or quote.
*/ */
static void quote_for_csv (char *out, const char *in) { static void quote_for_csv (char *out, size_t outsize, const char *in) {
const char *p; const char *p;
char *q = out; char *q = out;
int need_quote = 0; int need_quote = 0;
@ -66,6 +66,8 @@ static void quote_for_csv (char *out, const char *in) {
} }
} }
// BUG: need to avoid buffer overflow on "out". *strcpy*
if (need_quote) { if (need_quote) {
*q++ = '"'; *q++ = '"';
for (p = in; *p != '\0'; p++) { for (p = in; *p != '\0'; p++) {
@ -78,7 +80,7 @@ static void quote_for_csv (char *out, const char *in) {
*q = '\0'; *q = '\0';
} }
else { else {
strcpy (out, in); strlcpy (out, in, outsize);
} }
} }
@ -108,9 +110,9 @@ void log_init (char *path)
{ {
struct stat st; struct stat st;
strcpy (g_log_dir, ""); strlcpy (g_log_dir, "", sizeof(g_log_dir));
g_log_fp = NULL; g_log_fp = NULL;
strcpy (g_open_fname, ""); strlcpy (g_open_fname, "", sizeof(g_open_fname));
if (strlen(path) == 0) { if (strlen(path) == 0) {
return; return;
@ -120,13 +122,13 @@ void log_init (char *path)
// Exists, but is it a directory? // Exists, but is it a directory?
if (S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) {
// Specified directory exists. // Specified directory exists.
strcpy (g_log_dir, path); strlcpy (g_log_dir, path, sizeof(g_log_dir));
} }
else { else {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Log file location \"%s\" is not a directory.\n", path); dw_printf ("Log file location \"%s\" is not a directory.\n", path);
dw_printf ("Using current working directory \".\" instead.\n"); dw_printf ("Using current working directory \".\" instead.\n");
strcpy (g_log_dir, "."); strlcpy (g_log_dir, ".", sizeof(g_log_dir));
} }
} }
else { else {
@ -141,14 +143,14 @@ void log_init (char *path)
// Success. // Success.
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("Log file location \"%s\" has been created.\n", path); dw_printf ("Log file location \"%s\" has been created.\n", path);
strcpy (g_log_dir, path); strlcpy (g_log_dir, path, sizeof(g_log_dir));
} }
else { else {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Failed to create log file location \"%s\".\n", path); dw_printf ("Failed to create log file location \"%s\".\n", path);
dw_printf ("%s\n", strerror(errno)); dw_printf ("%s\n", strerror(errno));
dw_printf ("Using current working directory \".\" instead.\n"); dw_printf ("Using current working directory \".\" instead.\n");
strcpy (g_log_dir, "."); strlcpy (g_log_dir, ".", sizeof(g_log_dir));
} }
} }
} }
@ -185,7 +187,7 @@ void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_
// Generate the file name from current date, UTC. // Generate the file name from current date, UTC.
now = time(NULL); now = time(NULL);
gmtime_r (&now, &tm); (void)gmtime_r (&now, &tm);
// Microsoft doesn't recognize %F as equivalent to %Y-%m-%d // Microsoft doesn't recognize %F as equivalent to %Y-%m-%d
@ -204,13 +206,13 @@ void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_
struct stat st; struct stat st;
int already_there; int already_there;
strcpy (full_path, g_log_dir); strlcpy (full_path, g_log_dir, sizeof(full_path));
#if __WIN32__ #if __WIN32__
strcat (full_path, "\\"); strlcat (full_path, "\\", sizeof(full_path));
#else #else
strcat (full_path, "/"); strlcat (full_path, "/", sizeof(full_path));
#endif #endif
strcat (full_path, fname); strlcat (full_path, fname, sizeof(full_path));
// See if it already exists. // See if it already exists.
// This is used later to write a header if it did not exist already. // This is used later to write a header if it did not exist already.
@ -223,13 +225,13 @@ void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_
g_log_fp = fopen (full_path, "a"); g_log_fp = fopen (full_path, "a");
if (g_log_fp != NULL) { if (g_log_fp != NULL) {
strcpy (g_open_fname, fname); strlcpy (g_open_fname, fname, sizeof(g_open_fname));
} }
else { else {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf("Can't open log file \"%s\" for write.\n", full_path); dw_printf("Can't open log file \"%s\" for write.\n", full_path);
dw_printf ("%s\n", strerror(errno)); dw_printf ("%s\n", strerror(errno));
strcpy (g_open_fname, ""); strlcpy (g_open_fname, "", sizeof(g_open_fname));
return; return;
} }
@ -267,12 +269,12 @@ void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_
/* Who are we hearing? Original station or digipeater? */ /* Who are we hearing? Original station or digipeater? */
/* Similar code in direwolf.c. Combine into one function? */ /* Similar code in direwolf.c. Combine into one function? */
strcpy(heard, ""); strlcpy(heard, "", sizeof(heard));
if (pp != NULL) { if (pp != NULL) {
if (ax25_get_num_addr(pp) == 0) { if (ax25_get_num_addr(pp) == 0) {
/* Not AX.25. No station to display below. */ /* Not AX.25. No station to display below. */
h = -1; h = -1;
strcpy (heard, ""); strlcpy (heard, "", sizeof(heard));
} }
else { else {
h = ax25_get_heard(pp); h = ax25_get_heard(pp);
@ -285,7 +287,7 @@ void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_
heard[5] == '\0') { heard[5] == '\0') {
ax25_get_addr_with_ssid(pp, h-1, heard); ax25_get_addr_with_ssid(pp, h-1, heard);
strcat (heard, "?"); strlcat (heard, "?", sizeof(heard));
} }
} }
@ -294,35 +296,35 @@ void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_
// Might need to quote anything that could contain comma or quote. // Might need to quote anything that could contain comma or quote.
strcpy(sdti, ""); strlcpy(sdti, "", sizeof(sdti));
if (pp != NULL) { if (pp != NULL) {
stemp[0] = ax25_get_dti(pp); stemp[0] = ax25_get_dti(pp);
stemp[1] = '\0'; stemp[1] = '\0';
quote_for_csv (sdti, stemp); quote_for_csv (sdti, sizeof(sdti), stemp);
} }
quote_for_csv (sname, (strlen(A->g_name) > 0) ? A->g_name : A->g_src); quote_for_csv (sname, sizeof(sname), (strlen(A->g_name) > 0) ? A->g_name : A->g_src);
stemp[0] = A->g_symbol_table; stemp[0] = A->g_symbol_table;
stemp[1] = A->g_symbol_code; stemp[1] = A->g_symbol_code;
stemp[2] = '\0'; stemp[2] = '\0';
quote_for_csv (ssymbol, stemp); quote_for_csv (ssymbol, sizeof(ssymbol), stemp);
quote_for_csv (smfr, A->g_mfr); quote_for_csv (smfr, sizeof(smfr), A->g_mfr);
quote_for_csv (sstatus, A->g_mic_e_status); quote_for_csv (sstatus, sizeof(sstatus), A->g_mic_e_status);
quote_for_csv (stelemetry, A->g_telemetry); quote_for_csv (stelemetry, sizeof(stelemetry), A->g_telemetry);
quote_for_csv (scomment, A->g_comment); quote_for_csv (scomment, sizeof(scomment), A->g_comment);
strcpy (slat, ""); if (A->g_lat != G_UNKNOWN) sprintf (slat, "%.6f", A->g_lat); strlcpy (slat, "", sizeof(slat)); if (A->g_lat != G_UNKNOWN) snprintf (slat, sizeof(slat), "%.6f", A->g_lat);
strcpy (slon, ""); if (A->g_lon != G_UNKNOWN) sprintf (slon, "%.6f", A->g_lon); strlcpy (slon, "", sizeof(slon)); if (A->g_lon != G_UNKNOWN) snprintf (slon, sizeof(slon), "%.6f", A->g_lon);
strcpy (sspd, ""); if (A->g_speed != G_UNKNOWN) sprintf (sspd, "%.1f", DW_MPH_TO_KNOTS(A->g_speed)); strlcpy (sspd, "", sizeof(sspd)); if (A->g_speed_mph != G_UNKNOWN) snprintf (sspd, sizeof(sspd), "%.1f", DW_MPH_TO_KNOTS(A->g_speed_mph));
strcpy (scse, ""); if (A->g_course != G_UNKNOWN) sprintf (scse, "%.1f", A->g_course); strlcpy (scse, "", sizeof(scse)); if (A->g_course != G_UNKNOWN) snprintf (scse, sizeof(scse), "%.1f", A->g_course);
strcpy (salt, ""); if (A->g_altitude != G_UNKNOWN) sprintf (salt, "%.1f", DW_FEET_TO_METERS(A->g_altitude)); strlcpy (salt, "", sizeof(salt)); if (A->g_altitude_ft != G_UNKNOWN) snprintf (salt, sizeof(salt), "%.1f", DW_FEET_TO_METERS(A->g_altitude_ft));
strcpy (sfreq, ""); if (A->g_freq != G_UNKNOWN) sprintf (sfreq, "%.3f", A->g_freq); strlcpy (sfreq, "", sizeof(sfreq)); if (A->g_freq != G_UNKNOWN) snprintf (sfreq, sizeof(sfreq), "%.3f", A->g_freq);
strcpy (soffs, ""); if (A->g_offset != G_UNKNOWN) sprintf (soffs, "%+d", A->g_offset); strlcpy (soffs, "", sizeof(soffs)); if (A->g_offset != G_UNKNOWN) snprintf (soffs, sizeof(soffs), "%+d", A->g_offset);
strcpy (stone, ""); if (A->g_tone != G_UNKNOWN) sprintf (stone, "%.1f", A->g_tone); strlcpy (stone, "", sizeof(stone)); if (A->g_tone != G_UNKNOWN) snprintf (stone, sizeof(stone), "%.1f", A->g_tone);
if (A->g_dcs != G_UNKNOWN) sprintf (stone, "D%03o", A->g_dcs); if (A->g_dcs != G_UNKNOWN) snprintf (stone, sizeof(stone), "D%03o", A->g_dcs);
fprintf (g_log_fp, "%d,%d,%s,%s,%s,%s,%d,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", fprintf (g_log_fp, "%d,%d,%s,%s,%s,%s,%d,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
chan, (int)now, itime, chan, (int)now, itime,
@ -359,7 +361,7 @@ void log_term (void)
fclose (g_log_fp); fclose (g_log_fp);
g_log_fp = NULL; g_log_fp = NULL;
strcpy (g_open_fname, ""); strlcpy (g_open_fname, "", sizeof(g_open_fname));
} }
} /* end log_term */ } /* end log_term */

View File

@ -27,6 +27,8 @@
char *strsep(char **stringp, const char *delim); char *strsep(char **stringp, const char *delim);
#endif #endif
#include "direwolf.h"
/* /*
* Information we gather for each thing. * Information we gather for each thing.
@ -228,6 +230,20 @@ static void read_csv(FILE *fp)
ptelemetry = strsep(&next,"\t"); /* Currently unused. Add to description? */ ptelemetry = strsep(&next,"\t"); /* Currently unused. Add to description? */
pcomment = strsep(&next,"\t"); pcomment = strsep(&next,"\t");
/* Suppress the 'set but not used' warnings. */
/* Alternatively, we might use __attribute__((unused)) */
(void)(ptelemetry);
(void)(psystem);
(void)(psymbol);
(void)(pdti);
(void)(perror);
(void)(plevel);
(void)(pheard);
(void)(psource);
(void)(putime);
/* /*
* Skip header line with names of fields. * Skip header line with names of fields.
*/ */
@ -265,42 +281,42 @@ static void read_csv(FILE *fp)
if (pfreq != NULL && strlen(pfreq) > 0) { if (pfreq != NULL && strlen(pfreq) > 0) {
freq = atof(pfreq); freq = atof(pfreq);
sprintf (desc, "%.3f MHz", freq); snprintf (desc, sizeof(desc), "%.3f MHz", freq);
} }
else { else {
strcpy (desc, ""); strlcpy (desc, "", sizeof(desc));
} }
if (poffset != NULL && strlen(poffset) > 0) { if (poffset != NULL && strlen(poffset) > 0) {
offset = atoi(poffset); offset = atoi(poffset);
if (offset != 0 && offset % 1000 == 0) { if (offset != 0 && offset % 1000 == 0) {
sprintf (stemp, "%+dM", offset / 1000); snprintf (stemp, sizeof(stemp), "%+dM", offset / 1000);
} }
else { else {
sprintf (stemp, "%+dk", offset); snprintf (stemp, sizeof(stemp), "%+dk", offset);
} }
if (strlen(desc) > 0) strcat (desc, " "); if (strlen(desc) > 0) strlcat (desc, " ", sizeof(desc));
strcat (desc, stemp); strlcat (desc, stemp, sizeof(desc));
} }
if (ptone != NULL && strlen(ptone) > 0) { if (ptone != NULL && strlen(ptone) > 0) {
if (*ptone == 'D') { if (*ptone == 'D') {
sprintf (stemp, "DCS %s", ptone+1); snprintf (stemp, sizeof(stemp), "DCS %s", ptone+1);
} }
else { else {
sprintf (stemp, "PL %s", ptone); snprintf (stemp, sizeof(stemp), "PL %s", ptone);
} }
if (strlen(desc) > 0) strcat (desc, " "); if (strlen(desc) > 0) strlcat (desc, " ", sizeof(desc));
strcat (desc, stemp); strlcat (desc, stemp, sizeof(desc));
} }
strcpy (comment, ""); strlcpy (comment, "", sizeof(comment));
if (pstatus != NULL && strlen(pstatus) > 0) { if (pstatus != NULL && strlen(pstatus) > 0) {
strcpy (comment, pstatus); strlcpy (comment, pstatus, sizeof(comment));
} }
if (pcomment != NULL && strlen(pcomment) > 0) { if (pcomment != NULL && strlen(pcomment) > 0) {
if (strlen(comment) > 0) strcat (comment, ", "); if (strlen(comment) > 0) strlcat (comment, ", ", sizeof(comment));
strcat (comment, pcomment); strlcat (comment, pcomment, sizeof(comment));
} }
if (num_things == max_things) { if (num_things == max_things) {

View File

@ -77,7 +77,9 @@ u = Display non-ASCII text in hexadecimal.
.P .P
p = Packet dump in hexadecimal. p = Packet dump in hexadecimal.
.P .P
t = GPS Tracker. g = GPS interface.
.P
t = Tracker beacon.
.P .P
o = Output controls such as PTT and DCD. o = Output controls such as PTT and DCD.
.RE .RE

View File

@ -36,7 +36,7 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <sys/time.h> #include <time.h>
#include <math.h> #include <math.h>

328
nmea.c
View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2014 John Langner, WB2OSZ // Copyright (C) 2014, 2015 John Langner, WB2OSZ
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
@ -20,6 +20,9 @@
//#define DEBUG 1 //#define DEBUG 1
// TODO: rename this to waypoint & integrate.
/*------------------------------------------------------------------ /*------------------------------------------------------------------
* *
* Module: nmea.c * Module: nmea.c
@ -73,13 +76,9 @@ static MYFDTYPE nmea_port_fd = MYFDERROR;
static void nmea_send_sentence (char *sent); static void nmea_send_sentence (char *sent);
#if __WIN32__
static unsigned __stdcall nmea_listen_thread (void *arg);
#else
static void * nmea_listen_thread (void *arg);
#endif
static void nmea_parse_gps (char *sentence);
//static void nmea_parse_gps (char *sentence);
static int nmea_debug = 0; /* Print information flowing from and to attached device. */ static int nmea_debug = 0; /* Print information flowing from and to attached device. */
@ -102,7 +101,6 @@ void nmea_set_debug (int n)
* *
* *
* Description: (1) Open serial port device. * Description: (1) Open serial port device.
* (2) Start a new thread to listen for GPS receiver.
* *
*---------------------------------------------------------------*/ *---------------------------------------------------------------*/
@ -110,12 +108,6 @@ void nmea_set_debug (int n)
void nmea_init (struct misc_config_s *mc) void nmea_init (struct misc_config_s *mc)
{ {
#if __WIN32__
HANDLE nmea_listen_th;
#else
pthread_t nmea_listen_tid;
#endif
/* /*
* Open serial port connection. * Open serial port connection.
* 4800 baud is standard for GPS. * 4800 baud is standard for GPS.
@ -125,25 +117,8 @@ void nmea_init (struct misc_config_s *mc)
nmea_port_fd = serial_port_open (mc->nmea_port, 4800); nmea_port_fd = serial_port_open (mc->nmea_port, 4800);
if (nmea_port_fd != MYFDERROR) {
#if __WIN32__
nmea_listen_th = (HANDLE)_beginthreadex (NULL, 0, nmea_listen_thread, (void*)(long)nmea_port_fd, 0, NULL);
if (nmea_listen_th == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Could not create NMEA listening thread.\n");
return;
}
#else
int e;
e = pthread_create (&nmea_listen_tid, NULL, nmea_listen_thread, (void*)(long)nmea_port_fd);
if (e != 0) {
text_color_set(DW_COLOR_ERROR);
perror("Could not create NMEA listening thread.");
} }
#endif
}
}
#if DEBUG #if DEBUG
@ -231,7 +206,7 @@ void nmea_send_waypoint (char *wname_in, double dlat, double dlong, char symtab,
char sspeed[12]; /* speed as string, empty if unknown */ char sspeed[12]; /* speed as string, empty if unknown */
char scourse[12]; /* course as string, empty if unknown */ char scourse[12]; /* course as string, empty if unknown */
int grm_sym; /* Garmin symbol code. */ int grm_sym; /* Garmin symbol code. */
char sicon[4]; /* Magellan icon string */ char sicon[5]; /* Magellan icon string */
char stime[8]; char stime[8];
char sdate[8]; char sdate[8];
char *p; char *p;
@ -265,7 +240,7 @@ void nmea_send_waypoint (char *wname_in, double dlat, double dlong, char symtab,
* *99 is checksum * *99 is checksum
*/ */
sprintf (sentence, "$GPWPL,%s,%s,%s,%s,%s", slat, slat_ns, slong, slong_ew, wname); snprintf (sentence, sizeof(sentence), "$GPWPL,%s,%s,%s,%s,%s", slat, slat_ns, slong, slong_ew, wname);
append_checksum (sentence); append_checksum (sentence);
nmea_send_sentence (sentence); nmea_send_sentence (sentence);
@ -291,11 +266,11 @@ void nmea_send_waypoint (char *wname_in, double dlat, double dlong, char symtab,
strcpy (salt, ""); strcpy (salt, "");
} }
else { else {
sprintf (salt, "%.1f", alt); snprintf (salt, sizeof(salt), "%.1f", alt);
} }
grm_sym = 0x1234; // TODO grm_sym = 0x1234; // TODO
sprintf (sentence, "$PGRMW,%s,%s,%04X,%s", wname, salt, grm_sym, comment); snprintf (sentence, sizeof(sentence), "$PGRMW,%s,%s,%04X,%s", wname, salt, grm_sym, comment);
append_checksum (sentence); append_checksum (sentence);
nmea_send_sentence (sentence); nmea_send_sentence (sentence);
@ -319,8 +294,8 @@ void nmea_send_waypoint (char *wname_in, double dlat, double dlong, char symtab,
// TODO: icon // TODO: icon
sprintf (sicon, "??"); snprintf (sicon, sizeof(sicon), "??");
sprintf (sentence, "$PMGNWPL,%s,%s,%s,%s,%s,M,%s,%s,%s", snprintf (sentence, sizeof(sentence), "$PMGNWPL,%s,%s,%s,%s,%s,M,%s,%s,%s",
slat, slat_ns, slong, slong_ew, salt, wname, comment, sicon); slat, slat_ns, slong, slong_ew, salt, wname, comment, sicon);
append_checksum (sentence); append_checksum (sentence);
nmea_send_sentence (sentence); nmea_send_sentence (sentence);
@ -357,13 +332,13 @@ void nmea_send_waypoint (char *wname_in, double dlat, double dlong, char symtab,
strcpy (sspeed, ""); strcpy (sspeed, "");
} }
else { else {
sprintf (sspeed, "%.1f", speed); snprintf (sspeed, sizeof(sspeed), "%.1f", speed);
} }
if (course == G_UNKNOWN) { if (course == G_UNKNOWN) {
strcpy (scourse, ""); strcpy (scourse, "");
} }
else { else {
sprintf (scourse, "%.1f", course); snprintf (scourse, sizeof(scourse), "%.1f", course);
} }
// TODO: how to handle time & date ??? // TODO: how to handle time & date ???
@ -371,7 +346,7 @@ void nmea_send_waypoint (char *wname_in, double dlat, double dlong, char symtab,
strcpy (stime, "123456"); strcpy (stime, "123456");
strcpy (sdate, "123456"); strcpy (sdate, "123456");
sprintf (sentence, "$PKWDWPL,%s,V,%s,%s,%s,%s,%s,%s,%s,%s,%s,%c%c", snprintf (sentence, sizeof(sentence), "$PKWDWPL,%s,V,%s,%s,%s,%s,%s,%s,%s,%s,%s,%c%c",
stime, slat, slat_ns, slong, slong_ew, stime, slat, slat_ns, slong, slong_ew,
sspeed, scourse, sdate, salt, wname, symtab, symbol); sspeed, scourse, sdate, salt, wname, symtab, symbol);
append_checksum (sentence); append_checksum (sentence);
@ -434,7 +409,7 @@ http://gpsbabel.sourcearchive.com/documentation/1.3.7~cvs1/magproto_8c-source.ht
&lngdeg,&lngdir, &lngdeg,&lngdir,
&alt,&altunits,shortname,descr); then icon &alt,&altunits,shortname,descr); then icon
sprintf(obuf, "PMGNWPL,%4.3f,%c,%09.3f,%c,%07.0f,M,%-.*s,%-.46s,%s", snprintf(obuf, sizeof(), "PMGNWPL,%4.3f,%c,%09.3f,%c,%07.0f,M,%-.*s,%-.46s,%s",
lat, ilat < 0 ? 'S' : 'N', lat, ilat < 0 ? 'S' : 'N',
lon, ilon < 0 ? 'W' : 'E', lon, ilon < 0 ? 'W' : 'E',
waypointp->altitude == unknown_alt ? waypointp->altitude == unknown_alt ?
@ -485,277 +460,6 @@ static void nmea_send_sentence (char *sent)
/*-------------------------------------------------------------------
*
* Name: nmea_listen_thread
*
* Purpose: Wait for messages from GPS receiver.
*
* Inputs: arg - File descriptor for reading.
*
* Outputs: pt_slave_fd - File descriptor for communicating with client app.
*
* Description: Process messages from the client application.
*
*--------------------------------------------------------------------*/
// Maximum length of message from GPS receiver.
// 82 according to some people. Larger to be safe.
#define NMEA_MAX_LEN 120
static char gps_msg[NMEA_MAX_LEN];
int gps_msg_len = 0;
#if __WIN32__
static unsigned __stdcall nmea_listen_thread (void *arg)
#else
static void * nmea_listen_thread (void *arg)
#endif
{
MYFDTYPE fd = (MYFDTYPE)(long)arg;
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("nmea_listen_thread ( %d )\n", fd);
#endif
while (1) {
int ch;
ch = serial_port_get1(fd);
// TODO: if ch < 0, terminate thread ...
//CloseHandle (fd);
//fd = MYFDERROR;
//pthread_exit (NULL);
//text_color_set(DW_COLOR_ERROR);
//dw_printf ("\nError trying to read from GPS receiver. Closing connection %d.\n\n", fd);
//close (fd); -> serial_port_close(fd)
//fd = MYFDERROR;
//pthread_exit (NULL);
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] == '$') {
nmea_parse_gps (gps_msg);
text_color_set(DW_COLOR_REC);
dw_printf ("%s\n", gps_msg);
}
gps_msg_len = 0;
gps_msg[gps_msg_len] = '\0';
}
else {
if (gps_msg_len < NMEA_MAX_LEN-1) {
gps_msg[gps_msg_len++] = ch;
gps_msg[gps_msg_len] = '\0';
}
}
} /* while (1) */
return (NULL); /* Unreachable but avoids compiler warning. */
}
/*-------------------------------------------------------------------
*
* Name: ...
*
* Purpose: ...
*
* Inputs: ...
*
* Outputs: ...
*
* Description: ...
*
*--------------------------------------------------------------------*/
static void remove_checksum (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) {
text_color_set (DW_COLOR_INFO);
dw_printf("Missing GPS checksum.\n");
return;
}
if (cs != strtoul(p+1, NULL, 16)) {
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 nmea_parse_gps (char *sentence)
{
char stemp[NMEA_MAX_LEN];
char *ptype;
char *next;
double g_lat, g_lon;
float g_speed, g_course;
static float g_alt = G_UNKNOWN;
int fix; /* 0=none, 2=2D, 3=3D */
strcpy (stemp, sentence);
// TODO: process only if good.
remove_checksum (stemp);
next = stemp;
ptype = strsep(&next, ",");
// $GPRMC has everything we care about except altitude.
//
// Examples: $GPRMC,212404.000,V,4237.1505,N,07120.8602,W,,,150614,,*0B
// $GPRMC,000029.020,V,,,,,,,080810,,,N*45
// $GPRMC,003413.710,A,4237.1240,N,07120.8333,W,5.07,291.42,160614,,,A*7F
if (strcmp(ptype, "$GPRMC") == 0)
{
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 */
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, ",");
g_lat = G_UNKNOWN;
g_lon = G_UNKNOWN;
g_speed = G_UNKNOWN;
g_course = G_UNKNOWN;
if (plat != NULL && strlen(plat) > 0) {
g_lat = latitude_from_nmea(plat, pns);
}
if (plon != NULL && strlen(plon) > 0) {
g_lon = longitude_from_nmea(plon, pew);
}
if (pknots != NULL && strlen(pknots) > 0) {
g_speed = atof(pknots);
}
if (pcourse != NULL && strlen(pcourse) > 0) {
g_course = atof(pcourse);
}
if (*pstatus == 'A') {
if (g_alt != G_UNKNOWN) {
fix = 3;
}
else {
fix = 2;
}
}
else {
fix = 0;
}
text_color_set (DW_COLOR_INFO);
dw_printf("%d %.6f %.6f %.1f %.0f %.1f\n", fix, g_lat, g_lon, g_speed, g_course, g_alt);
#if WALK96
// TODO: Need to design a proper interface.
extern void walk96 (int fix, double lat, double lon, float knots, float course, float alt);
walk96 (fix, g_lat, g_lon, g_speed, g_course, g_alt);
#endif
}
// $GPGGA has altitude.
//
// Examples: $GPGGA,212407.000,4237.1505,N,07120.8602,W,0,00,,,M,,M,,*58
// $GPGGA,000409.392,,,,,0,00,,,M,0.0,M,,0000*53
// $GPGGA,003518.710,4237.1250,N,07120.8327,W,1,03,5.9,33.5,M,-33.5,M,,0000*5B
else if (strcmp(ptype, "$GPGGA") == 0)
{
char *ptime; /* Time, hhmmss[.sss] */
char *plat; /* Latitude */
char *pns; /* North/South */
char *plon; /* Longitude */
char *pew; /* East/West */
char *pfix; /* 0=invalid, 1=GPS fix, 2=DGPS fix */
char *pnum_sat; /* Number of satellites */
char *phdop; /* Horiz. Dilution fo Precision */
char *paltitude; /* Altitude, above mean sea level */
char *palt_u; /* Units for Altitude, typically M for meters. */
char *pheight; /* Height above ellipsoid */
char *pheight_u; /* Units for height, typically M for meters. */
char *psince; /* Time since last DGPS update. */
char *pdsta; /* DGPS reference station id. */
ptime = strsep(&next, ",");
plat = strsep(&next, ",");
pns = strsep(&next, ",");
plon = strsep(&next, ",");
pew = strsep(&next, ",");
pfix = strsep(&next, ",");
pnum_sat = strsep(&next, ",");
phdop = strsep(&next, ",");
paltitude = strsep(&next, ",");
palt_u = strsep(&next, ",");
pheight = strsep(&next, ",");
pheight_u = strsep(&next, ",");
psince = strsep(&next, ",");
pdsta = strsep(&next, ",");
g_alt = G_UNKNOWN;
if (paltitude != NULL && strlen(paltitude) > 0) {
g_alt = atof(paltitude);
}
}
} /* end nmea_parse_gps */
/* end nmea.c */ /* end nmea.c */

View File

@ -919,7 +919,7 @@ static void print_error (pfstate_t *pf, char *msg)
#if TEST #if PFTEST
/*------------------------------------------------------------------- /*-------------------------------------------------------------------
@ -928,7 +928,7 @@ static void print_error (pfstate_t *pf, char *msg)
* *
* Purpose: Unit test for packet filtering. * Purpose: Unit test for packet filtering.
* *
* Usage: gcc -Wall -o pftest -DTEST pfilter.c ax25_pad.o textcolor.o fcs_calc.o decode_aprs.o latlong.o symbols.o telemetry.o tt_text.c misc.a regex.a && ./pftest * Usage: gcc -Wall -o pftest -DPFTEST pfilter.c ax25_pad.o textcolor.o fcs_calc.o decode_aprs.o latlong.o symbols.o telemetry.o tt_text.c misc.a regex.a && ./pftest
* *
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
@ -940,6 +940,9 @@ static void pftest (int test_num, char *filter, char *packet, int expected);
int main () int main ()
{ {
dw_printf ("Quick test for packet filtering.\n");
dw_printf ("Some error messages are normal. Look at the final success/fail message.\n");
pftest (1, "", "WB2OSZ-5>APDW12,WIDE1-1,WIDE2-1:!4237.14NS07120.83W#PHG7140Chelmsford MA", 0); pftest (1, "", "WB2OSZ-5>APDW12,WIDE1-1,WIDE2-1:!4237.14NS07120.83W#PHG7140Chelmsford MA", 0);
pftest (2, "0", "WB2OSZ-5>APDW12,WIDE1-1,WIDE2-1:!4237.14NS07120.83W#PHG7140Chelmsford MA", 0); pftest (2, "0", "WB2OSZ-5>APDW12,WIDE1-1,WIDE2-1:!4237.14NS07120.83W#PHG7140Chelmsford MA", 0);
pftest (3, "1", "WB2OSZ-5>APDW12,WIDE1-1,WIDE2-1:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1); pftest (3, "1", "WB2OSZ-5>APDW12,WIDE1-1,WIDE2-1:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1);
@ -1057,12 +1060,12 @@ int main ()
if (error_count > 0) { if (error_count > 0) {
text_color_set (DW_COLOR_ERROR); text_color_set (DW_COLOR_ERROR);
dw_printf ("Packet Filtering Test - FAILED!\n"); dw_printf ("\nPacket Filtering Test - FAILED!\n");
exit (1); exit (EXIT_FAILURE);
} }
text_color_set (DW_COLOR_DEBUG ); text_color_set (DW_COLOR_REC);
dw_printf ("Packet Filtering Test - SUCCESS!\n"); dw_printf ("\nPacket Filtering Test - SUCCESS!\n");
exit (0); exit (EXIT_SUCCESS);
} }

100
ptt.c
View File

@ -50,12 +50,48 @@
* *
*---------------------------------------------------------------*/ *---------------------------------------------------------------*/
/*
Idea for future enhancement:
A couple people have asked about support for the DMK URI.
This uses a C-Media CM108/CM119 with one interesting addition, a GPIO
pin is used to drive PTT. Here is some related information.
DMK URI:
http://www.dmkeng.com/URI_Order_Page.htm
http://dmkeng.com/images/URI%20Schematic.pdf
http://www.repeater-builder.com/voip/pdf/cm119-datasheet.pdf
Homebrew versions of the same idea:
http://images.ohnosec.org/usbfob.pdf
http://www.qsl.net/kb9mwr/projects/voip/usbfob-119.pdf
http://rtpdir.weebly.com/uploads/1/6/8/7/1687703/usbfob.pdf
http://www.repeater-builder.com/projects/fob/USB-Fob-Construction.pdf
Applications that have support for this:
http://docs.allstarlink.org/drupal/
http://soundmodem.sourcearchive.com/documentation/0.16-1/ptt_8c_source.html
https://github.com/N0NB/hamlib/blob/master/src/cm108.c#L190
Information about the "hidraw" device:
http://unix.stackexchange.com/questions/85379/dev-hidraw-read-permissions
http://www.signal11.us/oss/udev/
http://www.signal11.us/oss/hidapi/
https://github.com/signal11/hidapi/blob/master/libusb/hid.c
http://stackoverflow.com/questions/899008/howto-write-to-the-gpio-pin-of-the-cm108-chip-in-linux
https://www.kernel.org/doc/Documentation/hid/hidraw.txt
*/
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <sys/time.h> #include <time.h>
#if __WIN32__ #if __WIN32__
#include <windows.h> #include <windows.h>
@ -164,7 +200,7 @@ static char otnames[NUM_OCTYPES][8];
void ptt_init (struct audio_s *audio_config_p) void ptt_init (struct audio_s *audio_config_p)
{ {
int ch; int ch;
HANDLE fd; HANDLE fd = INVALID_HANDLE_VALUE;
#if __WIN32__ #if __WIN32__
#else #else
int using_gpio; int using_gpio;
@ -177,9 +213,9 @@ void ptt_init (struct audio_s *audio_config_p)
save_audio_config_p = audio_config_p; save_audio_config_p = audio_config_p;
strcpy (otnames[OCTYPE_PTT], "PTT"); strlcpy (otnames[OCTYPE_PTT], "PTT", sizeof(otnames[OCTYPE_PTT]));
strcpy (otnames[OCTYPE_DCD], "DCD"); strlcpy (otnames[OCTYPE_DCD], "DCD", sizeof(otnames[OCTYPE_DCD]));
strcpy (otnames[OCTYPE_FUTURE], "FUTURE"); strlcpy (otnames[OCTYPE_FUTURE], "FUTURE", sizeof(otnames[OCTYPE_FUTURE]));
for (ch = 0; ch < MAX_CHANS; ch++) { for (ch = 0; ch < MAX_CHANS; ch++) {
@ -228,7 +264,7 @@ void ptt_init (struct audio_s *audio_config_p)
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("Converted %s device '%s'", audio_config_p->achan[ch].octrl[ot].ptt_device, otnames[ot]); dw_printf ("Converted %s device '%s'", audio_config_p->achan[ch].octrl[ot].ptt_device, otnames[ot]);
if (n < 1) n = 1; if (n < 1) n = 1;
sprintf (audio_config_p->achan[ch].octrl[ot].ptt_device, "/dev/ttyS%d", n-1); snprintf (audio_config_p->achan[ch].octrl[ot].ptt_device, sizeof(audio_config_p->achan[ch].octrl[ot].ptt_device), "/dev/ttyS%d", n-1);
dw_printf (" to Linux equivalent '%s'\n", audio_config_p->achan[ch].octrl[ot].ptt_device); dw_printf (" to Linux equivalent '%s'\n", audio_config_p->achan[ch].octrl[ot].ptt_device);
} }
#endif #endif
@ -259,13 +295,13 @@ void ptt_init (struct audio_s *audio_config_p)
// Bug fix in release 1.1 - Need to munge name for COM10 and up. // Bug fix in release 1.1 - Need to munge name for COM10 and up.
// http://support.microsoft.com/kb/115831 // http://support.microsoft.com/kb/115831
strcpy (bettername, audio_config_p->achan[ch].octrl[ot].ptt_device); strlcpy (bettername, audio_config_p->achan[ch].octrl[ot].ptt_device, sizeof(bettername));
if (strncasecmp(bettername, "COM", 3) == 0) { if (strncasecmp(bettername, "COM", 3) == 0) {
int n; int n;
n = atoi(bettername+3); n = atoi(bettername+3);
if (n >= 10) { if (n >= 10) {
strcpy (bettername, "\\\\.\\"); strlcpy (bettername, "\\\\.\\", sizeof(bettername));
strcat (bettername, audio_config_p->achan[ch].octrl[ot].ptt_device); strlcat (bettername, audio_config_p->achan[ch].octrl[ot].ptt_device, sizeof(bettername));
} }
} }
fd = CreateFile(bettername, fd = CreateFile(bettername,
@ -410,7 +446,7 @@ void ptt_init (struct audio_s *audio_config_p)
dw_printf (" chmod go+w /sys/class/gpio/export /sys/class/gpio/unexport\n"); dw_printf (" chmod go+w /sys/class/gpio/export /sys/class/gpio/unexport\n");
exit (1); exit (1);
} }
sprintf (stemp, "%d", audio_config_p->achan[ch].octrl[ot].ptt_gpio); snprintf (stemp, sizeof(stemp), "%d", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
if (write (fd, stemp, strlen(stemp)) != strlen(stemp)) { if (write (fd, stemp, strlen(stemp)) != strlen(stemp)) {
int e = errno; int e = errno;
/* Ignore EBUSY error which seems to mean */ /* Ignore EBUSY error which seems to mean */
@ -424,16 +460,36 @@ void ptt_init (struct audio_s *audio_config_p)
} }
close (fd); close (fd);
/*
Idea for future:
On the RPi, the device path for GPIO number XX is /sys/class/gpio/gpioXX.
There was a report that it is different for the Cubieboard. For instance
GPIO 61 has gpio61_pi13 in the path. This indicates connector "i" pin 13.
For another similar single board computer, we find the same thing:
https://www.olimex.com/wiki/A20-OLinuXino-LIME#GPIO_under_Linux
How should we deal with this? Some possibilities:
(1) The user might explicitly mention the name in direwolf.conf.
(2) We might be able to find the names in some system device config file.
(3) Get a directory listing of /sys/class/gpio then search for a
matching name. Suppose we wanted GPIO 61. First look for an exact
match to "gpio61". If that is not found, look for something
matching the pattern "gpio61_*".
*/
/* /*
* We will have the same permission problem if not root. * We will have the same permission problem if not root.
* We only care about "direction" and "value". * We only care about "direction" and "value".
*/ */
sprintf (stemp, "sudo chmod go+rw /sys/class/gpio/gpio%d/direction", audio_config_p->achan[ch].octrl[ot].ptt_gpio); snprintf (stemp, sizeof(stemp), "sudo chmod go+rw /sys/class/gpio/gpio%d/direction", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
err = system (stemp); err = system (stemp);
sprintf (stemp, "sudo chmod go+rw /sys/class/gpio/gpio%d/value", audio_config_p->achan[ch].octrl[ot].ptt_gpio); snprintf (stemp, sizeof(stemp), "sudo chmod go+rw /sys/class/gpio/gpio%d/value", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
err = system (stemp); err = system (stemp);
sprintf (stemp, "/sys/class/gpio/gpio%d/value", audio_config_p->achan[ch].octrl[ot].ptt_gpio); snprintf (stemp, sizeof(stemp), "/sys/class/gpio/gpio%d/value", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
if (stat(stemp, &finfo) < 0) { if (stat(stemp, &finfo) < 0) {
int e = errno; int e = errno;
@ -458,7 +514,7 @@ void ptt_init (struct audio_s *audio_config_p)
* Set output direction with initial state off. * Set output direction with initial state off.
*/ */
sprintf (stemp, "/sys/class/gpio/gpio%d/direction", audio_config_p->achan[ch].octrl[ot].ptt_gpio); snprintf (stemp, sizeof(stemp), "/sys/class/gpio/gpio%d/direction", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
fd = open(stemp, O_WRONLY); fd = open(stemp, O_WRONLY);
if (fd < 0) { if (fd < 0) {
int e = errno; int e = errno;
@ -470,10 +526,10 @@ void ptt_init (struct audio_s *audio_config_p)
char hilo[8]; char hilo[8];
if (audio_config_p->achan[ch].octrl[ot].ptt_invert) { if (audio_config_p->achan[ch].octrl[ot].ptt_invert) {
strcpy (hilo, "high"); strlcpy (hilo, "high", sizeof(hilo));
} }
else { else {
strcpy (hilo, "low"); strlcpy (hilo, "low", sizeof(hilo));
} }
if (write (fd, hilo, strlen(hilo)) != strlen(hilo)) { if (write (fd, hilo, strlen(hilo)) != strlen(hilo)) {
int e = errno; int e = errno;
@ -702,7 +758,7 @@ void ptt_set (int ot, int chan, int ptt_signal)
int fd; int fd;
char stemp[80]; char stemp[80];
sprintf (stemp, "/sys/class/gpio/gpio%d/value", save_audio_config_p->achan[chan].octrl[ot].ptt_gpio); snprintf (stemp, sizeof(stemp), "/sys/class/gpio/gpio%d/value", save_audio_config_p->achan[chan].octrl[ot].ptt_gpio);
fd = open(stemp, O_WRONLY); fd = open(stemp, O_WRONLY);
if (fd < 0) { if (fd < 0) {
@ -713,7 +769,7 @@ void ptt_set (int ot, int chan, int ptt_signal)
return; return;
} }
sprintf (stemp, "%d", ptt); snprintf (stemp, sizeof(stemp), "%d", ptt);
if (write (fd, stemp, 1) != 1) { if (write (fd, stemp, 1) != 1) {
int e = errno; int e = errno;
@ -840,14 +896,14 @@ main ()
my_audio_config.valid[0] = 1; my_audio_config.valid[0] = 1;
my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_SERIAL; my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_SERIAL;
//strcpy (my_audio_config.ptt_device, "COM1"); //strlcpy (my_audio_config.ptt_device, "COM1", sizeof(my_audio_config.ptt_device));
strcpy (my_audio_config.ptt_device, "/dev/ttyUSB0"); strlcpy (my_audio_config.ptt_device, "/dev/ttyUSB0", sizeof(my_audio_config.ptt_device));
my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_RTS; my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_RTS;
my_audio_config.valid[1] = 1; my_audio_config.valid[1] = 1;
my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_SERIAL; my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_SERIAL;
//strcpy (my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device, "COM1"); //strlcpy (my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device, "COM1", sizeof(my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device));
strcpy (my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device, "/dev/ttyUSB0"); strlcpy (my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device, "/dev/ttyUSB0", sizeof(my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device));
my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_DTR; my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_DTR;

2
recv.c
View File

@ -302,7 +302,7 @@ void recv_process (void)
#endif #endif
ok = dlq_remove (&type, &chan, &subchan, &pp, &alevel, &retries, spectrum); ok = dlq_remove (&type, &chan, &subchan, &pp, &alevel, &retries, spectrum, sizeof(spectrum));
#if DEBUG #if DEBUG
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("recv_process: dlq_remove() returned ok=%d, type=%d, chan=%d, pp=%p\n", dw_printf ("recv_process: dlq_remove() returned ok=%d, type=%d, chan=%d, pp=%p\n",

View File

@ -46,7 +46,6 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
//#include <sys/time.h>
#include <time.h> #include <time.h>
#if __WIN32__ #if __WIN32__

View File

@ -108,13 +108,13 @@ MYFDTYPE serial_port_open (char *devicename, int baud)
// Bug fix in release 1.1 - Need to munge name for COM10 and up. // Bug fix in release 1.1 - Need to munge name for COM10 and up.
// http://support.microsoft.com/kb/115831 // http://support.microsoft.com/kb/115831
strcpy (bettername, devicename); strlcpy (bettername, devicename, sizeof(bettername));
if (strncasecmp(devicename, "COM", 3) == 0) { if (strncasecmp(devicename, "COM", 3) == 0) {
int n; int n;
n = atoi(devicename+3); n = atoi(devicename+3);
if (n >= 10) { if (n >= 10) {
strcpy (bettername, "\\\\.\\"); strlcpy (bettername, "\\\\.\\", sizeof(bettername));
strcat (bettername, devicename); strlcat (bettername, devicename, sizeof(bettername));
} }
} }
@ -201,14 +201,14 @@ MYFDTYPE serial_port_open (char *devicename, int baud)
/* Translate Windows device name into Linux name. */ /* Translate Windows device name into Linux name. */
/* COM1 -> /dev/ttyS0, etc. */ /* COM1 -> /dev/ttyS0, etc. */
strcpy (linuxname, devicename); strlcpy (linuxname, devicename, sizeof(linuxname));
if (strncasecmp(devicename, "COM", 3) == 0) { if (strncasecmp(devicename, "COM", 3) == 0) {
int n = atoi (devicename + 3); int n = atoi (devicename + 3);
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("Converted serial port name '%s'", devicename); dw_printf ("Converted serial port name '%s'", devicename);
if (n < 1) n = 1; if (n < 1) n = 1;
sprintf (linuxname, "/dev/ttyS%d", n-1); snprintf (linuxname, sizeof(linuxname), "/dev/ttyS%d", n-1);
dw_printf (" to Linux equivalent '%s'\n", linuxname); dw_printf (" to Linux equivalent '%s'\n", linuxname);
} }

View File

@ -364,8 +364,8 @@ void server_init (struct audio_s *audio_config_p, struct misc_config_s *mc)
#else #else
pthread_t connect_listen_tid; pthread_t connect_listen_tid;
pthread_t cmd_listen_tid[MAX_NET_CLIENTS]; pthread_t cmd_listen_tid[MAX_NET_CLIENTS];
#endif
int e; int e;
#endif
int server_port = mc->agwpe_port; /* Usually 8000 but can be changed. */ int server_port = mc->agwpe_port; /* Usually 8000 but can be changed. */
@ -475,7 +475,7 @@ static THREAD_F connect_listen_thread (void *arg)
if (err != 0) { if (err != 0) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf("WSAStartup failed: %d\n", err); dw_printf("WSAStartup failed: %d\n", err);
return (NULL); // TODO: what should this be for Windows? return (0);
} }
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) { if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) {
@ -483,7 +483,7 @@ static THREAD_F connect_listen_thread (void *arg)
dw_printf("Could not find a usable version of Winsock.dll\n"); dw_printf("Could not find a usable version of Winsock.dll\n");
WSACleanup(); WSACleanup();
//sleep (1); //sleep (1);
return (NULL); // TODO: what should this be for Windows? return (0);
} }
memset (&hints, 0, sizeof(hints)); memset (&hints, 0, sizeof(hints));
@ -498,14 +498,14 @@ static THREAD_F connect_listen_thread (void *arg)
dw_printf("getaddrinfo failed: %d\n", err); dw_printf("getaddrinfo failed: %d\n", err);
//sleep (1); //sleep (1);
WSACleanup(); WSACleanup();
return (NULL); // TODO: what should this be for Windows? return (0);
} }
listen_sock= socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); listen_sock= socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (listen_sock == INVALID_SOCKET) { if (listen_sock == INVALID_SOCKET) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("connect_listen_thread: Socket creation failed, err=%d", WSAGetLastError()); dw_printf ("connect_listen_thread: Socket creation failed, err=%d", WSAGetLastError());
return (NULL); // TODO: what should this be for Windows? return (0);
} }
#if DEBUG #if DEBUG
@ -522,7 +522,7 @@ static THREAD_F connect_listen_thread (void *arg)
freeaddrinfo(ai); freeaddrinfo(ai);
closesocket(listen_sock); closesocket(listen_sock);
WSACleanup(); WSACleanup();
return (NULL); // TODO: what should this be for Windows? return (0);
} }
freeaddrinfo(ai); freeaddrinfo(ai);
@ -553,7 +553,7 @@ static THREAD_F connect_listen_thread (void *arg)
{ {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf("Listen failed with error: %d\n", WSAGetLastError()); dw_printf("Listen failed with error: %d\n", WSAGetLastError());
return (NULL); // TODO: what should this be for Windows? return (0);
} }
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
@ -566,7 +566,7 @@ static THREAD_F connect_listen_thread (void *arg)
dw_printf("Accept failed with error: %d\n", WSAGetLastError()); dw_printf("Accept failed with error: %d\n", WSAGetLastError());
closesocket(listen_sock); closesocket(listen_sock);
WSACleanup(); WSACleanup();
return (NULL); // TODO: what should this be for Windows? return (0);
} }
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
@ -779,7 +779,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
struct tm *tm; struct tm *tm;
clock = time(NULL); clock = time(NULL);
tm = localtime(&clock); tm = localtime(&clock); // TODO: should use localtime_r
memset (&agwpe_msg.hdr, 0, sizeof(agwpe_msg.hdr)); memset (&agwpe_msg.hdr, 0, sizeof(agwpe_msg.hdr));
@ -1017,7 +1017,7 @@ static THREAD_F cmd_listen_thread (void *arg)
close (client_sock[client]); close (client_sock[client]);
#endif #endif
client_sock[client] = -1; client_sock[client] = -1;
return NULL; // TODO: what should this be for Windows? return (0);
} }
cmd.data[0] = '\0'; cmd.data[0] = '\0';
@ -1035,7 +1035,7 @@ static THREAD_F cmd_listen_thread (void *arg)
close (client_sock[client]); close (client_sock[client]);
#endif #endif
client_sock[client] = -1; client_sock[client] = -1;
return NULL; return (0);
} }
if (n > 0) { if (n > 0) {
cmd.data[cmd.hdr.data_len] = '\0'; cmd.data[cmd.hdr.data_len] = '\0';
@ -1196,7 +1196,7 @@ static THREAD_F cmd_listen_thread (void *arg)
case 'H': /* Ask about recently heard stations. */ case 'H': /* Ask about recently heard stations. */
{ {
#if 0 #if 0 /* This information is not being collected. */
struct { struct {
struct agwpe_s hdr; struct agwpe_s hdr;
char info[100]; char info[100];
@ -1239,23 +1239,21 @@ static THREAD_F cmd_listen_thread (void *arg)
break; break;
case 'V': /* Transmit UI data frame */ case 'V': /* Transmit UI data frame (with digipeater path) */
{ {
// Data format is: // Data format is:
// 1 byte for number of digipeaters. // 1 byte for number of digipeaters.
// 10 bytes for each digipeater. // 10 bytes for each digipeater.
// data part of message. // data part of message.
char stemp[512]; char stemp[AX25_MAX_PACKET_LEN+2];
char *p; char *p;
int ndigi; int ndigi;
int k; int k;
packet_t pp; packet_t pp;
//unsigned char fbuf[AX25_MAX_PACKET_LEN+2];
//int flen;
// We have already assured these do not exceed 9 characters. // We have already verified these do not exceed 9 characters. (?)
strlcpy (stemp, cmd.hdr.call_from, sizeof(stemp)); strlcpy (stemp, cmd.hdr.call_from, sizeof(stemp));
strlcat (stemp, ">", sizeof(stemp)); strlcat (stemp, ">", sizeof(stemp));
@ -1396,13 +1394,13 @@ static THREAD_F cmd_listen_thread (void *arg)
break; break;
#if 0
case 'M': /* Send UNPROTO Information */
Not sure what we might want to do here. case 'M': /* Send UNPROTO Information (no digipeater path) */
/*
Added in version 1.3.
This is the same as 'V' except there is no provision for digipeaters.
AGWterminal sends this for beacon or ask QRA. AGWterminal sends this for beacon or ask QRA.
None of the other tested applications use it.
<<< Send UNPROTO Information from AGWPE client application 0, total length = 253 <<< Send UNPROTO Information from AGWPE client application 0, total length = 253
portx = 0, port_hi_reserved = 0 portx = 0, port_hi_reserved = 0
@ -1420,32 +1418,56 @@ static THREAD_F cmd_listen_thread (void *arg)
data_len = 1, user_reserved = 32218432, data = data_len = 1, user_reserved = 32218432, data =
000: 0d . 000: 0d .
There is also a report of it coming from UISS.
<<< Send UNPROTO Information from AGWPE client application 0, total length = 50
portx = 0, port_hi_reserved = 0
kind_lo = 77 = 'M', kind_hi = 0
call_from = "JH4XSY", call_to = "APRS"
data_len = 14, user_reserved = 0, data =
000: 21 22 3c 43 2e 74 71 6c 48 72 71 21 21 5f !"<C.tqlHrq!!_
*/
{ {
packet_t pp; int pid = cmd.hdr.kind_hi & 0xff;
int pid = cmd.datakind_hi & 0xff; (void)(pid);
/* The AGW protocol spec says, */
/* "AX.25 PID 0x00 or 0xF0 for AX.25 0xCF NETROM and others" */ /* "AX.25 PID 0x00 or 0xF0 for AX.25 0xCF NETROM and others" */
/* BUG: In theory, the AX.25 PID octet should be set from this. */
/* All examples seen (above) have 0. */
/* The AX.25 protocol spec doesn't list 0 as a valid value. */
/* We always send 0xf0, meaning no layer 3. */
/* Maybe we should have an ax25_set_pid function for cases when */
/* it is neither 0 nor 0xf0. */
This is not right. char stemp[AX25_MAX_PACKET_LEN];
It needs to be more like "V" Transmit UI data frame packet_t pp;
except there are no digipeaters involved.
pp = ax25_from_frame ((unsigned char *)cmd.data, cmd.hdr.data_len, -1); strlcpy (stemp, cmd.hdr.call_from, sizeof(stemp));
strlcat (stemp, ">", sizeof(stemp));
strlcat (stemp, cmd.hdr.call_to, sizeof(stemp));
if (pp != NULL) { cmd.data[cmd.hdr.data_len] = '\0';
tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp);
ax25_set_pid (pp, pid); strlcat (stemp, ":", sizeof(stemp));
} strlcat (stemp, cmd.data, sizeof(stemp));
else {
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("Transmit '%s'\n", stemp);
pp = ax25_from_text (stemp, 1);
if (pp == NULL) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Failed to create frame from AGW 'M' message.\n"); dw_printf ("Failed to create frame from AGW 'M' message.\n");
} }
else {
tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp);
}
} }
break; break;
#endif
case 'y': /* Ask Outstanding frames waiting on a Port */ case 'y': /* Ask Outstanding frames waiting on a Port */

View File

@ -1,15 +1,18 @@
APRS SYMBOL OVERLAY and EXTENSION TABLES in APRS 1.2 28 Aug 2014 APRS SYMBOL OVERLAY and EXTENSION TABLES in APRS 1.2 29 Oct 2015
--------------------------------------------------------------------- ---------------------------------------------------------------------
BACKGROUND: This file addresses new additions proposals (OVERLAYS) BACKGROUND: This file addresses new additions proposals (OVERLAYS)
to the APRS symbol set after 1 October 2007. The master symbol to the APRS symbol set after 1 October 2007. The master symbol
document remains on the www.aprs.org/symbols/symbolsX.txt page. document remains on the www.aprs.org/symbols/symbolsX.txt page, but
only has one line per symbol character. Since each of the symbols
can have up to 36 overlays, this gives us thousands of symbols codes.
NOTE: There was confusion with different copies of this file on Update 29 Oct 2015: Reorgainized list to Alphabetical Order.
different web pages and links. THIS file is now assumed to be the + Added many new Balloons (due to lost DoD radar Blimp yesterday)
CORRECT one. + Confirmed D^ for Drones was already in there since 2014
+ Added R^ type aircraft for remotely piloted
+ Added S^ Solar Powered Aircraft
UPDATES/REVISIONS/CORRECTIONS: UPDATES/REVISIONS/CORRECTIONS:
@ -26,12 +29,16 @@ UPDATES/REVISIONS/CORRECTIONS:
04 Jan 10 added #A to the table (correcting earlier omission) 04 Jan 10 added #A to the table (correcting earlier omission)
12 Oct 09 Added W0 for Yaesu WIRES nodes 12 Oct 09 Added W0 for Yaesu WIRES nodes
09 Apr 09 Changed APRStt symbol to overlayed BOX (#A) 09 Apr 09 Changed APRStt symbol to overlayed BOX (#A)
21 Aug 08 Added RFID R=, Stroller B], Radios#Y, & skull&Xbones (XH) 21 Aug 08 Added RFID R=, Babystroller B], Radio#Y, skull&Xbones XH
27 Apr 08 Added some definitions of the numbered circle #0.
25 Mar 08 Added these new definitions of overlays:
Original Alternate Symbol codes being modified for new Overlay Use:
25 Mar 08 Modified these Alternate Symbol codes for expanded Overlays.
Prior to this, each Alternate Table basic symbol had a unique defini-
tion, but this was every restrictive. SO the following alternate
base symbols were redefined so that the basic symbol could take on
dozens of unique overlay definitions:
\0 - Several overlays for the numbered Circle
\A - (BOX symbol) APRStt(DTMF), RFID users, XO (OLPC) \A - (BOX symbol) APRStt(DTMF), RFID users, XO (OLPC)
\' - Was Crash Site. Now expanded to be INCIDENT sites \' - Was Crash Site. Now expanded to be INCIDENT sites
\% - is an overlayed Powerplant. See definitions below \% - is an overlayed Powerplant. See definitions below
@ -114,20 +121,28 @@ letting that define a new graphic just for that combination.
The following tables will attempt to keep track of these and The following tables will attempt to keep track of these and
any other useful generic applications of overlay characters. any other useful generic applications of overlay characters.
AMPLIFIED some existing ALTERNATE SYMBOL Overlays: (new Aug 2014)
change Flooding #W to include Avalanche, Mudslide/Landslide change Flooding #W to include Avalanche, Mudslide/Landslide
change \' to crash & incident sites Update #' name to crash & incident sites
change \D to DEPOT family Update \D (was available) to DEPOT family
change overlayed car to generic with (1-9 overlays) change overlayed car to generic Vehicle with (1-9 overlays)
ADVISORIES: #< (new expansion possibilities)
/< = motorcycle
\< = Advisory (single gale flag)
AIRCRAFT AIRCRAFT
/^ = LARGE Aircraft /^ = LARGE Aircraft
\^ = top-view originally intended to point in direction of flight \^ = top-view originally intended to point in direction of flight
A^ = Autonomous (2015)
D^ = Drone (new may 2014) D^ = Drone (new may 2014)
E^ = Enemy aircraft (too bad I cant use the original Hostile) E^ = Electric aircraft (2015)
H^ = Hovercraft (new may 2014) H^ = Hovercraft (new may 2014)
J^ = JET (new may 2014) J^ = JET (new may 2014)
M^ = Missle (new may 2014) M^ = Missle (new may 2014)
P^ = Prop (new Aug 2014) P^ = Prop (new Aug 2014)
R^ = Remotely Piloted (new 2015)
S^ = Solar Powered (new 2015)
V^ = Vertical takeoff (new may 2014) V^ = Vertical takeoff (new may 2014)
X^ = Experimental (new Aug 2014) X^ = Experimental (new Aug 2014)
@ -138,6 +153,60 @@ U$ = US dollars
L$ = Brittish Pound L$ = Brittish Pound
Y$ = Japanese Yen Y$ = Japanese Yen
ARRL or DIAMOND: #a
/a = Ambulance
Aa = ARES
Da = DSTAR (had been ARES Dutch)
Ga = RSGB Radio Society of Great Brittan
Ra = RACES
Sa = SATERN Salvation Army
Wa = WinLink
BALLOONS and lighter than air #O (All new Oct 2015)
/O = Original Balloon (think Ham balloon)
\O = ROCKET (amateur)(2007)
BO = Blimp (2015)
MO = Manned Balloon (2015)
TO = Teathered (2015)
CO = Constant Pressure - Long duration (2015)
RO = Rocket bearing Balloon (Rockoon) (2015)
BOX SYMBOL: #A (and other system inputted symbols)
/A = Aid station
\A = numbered box
9A = Mobile DTMF user
7A = HT DTMF user
HA = House DTMF user
EA = Echolink DTMF report
IA = IRLP DTMF report
RA = RFID report
AA = AllStar DTMF report
DA = D-Star report
XA = OLPC Laptop XO
etc
BUILDINGS: #h
/h = Hospital
\h = Ham Store ** <= now used for HAMFESTS
Fh = HamFest (new Aug 2014)
Hh = Home Dept etc..
CARS: #> (Vehicles)
/> = normal car (side view)
\> = Top view and symbol POINTS in direction of travel
#> = Reserve overlays 1-9 for numbered cars (new Aug 2014)
E> = Electric
H> = Hybrid
S> = Solar powered
V> = GM Volt
CIVIL DEFENSE or TRIANGLE: #c
/c = Incident Command Post
\c = Civil Defense
Dc = Decontamination (new Aug 2014)
Rc = RACES
Sc = SATERN mobile canteen
DEPOT DEPOT
/D = was originally undefined /D = was originally undefined
\D = was drizzle (moved to ' ovlyD) \D = was drizzle (moved to ' ovlyD)
@ -155,17 +224,14 @@ EMERGENCY: #!
E! = ELT or EPIRB (new Aug 2014) E! = ELT or EPIRB (new Aug 2014)
V! = Volcanic Eruption or Lava (new Aug 2014) V! = Volcanic Eruption or Lava (new Aug 2014)
POWER PLANT: #% EYEBALL (EVENT) and VISIBILITY #E
/% = DX cluster <= the original primary table definition /E = Eyeball for special live events
C% = Coal \E = (existing smoke) the symbol with no overlay
E% = Emergency (new Aug 2014) HE = (H overlay) Haze
G% = Geothermal SE = (S overlay) Smoke
H% = Hydroelectric BE = (B overlay) Blowing Snow was \B
N% = Nuclear DE = (D overlay) blowing Dust or sand was \b
P% = Portable (new Aug 2014) FE = (F overlay) Fog was \{
S% = Solar
T% = Turbine
W% = Wind
GATEWAYS: #& GATEWAYS: #&
/& = HF Gateway <= the original primary table definition /& = HF Gateway <= the original primary table definition
@ -174,14 +240,17 @@ R& = Receive only IGate (do not send msgs back to RF)
T& = TX igate with path set to 1 hop only) T& = TX igate with path set to 1 hop only)
2& = TX igate with path set to 2 hops (not generally good idea) 2& = TX igate with path set to 2 hops (not generally good idea)
INCIDENT SITES: #' GPS devices: #\
/' = Small Aircraft (original primary symbol) /\ = Triangle DF primary symbol
\' = Airplane Crash Site <= the original alternate deifinition \\ = was undefined alternate symbol
A' = Automobile crash site A\ = Avmap G5 * <= Recommend special symbol
H' = Hazardous incident
M' = Multi-Vehicle crash site HAZARDS: #H
P' = Pileup /H = hotel
T' = Truck wreck \H = Haze
RH = Radiation detector (new mar 2011)
WH = Hazardous Waste
XH = Skull&Crossbones
HUMAN SYMBOL: #[ HUMAN SYMBOL: #[
/[ = Human /[ = Human
@ -205,6 +274,15 @@ O- = Operator Present
S- = Solar Powered S- = Solar Powered
W- = Wind powered W- = Wind powered
INCIDENT SITES: #'
/' = Small Aircraft (original primary symbol)
\' = Airplane Crash Site <= the original alternate deifinition
A' = Automobile crash site
H' = Hazardous incident
M' = Multi-Vehicle crash site
P' = Pileup
T' = Truck wreck
NUMBERED CIRCLES: #0 NUMBERED CIRCLES: #0
E0 = Echolink Node (E0) E0 = Echolink Node (E0)
I0 = IRLP repeater (I0) I0 = IRLP repeater (I0)
@ -223,48 +301,17 @@ I; = Islands on the air
S; = Summits on the air S; = Summits on the air
W; = WOTA W; = WOTA
ADVISORIES: #< (new expansion possibilities) POWER or ENERGY: #%
/< = motorcycle /% = DX cluster <= the original primary table definition
\< = Advisory (single gale flag) C% = Coal
E% = Emergency (new Aug 2014)
CARS: #> (Vehicles) G% = Geothermal
/> = normal car (side view) H% = Hydroelectric
\> = Top view and symbol POINTS in direction of travel N% = Nuclear
#> = Reserve overlays 1-9 for numbered cars (new Aug 2014) P% = Portable (new Aug 2014)
E> = Electric S% = Solar
H> = Hybrid T% = Turbine
S> = Solar powered W% = Wind
V> = GM Volt
BOX SYMBOL: #A (and other system inputted symbols)
/A = Aid station
\A = numbered box
9A = Mobile DTMF user
7A = HT DTMF user
HA = House DTMF user
EA = Echolink DTMF report
IA = IRLP DTMF report
RA = RFID report
AA = AllStar DTMF report
DA = D-Star report
XA = OLPC Laptop XO
etc
EYEBALL and VISIBILITY #E
/E = Eyeball for special live events
\E = (existing smoke) the symbol with no overlay
HE = (H overlay) Haze
SE = (S overlay) Smoke
BE = (B overlay) Blowing Snow was \B
DE = (D overlay) blowing Dust or sand was \b
FE = (F overlay) Fog was \{
HAZARDS: #H
/H = hotel
\H = Haze
RH = Radiation detector (new mar 2011)
WH = Hazardous Waste
XH = Skull&Crossbones
RESTAURANTS: #R RESTAURANTS: #R
\R = Restaurant (generic) \R = Restaurant (generic)
@ -282,35 +329,6 @@ IY = Icom
KY = Kenwood * <= Recommend special symbol KY = Kenwood * <= Recommend special symbol
YY = Yaesu/Standard* <= Recommend special symbol YY = Yaesu/Standard* <= Recommend special symbol
GPS devices: #\
/\ = Triangle DF primary symbol
\\ = was undefined alternate symbol
A\ = Avmap G5 * <= Recommend special symbol
ARRL or DIAMOND: #a
/a = Ambulance
Aa = ARES
Da = DSTAR (had been ARES Dutch)
Ga = RSGB Radio Society of Great Brittan
Ra = RACES
Sa = SATERN Salvation Army
Wa = WinLink
CIVIL DEFENSE or TRIANGLE: #c
/c = Incident Command Post
\c = Civil Defense
Dc = Decontamination (new Aug 2014)
Rc = RACES
Sc = SATERN mobile canteen
BUILDINGS: #h
/h = Hospital
\h = Ham Store ** <= now used for HAMFESTS
Fh = HamFest (new Aug 2014)
Hh = Home Dept etc..
Mh = Morgue
Ch = Clinic
Th = Triage
SPECIAL VEHICLES: #k SPECIAL VEHICLES: #k
/k = truck /k = truck
@ -318,6 +336,14 @@ SPECIAL VEHICLES: #k
4k = 4x4 4k = 4x4
Ak = ATV (all terrain vehicle) Ak = ATV (all terrain vehicle)
SHELTERS: #z
/z = was available
\z = overlayed shelter
Cz = Clinic (new Aug 2014)
Gz = Government building (new Aug 2014)
Mz = Morgue (new Aug 2014)
Tz = Triage (new Aug 2014)
SHIPS: #s SHIPS: #s
/s = Power boat (ship) side view /s = Power boat (ship) side view
\s = Overlay Boat (Top view) \s = Overlay Boat (Top view)
@ -341,11 +367,10 @@ Ws = Wing-in-Ground effect (or Hovercraft)
Xs = Passenger (paX)(ferry) Xs = Passenger (paX)(ferry)
Ys = Sailing (large ship) Ys = Sailing (large ship)
TRUCKS: #u TRUCKS: #u
/u = Truck (18 wheeler) /u = Truck (18 wheeler)
\u = truck with overlay \u = truck with overlay
Bu = Buldozer/construction (new Aug 2014) Bu = Buldozer/construction/Backhoe (new Aug 2014)
Gu = Gas Gu = Gas
Pu = Plow or SnowPlow (new Aug 2014) Pu = Plow or SnowPlow (new Aug 2014)
Tu = Tanker Tu = Tanker

View File

@ -35,6 +35,8 @@
#include "direwolf.h" #include "direwolf.h"
#include "textcolor.h" #include "textcolor.h"
#include "symbols.h" #include "symbols.h"
#include "tt_text.h"
//#if __WIN32__ //#if __WIN32__
char *strcasestr(const char *S, const char *FIND); char *strcasestr(const char *S, const char *FIND);
@ -460,7 +462,7 @@ void symbols_list (void)
int symbol = new_sym_ptr[n].symbol; int symbol = new_sym_ptr[n].symbol;
char tones[12]; char tones[12];
symbols_to_tones (overlay, symbol, tones); symbols_to_tones (overlay, symbol, tones, sizeof(tones));
if (overlay == '/') { if (overlay == '/') {
@ -900,6 +902,7 @@ int symbols_code_from_description (char overlay, char *description, char *symtab
* *
* Inputs: symtab/overlay * Inputs: symtab/overlay
* symbol * symbol
* tonessiz - Amount of space available for result.
* *
* Output: tones - string of AB... * Output: tones - string of AB...
* *
@ -912,13 +915,12 @@ int symbols_code_from_description (char overlay, char *description, char *symtab
* *
*------------------------------------------------------------------*/ *------------------------------------------------------------------*/
void symbols_to_tones (char symtab, char symbol, char *tones) void symbols_to_tones (char symtab, char symbol, char *tones, size_t tonessiz)
{ {
if (symtab == '/') { if (symtab == '/') {
// TODO: potential buffer overflow. snprintf (tones, tonessiz, "AB1%02d", symbol - ' ');
sprintf (tones, "AB1%02d", symbol - ' ');
} }
else if (isupper(symtab) || isdigit(symtab)) { else if (isupper(symtab) || isdigit(symtab)) {
@ -930,11 +932,11 @@ void symbols_to_tones (char symtab, char symbol, char *tones)
tt_text_to_two_key (text, 0, tt); tt_text_to_two_key (text, 0, tt);
sprintf (tones, "AB0%02d%s", symbol - ' ', tt); snprintf (tones, tonessiz, "AB0%02d%s", symbol - ' ', tt);
} }
else { else {
sprintf (tones, "AB2%02d", symbol - ' '); snprintf (tones, tonessiz, "AB2%02d", symbol - ' ');
} }
} /* end symbols_to_tones */ } /* end symbols_to_tones */

View File

@ -13,7 +13,7 @@ void symbols_get_description (char symtab, char symbol, char *description, size_
int symbols_code_from_description (char overlay, char *description, char *symtab, char *symbol); int symbols_code_from_description (char overlay, char *description, char *symtab, char *symbol);
void symbols_to_tones (char symtab, char symbol, char *tones); void symbols_to_tones (char symtab, char symbol, char *tones, size_t tonessize);
/* end symbols.h */ /* end symbols.h */

View File

@ -1,4 +1,4 @@
APRS SYMBOLS (Icons) 28 Aug 2014 APRS SYMBOLS (Icons) 23 Jun 2015
----------------------------------------------------------------------- -----------------------------------------------------------------------
WB4APR WB4APR
@ -28,6 +28,7 @@ http://aprs.org/symbols/symbols-background.txt
UPDATE CHRONOLOGY: UPDATE CHRONOLOGY:
23 Jun 15: Changed Aircraft to SSID-11 and Human to SSID-7
28 Aug 14: Added notation on newly availble BASE codes (begun in 2007) 28 Aug 14: Added notation on newly availble BASE codes (begun in 2007)
Old WX versions of these: Bb{*:DFegJp were moved to ovlays Old WX versions of these: Bb{*:DFegJp were moved to ovlays
Expanded #w Flooding to include Avalanches, Mud/Landslides Expanded #w Flooding to include Avalanches, Mud/Landslides
@ -178,7 +179,7 @@ for the stand-alone trackers described above.
/$ BE PHONE \$ OEO Bank or ATM (green box) /$ BE PHONE \$ OEO Bank or ATM (green box)
/% BF DX CLUSTER \% OFO Power Plant with overlay /% BF DX CLUSTER \% OFO Power Plant with overlay
/& BG HF GATEway \& OG# I=Igte R=RX T=1hopTX 2=2hopTX /& BG HF GATEway \& OG# I=Igte R=RX T=1hopTX 2=2hopTX
/' BH Small AIRCRAFT (SSID = 7) \' OHO Crash (& now Incident sites) /' BH Small AIRCRAFT (SSID-11) \' OHO Crash (& now Incident sites)
/( BI Mobile Satellite Station \( OIO CLOUDY (other clouds w ovrly) /( BI Mobile Satellite Station \( OIO CLOUDY (other clouds w ovrly)
/) BJ Wheelchair (handicapped) \) OJO Firenet MEO, MODIS Earth Obs. /) BJ Wheelchair (handicapped) \) OJO Firenet MEO, MODIS Earth Obs.
/* BK SnowMobile \* OK AVAIL (SNOW moved to ` ovly S) /* BK SnowMobile \* OK AVAIL (SNOW moved to ` ovly S)
@ -203,9 +204,9 @@ for the stand-alone trackers described above.
/9 P9 TBD (as mobiles at events)\9 A9 Gas Station (blue pump) /9 P9 TBD (as mobiles at events)\9 A9 Gas Station (blue pump)
/: MR FIRE \: NR AVAIL (Hail ==> ` ovly H) /: MR FIRE \: NR AVAIL (Hail ==> ` ovly H)
/; MS Campground (Portable ops) \; NSO Park/Picnic + overlay events /; MS Campground (Portable ops) \; NSO Park/Picnic + overlay events
/< MT Motorcycle (SSID =10) \< NTO ADVISORY (one WX flag) /< MT Motorcycle (SSID-10) \< NTO ADVISORY (one WX flag)
/= MU RAILROAD ENGINE \= NUO APRStt Touchtone (DTMF users) /= MU RAILROAD ENGINE \= NUO APRStt Touchtone (DTMF users)
/> MV CAR (SSID = 9) \> NV# OVERLAYED CARs & Vehicles /> MV CAR (SSID-9) \> NV# OVERLAYED CARs & Vehicles
/? MW SERVER for Files \? NW INFO Kiosk (Blue box with ?) /? MW SERVER for Files \? NW INFO Kiosk (Blue box with ?)
/@ MX HC FUTURE predict (dot) \@ NX HURICANE/Trop-Storm /@ MX HC FUTURE predict (dot) \@ NX HURICANE/Trop-Storm
/A PA Aid Station \A AA# overlayBOX DTMF & RFID & XO /A PA Aid Station \A AA# overlayBOX DTMF & RFID & XO
@ -222,19 +223,19 @@ for the stand-alone trackers described above.
/L PL PC user (Jan 03) \L AL Lighthouse /L PL PC user (Jan 03) \L AL Lighthouse
/M PM MacAPRS \M AMO MARS (A=Army,N=Navy,F=AF) /M PM MacAPRS \M AMO MARS (A=Army,N=Navy,F=AF)
/N PN NTS Station \N AN Navigation Buoy /N PN NTS Station \N AN Navigation Buoy
/O PO BALLOON (SSID =11) \O AO Overlay Balloon (Rocket = \O) /O PO BALLOON (SSID-11) \O AO Overlay Balloon (Rocket = \O)
/P PP Police \P AP Parking /P PP Police \P AP Parking
/Q PQ TBD \Q AQ QUAKE /Q PQ TBD \Q AQ QUAKE
/R PR REC. VEHICLE (SSID =13) \R ARO Restaurant /R PR REC. VEHICLE (SSID-13) \R ARO Restaurant
/S PS SHUTTLE \S AS Satellite/Pacsat /S PS SHUTTLE \S AS Satellite/Pacsat
/T PT SSTV \T AT Thunderstorm /T PT SSTV \T AT Thunderstorm
/U PU BUS (SSID = 2) \U AU SUNNY /U PU BUS (SSID-2) \U AU SUNNY
/V PV ATV \V AV VORTAC Nav Aid /V PV ATV \V AV VORTAC Nav Aid
/W PW National WX Service Site \W AW# # NWS site (NWS options) /W PW National WX Service Site \W AW# # NWS site (NWS options)
/X PX HELO (SSID = 6) \X AX Pharmacy Rx (Apothicary) /X PX HELO (SSID-6) \X AX Pharmacy Rx (Apothicary)
/Y PY YACHT (sail) (SSID = 5) \Y AYO Radios and devices /Y PY YACHT (sail) (SSID-5) \Y AYO Radios and devices
/Z PZ WinAPRS \Z AZ AVAIL /Z PZ WinAPRS \Z AZ AVAIL
/[ HS Human/Person (HT) \[ DSO W.Cloud (& humans w Ovrly) /[ HS Human/Person (SSID-7) \[ DSO W.Cloud (& humans w Ovrly)
/\ HT TRIANGLE(DF station) \\ DTO New overlayable GPS symbol /\ HT TRIANGLE(DF station) \\ DTO New overlayable GPS symbol
/] HU MAIL/PostOffice(was PBBS) \] DU AVAIL /] HU MAIL/PostOffice(was PBBS) \] DU AVAIL
/^ HV LARGE AIRCRAFT \^ DV# other Aircraft ovrlys (2014) /^ HV LARGE AIRCRAFT \^ DV# other Aircraft ovrlys (2014)
@ -243,17 +244,17 @@ for the stand-alone trackers described above.
/$ XYZ LOWER CASE SYMBOL TABLE \$ XYZ SECONDARY SYMBOL TABLE (\) /$ XYZ LOWER CASE SYMBOL TABLE \$ XYZ SECONDARY SYMBOL TABLE (\)
-- --- ------------------------ -- --- -------------------------- -- --- ------------------------ -- --- --------------------------
/a LA AMBULANCE (SSID = 1) \a SA#O ARRL,ARES,WinLINK,Dstar, etc /a LA AMBULANCE (SSID-1) \a SA#O ARRL,ARES,WinLINK,Dstar, etc
/b LB BIKE (SSID = 4) \b SB AVAIL(Blwng Dst/Snd => E ovly) /b LB BIKE (SSID-4) \b SB AVAIL(Blwng Dst/Snd => E ovly)
/c LC Incident Command Post \c SC#O CD triangle RACES/SATERN/etc /c LC Incident Command Post \c SC#O CD triangle RACES/SATERN/etc
/d LD Fire dept \d SD DX spot by callsign /d LD Fire dept \d SD DX spot by callsign
/e LE HORSE (equestrian) \e SE Sleet (& future ovrly codes) /e LE HORSE (equestrian) \e SE Sleet (& future ovrly codes)
/f LF FIRE TRUCK (SSID = 3) \f SF Funnel Cloud /f LF FIRE TRUCK (SSID-3) \f SF Funnel Cloud
/g LG Glider \g SG Gale Flags /g LG Glider \g SG Gale Flags
/h LH HOSPITAL \h SHO Store. or HAMFST Hh=HAM store /h LH HOSPITAL \h SHO Store. or HAMFST Hh=HAM store
/i LI IOTA (islands on the air) \i SI# BOX or points of Interest /i LI IOTA (islands on the air) \i SI# BOX or points of Interest
/j LJ JEEP (SSID-12) \j SJ WorkZone (Steam Shovel) /j LJ JEEP (SSID-12) \j SJ WorkZone (Steam Shovel)
/k LK TRUCK (SSID = 14) \k SKO Special Vehicle SUV,ATV,4x4 /k LK TRUCK (SSID-14) \k SKO Special Vehicle SUV,ATV,4x4
/l LL Laptop (Jan 03) (Feb 07) \l SL Areas (box,circles,etc) /l LL Laptop (Jan 03) (Feb 07) \l SL Areas (box,circles,etc)
/m LM Mic-E Repeater \m SM Value Sign (3 digit display) /m LM Mic-E Repeater \m SM Value Sign (3 digit display)
/n LN Node (black bulls-eye) \n SN# OVERLAY TRIANGLE /n LN Node (black bulls-eye) \n SN# OVERLAY TRIANGLE
@ -264,7 +265,7 @@ for the stand-alone trackers described above.
/s LS SHIP (pwr boat) (SSID-8) \s SS# OVERLAY SHIP/boats /s LS SHIP (pwr boat) (SSID-8) \s SS# OVERLAY SHIP/boats
/t LT TRUCK STOP \t ST Tornado /t LT TRUCK STOP \t ST Tornado
/u LU TRUCK (18 wheeler) \u SU# OVERLAYED TRUCK /u LU TRUCK (18 wheeler) \u SU# OVERLAYED TRUCK
/v LV VAN (SSID = 15) \v SV# OVERLAYED Van /v LV VAN (SSID-15) \v SV# OVERLAYED Van
/w LW WATER station \w SWO Flooding (Avalanches/Slides) /w LW WATER station \w SWO Flooding (Avalanches/Slides)
/x LX xAPRS (Unix) \x SX Wreck or Obstruction ->X<- /x LX xAPRS (Unix) \x SX Wreck or Obstruction ->X<-
/y LY YAGI @ QTH \y SY Skywarn /y LY YAGI @ QTH \y SY Skywarn

View File

@ -57,10 +57,6 @@
#include <math.h> #include <math.h>
#include <ctype.h> #include <ctype.h>
#if __WIN32__
char *strsep(char **stringp, const char *delim);
#endif
#include "direwolf.h" #include "direwolf.h"
#include "ax25_pad.h" // for packet_t, AX25_MAX_ADDR_LEN #include "ax25_pad.h" // for packet_t, AX25_MAX_ADDR_LEN
#include "decode_aprs.h" // for decode_aprs_t, G_UNKNOWN #include "decode_aprs.h" // for decode_aprs_t, G_UNKNOWN
@ -117,7 +113,7 @@ struct t_metadata_s {
static struct t_metadata_s * md_list_head = NULL; static struct t_metadata_s * md_list_head = NULL;
static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_ANALOG], int ndp[T_NUM_ANALOG], int draw[T_NUM_DIGITAL], char *output); static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_ANALOG], int ndp[T_NUM_ANALOG], int draw[T_NUM_DIGITAL], char *output, size_t outputsize);
/*------------------------------------------------------------------- /*-------------------------------------------------------------------
@ -136,7 +132,7 @@ static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_A
static struct t_metadata_s * t_get_metadata (char *station) static struct t_metadata_s * t_get_metadata (char *station)
{ {
struct t_metadata_s *p; struct t_metadata_s *p;
int n, j; int n;
#if DEBUG3 #if DEBUG3
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
@ -158,13 +154,13 @@ static struct t_metadata_s * t_get_metadata (char *station)
p->magic1 = MAGIC1; p->magic1 = MAGIC1;
strncpy (p->station, station, sizeof(p->station)-1); strlcpy (p->station, station, sizeof(p->station));
for (n = 0; n < T_NUM_ANALOG; n++) { for (n = 0; n < T_NUM_ANALOG; n++) {
sprintf (p->name[n], "A%d", n+1); snprintf (p->name[n], sizeof(p->name[n]), "A%d", n+1);
} }
for (n = 0; n < T_NUM_DIGITAL; n++) { for (n = 0; n < T_NUM_DIGITAL; n++) {
sprintf (p->name[T_NUM_ANALOG+n], "D%d", n+1); snprintf (p->name[T_NUM_ANALOG+n], sizeof(p->name[T_NUM_ANALOG+n]), "D%d", n+1);
} }
for (n = 0; n < T_NUM_ANALOG; n++) { for (n = 0; n < T_NUM_ANALOG; n++) {
@ -259,7 +255,7 @@ static int t_ndp (char *str)
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
void telemetry_data_original (char *station, char *info, int quiet, char *output, char *comment) void telemetry_data_original (char *station, char *info, int quiet, char *output, size_t outputsize, char *comment, size_t commentsize)
{ {
int n; int n;
int seq; int seq;
@ -280,6 +276,9 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output
dw_printf ("\n%s\n\n", info); dw_printf ("\n%s\n\n", info);
#endif #endif
strlcpy (output, "", outputsize);
strlcpy (comment, "", commentsize);
pm = t_get_metadata(station); pm = t_get_metadata(station);
assert (pm->magic1 == MAGIC1); assert (pm->magic1 == MAGIC1);
@ -307,8 +306,7 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output
* Remove any trailing CR/LF. * Remove any trailing CR/LF.
*/ */
memset (stemp, 0, sizeof(stemp)); strlcpy (stemp, info+2, sizeof(stemp));
strncpy (stemp, info+2, sizeof(stemp)-1);
for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) { for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) {
*p = '\0'; *p = '\0';
@ -352,7 +350,7 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output
// TODO: test this! // TODO: test this!
if (strlen(next) > 8) { if (strlen(next) > 8) {
strlcpy (comment, next+8, sizeof(comment)); strlcpy (comment, next+8, commentsize);
next[8] = '\0'; next[8] = '\0';
} }
for (k = 0; k < strlen(next); k++) { for (k = 0; k < strlen(next); k++) {
@ -394,7 +392,7 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output
#endif #endif
t_data_process (pm, seq, araw, ndp, draw, output); t_data_process (pm, seq, araw, ndp, draw, output, outputsize);
} /* end telemtry_data_original */ } /* end telemtry_data_original */
@ -436,6 +434,7 @@ static int two_base91_to_i (char *c)
else { else {
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("\"%c\" is not a valid character for base 91 telemetry data.\n", c[0]); dw_printf ("\"%c\" is not a valid character for base 91 telemetry data.\n", c[0]);
return (G_UNKNOWN);
} }
if (isdigit91(c[1])) { if (isdigit91(c[1])) {
@ -444,11 +443,12 @@ static int two_base91_to_i (char *c)
else { else {
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("\"%c\" is not a valid character for base 91 telemetry data.\n", c[1]); dw_printf ("\"%c\" is not a valid character for base 91 telemetry data.\n", c[1]);
return (G_UNKNOWN);
} }
return (result); return (result);
} }
void telemetry_data_base91 (char *station, char *cdata, char *output) void telemetry_data_base91 (char *station, char *cdata, char *output, size_t outputsize)
{ {
int n; int n;
int seq; int seq;
@ -465,6 +465,8 @@ void telemetry_data_base91 (char *station, char *cdata, char *output)
dw_printf ("\n%s\n\n", cdata); dw_printf ("\n%s\n\n", cdata);
#endif #endif
strlcpy (output, "", outputsize);
pm = t_get_metadata(station); pm = t_get_metadata(station);
assert (pm->magic1 == MAGIC1); assert (pm->magic1 == MAGIC1);
@ -516,7 +518,7 @@ void telemetry_data_base91 (char *station, char *cdata, char *output)
#endif #endif
t_data_process (pm, seq, araw, ndp, draw, output); t_data_process (pm, seq, araw, ndp, draw, output, outputsize);
} /* end telemtry_data_base91 */ } /* end telemtry_data_base91 */
@ -566,8 +568,7 @@ void telemetry_name_message (char *station, char *msg)
* Remove any trailing CR LF. * Remove any trailing CR LF.
*/ */
memset (stemp, 0, sizeof(stemp)); strlcpy (stemp, msg, sizeof(stemp));
strncpy (stemp, msg, sizeof(stemp)-1);
for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) { for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) {
*p = '\0'; *p = '\0';
@ -583,8 +584,7 @@ void telemetry_name_message (char *station, char *msg)
while ((p = strsep(&next,",")) != NULL) { while ((p = strsep(&next,",")) != NULL) {
if (n < T_NUM_ANALOG + T_NUM_DIGITAL) { if (n < T_NUM_ANALOG + T_NUM_DIGITAL) {
if (strlen(p) > 0 && strcmp(p,"-") != 0) { if (strlen(p) > 0 && strcmp(p,"-") != 0) {
memset (pm->name[n], 0, T_STR_LEN); strlcpy (pm->name[n], p, sizeof(pm->name[n]));
strncpy (pm->name[n], p, T_STR_LEN-1);
} }
n++; n++;
} }
@ -644,8 +644,7 @@ void telemetry_unit_label_message (char *station, char *msg)
* Remove any trailing CR LF. * Remove any trailing CR LF.
*/ */
memset (stemp, 0, sizeof(stemp)); strlcpy (stemp, msg, sizeof(stemp));
strncpy (stemp, msg, sizeof(stemp)-1);
for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) { for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) {
*p = '\0'; *p = '\0';
@ -661,8 +660,7 @@ void telemetry_unit_label_message (char *station, char *msg)
while ((p = strsep(&next,",")) != NULL) { while ((p = strsep(&next,",")) != NULL) {
if (n < T_NUM_ANALOG + T_NUM_DIGITAL) { if (n < T_NUM_ANALOG + T_NUM_DIGITAL) {
if (strlen(p) > 0) { if (strlen(p) > 0) {
memset (pm->unit[n], 0, T_STR_LEN); strlcpy (pm->unit[n], p, sizeof(pm->unit[n]));
strncpy (pm->unit[n], p, T_STR_LEN-1);
} }
n++; n++;
} }
@ -723,8 +721,7 @@ void telemetry_coefficents_message (char *station, char *msg, int quiet)
* Remove any trailing CR LF. * Remove any trailing CR LF.
*/ */
memset (stemp, 0, sizeof(stemp)); strlcpy (stemp, msg, sizeof(stemp));
strncpy (stemp, msg, sizeof(stemp)-1);
for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) { for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) {
*p = '\0'; *p = '\0';
@ -841,7 +838,7 @@ void telemetry_bit_sense_message (char *station, char *msg, int quiet)
if (msg[n] == ',') n++; if (msg[n] == ',') n++;
strncpy (pm->project, msg+n, sizeof(pm->project)-1); strlcpy (pm->project, msg+n, sizeof(pm->project));
#if DEBUG3 #if DEBUG3
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
@ -905,7 +902,7 @@ static void ival_to_str (int x, char str[VAL_STR_SIZE])
} }
} }
static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_ANALOG], int ndp[T_NUM_ANALOG], int draw[T_NUM_DIGITAL], char *output) static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_ANALOG], int ndp[T_NUM_ANALOG], int draw[T_NUM_DIGITAL], char *output, size_t outputsize)
{ {
int n; int n;
char val_str[VAL_STR_SIZE]; char val_str[VAL_STR_SIZE];
@ -915,16 +912,16 @@ static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_A
assert (pm->magic1 == MAGIC1); assert (pm->magic1 == MAGIC1);
assert (pm->magic2 == MAGIC2); assert (pm->magic2 == MAGIC2);
strcpy (output, ""); strlcpy (output, "", outputsize);
if (strlen(pm->project) > 0) { if (strlen(pm->project) > 0) {
strcpy (output, pm->project); strlcpy (output, pm->project, outputsize);
strcat (output, ": "); strlcat (output, ": ", outputsize);
} }
ival_to_str (seq, val_str); ival_to_str (seq, val_str);
strcat (output, "Seq="); strlcat (output, "Seq=", outputsize);
strcat (output, val_str); strlcat (output, val_str, outputsize);
for (n = 0; n < T_NUM_ANALOG; n++) { for (n = 0; n < T_NUM_ANALOG; n++) {
@ -934,10 +931,10 @@ static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_A
float fval; float fval;
int fndp; int fndp;
strcat (output, ", "); strlcat (output, ", ", outputsize);
strcat (output, pm->name[n]); strlcat (output, pm->name[n], outputsize);
strcat (output, "="); strlcat (output, "=", outputsize);
// Scaling and suitable number of decimal places for display. // Scaling and suitable number of decimal places for display.
@ -956,10 +953,10 @@ static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_A
fndp = MAX (z, MAX(pm->coeff_ndp[n][C_B] + ndp[n], pm->coeff_ndp[n][C_C])); fndp = MAX (z, MAX(pm->coeff_ndp[n][C_B] + ndp[n], pm->coeff_ndp[n][C_C]));
} }
fval_to_str (fval, fndp, val_str); fval_to_str (fval, fndp, val_str);
strcat (output, val_str); strlcat (output, val_str, outputsize);
if (strlen(pm->unit[n]) > 0) { if (strlen(pm->unit[n]) > 0) {
strcat (output, " "); strlcat (output, " ", outputsize);
strcat (output, pm->unit[n]); strlcat (output, pm->unit[n], outputsize);
} }
} }
@ -972,10 +969,10 @@ static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_A
if (draw[n] != G_UNKNOWN) { if (draw[n] != G_UNKNOWN) {
int dval; int dval;
strcat (output, ", "); strlcat (output, ", ", outputsize);
strcat (output, pm->name[T_NUM_ANALOG+n]); strlcat (output, pm->name[T_NUM_ANALOG+n], outputsize);
strcat (output, "="); strlcat (output, "=", outputsize);
// Possible inverting for bit sense. // Possible inverting for bit sense.
@ -989,10 +986,10 @@ static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_A
ival_to_str (dval, val_str); ival_to_str (dval, val_str);
if (strlen(pm->unit[T_NUM_ANALOG+n]) > 0) { if (strlen(pm->unit[T_NUM_ANALOG+n]) > 0) {
strcat (output, " "); strlcat (output, " ", outputsize);
strcat (output, pm->unit[T_NUM_ANALOG+n]); strlcat (output, pm->unit[T_NUM_ANALOG+n], outputsize);
} }
strcat (output, val_str); strlcat (output, val_str, outputsize);
} }
} }
@ -1011,7 +1008,8 @@ static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_A
* *
* Unit test. Run with: * Unit test. Run with:
* *
* make -f Makefile.? etest * make etest
*
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
@ -1019,14 +1017,14 @@ static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_A
#if TEST #if TEST
int main ( ) int main ( )
{ {
char result[256]; char result[120];
char comment[256]; char comment[40];
int errors = 0;
strcpy (result, ""); strlcpy (result, "", sizeof(result));
strcpy (comment, ""); strlcpy (comment, "", sizeof(comment));
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
@ -1039,24 +1037,69 @@ int main ( )
// From protocol spec. // From protocol spec.
telemetry_data_original ("WB2OSZ", "T#005,199,000,255,073,123,01101001", 0, result, comment); telemetry_data_original ("WB2OSZ", "T#005,199,000,255,073,123,01101001", 0, result, sizeof(result), comment, sizeof(comment));
if (strcmp(result, "Seq=5, A1=199, A2=0, A3=255, A4=73, A5=123, D1=0, D2=1, D3=1, D4=0, D5=1, D6=0, D7=0, D8=1") != 0 ||
strcmp(comment, "") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 101\n");
}
// Try adding a comment. // Try adding a comment.
telemetry_data_original ("WB2OSZ", "T#005,199,000,255,073,123,01101001Comment,with,commas", 0, result, comment); telemetry_data_original ("WB2OSZ", "T#005,199,000,255,073,123,01101001Comment,with,commas", 0, result, sizeof(result), comment, sizeof(comment));
strcpy (comment, "");
// Try shortening or omitting parts. if (strcmp(result, "Seq=5, A1=199, A2=0, A3=255, A4=73, A5=123, D1=0, D2=1, D3=1, D4=0, D5=1, D6=0, D7=0, D8=1") != 0 ||
strcmp(comment, "Comment,with,commas") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 102\n");
}
// Error handling - Try shortening or omitting parts.
telemetry_data_original ("WB2OSZ", "T005,199,000,255,073,123,0110", 0, result, sizeof(result), comment, sizeof(comment));
if (strcmp(result, "") != 0 ||
strcmp(comment, "") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 103\n");
}
telemetry_data_original ("WB2OSZ", "T#005,199,000,255,073,123,0110", 0, result, sizeof(result), comment, sizeof(comment));
if (strcmp(result, "Seq=5, A1=199, A2=0, A3=255, A4=73, A5=123, D1=0, D2=1, D3=1, D4=0") != 0 ||
strcmp(comment, "") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 104\n");
}
telemetry_data_original ("WB2OSZ", "T#005,199,000,255,073,123", 0, result, sizeof(result), comment, sizeof(comment));
if (strcmp(result, "Seq=5, A1=199, A2=0, A3=255, A4=73, A5=123") != 0 ||
strcmp(comment, "") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 105\n");
}
telemetry_data_original ("WB2OSZ", "T#005,199,000,255,,123,01101001", 0, result, sizeof(result), comment, sizeof(comment));
if (strcmp(result, "Seq=5, A1=199, A2=0, A3=255, A5=123, D1=0, D2=1, D3=1, D4=0, D5=1, D6=0, D7=0, D8=1") != 0 ||
strcmp(comment, "") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 106\n");
}
telemetry_data_original ("WB2OSZ", "T#005,199,000,255,073,123,01101009", 0, result, sizeof(result), comment, sizeof(comment));
if (strcmp(result, "Seq=5, A1=199, A2=0, A3=255, A4=73, A5=123, D1=0, D2=1, D3=1, D4=0, D5=1, D6=0, D7=0") != 0 ||
strcmp(comment, "") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 107\n");
}
telemetry_data_original ("WB2OSZ", "T005,199,000,255,073,123,0110", 0, result, comment);
telemetry_data_original ("WB2OSZ", "T#005,199,000,255,073,123,0110", 0, result, comment);
telemetry_data_original ("WB2OSZ", "T#005,199,000,255,073,123", 0, result, comment);
telemetry_data_original ("WB2OSZ", "T#005,199,000,255,,123,01101001", 0, result, comment);
telemetry_data_original ("WB2OSZ", "T#005,199,000,255,073,123,01101009", 0, result, comment);
// Local observation. // Local observation.
telemetry_data_original ("WB2OSZ", "T#491,4.9,0.3,25.0,0.0,1.0,00000000", 0, result, comment); telemetry_data_original ("WB2OSZ", "T#491,4.9,0.3,25.0,0.0,1.0,00000000", 0, result, sizeof(result), comment, sizeof(comment));
if (strcmp(result, "Seq=491, A1=4.9, A2=0.3, A3=25.0, A4=0.0, A5=1.0, D1=0, D2=0, D3=0, D4=0, D5=0, D6=0, D7=0, D8=0") != 0 ||
strcmp(comment, "") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 108\n");
}
#endif #endif
@ -1067,43 +1110,187 @@ int main ( )
// From protocol spec. // From protocol spec.
telemetry_data_base91 ("WB2OSZ", "ss11", result); telemetry_data_base91 ("WB2OSZ", "ss11", result, sizeof(result));
dw_printf ("expect 7544: 1472 above.\n");
telemetry_data_base91 ("WB2OSZ", "ss11223344{{!\"", result); if (strcmp(result, "Seq=7544, A1=1472") != 0) {
dw_printf ("expect 7544: 1472, 1564, 1656, 1748, 8280, 10000000 above.\n"); errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 201\n");
}
telemetry_data_base91 ("WB2OSZ", "ss11223344{{!\"", result, sizeof(result));
if (strcmp(result, "Seq=7544, A1=1472, A2=1564, A3=1656, A4=1748, A5=8280, D1=1, D2=0, D3=0, D4=0, D5=0, D6=0, D7=0, D8=0") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 202\n");
}
// Error cases. Should not happen in practice because function // Error cases. Should not happen in practice because function
// should be called only with valid data that matches the pattern. // should be called only with valid data that matches the pattern.
telemetry_data_base91 ("WB2OSZ", "ss11223344{{!\"x", result); telemetry_data_base91 ("WB2OSZ", "ss11223344{{!\"x", result, sizeof(result));
telemetry_data_base91 ("WB2OSZ", "ss1", result);
telemetry_data_base91 ("WB2OSZ", "ss11223344{{!", result); if (strcmp(result, "") != 0) {
telemetry_data_base91 ("WB2OSZ", "s |1", result); errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 203\n");
}
telemetry_data_base91 ("WB2OSZ", "ss1", result, sizeof(result));
if (strcmp(result, "") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 204\n");
}
telemetry_data_base91 ("WB2OSZ", "ss11223344{{!", result, sizeof(result));
if (strcmp(result, "") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 205\n");
}
telemetry_data_base91 ("WB2OSZ", "s |1", result, sizeof(result));
if (strcmp(result, "Seq=?") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 206\n");
}
#endif #endif
#if DEBUG3 #if DEBUG3
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("part 3\n"); dw_printf ("part 3\n");
telemetry_name_message ("N0QBF-11", "Battery,Btemp,ATemp,Pres,Alt,Camra,Chut,Sun,10m,ATV"); telemetry_name_message ("N0QBF-11", "Battery,Btemp,ATemp,Pres,Alt,Camra,Chut,Sun,10m,ATV");
struct t_metadata_s *pm;
pm = t_get_metadata("N0QBF-11");
if (strcmp(pm->name[0], "Battery") != 0 ||
strcmp(pm->name[1], "Btemp") != 0 ||
strcmp(pm->name[2], "ATemp") != 0 ||
strcmp(pm->name[3], "Pres") != 0 ||
strcmp(pm->name[4], "Alt") != 0 ||
strcmp(pm->name[5], "Camra") != 0 ||
strcmp(pm->name[6], "Chut") != 0 ||
strcmp(pm->name[7], "Sun") != 0 ||
strcmp(pm->name[8], "10m") != 0 ||
strcmp(pm->name[9], "ATV") != 0 ||
strcmp(pm->name[10], "D6") != 0 ||
strcmp(pm->name[11], "D7") != 0 ||
strcmp(pm->name[12], "D8") != 0 ) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 301\n");
}
telemetry_unit_label_message ("N0QBF-11", "v/100,deg.F,deg.F,Mbar,Kft,Click,OPEN,on,on,hi"); telemetry_unit_label_message ("N0QBF-11", "v/100,deg.F,deg.F,Mbar,Kft,Click,OPEN,on,on,hi");
pm = t_get_metadata("N0QBF-11");
if (strcmp(pm->unit[0], "v/100") != 0 ||
strcmp(pm->unit[1], "deg.F") != 0 ||
strcmp(pm->unit[2], "deg.F") != 0 ||
strcmp(pm->unit[3], "Mbar") != 0 ||
strcmp(pm->unit[4], "Kft") != 0 ||
strcmp(pm->unit[5], "Click") != 0 ||
strcmp(pm->unit[6], "OPEN") != 0 ||
strcmp(pm->unit[7], "on") != 0 ||
strcmp(pm->unit[8], "on") != 0 ||
strcmp(pm->unit[9], "hi") != 0 ||
strcmp(pm->unit[10], "") != 0 ||
strcmp(pm->unit[11], "") != 0 ||
strcmp(pm->unit[12], "") != 0 ) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 302\n");
}
telemetry_coefficents_message ("N0QBF-11", "0,5.2,0,0,.53,-32,3,4.39,49,-32,3,18,1,2,3", 0); telemetry_coefficents_message ("N0QBF-11", "0,5.2,0,0,.53,-32,3,4.39,49,-32,3,18,1,2,3", 0);
pm = t_get_metadata("N0QBF-11");
if (pm->coeff[0][0] != 0 || pm->coeff[0][1] < 5.1999 || pm->coeff[0][1] > 5.2001 || pm->coeff[0][2] != 0 ||
pm->coeff[1][0] != 0 || pm->coeff[1][1] < .52999 || pm->coeff[1][1] > .53001 || pm->coeff[1][2] != -32 ||
pm->coeff[2][0] != 3 || pm->coeff[2][1] < 4.3899 || pm->coeff[2][1] > 4.3901 || pm->coeff[2][2] != 49 ||
pm->coeff[3][0] != -32 || pm->coeff[3][1] != 3 || pm->coeff[3][2] != 18 ||
pm->coeff[4][0] != 1 || pm->coeff[4][1] != 2 || pm->coeff[4][2] != 3) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 303c\n");
}
if (pm->coeff_ndp[0][0] != 0 || pm->coeff_ndp[0][1] != 1 || pm->coeff_ndp[0][2] != 0 ||
pm->coeff_ndp[1][0] != 0 || pm->coeff_ndp[1][1] != 2 || pm->coeff_ndp[1][2] != 0 ||
pm->coeff_ndp[2][0] != 0 || pm->coeff_ndp[2][1] != 2 || pm->coeff_ndp[2][2] != 0 ||
pm->coeff_ndp[3][0] != 0 || pm->coeff_ndp[3][1] != 0 || pm->coeff_ndp[3][2] != 0 ||
pm->coeff_ndp[4][0] != 0 || pm->coeff_ndp[4][1] != 0 || pm->coeff_ndp[4][2] != 0 ) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 303n\n");
}
// Error if less than 15 or empty field. // Error if less than 15 or empty field.
// Notice that we keep the previous value in this case.
telemetry_coefficents_message ("N0QBF-11", "0,5.2,0,0,.53,-32,3,4.39,49,-32,3,18,1,2", 0); telemetry_coefficents_message ("N0QBF-11", "0,5.2,0,0,.53,-32,3,4.39,49,-32,3,18,1,2", 0);
pm = t_get_metadata("N0QBF-11");
if (pm->coeff[0][0] != 0 || pm->coeff[0][1] < 5.1999 || pm->coeff[0][1] > 5.2001 || pm->coeff[0][2] != 0 ||
pm->coeff[1][0] != 0 || pm->coeff[1][1] < .52999 || pm->coeff[1][1] > .53001 || pm->coeff[1][2] != -32 ||
pm->coeff[2][0] != 3 || pm->coeff[2][1] < 4.3899 || pm->coeff[2][1] > 4.3901 || pm->coeff[2][2] != 49 ||
pm->coeff[3][0] != -32 || pm->coeff[3][1] != 3 || pm->coeff[3][2] != 18 ||
pm->coeff[4][0] != 1 || pm->coeff[4][1] != 2 || pm->coeff[4][2] != 3) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 304c\n");
}
if (pm->coeff_ndp[0][0] != 0 || pm->coeff_ndp[0][1] != 1 || pm->coeff_ndp[0][2] != 0 ||
pm->coeff_ndp[1][0] != 0 || pm->coeff_ndp[1][1] != 2 || pm->coeff_ndp[1][2] != 0 ||
pm->coeff_ndp[2][0] != 0 || pm->coeff_ndp[2][1] != 2 || pm->coeff_ndp[2][2] != 0 ||
pm->coeff_ndp[3][0] != 0 || pm->coeff_ndp[3][1] != 0 || pm->coeff_ndp[3][2] != 0 ||
pm->coeff_ndp[4][0] != 0 || pm->coeff_ndp[4][1] != 0 || pm->coeff_ndp[4][2] != 0 ) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 304n\n");
}
telemetry_coefficents_message ("N0QBF-11", "0,5.2,0,0,.53,-32,3,4.39,49,-32,3,18,1,,3", 0); telemetry_coefficents_message ("N0QBF-11", "0,5.2,0,0,.53,-32,3,4.39,49,-32,3,18,1,,3", 0);
pm = t_get_metadata("N0QBF-11");
if (pm->coeff[0][0] != 0 || pm->coeff[0][1] < 5.1999 || pm->coeff[0][1] > 5.2001 || pm->coeff[0][2] != 0 ||
pm->coeff[1][0] != 0 || pm->coeff[1][1] < .52999 || pm->coeff[1][1] > .53001 || pm->coeff[1][2] != -32 ||
pm->coeff[2][0] != 3 || pm->coeff[2][1] < 4.3899 || pm->coeff[2][1] > 4.3901 || pm->coeff[2][2] != 49 ||
pm->coeff[3][0] != -32 || pm->coeff[3][1] != 3 || pm->coeff[3][2] != 18 ||
pm->coeff[4][0] != 1 || pm->coeff[4][1] != 2 || pm->coeff[4][2] != 3) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 305c\n");
}
if (pm->coeff_ndp[0][0] != 0 || pm->coeff_ndp[0][1] != 1 || pm->coeff_ndp[0][2] != 0 ||
pm->coeff_ndp[1][0] != 0 || pm->coeff_ndp[1][1] != 2 || pm->coeff_ndp[1][2] != 0 ||
pm->coeff_ndp[2][0] != 0 || pm->coeff_ndp[2][1] != 2 || pm->coeff_ndp[2][2] != 0 ||
pm->coeff_ndp[3][0] != 0 || pm->coeff_ndp[3][1] != 0 || pm->coeff_ndp[3][2] != 0 ||
pm->coeff_ndp[4][0] != 0 || pm->coeff_ndp[4][1] != 0 || pm->coeff_ndp[4][2] != 0 ) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 305n\n");
}
telemetry_bit_sense_message ("N0QBF-11", "10110000,N0QBF's Big Balloon", 0); telemetry_bit_sense_message ("N0QBF-11", "10110000,N0QBF's Big Balloon", 0);
pm = t_get_metadata("N0QBF-11");
if (pm->sense[0] != 1 || pm->sense[1] != 0 || pm->sense[2] != 1 || pm->sense[3] != 1 ||
pm->sense[4] != 0 || pm->sense[5] != 0 || pm->sense[6] != 0 || pm->sense[7] != 0 ||
strcmp(pm->project, "N0QBF's Big Balloon") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 306\n");
}
// Too few and invalid digits. // Too few and invalid digits.
telemetry_bit_sense_message ("N0QBF-11", "1011000", 0); telemetry_bit_sense_message ("N0QBF-11", "1011000", 0);
pm = t_get_metadata("N0QBF-11");
if (pm->sense[0] != 1 || pm->sense[1] != 0 || pm->sense[2] != 1 || pm->sense[3] != 1 ||
pm->sense[4] != 0 || pm->sense[5] != 0 || pm->sense[6] != 0 || pm->sense[7] != 0 ||
strcmp(pm->project, "") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 307\n");
}
telemetry_bit_sense_message ("N0QBF-11", "10110008", 0); telemetry_bit_sense_message ("N0QBF-11", "10110008", 0);
pm = t_get_metadata("N0QBF-11");
if (pm->sense[0] != 1 || pm->sense[1] != 0 || pm->sense[2] != 1 || pm->sense[3] != 1 ||
pm->sense[4] != 0 || pm->sense[5] != 0 || pm->sense[6] != 0 || pm->sense[7] != 0 ||
strcmp(pm->project, "") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 308\n");
}
#endif #endif
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
@ -1114,15 +1301,46 @@ int main ( )
telemetry_name_message ("M0XER-3", "Vbat,Vsolar,Temp,Sat"); telemetry_name_message ("M0XER-3", "Vbat,Vsolar,Temp,Sat");
telemetry_unit_label_message ("M0XER-3", "V,V,C,,m"); telemetry_unit_label_message ("M0XER-3", "V,V,C,,m");
telemetry_data_base91 ("M0XER-3", "DyR.&^<A!.", result); telemetry_data_base91 ("M0XER-3", "DyR.&^<A!.", result, sizeof(result));
telemetry_data_base91 ("M0XER-3", "cNOv'C?=!-", result);
telemetry_data_base91 ("M0XER-3", "n0RS(:>b!+", result); if (strcmp(result, "10mW research balloon: Seq=3273, Vbat=4.472 V, Vsolar=0.516 V, Temp=-24.3 C, Sat=13") != 0 ||
telemetry_data_base91 ("M0XER-3", "x&G=!(8s!,", result); strcmp(comment, "") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 401\n");
}
telemetry_data_base91 ("M0XER-3", "cNOv'C?=!-", result, sizeof(result));
if (strcmp(result, "10mW research balloon: Seq=6051, Vbat=4.271 V, Vsolar=0.580 V, Temp=2.6 C, Sat=12") != 0 ||
strcmp(comment, "") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 402\n");
}
telemetry_data_base91 ("M0XER-3", "n0RS(:>b!+", result, sizeof(result));
if (strcmp(result, "10mW research balloon: Seq=7022, Vbat=4.509 V, Vsolar=0.662 V, Temp=-2.8 C, Sat=10") != 0 ||
strcmp(comment, "") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 403\n");
}
telemetry_data_base91 ("M0XER-3", "x&G=!(8s!,", result, sizeof(result));
if (strcmp(result, "10mW research balloon: Seq=7922, Vbat=3.486 V, Vsolar=0.007 V, Temp=-55.7 C, Sat=11") != 0 ||
strcmp(comment, "") != 0) {
errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 404\n");
}
// TODO: Should return success/fail so visual inspection is not needed. /* final score. */
exit (0); if (errors != 0) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("\nTEST FAILED with %d errors.\n", errors);
exit (EXIT_FAILURE);
}
text_color_set (DW_COLOR_REC);
dw_printf ("\nTEST WAS SUCCESSFUL.\n");
exit (EXIT_SUCCESS);
} }
/* /*

View File

@ -2,9 +2,9 @@
/* telemetry.h */ /* telemetry.h */
void telemetry_data_original (char *station, char *info, int quiet, char *output, char *comment); void telemetry_data_original (char *station, char *info, int quiet, char *output, size_t outputsize, char *comment, size_t commentsize);
void telemetry_data_base91 (char *station, char *cdata, char *output); void telemetry_data_base91 (char *station, char *cdata, char *output, size_t outputsize);
void telemetry_name_message (char *station, char *msg); void telemetry_name_message (char *station, char *msg);

View File

@ -1,6 +1,9 @@
APRS TO-CALL VERSION NUMBERS 27 Apr 2015 APRS TO-CALL VERSION NUMBERS 26 Oct 2015
------------------------------------------------------------------- -------------------------------------------------------------------
WB4APR WB4APR
26 Oct 15 added APZ247 for UPRS
09 Sep 15 added APHTxx for HMTracker by IU0AAC
06 Aug 15 added APMTxx for LZ1PPL for tracker
27 Apr 15 added APZMAJ for Martyn M1MAJ DeLorme inReach Tracker 27 Apr 15 added APZMAJ for Martyn M1MAJ DeLorme inReach Tracker
21 Apr 15 added APB2MF & APR2MF DL2MF - MF2APRS Radiosonde 21 Apr 15 added APB2MF & APR2MF DL2MF - MF2APRS Radiosonde
06 Apr 15 added APAVT5 SainSonic AP510 - a 1watt tracker 06 Apr 15 added APAVT5 SainSonic AP510 - a 1watt tracker
@ -9,26 +12,7 @@ APRS TO-CALL VERSION NUMBERS 27 Apr 2015
21 Aug 14 added APSMSx Paul Defrusne's SMS gateway 21 Aug 14 added APSMSx Paul Defrusne's SMS gateway
11 Aug 14 added APCWP8 John GM7HHB, WinphoneAPRS 11 Aug 14 added APCWP8 John GM7HHB, WinphoneAPRS
18 Dec 13 added APZWKR GM1WKR NetSked application 18 Dec 13 added APZWKR GM1WKR NetSked application
22 Oct 13 added APFIxx APRS.FI OH7LZB, Hessu
23 Aug 13 added APOxxx OSCAR satellites for AMSAT-LU by LU9DO
22 Feb 13 added APNWxx SQ3FYK.com & SQ3PLX http://microsat.com.pl/
and APMIxx SQ3PLX http://microsat.com.pl/
29 Jan 13 added APICxx for HA9MCQ Pic IGate
23 Jan 13 added APWAxx APRSISCE Android version
18 Jan 13 added APDGxx,APDHxx,APDOxx,APDDxx,APDKxx,APD4xx for Dstar
13 Jan 13 added APLMxx WA0TQG transceiver controller
17 Dec 12 added APAMxx Altus Metrum GPS trackers
03 Dec 12 added APUDRx NW Digital Radio's UDR (APRS/Dstar)
03 Nov 12 added APHAXn SM2APRS by PY2UEP
17 Sep 12 added APSCxx aprsc APRS-IS core server (OH7LZB, OH2MQK)
12 Sep 12 added APSARx for ZL4FOX's SARTRACK
02 Jul 12 added APDGxx D-Star Gateways by G4KLX
28 Jun 12 added APDInn DIXPRS - Bela, HA5DI
27 jun 12 added APMGxx MiniGate - Alex, AB0TJ
17 Feb 12 added APJYnn KA2DDO yet another APRS system
20 Jan 12 added APDSXX SP9UOB for dsDigi and ds-tracker
APBPQx John G8BPQ Digipeater/IGate
APLQRU Charlie - QRU Server
11 Jan 12 added APYTxx for YagTracker and updated Yaesu APY008/350 11 Jan 12 added APYTxx for YagTracker and updated Yaesu APY008/350
In APRS, the AX.25 Destination address is not used for packet In APRS, the AX.25 Destination address is not used for packet
@ -92,6 +76,7 @@ a TOCALL number series:
APGOxx for AA3NJ PDA application APGOxx for AA3NJ PDA application
APH APHKxx for LA1BR tracker/digipeater APH APHKxx for LA1BR tracker/digipeater
APHAXn SM2APRS by PY2UEP APHAXn SM2APRS by PY2UEP
APHTxx HMTracker by IU0AAC
API APICQx for ICQ API APICQx for ICQ
APICxx for HA9MCQ Pic IGate APICxx for HA9MCQ Pic IGate
APJ APJAxx JavAPRS APJ APJAxx JavAPRS
@ -109,6 +94,7 @@ a TOCALL number series:
APM APMxxx MacAPRS, APM APMxxx MacAPRS,
APMGxx MiniGate - Alex, AB0TJ APMGxx MiniGate - Alex, AB0TJ
APMIxx SQ3PLX http://microsat.com.pl/ APMIxx SQ3PLX http://microsat.com.pl/
APMTxx LZ1PPL for tracker
APN APNxxx Network nodes, digis, etc APN APNxxx Network nodes, digis, etc
APN3xx Kantronics KPC-3 rom versions APN3xx Kantronics KPC-3 rom versions
APN9xx Kantronics KPC-9612 Roms APN9xx Kantronics KPC-9612 Roms
@ -180,6 +166,7 @@ a TOCALL number series:
APY350 Yaesu FTM-350 series APY350 Yaesu FTM-350 series
APYTxx for YagTracker APYTxx for YagTracker
APZ APZxxx Experimental APZ APZxxx Experimental
APZ247 for UPRS NR0Q
APZ0xx Xastir (old versions. See APX) APZ0xx Xastir (old versions. See APX)
APZMAJ Martyn M1MAJ DeLorme inReach Tracker APZMAJ Martyn M1MAJ DeLorme inReach Tracker
APZMDR for HaMDR trackers - hessu * hes.iki.fi] APZMDR for HaMDR trackers - hessu * hes.iki.fi]

391
tt_text.c
View File

@ -162,6 +162,7 @@ static const char grid[10][10][3] =
#include <assert.h> #include <assert.h>
#include <stdarg.h> #include <stdarg.h>
#include "direwolf.h"
#include "textcolor.h" #include "textcolor.h"
#include "tt_text.h" #include "tt_text.h"
@ -202,9 +203,9 @@ int dw_printf (const char *fmt, ...)
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
int tt_text_to_multipress (char *text, int quiet, char *buttons) int tt_text_to_multipress (const char *text, int quiet, char *buttons)
{ {
char *t = text; const char *t = text;
char *b = buttons; char *b = buttons;
char c; char c;
int row, col; int row, col;
@ -305,9 +306,9 @@ int tt_text_to_multipress (char *text, int quiet, char *buttons)
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
int tt_text_to_two_key (char *text, int quiet, char *buttons) int tt_text_to_two_key (const char *text, int quiet, char *buttons)
{ {
char *t = text; const char *t = text;
char *b = buttons; char *b = buttons;
char c; char c;
int row, col; int row, col;
@ -382,6 +383,7 @@ int tt_text_to_two_key (char *text, int quiet, char *buttons)
* "00" for error because this is probably * "00" for error because this is probably
* being used to build up a fixed length * being used to build up a fixed length
* string where positions are signficant. * string where positions are signficant.
* Must be at least 3 bytes.
* *
* Returns: Number of errors detected. * Returns: Number of errors detected.
* *
@ -390,14 +392,13 @@ int tt_text_to_two_key (char *text, int quiet, char *buttons)
// TODO: need to test this. // TODO: need to test this.
int tt_letter_to_two_digits (char c, int quiet, char *buttons) int tt_letter_to_two_digits (char c, int quiet, char buttons[3])
{ {
char *b = buttons;
int row, col; int row, col;
int errors = 0; int errors = 0;
int found; int found;
*b = '\0'; strlcpy(buttons, "", 3);
if (islower(c)) { if (islower(c)) {
c = toupper(c); c = toupper(c);
@ -409,7 +410,7 @@ int tt_letter_to_two_digits (char c, int quiet, char *buttons)
text_color_set (DW_COLOR_ERROR); text_color_set (DW_COLOR_ERROR);
dw_printf ("Letter to two digits: \"%c\" found where a letter is required.\n", c); dw_printf ("Letter to two digits: \"%c\" found where a letter is required.\n", c);
} }
strcpy (buttons, "00"); strlcpy (buttons, "00", 3);
return (errors); return (errors);
} }
@ -420,9 +421,9 @@ int tt_letter_to_two_digits (char c, int quiet, char *buttons)
for (row=0; row<10 && ! found; row++) { for (row=0; row<10 && ! found; row++) {
for (col=0; col<4 && ! found; col++) { for (col=0; col<4 && ! found; col++) {
if (c == translate[row][col]) { if (c == translate[row][col]) {
*b++ = '0' + row; buttons[0] = '0' + row;
*b++ = '1' + col; buttons[1] = '1' + col;
*b = '\0'; buttons[2] = '\0';
found = 1; found = 1;
} }
} }
@ -431,7 +432,7 @@ int tt_letter_to_two_digits (char c, int quiet, char *buttons)
errors++; errors++;
text_color_set (DW_COLOR_ERROR); text_color_set (DW_COLOR_ERROR);
dw_printf ("Letter to two digits: INTERNAL ERROR. Should not be here.\n"); dw_printf ("Letter to two digits: INTERNAL ERROR. Should not be here.\n");
strcpy (buttons, "00"); strlcpy (buttons, "00", 3);
} }
return (errors); return (errors);
@ -457,9 +458,9 @@ int tt_letter_to_two_digits (char c, int quiet, char *buttons)
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
int tt_text_to_call10 (char *text, int quiet, char *buttons) int tt_text_to_call10 (const char *text, int quiet, char *buttons)
{ {
char *t; const char *t;
char *b; char *b;
char c; char c;
int packed; /* two bits per character */ int packed; /* two bits per character */
@ -538,7 +539,7 @@ int tt_text_to_call10 (char *text, int quiet, char *buttons)
/* Binary to decimal for the columns. */ /* Binary to decimal for the columns. */
sprintf (stemp, "%04d", packed); snprintf (stemp, sizeof(stemp), "%04d", packed);
strcat (buttons, stemp); strcat (buttons, stemp);
return (errors); return (errors);
@ -568,7 +569,7 @@ int tt_text_to_call10 (char *text, int quiet, char *buttons)
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
int tt_text_to_satsq (char *text, int quiet, char *buttons) int tt_text_to_satsq (const char *text, int quiet, char *buttons, size_t buttonsize)
{ {
int row, col; int row, col;
@ -577,7 +578,7 @@ int tt_text_to_satsq (char *text, int quiet, char *buttons)
char uc[3]; char uc[3];
strcpy (buttons, ""); strlcpy (buttons, "", buttonsize);
/* Quick validity check. */ /* Quick validity check. */
@ -625,11 +626,16 @@ int tt_text_to_satsq (char *text, int quiet, char *buttons)
for (row=0; row<10 && ! found; row++) { for (row=0; row<10 && ! found; row++) {
for (col=0; col<10 && ! found; col++) { for (col=0; col<10 && ! found; col++) {
if (strcmp(uc,grid[row][col]) == 0) { if (strcmp(uc,grid[row][col]) == 0) {
buttons[0] = row + '0';
buttons[1] = col + '0'; char btemp[8];
buttons[2] = text[2];
buttons[3] = text[3]; btemp[0] = row + '0';
buttons[4] = '\0'; btemp[1] = col + '0';
btemp[2] = text[2];
btemp[3] = text[3];
btemp[4] = '\0';
strlcpy (buttons, btemp, buttonsize);
found = 1; found = 1;
} }
} }
@ -667,9 +673,9 @@ int tt_text_to_satsq (char *text, int quiet, char *buttons)
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
int tt_multipress_to_text (char *buttons, int quiet, char *text) int tt_multipress_to_text (const char *buttons, int quiet, char *text)
{ {
char *b = buttons; const char *b = buttons;
char *t = text; char *t = text;
char c; char c;
int row, col; int row, col;
@ -766,9 +772,9 @@ int tt_multipress_to_text (char *buttons, int quiet, char *text)
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
int tt_two_key_to_text (char *buttons, int quiet, char *text) int tt_two_key_to_text (const char *buttons, int quiet, char *text)
{ {
char *b = buttons; const char *b = buttons;
char *t = text; char *t = text;
char c; char c;
int row, col; int row, col;
@ -847,24 +853,24 @@ int tt_two_key_to_text (char *buttons, int quiet, char *text)
* *
* quiet - True to suppress error messages. * quiet - True to suppress error messages.
* *
* textsiz - Size of result storage. Typically 2.
*
* Outputs: text - Converted to string which should contain one upper case letter. * Outputs: text - Converted to string which should contain one upper case letter.
* If error, use 'x' as a placeholder because we are probably * Empty string on error.
* dealing with fixed length strings where position matters.
* *
* Returns: Number of errors detected. * Returns: Number of errors detected.
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
// TODO: need to test int tt_two_digits_to_letter (const char *buttons, int quiet, char *text, size_t textsiz)
int tt_two_digits_to_letter (char *buttons, int quiet, char *text)
{ {
char c1 = buttons[0]; char c1 = buttons[0];
char c2 = buttons[1]; char c2 = buttons[1];
int row, col; int row, col;
int errors = 0; int errors = 0;
char stemp2[2];
strcpy (text, "x"); strlcpy (text, "", textsiz);
if (c1 >= '2' && c1 <= '9') { if (c1 >= '2' && c1 <= '9') {
@ -874,12 +880,14 @@ int tt_two_digits_to_letter (char *buttons, int quiet, char *text)
col = c2 - '1'; col = c2 - '1';
if (translate[row][col] != 0) { if (translate[row][col] != 0) {
text[0] = translate[row][col];
text[1] = '\0'; stemp2[0] = translate[row][col];
stemp2[1] = '\0';
strlcpy (text, stemp2, textsiz);
} }
else { else {
errors++; errors++;
strcpy (text, "x"); strlcpy (text, "", textsiz);
if (! quiet) { if (! quiet) {
text_color_set (DW_COLOR_ERROR); text_color_set (DW_COLOR_ERROR);
dw_printf ("Two digits to letter: Invalid combination \"%c%c\".\n", c1, c2); dw_printf ("Two digits to letter: Invalid combination \"%c%c\".\n", c1, c2);
@ -888,7 +896,7 @@ int tt_two_digits_to_letter (char *buttons, int quiet, char *text)
} }
else { else {
errors++; errors++;
strcpy (text, "x"); strlcpy (text, "", textsiz);
if (! quiet) { if (! quiet) {
text_color_set (DW_COLOR_ERROR); text_color_set (DW_COLOR_ERROR);
dw_printf ("Two digits to letter: Second character \"%c\" must be in range of 1 through 4.\n", c2); dw_printf ("Two digits to letter: Second character \"%c\" must be in range of 1 through 4.\n", c2);
@ -897,7 +905,7 @@ int tt_two_digits_to_letter (char *buttons, int quiet, char *text)
} }
else { else {
errors++; errors++;
strcpy (text, "x"); strlcpy (text, "", textsiz);
if (! quiet) { if (! quiet) {
text_color_set (DW_COLOR_ERROR); text_color_set (DW_COLOR_ERROR);
dw_printf ("Two digits to letter: First character \"%c\" must be in range of 2 through 9.\n", c1); dw_printf ("Two digits to letter: First character \"%c\" must be in range of 2 through 9.\n", c1);
@ -926,9 +934,9 @@ int tt_two_digits_to_letter (char *buttons, int quiet, char *text)
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
int tt_call10_to_text (char *buttons, int quiet, char *text) int tt_call10_to_text (const char *buttons, int quiet, char *text)
{ {
char *b; const char *b;
char *t; char *t;
char c; char c;
int packed; /* from last 4 digits */ int packed; /* from last 4 digits */
@ -1010,39 +1018,57 @@ int tt_call10_to_text (char *buttons, int quiet, char *text)
* *
* Name: tt_mhead_to_text * Name: tt_mhead_to_text
* *
* Purpose: Convert the 4, 6, 10, or 12 digit DTMF representation of Maidenhead * Purpose: Convert the DTMF representation of
* Grid Square Locator to normal text representation. * Maidenhead Grid Square Locator to normal text representation.
* *
* Inputs: buttons - Input string. * Inputs: buttons - Input string.
* Must contain 4, 6, 10, or 12 digits. * Must contain 4, 6, 10, or 12, 16, or 18 digits.
* *
* quiet - True to suppress error messages. * quiet - True to suppress error messages.
* *
* Outputs: text - Converted to gridsquare with upper case letters and digits. * Outputs: text - Converted to gridsquare with upper case letters and digits.
* Length should be 2, 4, 6, or 8 with alternating letter or digit pairs. * Length should be 2, 4, 6, or 8 with alternating letter or digit pairs.
* Zero length if any error.
* *
* Returns: Number of errors detected. * Returns: Number of errors detected.
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
#define MAXMHPAIRS 6
int tt_mhead_to_text (char *buttons, int quiet, char *text) static const struct {
char *position;
char min_ch;
char max_ch;
} mhpair[MAXMHPAIRS] = {
{ "first", 'A', 'R' },
{ "second", '0', '9' },
{ "third", 'A', 'X' },
{ "fourth", '0', '9' },
{ "fifth", 'A', 'X' },
{ "sixth", '0', '9' }
};
int tt_mhead_to_text (const char *buttons, int quiet, char *text, size_t textsiz)
{ {
char *b; const char *b;
char *t;
int errors = 0; int errors = 0;
strcpy (text, ""); strlcpy (text, "", textsiz);
/* Validity check. */ /* Validity check. */
if (strlen(buttons) != 4 && strlen(buttons) != 6 && strlen(buttons) != 10 && strlen(buttons) != 12) { if (strlen(buttons) != 4 && strlen(buttons) != 6 &&
strlen(buttons) != 10 && strlen(buttons) != 12 &&
strlen(buttons) != 16 && strlen(buttons) != 18) {
if (! quiet) { if (! quiet) {
text_color_set (DW_COLOR_ERROR); text_color_set (DW_COLOR_ERROR);
dw_printf ("DTMF to Maidenhead Gridsquare Locator: Input \"%s\" must be exactly 4, 6, 10, or 12 digits.\n", buttons); dw_printf ("DTMF to Maidenhead Gridsquare Locator: Input \"%s\" must be exactly 4, 6, 10, or 12 digits.\n", buttons);
} }
errors++; errors++;
strlcpy (text, "", textsiz);
return (errors); return (errors);
} }
@ -1054,46 +1080,48 @@ int tt_mhead_to_text (char *buttons, int quiet, char *text)
dw_printf ("DTMF to Maidenhead Gridsquare Locator: Input \"%s\" can contain only digits.\n", buttons); dw_printf ("DTMF to Maidenhead Gridsquare Locator: Input \"%s\" can contain only digits.\n", buttons);
} }
errors++; errors++;
strlcpy (text, "", textsiz);
return (errors); return (errors);
} }
} }
/* Convert DTMF to normal representation. */
b = buttons; b = buttons;
t = text;
errors += tt_two_digits_to_letter (b, quiet, t); int n;
for (n = 0; n < 6 && b < buttons+strlen(buttons); n++) {
if ((n % 2) == 0) {
/* Convert pairs of digits to letter. */
char t2[2];
errors += tt_two_digits_to_letter (b, quiet, t2, sizeof(t2));
strlcat (text, t2, textsiz);
b += 2; b += 2;
t++;
errors += tt_two_digits_to_letter (b, quiet, t); errors += tt_two_digits_to_letter (b, quiet, t2, sizeof(t2));
strlcat (text, t2, textsiz);
b += 2; b += 2;
t++; }
else {
if (strlen(buttons) > 4) { /* Copy the digits. */
*t++ = *b++; char d3[3];
*t++ = *b++; d3[0] = *b++;
*t = '\0'; d3[1] = *b++;
d3[2] = '\0';
if (strlen(buttons) > 6) { strlcat (text, d3, textsiz);
errors += tt_two_digits_to_letter (b, quiet, t);
b += 2;
t++;
errors += tt_two_digits_to_letter (b, quiet, t);
b += 2;
t++;
if (strlen(buttons) > 10) {
*t++ = *b++;
*t++ = *b++;
*t = '\0';
}
} }
} }
if (errors != 0) {
strlcpy (text, "", textsiz);
}
return (errors); return (errors);
} /* end tt_mhead_to_text */ } /* end tt_mhead_to_text */
@ -1103,113 +1131,92 @@ int tt_mhead_to_text (char *buttons, int quiet, char *text)
* *
* Name: tt_text_to_mhead * Name: tt_text_to_mhead
* *
* Purpose: Convert the 2, 4, 6, or 8 character Maidenhead * Purpose: Convert normal text Maidenhead Grid Square Locator to DTMF representation.
* Grid Square Locator to DTMF representation.
* *
* Outputs: text - Maidenhead Grid Square locator in usual format. * Inputs: text - Maidenhead Grid Square locator in usual format.
* Length should be 2, 4, 6, or 8 with alternating letter or digit pairs. * Length should be 1 to 6 pairs with alternating letter or digit pairs.
*
* Inputs: buttons - Result with 4, 6, 10, or 12 digits.
* Each letter is replaced by two digits.
* *
* quiet - True to suppress error messages. * quiet - True to suppress error messages.
* *
* buttonsize - space available for 'buttons' result.
*
* Outputs: buttons - Result with 4, 6, 10, 12, 16, 18 digits.
* Each letter is replaced by two digits.
* Digits are simply copied.
*
* Returns: Number of errors detected. * Returns: Number of errors detected.
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
int tt_text_to_mhead (const char *text, int quiet, char *buttons, size_t buttonsize)
int tt_text_to_mhead (char *text, int quiet, char *buttons)
{ {
char *b;
char *t;
int errors = 0; int errors = 0;
int np, i;
strcpy (buttons, ""); strlcpy (buttons, "", buttonsize);
np = strlen(text) / 2;
if (strlen(text) != 2 && strlen(text) != 4 && strlen(text) != 6 && strlen(text) != 8) { if ((strlen(text) % 2) != 0) {
if (! quiet) { if (! quiet) {
text_color_set (DW_COLOR_ERROR); text_color_set (DW_COLOR_ERROR);
dw_printf ("Maidenhead Gridsquare Locator to DTMF: Input \"%s\" must be exactly 2, 4, 6, or 8 characters.\n", text); dw_printf ("Maidenhead Gridsquare Locator to DTMF: Input \"%s\" must be even number of characters.\n", text);
} }
errors++; errors++;
return (errors); return (errors);
} }
t = text; if (np < 1 || np > MAXMHPAIRS) {
b = buttons;
if (toupper(t[0]) < 'A' || toupper(t[0]) > 'R' || toupper(t[1]) < 'A' || toupper(t[1]) > 'R') {
if (! quiet) { if (! quiet) {
text_color_set (DW_COLOR_ERROR); text_color_set (DW_COLOR_ERROR);
dw_printf("The first pair of characters in Maidenhead locator \"%s\" must be in range of A thru R.\n", text); dw_printf ("Maidenhead Gridsquare Locator to DTMF: Input \"%s\" must be 1 to %d pairs of characters.\n", text, np);
} }
errors++; errors++;
return (errors); return (errors);
} }
errors += tt_letter_to_two_digits (*t, quiet, b); for (i = 0; i < np; i++) {
t++;
b += 2;
errors += tt_letter_to_two_digits (*t, quiet, b); char t0 = text[i*2];
t++; char t1 = text[i*2+1];
b += 2;
if (strlen(text) > 2) { if (toupper(t0) < mhpair[i].min_ch || toupper(t0) > mhpair[i].max_ch ||
toupper(t1) < mhpair[i].min_ch || toupper(t1) > mhpair[i].max_ch) {
if ( ! isdigit(t[0]) || ! isdigit(t[1])) {
if (! quiet) { if (! quiet) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf("The second pair of characters in Maidenhead locator \"%s\" must digits 0 thru 9.\n", text); dw_printf("The %s pair of characters in Maidenhead locator \"%s\" must be in range of %c thru %c.\n",
mhpair[i].position, text, mhpair[i].min_ch, mhpair[i].max_ch);
} }
strlcpy (buttons, "", buttonsize);
errors++; errors++;
return(errors); return(errors);
} }
*b++ = *t++; if (mhpair[i].min_ch == 'A') { /* Should be letters */
*b++ = *t++;
*b = '\0';
if (strlen(text) > 4) { char b3[3];
if (toupper(t[0]) < 'A' || toupper(t[0]) > 'X' || toupper(t[1]) < 'A' || toupper(t[1]) > 'X') { errors += tt_letter_to_two_digits (t0, quiet, b3);
if (! quiet) { strlcat (buttons, b3, buttonsize);
text_color_set(DW_COLOR_ERROR);
dw_printf("The third pair of characters in Maidenhead locator \"%s\" must be in range of A thru X.\n", text); errors += tt_letter_to_two_digits (t1, quiet, b3);
} strlcat (buttons, b3, buttonsize);
errors++;
return(errors);
} }
else { /* Should be digits */
errors += tt_letter_to_two_digits (*t, quiet, b); char b3[3];
t++;
b += 2;
errors += tt_letter_to_two_digits (*t, quiet, b); b3[0] = t0;
t++; b3[1] = t1;
b += 2; b3[2] = '\0';
strlcat (buttons, b3, buttonsize);
if (strlen(text) > 6) {
if ( ! isdigit(t[0]) || ! isdigit(t[1])) {
if (! quiet) {
text_color_set(DW_COLOR_ERROR);
dw_printf("The fourth pair of characters in Maidenhead locator \"%s\" must digits 0 thru 9.\n", text);
}
errors++;
return(errors);
}
*b++ = *t++;
*b++ = *t++;
*b = '\0';
}
} }
} }
if (errors != 0) strlcpy (buttons, "", buttonsize);
return (errors); return (errors);
} /* tt_text_to_mhead */ } /* tt_text_to_mhead */
@ -1232,9 +1239,9 @@ int tt_text_to_mhead (char *text, int quiet, char *buttons)
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
int tt_satsq_to_text (char *buttons, int quiet, char *text) int tt_satsq_to_text (const char *buttons, int quiet, char *text)
{ {
char *b; const char *b;
int row, col; int row, col;
int errors = 0; int errors = 0;
@ -1395,13 +1402,13 @@ int main (int argc, char *argv[])
dw_printf ("\"%s\"\n", buttons); dw_printf ("\"%s\"\n", buttons);
} }
n = tt_text_to_mhead (text, 1, buttons); n = tt_text_to_mhead (text, 1, buttons, sizeof(buttons));
if (n == 0) { if (n == 0) {
dw_printf ("Push buttons for Maidenhead Grid Square Locator:\n"); dw_printf ("Push buttons for Maidenhead Grid Square Locator:\n");
dw_printf ("\"%s\"\n", buttons); dw_printf ("\"%s\"\n", buttons);
} }
n = tt_text_to_satsq (text, 1, buttons); n = tt_text_to_satsq (text, 1, buttons, sizeof(buttons));
if (n == 0) { if (n == 0) {
dw_printf ("Push buttons for satellite gridsquare:\n"); dw_printf ("Push buttons for satellite gridsquare:\n");
dw_printf ("\"%s\"\n", buttons); dw_printf ("\"%s\"\n", buttons);
@ -1442,7 +1449,7 @@ int main (int argc, char *argv[])
strcpy (buttons, argv[1]); strcpy (buttons, argv[1]);
for (n = 2; n < argc; n++) { for (n = 2; n < argc; n++) {
strcat (buttons, argv[n]); strlcat (buttons, argv[n], sizeof(buttons));
} }
switch (tt_guess_type(buttons)) { switch (tt_guess_type(buttons)) {
@ -1471,7 +1478,7 @@ int main (int argc, char *argv[])
dw_printf ("\"%s\"\n", text); dw_printf ("\"%s\"\n", text);
} }
n = tt_mhead_to_text (buttons, 1, text); n = tt_mhead_to_text (buttons, 1, text, sizeof(text));
if (n == 0) { if (n == 0) {
dw_printf ("Decoded Maidenhead Locator from DTMF digits:\n"); dw_printf ("Decoded Maidenhead Locator from DTMF digits:\n");
dw_printf ("\"%s\"\n", text); dw_printf ("\"%s\"\n", text);
@ -1490,6 +1497,108 @@ int main (int argc, char *argv[])
#endif /* decoding */ #endif /* decoding */
#if TTT_TEST
/* end tt-text.c */ /* gcc -g -DTTT_TEST tt_text.c textcolor.o misc.a && ./a.exe */
/* Quick unit test. */
static int error_count;
static void test_text2tt (char *text, char *expect_mp, char *expect_2k, char *expect_c10, char *expect_loc, char *expect_sat)
{
char buttons[100];
text_color_set(DW_COLOR_INFO);
dw_printf ("\nConvert from text \"%s\" to tone sequence.\n", text);
tt_text_to_multipress (text, 0, buttons);
if (strcmp(buttons, expect_mp) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected multi-press \"%s\" but got \"%s\"\n", expect_mp, buttons); }
tt_text_to_two_key (text, 0, buttons);
if (strcmp(buttons, expect_2k) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected two-key \"%s\" but got \"%s\"\n", expect_2k, buttons); }
tt_text_to_call10 (text, 0, buttons);
if (strcmp(buttons, expect_c10) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected call 6+4 \"%s\" but got \"%s\"\n", expect_c10, buttons); }
tt_text_to_mhead (text, 0, buttons, sizeof(buttons));
if (strcmp(buttons, expect_loc) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected Maidenhead \"%s\" but got \"%s\"\n", expect_loc, buttons); }
tt_text_to_satsq (text, 0, buttons, sizeof(buttons));
if (strcmp(buttons, expect_sat) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected Sat Sq \"%s\" but got \"%s\"\n", expect_sat, buttons); }
}
static void test_tt2text (char *buttons, char *expect_mp, char *expect_2k, char *expect_c10, char *expect_loc, char *expect_sat)
{
char text[100];
text_color_set(DW_COLOR_INFO);
dw_printf ("\nConvert tone sequence \"%s\" to text.\n", buttons);
tt_multipress_to_text (buttons, 0, text);
if (strcmp(text, expect_mp) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected multi-press \"%s\" but got \"%s\"\n", expect_mp, text); }
tt_two_key_to_text (buttons, 0, text);
if (strcmp(text, expect_2k) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected two-key \"%s\" but got \"%s\"\n", expect_2k, text); }
tt_call10_to_text (buttons, 0, text);
if (strcmp(text, expect_c10) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected call 6+4 \"%s\" but got \"%s\"\n", expect_c10, text); }
tt_mhead_to_text (buttons, 0, text, sizeof(text));
if (strcmp(text, expect_loc) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected Maidenhead \"%s\" but got \"%s\"\n", expect_loc, text); }
tt_satsq_to_text (buttons, 0, text);
if (strcmp(text, expect_sat) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected Sat Sq \"%s\" but got \"%s\"\n", expect_sat, text); }
}
int main (int argc, char *argv[])
{
text_color_set (DW_COLOR_INFO);
dw_printf ("Test conversions between normal text and DTMF representation.\n");
dw_printf ("Some error messages are normal. Just look for number of errors at end.\n");
error_count = 0;
/* original text multipress two-key call10 mhead satsq */
test_text2tt ("abcdefg 0123", "2A22A2223A33A33340A00122223333", "2A2B2C3A3B3C4A0A0123", "", "", "");
test_text2tt ("WB4APR", "922444427A777", "9A2B42A7A7C", "9242771558", "", "");
test_text2tt ("EM29QE78", "3362222999997733777778888", "3B6A297B3B78", "", "326129723278", "");
test_text2tt ("FM19", "3336199999", "3C6A19", "3619003333", "336119", "1819");
/* tone_seq multipress two-key call10 mhead satsq */
test_tt2text ("2A22A2223A33A33340A00122223333", "ABCDEFG 0123", "A2A222D3D3334 00122223333", "", "", "");
test_tt2text ("9242771558", "WAGAQ1KT", "9242771558", "WB4APR", "", "");
test_tt2text ("326129723278", "DAM1AWPADAPT", "326129723278", "", "EM29QE78", "");
test_tt2text ("1819", "1T1W", "1819", "", "", "FM19");
if (error_count > 0) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("\nERROR: %d tests failed.\n", error_count);
exit (EXIT_FAILURE);
}
text_color_set (DW_COLOR_REC);
dw_printf ("\nSUCCESS! All tests passed.\n");
exit (EXIT_SUCCESS);
} /* end main */
#endif
/* end tt_text.c */

View File

@ -4,28 +4,28 @@
/* Encode normal human readable to DTMF representation. */ /* Encode normal human readable to DTMF representation. */
int tt_text_to_multipress (char *text, int quiet, char *buttons); int tt_text_to_multipress (const char *text, int quiet, char *buttons);
int tt_text_to_two_key (char *text, int quiet, char *buttons); int tt_text_to_two_key (const char *text, int quiet, char *buttons);
int tt_text_to_call10 (char *text, int quiet, char *buttons) ; int tt_text_to_call10 (const char *text, int quiet, char *buttons);
int tt_text_to_mhead (char *text, int quiet, char *buttons) ; int tt_text_to_mhead (const char *text, int quiet, char *buttons, size_t buttonsiz);
int tt_text_to_satsq (char *text, int quiet, char *buttons) ; int tt_text_to_satsq (const char *text, int quiet, char *buttons, size_t buttonsiz);
/* Decode DTMF to normal human readable form. */ /* Decode DTMF to normal human readable form. */
int tt_multipress_to_text (char *buttons, int quiet, char *text); int tt_multipress_to_text (const char *buttons, int quiet, char *text);
int tt_two_key_to_text (char *buttons, int quiet, char *text); int tt_two_key_to_text (const char *buttons, int quiet, char *text);
int tt_call10_to_text (char *buttons, int quiet, char *text); int tt_call10_to_text (const char *buttons, int quiet, char *text);
int tt_mhead_to_text (char *buttons, int quiet, char *text); int tt_mhead_to_text (const char *buttons, int quiet, char *text, size_t textsiz);
int tt_satsq_to_text (char *buttons, int quiet, char *text); int tt_satsq_to_text (const char *buttons, int quiet, char *text);
/* end tt_text.h */ /* end tt_text.h */

View File

@ -427,8 +427,10 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, char *lo
int i; int i;
//text_color_set(DW_COLOR_DEBUG); // TODO: remove debug
//dw_printf ("tt_user_heard (%s, %d, %c, %c, %s, ...)\n", callsign, ssid, overlay, symbol, loc_text);
text_color_set(DW_COLOR_DEBUG);
dw_printf ("tt_user_heard (%s, %d, %c, %c, %s, ...)\n", callsign, ssid, overlay, symbol, loc_text);
/* /*
* At this time all messages are expected to contain a callsign. * At this time all messages are expected to contain a callsign.
@ -766,7 +768,7 @@ static void xmit_object_report (int i, int first_time)
encode_object (object_name, 0, tt_user[i].last_heard, olat, olong, encode_object (object_name, 0, tt_user[i].last_heard, olat, olong,
tt_user[i].overlay, tt_user[i].symbol, tt_user[i].overlay, tt_user[i].symbol,
0,0,0,NULL, 0,0, /* PHGD, C/S */ 0,0,0,NULL, G_UNKNOWN, G_UNKNOWN, /* PHGD, Course/Speed */
atof(tt_user[i].freq), 0, 0, info_comment, object_info, sizeof(object_info)); atof(tt_user[i].freq), 0, 0, info_comment, object_info, sizeof(object_info));
strlcat (stemp, object_info, sizeof(stemp)); strlcat (stemp, object_info, sizeof(stemp));

View File

@ -237,7 +237,7 @@ int main (int argc, char *argv[])
* Convert to AX.25 frame. * Convert to AX.25 frame.
* Notice that the special destination will cause it to be spoken. * Notice that the special destination will cause it to be spoken.
*/ */
sprintf (reply_text, "N0CALL>SPEECH:%d", n); snprintf (reply_text, sizeof(reply_text), "N0CALL>SPEECH:%d", n);
reply_pp = ax25_from_text(reply_text, 1); reply_pp = ax25_from_text(reply_text, 1);
/* /*
@ -508,7 +508,7 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S
case AF_INET: case AF_INET:
sa4 = (struct sockaddr_in *)pAddr; sa4 = (struct sockaddr_in *)pAddr;
#if __WIN32__ #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_b2,
sa4->sin_addr.S_un.S_un_b.s_b3, sa4->sin_addr.S_un.S_un_b.s_b3,
sa4->sin_addr.S_un.S_un_b.s_b4); sa4->sin_addr.S_un.S_un_b.s_b4);
@ -519,7 +519,7 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S
case AF_INET6: case AF_INET6:
sa6 = (struct sockaddr_in6 *)pAddr; sa6 = (struct sockaddr_in6 *)pAddr;
#if __WIN32__ #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)))[0]),
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[1]), ntohs(((unsigned short *)(&(sa6->sin6_addr)))[1]),
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[2]), ntohs(((unsigned short *)(&(sa6->sin6_addr)))[2]),
@ -533,9 +533,9 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S
#endif #endif
break; break;
default: default:
sprintf (pStringBuf, "Invalid address family!"); snprintf (pStringBuf, StringBufSize, "Invalid address family!");
} }
assert (strlen(pStringBuf) < StringBufSize);
return pStringBuf; return pStringBuf;
} /* end ia_to_text */ } /* end ia_to_text */

View File

@ -32,47 +32,43 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#if __WIN32__
#include <stdlib.h> #include <stdlib.h>
#include <windows.h>
#else
#define __USE_XOPEN2KXSI 1
#define __USE_XOPEN 1
#include <stdlib.h>
#include <ctype.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#endif
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <math.h>
#include "direwolf.h" #include "direwolf.h"
#include "config.h" #include "config.h"
#include "ax25_pad.h" #include "ax25_pad.h"
#include "textcolor.h" #include "textcolor.h"
#include "latlong.h" #include "latlong.h"
#include "nmea.h" #include "dwgps.h"
#include "encode_aprs.h" #include "encode_aprs.h"
#include "serial_port.h" #include "serial_port.h"
#include "kiss_frame.h"
#define MYCALL "WB2OSZ" /************ Change this if you use it!!! ***************/ #define MYCALL "WB2OSZ" /************ Change this if you use it!!! ***************/
#define HOWLONG 20 /* Run for 20 seconds then quit. */
static MYFDTYPE tnc; static MYFDTYPE tnc;
static void walk96 (int fix, double lat, double lon, float knots, float course, float alt);
main (int argc, char *argv[])
int main (int argc, char *argv[])
{ {
struct misc_config_s config; struct misc_config_s config;
char cmd[100]; char cmd[100];
int debug_gps = 0;
int n;
// Look for Silicon Labs CP210x // TD-D72A USB - Look for Silicon Labs CP210x.
// Just happens to be same on desktop & laptop. // Just happens to be same on desktop & laptop.
tnc = serial_port_open ("COM5", 9600); tnc = serial_port_open ("COM5", 9600);
@ -82,35 +78,60 @@ main (int argc, char *argv[])
exit (EXIT_FAILURE); // defined in stdlib.h exit (EXIT_FAILURE); // defined in stdlib.h
} }
strcpy (cmd, "\r\rhbaud 9600\rkiss on\rrestart\r"); strlcpy (cmd, "\r\rhbaud 9600\rkiss on\rrestart\r", sizeof(cmd));
serial_port_write (tnc, cmd, strlen(cmd)); serial_port_write (tnc, cmd, strlen(cmd));
SLEEP_MS(500);
// USB GPS happens to be COM22
memset (&config, 0, sizeof(config)); memset (&config, 0, sizeof(config));
strcpy (config.nmea_port, "COM1"); strlcpy (config.gpsnmea_port, "COM22", sizeof(config.nmea_port));
nmea_init (&config);
dwgps_init (&config, debug_gps);
SLEEP_SEC(1); /* Wait for sample before reading. */
for (n=0; n<HOWLONG; n++) {
dwgps_info_t info;
dwfix_t fix;
fix = dwgps_read (&info);
if (fix > DWFIX_2D) {
walk96 (fix, info.dlat, info.dlon, info.speed_knots, info.track, info.altitude);
}
else if (fix < 0) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Can't communicate with GPS receiver.\n");
exit (EXIT_FAILURE);
}
else {
text_color_set (DW_COLOR_ERROR);
dw_printf ("GPS fix not available.\n");
}
SLEEP_SEC(1);
}
SLEEP_SEC(20);
// Exit out of KISS mode. // Exit out of KISS mode.
serial_port_write (tnc, "\xc0\xff\c0", 3); serial_port_write (tnc, "\xc0\xff\xc0", 3);
SLEEP_MS(100); SLEEP_MS(100);
exit (EXIT_SUCCESS);
} }
/* Should be called once per second. */ /* Should be called once per second. */
void walk96 (int fix, double lat, double lon, float knots, float course, float alt) static void walk96 (int fix, double lat, double lon, float knots, float course, float alt)
{ {
static int sequence = 0; static int sequence = 0;
char comment[50]; char comment[50];
sequence++; sequence++;
sprintf (comment, "Sequence number %04d", sequence); snprintf (comment, sizeof(comment), "Sequence number %04d", sequence);
/* /*
@ -125,16 +146,20 @@ void walk96 (int fix, double lat, double lon, float knots, float course, float a
char position_report[AX25_MAX_PACKET_LEN]; char position_report[AX25_MAX_PACKET_LEN];
// TODO (high, bug): Why do we see !4237.13N/07120.84W=PHG0000... when all values set to unknown.
info_len = encode_position (messaging, compressed, info_len = encode_position (messaging, compressed,
lat, lon, (int)(DW_METERS_TO_FEET(alt)), lat, lon, (int)(DW_METERS_TO_FEET(alt)),
'/', '?', // TODO: look up code for person. '/', '=',
G_UNKNOWN, G_UNKNOWN, G_UNKNOWN, "", // PHG G_UNKNOWN, G_UNKNOWN, G_UNKNOWN, "", // PHGd
(int)course, (int)knots, (int)roundf(course), (int)roundf(knots),
445.925, 0, 0, 445.925, 0, 0,
comment, comment,
info, sizeof(info)); info, sizeof(info));
sprintf (position_report, "%s>WALK96:%s", MYCALL, info); snprintf (position_report, sizeof(position_report), "%s>WALK96:%s", MYCALL, info);
text_color_set (DW_COLOR_XMIT); text_color_set (DW_COLOR_XMIT);
dw_printf ("%s\n", position_report); dw_printf ("%s\n", position_report);

17
xmit.c
View File

@ -59,13 +59,6 @@
#include <math.h> #include <math.h>
#include <errno.h> #include <errno.h>
//#include <sys/time.h>
//#include <time.h>
//#if __WIN32__
//#include <windows.h>
//#endif
#include "direwolf.h" #include "direwolf.h"
#include "ax25_pad.h" #include "ax25_pad.h"
#include "textcolor.h" #include "textcolor.h"
@ -449,7 +442,7 @@ static void * xmit_thread (void *arg)
ssid = ax25_get_ssid(pp, AX25_DESTINATION); ssid = ax25_get_ssid(pp, AX25_DESTINATION);
} }
else { else {
strcpy (dest, ""); strlcpy (dest, "", sizeof(dest));
} }
if (strcmp(dest, "SPEECH") == 0) { if (strcmp(dest, "SPEECH") == 0) {
@ -852,16 +845,16 @@ int xmit_speak_it (char *script, int c, char *orig_msg)
/* Remove any quotes because it will mess up command line argument parsing. */ /* Remove any quotes because it will mess up command line argument parsing. */
strcpy (msg, orig_msg); strlcpy (msg, orig_msg, sizeof(msg));
for (p=msg; *p!='\0'; p++) { for (p=msg; *p!='\0'; p++) {
if (*p == '"') *p = ' '; if (*p == '"') *p = ' ';
} }
#if __WIN32__ #if __WIN32__
sprintf (cmd, "%s %d \"%s\" >nul", script, c, msg); snprintf (cmd, sizeof(cmd), "%s %d \"%s\" >nul", script, c, msg);
#else #else
sprintf (cmd, "%s %d \"%s\"", script, c, msg); snprintf (cmd, sizeof(cmd), "%s %d \"%s\"", script, c, msg);
#endif #endif
//text_color_set(DW_COLOR_DEBUG); //text_color_set(DW_COLOR_DEBUG);
@ -878,7 +871,7 @@ int xmit_speak_it (char *script, int c, char *orig_msg)
dw_printf ("Failed to run text-to-speech script, %s\n", script); dw_printf ("Failed to run text-to-speech script, %s\n", script);
ignore = getcwd (cwd, sizeof(cwd)); ignore = getcwd (cwd, sizeof(cwd));
strcpy (path, getenv("PATH")); strlcpy (path, getenv("PATH"), sizeof(path));
dw_printf ("CWD = %s\n", cwd); dw_printf ("CWD = %s\n", cwd);
dw_printf ("PATH = %s\n", path); dw_printf ("PATH = %s\n", path);