diff --git a/.gitattributes b/.gitattributes index 5d99a3d..9320eae 100644 --- a/.gitattributes +++ b/.gitattributes @@ -28,8 +28,14 @@ *.desktop text *.conf text *.rc text +*.spec text +*.bat text +*.1 text +*.md text +COPYING text +Makefile* text +README* text *.ico binary *.png binary -Makefile* text \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2322864..496a5da 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,10 @@ z* *~ *.xlsx *.stackdump +direwolf.conf +*.wav +fsk_fast_filter.h + # Object files *.o diff --git a/CHANGES.md b/CHANGES.md index 8b714e0..355cffd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,22 @@ ---------- +## Version 1.3 -- Development snapshot G -- November 2015 ## + +### New Feature: ### + +- GPS Tracker beacons are now available for the Windows version. Previously this was only in the Linux version. + +- Implemented AGW network protocol 'M' message for sending UNPROTO information without digipeater path. + +### Bugs Fixed: ### + +- Tracker beacons were not always updating the location properly. + +- In Mac OSX version: Assertion failed: (adev[a].inbuf_size_in_bytes >= 100 && adev[a].inbuf_size_in_bytes <= 32768), function audio_get, file audio_portaudio.c, line 917. + +---------- + ## Version 1.3 -- Development snapshot F -- September 2015 ## ### New Feature: ### diff --git a/Makefile.linux b/Makefile.linux index ff5cfe9..645824b 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -2,7 +2,9 @@ # Makefile for Linux version of Dire Wolf. # -all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc direwolf.desktop direwolf.conf +APPS := direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc + +all : $(APPS) direwolf.desktop direwolf.conf @echo " " @echo "Next step - install with:" @echo " " @@ -12,6 +14,9 @@ all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx CC := gcc CFLAGS := -O3 -pthread -Igeotranz +LDFLAGS := -lm -lpthread -lrt + + # # The DSP filters spend a lot of time spinning around in little @@ -184,24 +189,27 @@ endif # # If you are planning to distribute the binary version to other # people (in some ham radio software collection, RPM, or DEB package), -# avoid # fine tuning it for your particular computer. It could +# avoid fine tuning it for your particular computer. It could # cause compatibility issues for those with older computers. # -#CFLAGS += -D_FORTIFY_SOURCE - # If you want to use OSS (for FreeBSD, OpenBSD) instead of # ALSA (for Linux), comment out (or remove) the two lines below. CFLAGS += -DUSE_ALSA -LDLIBS += -lasound +LDFLAGS += -lasound -# Uncomment following lines to enable GPS interface & tracker function. +# Enable GPS if header file is present. +# Finding libgps.so* is more difficult because it +# is in different places on different operating systems. -#CFLAGS += -DENABLE_GPS -#LDLIBS += -lgps +enable_gpsd := $(wildcard /usr/include/gps.h) +ifneq ($(enable_gpsd),) +CFLAGS += -DENABLE_GPSD +LDFLAGS += -lgps +endif # Name of current directory. @@ -210,7 +218,10 @@ LDLIBS += -lasound z := $(notdir ${CURDIR}) -# Main application. + +# -------------------------------- Main application ----------------------------------------- + + direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_9600.o hdlc_rec.o \ hdlc_rec2.o multi_modem.o redecode.o rdq.o rrbb.o dlq.o \ @@ -218,10 +229,17 @@ direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_9600.o hd decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \ gen_tone.o audio.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o morse.o \ ptt.o beacon.o dwgps.o encode_aprs.o latlong.o encode_aprs.o latlong.o textcolor.o \ - dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o serial_port.o log.o telemetry.o dtime_now.o \ + dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o serial_port.o log.o telemetry.o \ + dwgps.o dwgpsnmea.o dwgpsd.o dtime_now.o \ misc.a geotranz.a - $(CC) $(CFLAGS) -o $@ $^ -lpthread -lrt $(LDLIBS) -lm - + $(CC) -o $@ $^ $(LDFLAGS) +ifneq ($(enable_gpsd),) + @echo " " + @echo "This includes support for gpsd." +else + @echo " " + @echo "This does NOT include support for gpsd." +endif # Optimization for slow processors. @@ -231,10 +249,100 @@ demod_afsk.o : fsk_fast_filter.h fsk_fast_filter.h : demod_afsk.c - $(CC) $(CFLAGS) -o gen_fff -DGEN_FFF demod_afsk.c dsp.c textcolor.c -lm + $(CC) $(CFLAGS) -o gen_fff -DGEN_FFF demod_afsk.c dsp.c textcolor.c $(LDFLAGS) ./gen_fff > fsk_fast_filter.h +# +# The destination field is often used to identify the manufacturer/model. +# These are not hardcoded into Dire Wolf. Instead they are read from +# a file called tocalls.txt at application start up time. +# +# The original permanent symbols are built in but the "new" symbols, +# using overlays, are often updated. These are also read from files. +# +# You can obtain an updated copy by typing "make tocalls-symbols". +# This is not part of the normal build process. You have to do this explicitly. +# +# The locations below appear to be the most recent. +# The copy at http://www.aprs.org/tocalls.txt is out of date. +# + +.PHONY: tocalls-symbols +tocalls-symbols : + cp tocalls.txt tocalls.txt~ + wget http://www.aprs.org/aprs11/tocalls.txt -O tocalls.txt + -diff tocalls.txt~ tocalls.txt + cp symbols-new.txt symbols-new.txt~ + wget http://www.aprs.org/symbols/symbols-new.txt -O symbols-new.txt + -diff symbols-new.txt~ symbols-new.txt + cp symbolsX.txt symbolsX.txt~ + wget http://www.aprs.org/symbols/symbolsX.txt -O symbolsX.txt + -diff symbolsX.txt~ symbolsX.txt + + +# ---------------------------------------- Other utilities included ------------------------------ + + +# Separate application to decode raw data. + +decode_aprs : decode_aprs.c dwgpsnmea.o dwgps.o serial_port.o symbols.o ax25_pad.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o misc.a + $(CC) $(CFLAGS) -DDECAMAIN -o $@ $^ $(LDFLAGS) + + + +# Convert between text and touch tone representation. + +text2tt : tt_text.c misc.a + $(CC) $(CFLAGS) -DENC_MAIN -o $@ $^ $(LDFLAGS) + +tt2text : tt_text.c misc.a + $(CC) $(CFLAGS) -DDEC_MAIN -o $@ $^ $(LDFLAGS) + + +# Convert between Latitude/Longitude and UTM coordinates. + +ll2utm : ll2utm.c geotranz.a textcolor.o misc.a + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +utm2ll : utm2ll.c geotranz.a textcolor.o misc.a + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + + +# Convert from log file to GPX. + +log2gpx : log2gpx.c textcolor.o misc.a + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + + +# Test application to generate sound. + +gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c morse.c textcolor.c dsp.c misc.a + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +# Unit test for AFSK demodulator + +atest : atest.c fsk_fast_filter.h demod.c demod_afsk.c demod_9600.c \ + dsp.o hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \ + fcs_calc.c ax25_pad.c decode_aprs.c dwgpsnmea.o \ + dwgps.o serial_port.o telemetry.c latlong.c symbols.c tt_text.c textcolor.c \ + misc.a + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + + +# Multiple AGWPE network or serial port clients to test TNCs side by side. + +aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.o misc.a + $(CC) $(CFLAGS) -g -o $@ $^ + + +# Touch Tone to Speech sample application. + +ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a + $(CC) $(CFLAGS) -g -o $@ $^ + + +# ----------------------------------------- Libraries -------------------------------------------- # UTM, USNG, MGRS conversions. @@ -277,7 +385,19 @@ strlcat.o : misc/strlcat.c +# ------------------------------------- Installation ---------------------------------- + + + # Generate apprpriate sample configuration file for this platform. +# Originally, there was one sample for all platforms. It got too cluttered +# and confusing saying, this is for windows, and this is for Linux, and this ... +# Trying to maintain 3 different versions in parallel is error prone. +# We now have a single generic version which can be used to generate +# the various platform specific versions. + +# generic.conf should be checked into source control. +# direwolf.conf should NOT. It is generated when compiling on the target platform. direwolf.conf : generic.conf egrep '^C|^L' generic.conf | cut -c2-999 > direwolf.conf @@ -289,13 +409,17 @@ direwolf.conf : generic.conf # from source, that is not a standard part of the operating system, # should go in /usr/local/bin. -# However, if you are preparing a "binary" RPM or DEB package, the +# However, if you are preparing a "binary" DEB or RPM package, the # installation location should be /usr/bin. # This is a step in the right direction but not sufficient to use /usr instead. +# Eventually I'd like to have targets here to build the .DEB and .RPM packages. INSTALLDIR := /usr/local +# Command to "install" to system directories. Use "ginstall" for Mac. + +INSTALL=install # direwolf.desktop was previously handcrafted for the Raspberry Pi. # It was hardcoded with lxterminal, /home/pi, and so on. @@ -324,49 +448,89 @@ endif @echo 'Keywords=Ham Radio;APRS;Soundcard TNC;KISS;AGWPE;AX.25' >> $@ -# Optional installation into /usr/local/... +# Installation into /usr/local/... # Needs to be run as root or with sudo. -# TODO: Review file locations. -install : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets \ - tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png direwolf.desktop - install direwolf $(INSTALLDIR)/bin - install decode_aprs $(INSTALLDIR)/bin - install text2tt $(INSTALLDIR)/bin - install tt2text $(INSTALLDIR)/bin - install ll2utm $(INSTALLDIR)/bin - install utm2ll $(INSTALLDIR)/bin - install aclients $(INSTALLDIR)/bin - install log2gpx $(INSTALLDIR)/bin - install gen_packets $(INSTALLDIR)/bin - install atest $(INSTALLDIR)/bin - install ttcalc $(INSTALLDIR)/bin - install dwespeak.sh $(INSTALLDIR)/bin - install telemetry-toolkit/*.p[ly] $(INSTALLDIR)/bin - install -D --mode=644 tocalls.txt /usr/share/direwolf/tocalls.txt - install -D --mode=644 symbols-new.txt /usr/share/direwolf/symbols-new.txt - install -D --mode=644 symbolsX.txt /usr/share/direwolf/symbolsX.txt - install -D --mode=644 dw-icon.png /usr/share/direwolf/dw-icon.png - install -D --mode=644 direwolf.desktop /usr/share/applications/direwolf.desktop - install -D --mode=644 README.md $(INSTALLDIR)/share/doc/direwolf/README.md - install -D --mode=644 CHANGES.md $(INSTALLDIR)/share/doc/direwolf/CHANGES.md - install -D --mode=644 LICENSE-dire-wolf.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-dire-wolf.txt - install -D --mode=644 LICENSE-other.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-other.txt - install -D --mode=644 doc/User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf - install -D --mode=644 doc/Raspberry-Pi-APRS.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf - install -D --mode=644 doc/Raspberry-Pi-APRS-Tracker.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf - install -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf - install -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf - install -D --mode=644 man1/aclients.1 $(INSTALLDIR)/man/man1/aclients.1 - install -D --mode=644 man1/atest.1 $(INSTALLDIR)/man/man1/atest.1 - install -D --mode=644 man1/decode_aprs.1 $(INSTALLDIR)/man/man1/decode_aprs.1 - install -D --mode=644 man1/direwolf.1 $(INSTALLDIR)/man/man1/direwolf.1 - install -D --mode=644 man1/gen_packets.1 $(INSTALLDIR)/man/man1/gen_packets.1 - install -D --mode=644 man1/ll2utm.1 $(INSTALLDIR)/man/man1/ll2utm.1 - install -D --mode=644 man1/log2gpx.1 $(INSTALLDIR)/man/man1/log2gpx.1 - install -D --mode=644 man1/text2tt.1 $(INSTALLDIR)/man/man1/text2tt.1 - install -D --mode=644 man1/tt2text.1 $(INSTALLDIR)/man/man1/tt2text.1 - install -D --mode=644 man1/utm2ll.1 $(INSTALLDIR)/man/man1/utm2ll.1 + +.PHONY: install +install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png direwolf.desktop +# +# Applications, not installed with package manager, normally go in /usr/local/bin. +# /usr/bin is used instead when installing from .DEB or .RPM package. +# + $(INSTALL) direwolf $(INSTALLDIR)/bin + $(INSTALL) decode_aprs $(INSTALLDIR)/bin + $(INSTALL) text2tt $(INSTALLDIR)/bin + $(INSTALL) tt2text $(INSTALLDIR)/bin + $(INSTALL) ll2utm $(INSTALLDIR)/bin + $(INSTALL) utm2ll $(INSTALLDIR)/bin + $(INSTALL) aclients $(INSTALLDIR)/bin + $(INSTALL) log2gpx $(INSTALLDIR)/bin + $(INSTALL) gen_packets $(INSTALLDIR)/bin + $(INSTALL) atest $(INSTALLDIR)/bin + $(INSTALL) ttcalc $(INSTALLDIR)/bin + $(INSTALL) dwespeak.sh $(INSTALLDIR)/bin +# +# Telemetry Toolkit executables. Other .conf and .txt files will go into doc directory. +# + $(INSTALL) telemetry-toolkit/telem-balloon.pl $(INSTALLDIR)/bin + $(INSTALL) telemetry-toolkit/telem-bits.pl $(INSTALLDIR)/bin + $(INSTALL) telemetry-toolkit/telem-data.pl $(INSTALLDIR)/bin + $(INSTALL) telemetry-toolkit/telem-data91.pl $(INSTALLDIR)/bin + $(INSTALL) telemetry-toolkit/telem-eqns.pl $(INSTALLDIR)/bin + $(INSTALL) telemetry-toolkit/telem-parm.pl $(INSTALLDIR)/bin + $(INSTALL) telemetry-toolkit/telem-unit.pl $(INSTALLDIR)/bin + $(INSTALL) telemetry-toolkit/telem-volts.py $(INSTALLDIR)/bin +# +# Misc. data such as "tocall" to system mapping. +# + $(INSTALL) -D --mode=644 tocalls.txt /usr/share/direwolf/tocalls.txt + $(INSTALL) -D --mode=644 symbols-new.txt /usr/share/direwolf/symbols-new.txt + $(INSTALL) -D --mode=644 symbolsX.txt /usr/share/direwolf/symbolsX.txt + $(INSTALL) -D --mode=644 dw-icon.png /usr/share/direwolf/dw-icon.png + $(INSTALL) -D --mode=644 direwolf.desktop /usr/share/applications/direwolf.desktop +# +# Documentation. Various plain text files and PDF. +# + $(INSTALL) -D --mode=644 README.md $(INSTALLDIR)/share/doc/direwolf/README.md + $(INSTALL) -D --mode=644 CHANGES.md $(INSTALLDIR)/share/doc/direwolf/CHANGES.md + $(INSTALL) -D --mode=644 LICENSE-dire-wolf.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-dire-wolf.txt + $(INSTALL) -D --mode=644 LICENSE-other.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-other.txt +# + $(INSTALL) -D --mode=644 doc/User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf + $(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf + $(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS-Tracker.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf + $(INSTALL) -D --mode=644 doc/Raspberry-Pi-SDR-IGate.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-SDR-IGate.pdf + $(INSTALL) -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf + $(INSTALL) -D --mode=644 doc/APRStt-interface-for-SARTrack.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-interface-for-SARTrack.pdf + $(INSTALL) -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf + $(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf + $(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf +# +# Sample config files also go into the doc directory. +# When building from source, these can be put in home directory with "make install-conf". +# When installed from .DEB or .RPM package, the user will need to copy these to +# the home directory or other desired location. +# Someone suggested that these could go into an "examples" subdirectory under doc. +# + $(INSTALL) -D --mode=644 direwolf.conf $(INSTALLDIR)/share/doc/direwolf/direwolf.conf + $(INSTALL) -D --mode=644 telemetry-toolkit/telem-m0xer-3.txt $(INSTALLDIR)/share/doc/direwolf/telem-m0xer-3.txt + $(INSTALL) -D --mode=644 telemetry-toolkit/telem-balloon.conf $(INSTALLDIR)/share/doc/direwolf/telem-balloon.conf + $(INSTALL) -D --mode=644 telemetry-toolkit/telem-volts.conf $(INSTALLDIR)/share/doc/direwolf/telem-volts.conf +# +# "man" pages +# + $(INSTALL) -D --mode=644 man1/aclients.1 $(INSTALLDIR)/man/man1/aclients.1 + $(INSTALL) -D --mode=644 man1/atest.1 $(INSTALLDIR)/man/man1/atest.1 + $(INSTALL) -D --mode=644 man1/decode_aprs.1 $(INSTALLDIR)/man/man1/decode_aprs.1 + $(INSTALL) -D --mode=644 man1/direwolf.1 $(INSTALLDIR)/man/man1/direwolf.1 + $(INSTALL) -D --mode=644 man1/gen_packets.1 $(INSTALLDIR)/man/man1/gen_packets.1 + $(INSTALL) -D --mode=644 man1/ll2utm.1 $(INSTALLDIR)/man/man1/ll2utm.1 + $(INSTALL) -D --mode=644 man1/log2gpx.1 $(INSTALLDIR)/man/man1/log2gpx.1 + $(INSTALL) -D --mode=644 man1/text2tt.1 $(INSTALLDIR)/man/man1/text2tt.1 + $(INSTALL) -D --mode=644 man1/tt2text.1 $(INSTALLDIR)/man/man1/tt2text.1 + $(INSTALL) -D --mode=644 man1/utm2ll.1 $(INSTALLDIR)/man/man1/utm2ll.1 +# @echo " " @echo "If this is your first install, not an upgrade, type this to put a copy" @echo "of the sample configuration file (direwolf.conf) in your home directory:" @@ -375,12 +539,6 @@ install : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx ge @echo " " -# TODO: Should we put the sample direwolf.conf file somewhere like -# /usr/share/doc/direwolf/examples and add that to the -# end of the search path list? -# That would make it easy to see user customizations compared to the -# latest sample. - # These would be done as ordinary user. # The Raspberry Pi has ~/Desktop but Ubuntu does not. @@ -409,41 +567,123 @@ install-rpi : dw-start.sh -# Separate application to decode raw data. - -decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c latlong.c log.c telemetry.o tt_text.o misc.a - $(CC) $(CFLAGS) -o decode_aprs -DTEST $^ -lm +# ---------------------------------- Automated Smoke Test -------------------------------- -# Convert between text and touch tone representation. - -text2tt : tt_text.c - $(CC) $(CFLAGS) -DENC_MAIN -o text2tt tt_text.c - -tt2text : tt_text.c - $(CC) $(CFLAGS) -DDEC_MAIN -o tt2text tt_text.c +# Combine some unit tests into a single regression sanity check. -# Convert between Latitude/Longitude and UTM coordinates. +check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest check-modem1200 check-modem300 check-modem9600 -ll2utm : ll2utm.c geotranz.a textcolor.o misc.a - $(CC) $(CFLAGS) -o $@ $^ -lm +# Can we encode and decode at popular data rates? -utm2ll : utm2ll.c geotranz.a textcolor.o misc.a - $(CC) $(CFLAGS) -o $@ $^ -lm +check-modem1200 : gen_packets atest + gen_packets -n 100 -o /tmp/test1.wav + atest -F0 -PE -L70 -G71 /tmp/test1.wav + atest -F1 -PE -L73 -G75 /tmp/test1.wav + #rm /tmp/test1.wav + +check-modem300 : gen_packets atest + gen_packets -B300 -n 100 -o /tmp/test3.wav + atest -B300 -F0 -L68 -G69 /tmp/test3.wav + atest -B300 -F1 -L73 -G75 /tmp/test3.wav + rm /tmp/test3.wav + +check-modem9600 : gen_packets atest + gen_packets -B9600 -n 100 -o /tmp/test9.wav + atest -B9600 -F0 -L57 -G59 /tmp/test9.wav + atest -B9600 -F1 -L66 -G67 /tmp/test9.wav + rm /tmp/test9.wav -# Convert from log file to GPX. -log2gpx : log2gpx.c textcolor.o misc.a - $(CC) $(CFLAGS) -o $@ $^ -lm +# Unit test for inner digipeater algorithm + +.PHONY : dtest +dtest : digipeater.c pfilter.o ax25_pad.o dedupe.o fcs_calc.o tq.o textcolor.o \ + decode_aprs.o dwgpsnmea.o dwgps.o serial_port.o latlong.o telemetry.o symbols.o tt_text.o misc.a + $(CC) $(CFLAGS) -DDIGITEST -o $@ $^ $(LDFLAGS) + ./dtest + rm dtest -# Test application to generate sound. +# Unit test for APRStt tone sequence parsing. -gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c morse.c textcolor.c dsp.c misc.a - $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -lm +.PHONY : ttest +ttest : aprs_tt.c tt_text.c latlong.o textcolor.o misc.a geotranz.a misc.a + $(CC) $(CFLAGS) -DTT_MAIN -o $@ $^ $(LDFLAGS) + ./ttest + rm ttest + + +# Unit test for APRStt tone sequence / text conversions. + +.PHONY: tttexttest +tttexttest : tt_text.c textcolor.o misc.a + $(CC) $(CFLAGS) -DTTT_TEST -o $@ $^ $(LDFLAGS) + ./tttexttest + rm tttexttest + + +# Unit test for Packet Filtering. + +.PHONY: pftest +pftest : pfilter.c ax25_pad.o textcolor.o fcs_calc.o decode_aprs.o dwgpsnmea.o dwgps.o serial_port.o latlong.o symbols.o telemetry.o tt_text.o misc.a + $(CC) $(CFLAGS) -DPFTEST -o $@ $^ $(LDFLAGS) + ./pftest + rm pftest + +# Unit test for telemetry decoding. + +.PHONY: tlmtest +tlmtest : telemetry.c ax25_pad.o fcs_calc.o textcolor.o misc.a + $(CC) $(CFLAGS) -DTEST -o $@ $^ $(LDFLAGS) + ./tlmtest + rm tlmtest + +# Unit test for location coordinate conversion. + +.PHONY: lltest +lltest : latlong.c textcolor.o misc.a + $(CC) $(CFLAGS) -DLLTEST -o $@ $^ $(LDFLAGS) + ./lltest + rm lltest + +# Unit test for encoding position & object report. + +.PHONY: enctest +enctest : encode_aprs.c latlong.c textcolor.c misc.a + $(CC) $(CFLAGS) -DEN_MAIN -o $@ $^ $(LDFLAGS) + ./enctest + rm enctest + + +# Unit test for KISS encapsulation. + +.PHONY: kisstest +kisstest : kiss_frame.c + $(CC) $(CFLAGS) -DKISSTEST -o $@ $^ $(LDFLAGS) + ./kisstest + rm kisstest + + + +# ----------------------------- Manual tests and experiments --------------------------- + + +# Unit test for IGate + +itest : igate.c textcolor.c ax25_pad.c fcs_calc.c textcolor.o misc.a + $(CC) $(CFLAGS) -DITEST -o $@ $^ + ./itest + +# Unit test for UDP reception with AFSK demodulator + +udptest : udp_test.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c rrbb.c + fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c misc.a + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + ./udptest demod.o : tune.h demod_afsk.o : tune.h @@ -451,80 +691,19 @@ demod_9600.o : tune.h testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \ fcs_calc.c ax25_pad.c decode_aprs.c telemetry.c latlong.c symbols.c tune.h textcolor.c misc.a - $(CC) $(CFLAGS) -o atest $^ -lm + $(CC) $(CFLAGS) -o atest $^ $(LDFLAGS) ./atest 02_Track_2.wav | grep "packets decoded in" > atest.out -# Unit test for AFSK demodulator -atest : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \ - fcs_calc.c ax25_pad.c decode_aprs.c telemetry.c latlong.c symbols.c tt_text.c textcolor.c \ - misc.a - $(CC) $(CFLAGS) -o $@ $^ -lm -lrt - -# Unit test for inner digipeater algorithm -dtest : digipeater.c pfilter.c ax25_pad.c dedupe.c fcs_calc.c tq.c textcolor.c misc.a - $(CC) $(CFLAGS) -DTEST -o $@ $^ - ./dtest +# ------------------------------- Source distribution --------------------------------- + +# probably obsolete and can be removed after move to github. -# Unit test for APRStt. - -ttest : aprs_tt.c tt_text.c latlong.c textcolor.o misc.a geotranz.a misc.a - $(CC) $(CFLAGS) -DTT_MAIN -o $@ $^ - - -# Unit test for IGate - - -itest : igate.c textcolor.c ax25_pad.c fcs_calc.c textcolor.o misc.a - $(CC) $(CFLAGS) -DITEST -o $@ $^ - ./itest - - -# Unit test for UDP reception with AFSK demodulator - -udptest : udp_test.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c rrbb.c - fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c misc.a - $(CC) $(CFLAGS) -o $@ $^ -lm -lrt - ./udptest - - -# Unit test for telemetry decoding. - - -etest : telemetry.c ax25_pad.c fcs_calc.c textcolor.o misc.a regex.a - $(CC) $(CFLAGS) -o $@ $^ -lm -lrt - ./etest - - -# Multiple AGWPE network or serial port clients to test TNCs side by side. - -aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.o misc.a - $(CC) $(CFLAGS) -g -o $@ $^ - - -# Touch Tone to Speech sample application. - -ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a - $(CC) $(CFLAGS) -g -o $@ $^ - - -depend : $(wildcard *.c) - makedepend -f $(lastword $(MAKEFILE_LIST)) -- $(CFLAGS) -- $^ - - -.PHONY: clean -clean : - rm -f direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc \ - fsk_fast_filter.h *.o *.a direwolf.desktop - echo " " > tune.h - - -# Package it up for distribution. .PHONY: dist-src dist-src : README.md CHANGES.md @@ -556,22 +735,17 @@ dist-src : README.md CHANGES.md $z/telemetry-toolkit/* ) -# -# The locations below appear to be the most recent. -# The copy at http://www.aprs.org/tocalls.txt is out of date. -# +# ----------------------------------------------------------------------------------------- -.PHONY: tocalls-symbols -tocalls-symbols : - cp tocalls.txt tocalls.txt~ - wget http://www.aprs.org/aprs11/tocalls.txt -O tocalls.txt - diff tocalls.txt~ tocalls.txt - cp symbols-new.txt symbols-new.txt~ - wget http://www.aprs.org/symbols/symbols-new.txt -O symbols-new.txt - diff symbols-new.txt~ symbols-new.txt - cp symbolsX.txt symbolsX.txt~ - wget http://www.aprs.org/symbols/symbolsX.txt -O symbolsX.txt - diff symbolsX.txt~ symbolsX.txt + +.PHONY: clean +clean : + rm -f $(APPS) fsk_fast_filter.h *.o *.a direwolf.desktop + echo " " > tune.h + + +depend : $(wildcard *.c) + makedepend -f $(lastword $(MAKEFILE_LIST)) -- $(CFLAGS) -- $^ # diff --git a/Makefile.macosx b/Makefile.macosx index f0770f1..b6330da 100644 --- a/Makefile.macosx +++ b/Makefile.macosx @@ -2,6 +2,14 @@ # Makefile for Macintosh 10.8+ version of Dire Wolf. # +# TODO: This is a modified version of Makefile.linux and it +# has fallen a little behind. For example, it is missing the check target. +# It would be more maintainable if we could use a single file for both. +# The differences are not that great. +# Maybe the most of the differences could go in to platform specific include +# files rather than cluttering it up with too many if blocks. + + all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc direwolf.conf @echo " " @echo "Next step install with: " @@ -179,7 +187,7 @@ CFLAGS += -DUSE_PORTAUDIO -I/opt/local/include # Not available for MacOSX. # Although MacPorts has gpsd, wonder if it's the same thing. -#CFLAGS += -DENABLE_GPS +#CFLAGS += -DENABLE_GPSD #LDLIBS += -lgps # Name of current directory. @@ -197,7 +205,8 @@ direwolf : direwolf.o aprs_tt.o audio_portaudio.o audio_stats.o ax25_pad.o beaco geotranz.a hdlc_rec.o hdlc_rec2.o hdlc_send.o igate.o kiss_frame.o \ kiss.o kissnet.o latlong.o latlong.o log.o morse.o multi_modem.o \ nmea.o serial_port.o pfilter.o ptt.o rdq.o recv.o redecode.o rrbb.o server.o \ - symbols.o telemetry.o textcolor.o tq.o tt_text.o tt_user.o xmit.o + symbols.o telemetry.o textcolor.o tq.o tt_text.o tt_user.o xmit.o \ + dwgps.o dwgpsnmea.o dwgpsd.o $(CC) $(CFLAGS) -o $@ $^ -lpthread $(LDLIBS) -lm @@ -264,45 +273,89 @@ INSTALLDIR := /usr/local # Needs to be run as root or with sudo. # TODO: Review file locations. -install : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets \ - tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png - ginstall direwolf $(INSTALLDIR)/bin - ginstall decode_aprs $(INSTALLDIR)/bin - ginstall text2tt $(INSTALLDIR)/bin - ginstall tt2text $(INSTALLDIR)/bin - ginstall ll2utm $(INSTALLDIR)/bin - ginstall utm2ll $(INSTALLDIR)/bin - ginstall aclients $(INSTALLDIR)/bin - ginstall log2gpx $(INSTALLDIR)/bin - ginstall gen_packets $(INSTALLDIR)/bin - ginstall atest $(INSTALLDIR)/bin - ginstall ttcalc $(INSTALLDIR)/bin - ginstall dwespeak.sh $(INSTALLDIR)/bin - ginstall telemetry-toolkit/*.p[ly] $(INSTALLDIR)/bin - ginstall -D --mode=644 tocalls.txt $(INSTALLDIR)/share/direwolf/tocalls.txt - ginstall -D --mode=644 symbols-new.txt $(INSTALLDIR)/share/direwolf/symbols-new.txt - ginstall -D --mode=644 symbolsX.txt $(INSTALLDIR)/share/direwolf/symbolsX.txt - ginstall -D --mode=644 dw-icon.png $(INSTALLDIR)/share/direwolf/dw-icon.png - ginstall -D --mode=644 README.md $(INSTALLDIR)/share/doc/direwolf/README.md - ginstall -D --mode=644 CHANGES.md $(INSTALLDIR)/share/doc/direwolf/CHANGES.md - ginstall -D --mode=644 direwolf.conf $(INSTALLDIR)/share/direwolf/config/direwolf.conf - ginstall -D --mode=644 LICENSE-dire-wolf.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-dire-wolf.txt - ginstall -D --mode=644 LICENSE-other.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-other.txt - ginstall -D --mode=644 doc/User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf - ginstall -D --mode=644 doc/Raspberry-Pi-APRS.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf - ginstall -D --mode=644 doc/Raspberry-Pi-APRS-Tracker.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf - ginstall -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf - ginstall -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf - ginstall -D --mode=644 man1/aclients.1 $(INSTALLDIR)/man/man1/aclients.1 - ginstall -D --mode=644 man1/atest.1 $(INSTALLDIR)/man/man1/atest.1 - ginstall -D --mode=644 man1/decode_aprs.1 $(INSTALLDIR)/man/man1/decode_aprs.1 - ginstall -D --mode=644 man1/direwolf.1 $(INSTALLDIR)/man/man1/direwolf.1 - ginstall -D --mode=644 man1/gen_packets.1 $(INSTALLDIR)/man/man1/gen_packets.1 - ginstall -D --mode=644 man1/ll2utm.1 $(INSTALLDIR)/man/man1/ll2utm.1 - ginstall -D --mode=644 man1/log2gpx.1 $(INSTALLDIR)/man/man1/log2gpx.1 - ginstall -D --mode=644 man1/text2tt.1 $(INSTALLDIR)/man/man1/text2tt.1 - ginstall -D --mode=644 man1/tt2text.1 $(INSTALLDIR)/man/man1/tt2text.1 - ginstall -D --mode=644 man1/utm2ll.1 $(INSTALLDIR)/man/man1/utm2ll.1 +# Command to "install" to system directories. "install" for Linux. "ginstall" for Mac. + +INSTALL=ginstall + +.PHONY: install +install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png direwolf.desktop +# +# Applications, not installed with package manager, normally go in /usr/local/bin. +# /usr/bin is used instead when installing from .DEB or .RPM package. +# + $(INSTALL) direwolf $(INSTALLDIR)/bin + $(INSTALL) decode_aprs $(INSTALLDIR)/bin + $(INSTALL) text2tt $(INSTALLDIR)/bin + $(INSTALL) tt2text $(INSTALLDIR)/bin + $(INSTALL) ll2utm $(INSTALLDIR)/bin + $(INSTALL) utm2ll $(INSTALLDIR)/bin + $(INSTALL) aclients $(INSTALLDIR)/bin + $(INSTALL) log2gpx $(INSTALLDIR)/bin + $(INSTALL) gen_packets $(INSTALLDIR)/bin + $(INSTALL) atest $(INSTALLDIR)/bin + $(INSTALL) ttcalc $(INSTALLDIR)/bin + $(INSTALL) dwespeak.sh $(INSTALLDIR)/bin +# +# Telemetry Toolkit executables. Other .conf and .txt files will go into doc directory. +# + $(INSTALL) telemetry-toolkit/telem-balloon.pl $(INSTALLDIR)/bin + $(INSTALL) telemetry-toolkit/telem-bits.pl $(INSTALLDIR)/bin + $(INSTALL) telemetry-toolkit/telem-data.pl $(INSTALLDIR)/bin + $(INSTALL) telemetry-toolkit/telem-data91.pl $(INSTALLDIR)/bin + $(INSTALL) telemetry-toolkit/telem-eqns.pl $(INSTALLDIR)/bin + $(INSTALL) telemetry-toolkit/telem-parm.pl $(INSTALLDIR)/bin + $(INSTALL) telemetry-toolkit/telem-unit.pl $(INSTALLDIR)/bin + $(INSTALL) telemetry-toolkit/telem-volts.py $(INSTALLDIR)/bin +# +# Misc. data such as "tocall" to system mapping. +# + $(INSTALL) -D --mode=644 tocalls.txt /usr/share/direwolf/tocalls.txt + $(INSTALL) -D --mode=644 symbols-new.txt /usr/share/direwolf/symbols-new.txt + $(INSTALL) -D --mode=644 symbolsX.txt /usr/share/direwolf/symbolsX.txt + $(INSTALL) -D --mode=644 dw-icon.png /usr/share/direwolf/dw-icon.png + $(INSTALL) -D --mode=644 direwolf.desktop /usr/share/applications/direwolf.desktop +# +# Documentation. Various plain text files and PDF. +# + $(INSTALL) -D --mode=644 README.md $(INSTALLDIR)/share/doc/direwolf/README.md + $(INSTALL) -D --mode=644 CHANGES.md $(INSTALLDIR)/share/doc/direwolf/CHANGES.md + $(INSTALL) -D --mode=644 LICENSE-dire-wolf.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-dire-wolf.txt + $(INSTALL) -D --mode=644 LICENSE-other.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-other.txt +# + $(INSTALL) -D --mode=644 doc/User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf + $(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf + $(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS-Tracker.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf + $(INSTALL) -D --mode=644 doc/Raspberry-Pi-SDR-IGate.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-SDR-IGate.pdf + $(INSTALL) -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf + $(INSTALL) -D --mode=644 doc/APRStt-interface-for-SARTrack.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-interface-for-SARTrack.pdf + $(INSTALL) -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf + $(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf + $(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf +# +# Sample config files also go into the doc directory. +# When building from source, these can be put in home directory with "make install-conf". +# When installed from .DEB or .RPM package, the user will need to copy these to +# the home directory or other desired location. +# Someone suggested that these could go into an "examples" subdirectory under doc. +# + $(INSTALL) -D --mode=644 direwolf.conf $(INSTALLDIR)/share/doc/direwolf/direwolf.conf + $(INSTALL) -D --mode=644 telemetry-toolkit/telem-m0xer-3.txt $(INSTALLDIR)/share/doc/direwolf/telem-m0xer-3.txt + $(INSTALL) -D --mode=644 telemetry-toolkit/telem-balloon.conf $(INSTALLDIR)/share/doc/direwolf/telem-balloon.conf + $(INSTALL) -D --mode=644 telemetry-toolkit/telem-volts.conf $(INSTALLDIR)/share/doc/direwolf/telem-volts.conf +# +# "man" pages +# + $(INSTALL) -D --mode=644 man1/aclients.1 $(INSTALLDIR)/man/man1/aclients.1 + $(INSTALL) -D --mode=644 man1/atest.1 $(INSTALLDIR)/man/man1/atest.1 + $(INSTALL) -D --mode=644 man1/decode_aprs.1 $(INSTALLDIR)/man/man1/decode_aprs.1 + $(INSTALL) -D --mode=644 man1/direwolf.1 $(INSTALLDIR)/man/man1/direwolf.1 + $(INSTALL) -D --mode=644 man1/gen_packets.1 $(INSTALLDIR)/man/man1/gen_packets.1 + $(INSTALL) -D --mode=644 man1/ll2utm.1 $(INSTALLDIR)/man/man1/ll2utm.1 + $(INSTALL) -D --mode=644 man1/log2gpx.1 $(INSTALLDIR)/man/man1/log2gpx.1 + $(INSTALL) -D --mode=644 man1/text2tt.1 $(INSTALLDIR)/man/man1/text2tt.1 + $(INSTALL) -D --mode=644 man1/tt2text.1 $(INSTALLDIR)/man/man1/tt2text.1 + $(INSTALL) -D --mode=644 man1/utm2ll.1 $(INSTALLDIR)/man/man1/utm2ll.1 +# @echo " " @echo "If this is your first install, not an upgrade, type this to put a copy" @echo "of the sample configuration file (direwolf.conf) in your home directory:" @@ -333,16 +386,16 @@ install-conf : direwolf.conf # Separate application to decode raw data. -decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c latlong.c log.c telemetry.o tt_text.o - $(CC) $(CFLAGS) -o decode_aprs -DTEST $^ -lm +decode_aprs : decode_aprs.c dwgpsnmea.o dwgps.o serial_port.o symbols.o ax25_pad.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o + $(CC) $(CFLAGS) -DDECAMAIN -o $@ $^ -lm # Convert between text and touch tone representation. text2tt : tt_text.c - $(CC) $(CFLAGS) -DENC_MAIN -o text2tt tt_text.c + $(CC) $(CFLAGS) -DENC_MAIN -o $@ $^ tt2text : tt_text.c - $(CC) $(CFLAGS) -DDEC_MAIN -o tt2text tt_text.c + $(CC) $(CFLAGS) -DDEC_MAIN -o $@ $^ # Convert between Latitude/Longitude and UTM coordinates. @@ -378,21 +431,22 @@ testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o # Unit test for AFSK demodulator -atest : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \ - fcs_calc.c ax25_pad.c decode_aprs.c telemetry.c latlong.c symbols.c textcolor.c tt_text.c +atest : atest.c fsk_fast_filter.h demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \ + fcs_calc.c ax25_pad.c decode_aprs.c dwgpsnmea.o dwgps.o serial_port.o telemetry.c latlong.c symbols.c textcolor.c tt_text.c $(CC) $(CFLAGS) -o $@ $^ -lm # Unit test for inner digipeater algorithm -dtest : digipeater.c pfilter.c ax25_pad.c dedupe.c fcs_calc.c tq.c textcolor.c +dtest : digipeater.c pfilter.o ax25_pad.o dedupe.o fcs_calc.o tq.o textcolor.o \ + decode_aprs.o dwgpsnmea.o dwgps.o serial_port.o latlong.o telemetry.o symbols.o tt_text.o $(CC) $(CFLAGS) -DTEST -o $@ $^ ./dtest # Unit test for APRStt. -ttest : aprs_tt.c tt_text.c latlong.c misc.a geotranz.a +ttest : aprs_tt.c tt_text.c latlong.c geotranz.a $(CC) $(CFLAGS) -DTT_MAIN -o $@ $^ @@ -414,9 +468,9 @@ udptest : udp_test.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec # Unit test for telemetry decoding. -etest : telemetry.c ax25_pad.c fcs_calc.c textcolor.c misc.a regex.a +tlmtest : telemetry.c ax25_pad.c fcs_calc.c textcolor.c $(CC) $(CFLAGS) -o $@ $^ -lm - ./etest + ./tlmtest # Multiple AGWPE network or serial port clients to test TNCs side by side. @@ -514,3 +568,31 @@ dist-src : README.md CHANGES.md \ $z/dw-start.sh $z/direwolf.spec \ $z/dwespeak.bat $z/dwespeak.sh \ $z/telemetry-toolkit/* ) + + +# +# The destination field is often used to identify the manufacturer/model. +# These are not hardcoded into Dire Wolf. Instead they are read from +# a file called tocalls.txt at application start up time. +# +# The original permanent symbols are built in but the "new" symbols, +# using overlays, are often updated. These are also read from files. +# +# You can obtain an updated copy by typing "make tocalls-symbols". +# This is not part of the normal build process. You have to do this explicitly. +# +# The locations below appear to be the most recent. +# The copy at http://www.aprs.org/tocalls.txt is out of date. +# + +.PHONY: tocalls-symbols +tocalls-symbols : + cp tocalls.txt tocalls.txt~ + wget http://www.aprs.org/aprs11/tocalls.txt -O tocalls.txt + -diff tocalls.txt~ tocalls.txt + cp symbols-new.txt symbols-new.txt~ + wget http://www.aprs.org/symbols/symbols-new.txt -O symbols-new.txt + -diff symbols-new.txt~ symbols-new.txt + cp symbolsX.txt symbolsX.txt~ + wget http://www.aprs.org/symbols/symbolsX.txt -O symbolsX.txt + -diff symbolsX.txt~ symbolsX.txt diff --git a/Makefile.win b/Makefile.win index b59498c..4fcda6f 100644 --- a/Makefile.win +++ b/Makefile.win @@ -56,13 +56,10 @@ CFLAGS += -g # you can compile this yourself with different options. # -# Name of zip file for distribution. - -z := $(notdir ${CURDIR}) -# Main application. +# -------------------------------------- Main application -------------------------------- demod.o : fsk_demod_state.h demod_9600.o : fsk_demod_state.h @@ -75,7 +72,8 @@ direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_9600.o hd decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \ gen_tone.o morse.o audio_win.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o \ ptt.o beacon.o dwgps.o encode_aprs.o latlong.o textcolor.o \ - dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o serial_port.o log.o telemetry.o dtime_now.o \ + dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o serial_port.o log.o telemetry.o \ + dwgps.o dwgpsnmea.o dtime_now.o \ dw-icon.o regex.a misc.a geotranz.a $(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32 @@ -95,6 +93,79 @@ fsk_fast_filter.h : demod_afsk.c ./gen_fff > fsk_fast_filter.h +# +# The destination field is often used to identify the manufacturer/model. +# These are not hardcoded into Dire Wolf. Instead they are read from +# a file called tocalls.txt at application start up time. +# +# The original permanent symbols are built in but the "new" symbols, +# using overlays, are often updated. These are also read from files. +# +# You can obtain an updated copy by typing "make tocalls-symbols". +# This is not part of the normal build process. You have to do this explicitly. +# +# The locations below appear to be the most recent. +# The copy at http://www.aprs.org/tocalls.txt is out of date. +# + +.PHONY: tocalls-symbols +tocalls-symbols : + cp tocalls.txt tocalls.txt~ + wget http://www.aprs.org/aprs11/tocalls.txt -O tocalls.txt + -diff tocalls.txt~ tocalls.txt + cp symbols-new.txt symbols-new.txt~ + wget http://www.aprs.org/symbols/symbols-new.txt -O symbols-new.txt + -diff symbols-new.txt~ symbols-new.txt + cp symbolsX.txt symbolsX.txt~ + wget http://www.aprs.org/symbols/symbolsX.txt -O symbolsX.txt + -diff symbolsX.txt~ symbolsX.txt + + + +# ---------------------------- Other utilities included with distribution ------------------------- + + +# Separate application to decode raw data. + +decode_aprs : decode_aprs.c dwgpsnmea.o dwgps.o serial_port.o symbols.o ax25_pad.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o regex.a misc.a geotranz.a + $(CC) $(CFLAGS) -DDECAMAIN -o decode_aprs $^ + + +# Convert between text and touch tone representation. + +text2tt : tt_text.c misc.a + $(CC) $(CFLAGS) -DENC_MAIN -o $@ $^ + +tt2text : tt_text.c misc.a + $(CC) $(CFLAGS) -DDEC_MAIN -o $@ $^ + + +# Convert between Latitude/Longitude and UTM coordinates. + +ll2utm : ll2utm.c textcolor.c geotranz.a misc.a + $(CC) $(CFLAGS) -o $@ $^ + +utm2ll : utm2ll.c textcolor.c geotranz.a misc.a + $(CC) $(CFLAGS) -o $@ $^ + + +# Convert from log file to GPX. + +log2gpx : log2gpx.c textcolor.o misc.a + $(CC) $(CFLAGS) -o $@ $^ + + +# Test application to generate sound. + +gen_packets : gen_packets.o ax25_pad.o hdlc_send.o fcs_calc.o gen_tone.o morse.o textcolor.o dsp.o misc.a regex.a + $(CC) $(CFLAGS) -o $@ $^ + + + +# ------------------------------------------- Libraries -------------------------------------------- + + + # UTM, USNG, MGRS conversions. geotranz.a : error_string.o mgrs.o polarst.o tranmerc.o ups.o usng.o utm.o @@ -136,8 +207,8 @@ regex.o : regex/regex.c $(CC) $(CFLAGS) -Dbool=int -Dtrue=1 -Dfalse=0 -c -o $@ $^ -# There are also a couple other functions in the misc -# subdirectory that are missing on Windows. +# There are several string functios found in Linux +# but not on Windows. Need to provide our own copy. misc.a : strsep.o strtok_r.o strcasestr.o strlcpy.o strlcat.o ar -cr $@ $^ @@ -158,41 +229,129 @@ strlcat.o : misc/strlcat.c $(CC) $(CFLAGS) -I. -c -o $@ $^ -# Separate application to decode raw data. - -decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c latlong.c log.o telemetry.o tt_text.o regex.a misc.a geotranz.a - $(CC) $(CFLAGS) -o decode_aprs -DTEST $^ +# --------------------------------- Automated Smoke Test -------------------------------- -# Convert between text and touch tone representation. - -text2tt : tt_text.c - $(CC) $(CFLAGS) -DENC_MAIN -o text2tt tt_text.c - -tt2text : tt_text.c - $(CC) $(CFLAGS) -DDEC_MAIN -o tt2text tt_text.c +# Combine some unit tests into a single regression sanity check. -# Convert between Latitude/Longitude and UTM coordinates. +check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest check-modem1200 check-modem300 check-modem9600 -ll2utm : ll2utm.c textcolor.c geotranz.a misc.a +# Can we encode and decode at popular data rates? + +check-modem1200 : gen_packets atest + gen_packets -n 100 -o test1.wav + atest -F0 -PE -L70 -G71 test1.wav + atest -F1 -PE -L73 -G75 test1.wav + #rm test1.wav + +check-modem300 : gen_packets atest + gen_packets -B300 -n 100 -o test3.wav + atest -B300 -F0 -L68 -G69 test3.wav + atest -B300 -F1 -L73 -G75 test3.wav + rm test3.wav + +check-modem9600 : gen_packets atest + gen_packets -B9600 -n 100 -o test9.wav + atest -B9600 -F0 -L57 -G59 test9.wav + atest -B9600 -F1 -L66 -G67 test9.wav + rm test9.wav + +# Unit test for AFSK demodulator + +atest : atest.c fsk_fast_filter.h demod.c demod_afsk.c demod_9600.c \ + dsp.o hdlc_rec.o hdlc_rec2.o multi_modem.o \ + rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o \ + dwgpsnmea.o dwgps.o serial_port.o latlong.c \ + symbols.c tt_text.c textcolor.c telemetry.c \ + misc.a regex.a + echo " " > tune.h $(CC) $(CFLAGS) -o $@ $^ + #./atest ..\\direwolf-0.2\\02_Track_2.wav + #atest -B 9600 z9.wav + #atest za100.wav -utm2ll : utm2ll.c textcolor.c geotranz.a misc.a +atest9 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \ + rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c latlong.c symbols.c textcolor.c telemetry.c misc.a regex.a \ + fsk_fast_filter.h + echo " " > tune.h $(CC) $(CFLAGS) -o $@ $^ + ./atest9 -B 9600 ../walkabout9600.wav | grep "packets decoded in" >atest.out + #./atest9 -B 9600 noise96.wav -# Convert from log file to GPX. +# Unit test for inner digipeater algorithm -#log2gpx : log2gpx.c misc/strsep.c misc/strtok_r.c -log2gpx : log2gpx.c misc.a - $(CC) $(CFLAGS) -o $@ $^ +.PHONY: dtest +dtest : digipeater.c pfilter.o ax25_pad.o dedupe.o fcs_calc.o tq.o textcolor.o \ + decode_aprs.o dwgpsnmea.o dwgps.o serial_port.o latlong.o telemetry.o symbols.o tt_text.o misc.a regex.a + $(CC) $(CFLAGS) -DDIGITEST -o $@ $^ + ./dtest + rm dtest.exe + +# Unit test for APRStt tone seqence parsing. + +.PHONTY: ttest +ttest : aprs_tt.c tt_text.c latlong.o textcolor.o geotranz.a misc.a + $(CC) $(CFLAGS) -Igeotranz -DTT_MAIN -o $@ $^ + ./ttest + rm ttest.exe + +# Unit test for APRStt tone sequence / text conversions. + +.PHONY: tttexttest +tttexttest : tt_text.c textcolor.o misc.a + $(CC) $(CFLAGS) -DTTT_TEST -o $@ $^ + ./tttexttest + rm tttexttest.exe + +# Unit test for Packet Filtering. + +.PHONY: pftest +pftest : pfilter.c ax25_pad.o textcolor.o fcs_calc.o decode_aprs.o dwgpsnmea.o dwgps.o serial_port.o latlong.o symbols.o telemetry.o tt_text.o misc.a regex.a + $(CC) $(CFLAGS) -DPFTEST -o $@ $^ + ./pftest + rm pftest.exe -# Test application to generate sound. -gen_packets : gen_packets.o ax25_pad.o hdlc_send.o fcs_calc.o gen_tone.o morse.o textcolor.o dsp.o misc.a regex.a - $(CC) $(CFLAGS) -o $@ $^ +# Unit test for telemetry decoding. + +.PHONY: tlmtest +tlmtest : telemetry.c ax25_pad.o fcs_calc.o textcolor.o misc.a regex.a + $(CC) $(CFLAGS) -DTEST -o $@ $^ + ./tlmtest + rm tlmtest.exe + + +# Unit test for location coordinate conversion. + +.PHONY: lltest +lltest : latlong.c textcolor.o misc.a + $(CC) $(CFLAGS) -DLLTEST -o $@ $^ + ./lltest + rm lltest.exe + +# Unit test for encoding position & object report. + +.PHONY: enctest +enctest : encode_aprs.c latlong.c textcolor.c misc.a + $(CC) $(CFLAGS) -DEN_MAIN -o $@ $^ + ./enctest + rm enctest.exe + + +# Unit test for KISS encapsulation. + +.PHONY: kisstest +kisstest : kiss_frame.c + $(CC) $(CFLAGS) -DKISSTEST -o $@ $^ + ./kisstest + rm kisstest.exe + + +# ------------------------------ Other manual testing & experimenting ------------------------------- + # For tweaking the demodulator. @@ -236,54 +395,14 @@ testagc9 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2. #./atest -B 9600 noisy96.wav | grep "packets decoded in" >atest.out echo " " > tune.h - -# Unit test for AFSK demodulator - -atest : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \ - rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c latlong.c symbols.c tt_text.c textcolor.c telemetry.c misc.a regex.a \ - fsk_fast_filter.h - echo " " > tune.h - $(CC) $(CFLAGS) -o $@ $^ - #./atest ..\\direwolf-0.2\\02_Track_2.wav - #atest -B 9600 z9.wav - #atest za100.wav - -atest9 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \ - rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c latlong.c symbols.c textcolor.c telemetry.c misc.a regex.a \ - fsk_fast_filter.h - echo " " > tune.h - $(CC) $(CFLAGS) -o $@ $^ - ./atest9 -B 9600 ../walkabout9600.wav | grep "packets decoded in" >atest.out - #./atest9 -B 9600 noise96.wav - - -# Unit test for inner digipeater algorithm - - -dtest : digipeater.c pfilter.c ax25_pad.c dedupe.c fcs_calc.c tq.c textcolor.c misc.a regex.a - $(CC) $(CFLAGS) -DTEST -o $@ $^ - ./dtest - rm dtest.exe - -# Unit test for APRStt. - -ttest : aprs_tt.c tt_text.c latlong.c misc.a geotranz.a - $(CC) $(CFLAGS) -DTT_MAIN -o $@ $^ - - # Unit test for IGate itest : igate.c textcolor.c ax25_pad.c fcs_calc.c misc.a regex.a $(CC) $(CFLAGS) -DITEST -o $@ $^ -lwinmm -lws2_32 -# Unit test for telemetry decoding. -etest : telemetry.c ax25_pad.c fcs_calc.c textcolor.c misc.a regex.a - $(CC) $(CFLAGS) -DTEST -o $@ $^ - ./etest - # Multiple AGWPE network or serial port clients to test TNCs side by side. @@ -299,14 +418,20 @@ ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a regex.a # Send GPS location to KISS TNC each second. -walk96 : walk96.c nmea.c kiss_frame.c \ +walk96 : walk96.c dwgps.o dwgpsnmea.o kiss_frame.o \ latlong.o encode_aprs.o serial_port.o textcolor.o \ - ax25_pad.o fcs_calc.o regex.a \ - misc.a + ax25_pad.o fcs_calc.o \ + xmit.o hdlc_send.o gen_tone.o ptt.o tq.o \ + hdlc_rec.o hdlc_rec2.o rrbb.o dsp.o audio_win.o \ + multi_modem.o demod.o demod_afsk.o demod_9600.o rdq.o \ + server.o morse.o audio_stats.o dtime_now.o dlq.o \ + regex.a misc.a $(CC) $(CFLAGS) -DWALK96 -o $@ $^ -lwinmm -lws2_32 +#-------------------------------------------------------------- + .PHONY: depend depend : $(wildcard *.c) @@ -318,7 +443,12 @@ clean : echo " " > tune.h -# Package it up for distribution: Prebuilt Windows & source versions. + +# ------------------------------- Packaging for distribution ---------------------- + +# Name of zip file for distribution. + +z := $(notdir ${CURDIR}) # Left out RPi Tracker due to Comcast upload size limit. @@ -416,9 +546,9 @@ dist-src : README.md CHANGES.md \ unix2dos telemetry-toolkit/telem-unit.pl unix2dos telemetry-toolkit/telem-volts.py unix2dos telemetry-toolkit/telem-volts.conf - +# Reminders if pdf files are not up to date. doc/User-Guide.pdf : doc/User-Guide.docx echo "***** User-Guide.pdf is out of date *****" @@ -448,22 +578,6 @@ backup : mkdir /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"` cp -r . /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"` -# -# The locations below appear to be the most recent. -# The copy at http://www.aprs.org/tocalls.txt is out of date. -# - -.PHONY: tocalls-symbols -tocalls-symbols : - cp tocalls.txt tocalls.txt~ - wget http://www.aprs.org/aprs11/tocalls.txt -O tocalls.txt - diff tocalls.txt~ tocalls.txt - cp symbols-new.txt symbols-new.txt~ - wget http://www.aprs.org/symbols/symbols-new.txt -O symbols-new.txt - diff symbols-new.txt~ symbols-new.txt - cp symbolsX.txt symbolsX.txt~ - wget http://www.aprs.org/symbols/symbolsX.txt -O symbolsX.txt - diff symbolsX.txt~ symbolsX.txt # # The following is updated by "make depend" diff --git a/aclients.c b/aclients.c index ecc9cea..226e00f 100644 --- a/aclients.c +++ b/aclients.c @@ -71,6 +71,7 @@ #include #include #include +#include #include "direwolf.h" @@ -115,7 +116,7 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S case AF_INET: sa4 = (struct sockaddr_in *)pAddr; #if __WIN32__ - sprintf (pStringBuf, "%d.%d.%d.%d", sa4->sin_addr.S_un.S_un_b.s_b1, + snprintf (pStringBuf, StringBufSize, "%d.%d.%d.%d", sa4->sin_addr.S_un.S_un_b.s_b1, sa4->sin_addr.S_un.S_un_b.s_b2, sa4->sin_addr.S_un.S_un_b.s_b3, sa4->sin_addr.S_un.S_un_b.s_b4); @@ -126,7 +127,7 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S case AF_INET6: sa6 = (struct sockaddr_in6 *)pAddr; #if __WIN32__ - sprintf (pStringBuf, "%x:%x:%x:%x:%x:%x:%x:%x", + snprintf (pStringBuf, StringBufSize, "%x:%x:%x:%x:%x:%x:%x:%x", ntohs(((unsigned short *)(&(sa6->sin6_addr)))[0]), ntohs(((unsigned short *)(&(sa6->sin6_addr)))[1]), ntohs(((unsigned short *)(&(sa6->sin6_addr)))[2]), @@ -140,7 +141,7 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S #endif break; default: - sprintf (pStringBuf, "Invalid address family!"); + snprintf (pStringBuf, StringBufSize, "Invalid address family!"); } assert (strlen(pStringBuf) < StringBufSize); return pStringBuf; @@ -222,20 +223,20 @@ int main (int argc, char *argv[]) char stemp[100]; char *p; - strcpy (stemp, argv[j+1]); + strlcpy (stemp, argv[j+1], sizeof(stemp)); p = strtok (stemp, "="); if (p == NULL) { printf ("Internal error 1\n"); exit (1); } - strcpy (hostname[j], "localhost"); - strcpy (port[j], p); + strlcpy (hostname[j], "localhost", sizeof(hostname[j])); + strlcpy (port[j], p, sizeof(port[j])); p = strtok (NULL, "="); if (p == NULL) { printf ("Missing description after %s\n", port[j]); exit (1); } - strcpy (description[j], p); + strlcpy (description[j], p, sizeof(description[j])); } //printf ("_WIN32_WINNT = %04x\n", _WIN32_WINNT); @@ -579,14 +580,14 @@ static void * client_thread_net (void *arg) //printf ("server %d, portx = %d\n", my_index, mon_cmd.portx); - use_chan == mon_cmd.portx; + use_chan = mon_cmd.portx; memset (&alevel, 0xff, sizeof(alevel)); pp = ax25_from_frame ((unsigned char *)(data+1), mon_cmd.data_len-1, alevel); assert (pp != NULL); ax25_format_addrs (pp, result); info_len = ax25_get_info (pp, (unsigned char **)(&pinfo)); pinfo[info_len] = '\0'; - strcat (result, pinfo); + strlcat (result, pinfo, sizeof(result)); for (p=result; *p!='\0'; p++) { if (! isprint(*p)) *p = ' '; } diff --git a/aprs_tt.c b/aprs_tt.c index cf5fb4a..9352f09 100644 --- a/aprs_tt.c +++ b/aprs_tt.c @@ -22,9 +22,14 @@ * * Module: aprs_tt.c * - * Purpose: APRStt gateway. + * Purpose: First half of APRStt gateway. + * + * Description: This file contains functions to parse the tone sequences + * and extract meaning from them. + * + * tt_user.c maintains information about users and + * generates the APRS Object Reports. * - * Description: Transfer touch tone messages into the APRS network. * * References: This is based upon APRStt (TM) documents with some * artistic freedom. @@ -38,7 +43,7 @@ // TODO: clean up terminolgy. // "Message" has a specific meaning in APRS and this is not it. -// Touch Tone sequence might be appropriate. +// Touch Tone sequence should be appropriate. // What do we call the parts separated by * key? Entry? Field? @@ -67,9 +72,7 @@ #include "tq.h" -#if __WIN32__ -char *strtok_r(char *str, const char *delim, char **saveptr); -#endif + // geotranz @@ -103,9 +106,11 @@ static int parse_location (char *e); static int parse_comment (char *e); static int expand_macro (char *e); static void raw_tt_data_to_app (int chan, char *msg); -static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *bstr, char *dstr); - +static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *bstr, char *dstr, size_t valstrsize); +#if TT_MAIN +static void check_result (void); +#endif /*------------------------------------------------------------------ @@ -285,8 +290,8 @@ void aprs_tt_button (int chan, char button) * * Returns: None * - * Description: Process a complete message. - * It should have one or more fields separatedy by * + * Description: Process a complete tone sequence. + * It should have one or more fields separated by * * and terminated by a final # like these: * * callsign # @@ -312,27 +317,22 @@ static char m_callsign[20]; /* really object name */ */ static char m_symtab_or_overlay; -static char m_symbol_code; +static char m_symbol_code; // Default 'A' static char m_loc_text[24]; -static double m_longitude; -static double m_latitude; +static double m_longitude; // Set to G_UNKNOWN if not defined. +static double m_latitude; // Set to G_UNKNOWN if not defined. static char m_comment[200]; static char m_freq[12]; static char m_mic_e; static char m_dao[6]; -static int m_ssid; - -//#define G_UNKNOWN -999999 +static int m_ssid; // Default 12 for APRStt user. void aprs_tt_sequence (int chan, char *msg) { int err; - char audible_response[1000]; - packet_t pp; - char script_response[1000]; #if DEBUG @@ -367,37 +367,40 @@ void aprs_tt_sequence (int chan, char *msg) */ err = parse_fields (msg); -#if defined(DEBUG) || defined(TT_MAIN) +#if defined(DEBUG) text_color_set(DW_COLOR_DEBUG); dw_printf ("callsign=\"%s\", ssid=%d, symbol=\"%c%c\", freq=\"%s\", comment=\"%s\", lat=%.4f, lon=%.4f, dao=\"%s\"\n", m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code, m_freq, m_comment, m_latitude, m_longitude, m_dao); #endif +#if TT_MAIN + check_result (); // for unit testing. +#else + +/* + * If digested successfully. Add to our list of users and schedule transmissions. + */ if (err == 0) { -/* - * Digested successfully. Add to our list of users and schedule transmissions. - */ - -#ifndef TT_MAIN err = tt_user_heard (m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code, m_loc_text, m_latitude, m_longitude, m_freq, m_comment, m_mic_e, m_dao); -#endif } /* * If a command / script was supplied, run it now. * This can do additional processing and provide a custom audible response. + * This is done even for the error case. */ + char script_response[1000]; strlcpy (script_response, "", sizeof(script_response)); if (strlen(tt_config.ttcmd) > 0) { - dw_run_cmd (tt_config.ttcmd, 1, script_response, (int)sizeof(script_response)); + dw_run_cmd (tt_config.ttcmd, 1, script_response, sizeof(script_response)); } @@ -407,11 +410,15 @@ void aprs_tt_sequence (int chan, char *msg) * Use high priority queue for consistent timing. */ + char audible_response[1000]; + snprintf (audible_response, sizeof(audible_response), "APRSTT>%s:%s", tt_config.response[err].method, (strlen(script_response) > 0) ? script_response : tt_config.response[err].mtext); + packet_t pp; + pp = ax25_from_text (audible_response, 0); if (pp == NULL) { @@ -422,6 +429,7 @@ void aprs_tt_sequence (int chan, char *msg) tq_append (chan, TQ_PRIO_0_HI, pp); +#endif /* ifndef TT_MAIN */ } /* end aprs_tt_sequence */ @@ -461,14 +469,14 @@ static int parse_fields (char *msg) //text_color_set(DW_COLOR_DEBUG); - //printf ("parse_fields (%s).\n", msg); + //dw_printf ("parse_fields (%s).\n", msg); strlcpy (stemp, msg, sizeof(stemp)); e = strtok_r (stemp, "*#", &save); while (e != NULL) { //text_color_set(DW_COLOR_DEBUG); - //printf ("parse_fields () field = %s\n", e); + //dw_printf ("parse_fields () field = %s\n", e); switch (*e) { @@ -539,7 +547,7 @@ static int parse_fields (char *msg) } //text_color_set(DW_COLOR_DEBUG); - //printf ("parse_fields () normal return\n"); + //dw_printf ("parse_fields () normal return\n"); return (0); @@ -566,21 +574,23 @@ static int parse_fields (char *msg) * *----------------------------------------------------------------*/ +#define VALSTRSIZE 20 + static int expand_macro (char *e) { - int len; + //int len; int ipat; - char xstr[20], ystr[20], zstr[20], bstr[20], dstr[20]; + char xstr[VALSTRSIZE], ystr[VALSTRSIZE], zstr[VALSTRSIZE], bstr[VALSTRSIZE], dstr[VALSTRSIZE]; char stemp[MAX_MSG_LEN+1]; - char *d, *s; + char *d; text_color_set(DW_COLOR_DEBUG); dw_printf ("Macro tone sequence: '%s'\n", e); - len = strlen(e); + //len = strlen(e); - ipat = find_ttloc_match (e, xstr, ystr, zstr, bstr, dstr); + ipat = find_ttloc_match (e, xstr, ystr, zstr, bstr, dstr, VALSTRSIZE); if (ipat >= 0) { @@ -723,7 +733,7 @@ static int checksum_not_ok (char *str, int len, char found) static int parse_callsign (char *e) { int len; - int c_length; + //int c_length; char tttemp[40], stemp[30]; assert (*e == 'A'); @@ -838,8 +848,9 @@ static int parse_callsign (char *e) static int parse_object_name (char *e) { int len; - int c_length; - char tttemp[40], stemp[30]; + //int c_length; + //char tttemp[40]; + //char stemp[30]; assert (e[0] == 'A'); assert (e[1] == 'A'); @@ -991,19 +1002,15 @@ static int parse_symbol (char *e) * *----------------------------------------------------------------*/ - - /* Average radius of earth in meters. */ #define R 6371000. - - static int parse_location (char *e) { - int len; + //int len; int ipat; - char xstr[20], ystr[20], zstr[20], bstr[20], dstr[20]; + char xstr[VALSTRSIZE], ystr[VALSTRSIZE], zstr[VALSTRSIZE], bstr[VALSTRSIZE], dstr[VALSTRSIZE]; double x, y, dist, bearing; double lat0, lon0; double lat9, lon9; @@ -1022,9 +1029,9 @@ static int parse_location (char *e) /* If this ever changes, be sure to update corresponding */ /* section in process_comment() in decode_aprs.c */ - len = strlen(e); + //len = strlen(e); - ipat = find_ttloc_match (e, xstr, ystr, zstr, bstr, dstr); + ipat = find_ttloc_match (e, xstr, ystr, zstr, bstr, dstr, VALSTRSIZE); if (ipat >= 0) { //dw_printf ("ipat=%d, x=%s, y=%s, b=%s, d=%s\n", ipat, xstr, ystr, bstr, dstr); @@ -1140,7 +1147,7 @@ static int parse_location (char *e) m_latitude = R2D(lat0); m_longitude = R2D(lon0); - //printf ("DEBUG: from UTM, latitude = %.6f, longitude = %.6f\n", m_latitude, m_longitude); + //dw_printf ("DEBUG: from UTM, latitude = %.6f, longitude = %.6f\n", m_latitude, m_longitude); } else { char message[300]; @@ -1190,7 +1197,7 @@ static int parse_location (char *e) m_latitude = R2D(lat0); m_longitude = R2D(lon0); - //printf ("DEBUG: from MGRS/USNG, latitude = %.6f, longitude = %.6f\n", m_latitude, m_longitude); + //dw_printf ("DEBUG: from MGRS/USNG, latitude = %.6f, longitude = %.6f\n", m_latitude, m_longitude); } else { char message[300]; @@ -1219,7 +1226,7 @@ static int parse_location (char *e) //text_color_set(DW_COLOR_DEBUG); //dw_printf ("Case MHEAD: Convert to text \"%s\".\n", stemp); - if (tt_mhead_to_text (stemp, 0, mh) == 0) { + if (tt_mhead_to_text (stemp, 0, mh, sizeof(mh)) == 0) { //text_color_set(DW_COLOR_DEBUG); //dw_printf ("Case MHEAD: Resulting text \"%s\".\n", mh); @@ -1277,6 +1284,8 @@ static int parse_location (char *e) * APRStt messsage. * In this case, it should start with "B". * + * valstrsize - size of the outputs so we can check for buffer overflow. + * * Outputs: xstr - All digits matching x positions in configuration. * ystr - y * zstr - z @@ -1290,7 +1299,7 @@ static int parse_location (char *e) * *----------------------------------------------------------------*/ -static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *bstr, char *dstr) +static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *bstr, char *dstr, size_t valstrsize) { int ipat; /* Index into patterns from configuration file */ int len; /* Length of pattern we are trying to match. */ @@ -1307,11 +1316,11 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char * if (strlen(e) == len) { match = 1; - strlcpy (xstr, "", sizeof(xstr)); - strlcpy (ystr, "", sizeof(ystr)); - strlcpy (zstr, "", sizeof(zstr)); - strlcpy (bstr, "", sizeof(bstr)); - strlcpy (dstr, "", sizeof(dstr)); + strlcpy (xstr, "", valstrsize); + strlcpy (ystr, "", valstrsize); + strlcpy (zstr, "", valstrsize); + strlcpy (bstr, "", valstrsize); + strlcpy (dstr, "", valstrsize); for (k=0; k 2 && fgets(pr, remaining, fp) != NULL) { pr = result + strlen(result); - remaining = maxresult - strlen(result); + remaining = (int)resultsiz - strlen(result); } if ((err = pclose(fp)) != 0) { @@ -1670,106 +1678,182 @@ int dw_run_cmd (char *cmd, int oneline, char *result, int maxresult) * * Description: Run unit test like this: * - * rm a.exe ; gcc tt_text.c -DTT_MAIN -DDEBUG aprs_tt.c latlong.c strtok_r.o utm/LatLong-UTMconversion.c ; ./a.exe - * - * Bugs: No automatic checking. - * Just eyeball it to see if things look right. + * rm a.exe ; gcc tt_text.c -DTT_MAIN -Igeotranz aprs_tt.c latlong.o textcolor.o geotranz.a misc.a ; ./a.exe + * or + * make ttest * *----------------------------------------------------------------*/ +// TODO: add this to "make check" + + #if TT_MAIN +/* + * Regression test for the parsing. + * It does not maintain any history so abbreviation will not invoke previous full call. + */ -void text_color_set (dw_color_t c) { return; } - -int dw_printf (const char *fmt, ...) -{ - va_list args; - int len; +static const struct { + char *toneseq; /* Tone sequence in. */ - va_start (args, fmt); - len = vprintf (fmt, args); - va_end (args); - return (len); -} + char *callsign; /* Expected results... */ + char *ssid; + char *symbol; + char *freq; + char *comment; + char *lat; + char *lon; + char *dao; +} testcases[] = { + /* Callsigns & abbreviations. */ + + { "A9A2B42A7A7C71#", "WB4APR", "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* WB4APR/7 */ + { "A27773#", "277", "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* abbreviated form */ + /* Example in http://www.aprs.org/aprstt/aprstt-coding24.txt has a bad checksum! */ + /* Bad checksum for "2777". Expected 3 but received 6. */ + { "A27776#", "", "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* Expect error message. */ + + /* Bad checksum for "2A7A7C7". E xpected 5 but received 1. */ + { "A2A7A7C71#", "", "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* Spelled suffix, overlay, checksum */ + { "A27773#", "277", "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* Suffix digits, overlay, checksum */ + + { "A9A2B26C7D9D71#", "WB2OSZ", "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* WB2OSZ/7 numeric overlay */ + { "A67979#", "679", "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* abbreviated form */ + + { "A9A2B26C7D9D5A9#", "WB2OSZ", "12", "JA", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* WB2OSZ/J letter overlay */ + { "A6795A7#", "679", "12", "JA", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* abbreviated form */ + + { "A277#", "277", "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* Tactical call "277" no overlay and no checksum */ + + /* Locations */ + + { "B01*A67979#", "679", "12", "7A", "", "", "12.2500", "56.2500", "!T1 !" }, + { "B988*A67979#", "679", "12", "7A", "", "", "12.5000", "56.5000", "!T88!" }, + + { "B51000125*A67979#", "679", "12", "7A", "", "", "52.7907", "0.8309", "!TB5!" }, /* expect about 52.79 +0.83 */ + + { "B5206070*A67979#", "679", "12", "7A", "", "", "37.9137", "-81.1366", "!TB5!" }, /* Try to get from Hilltop Tower to Archery & Target Range. */ + /* Latitude comes out ok, 37.9137 -> 55.82 min. */ + /* Longitude -81.1254 -> 8.20 min */ + { "B21234*A67979#", "679", "12", "7A", "", "", "12.3400", "56.1200", "!TB2!" }, + { "B533686*A67979#", "679", "12", "7A", "", "", "37.9222", "81.1143", "!TB5!" }, + + /* Comments */ + + { "C1", "", "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T !" }, + { "C2", "", "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T !" }, + { "C146520", "", "12", "\\A", "146.520MHz", "", "-999999.0000", "-999999.0000", "!T !" }, + { "C7788444222550227776669660333666990122223333", + "", "12", "\\A", "", "QUICK BROWN FOX 123", "-999999.0000", "-999999.0000", "!T !" }, + /* Macros */ + + { "88345", "BIKE 345", "0", "/b", "", "", "12.5000", "56.5000", "!T88!" }, + + /* 10 digit representation for callsign & satellite grid. WB4APR near 39.5, -77 */ + + { "AC9242771558*BA1819", "WB4APR", "12", "\\A", "", "", "39.5000", "-77.0000", "!TBA!" }, + { "18199242771558", "WB4APR", "12", "\\A", "", "", "39.5000", "-77.0000", "!TBA!" }, +}; + + +static int test_num; +static int error_count; + +static void check_result (void) +{ + char stemp[32]; + + text_color_set(DW_COLOR_DEBUG); + dw_printf ("callsign=\"%s\", ssid=%d, symbol=\"%c%c\", freq=\"%s\", comment=\"%s\", lat=%.4f, lon=%.4f, dao=\"%s\"\n", + m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code, m_freq, m_comment, m_latitude, m_longitude, m_dao); + + + if (strcmp(m_callsign, testcases[test_num].callsign) != 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("ERROR: Expected \"%s\" for callsign.\n", testcases[test_num].callsign); + error_count++; + } + + snprintf (stemp, sizeof(stemp), "%d", m_ssid); + if (strcmp(stemp, testcases[test_num].ssid) != 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("ERROR: Expected \"%s\" for SSID.\n", testcases[test_num].ssid); + error_count++; + } + + stemp[0] = m_symtab_or_overlay; + stemp[1] = m_symbol_code; + stemp[2] = '\0'; + if (strcmp(stemp, testcases[test_num].symbol) != 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("ERROR: Expected \"%s\" for Symbol.\n", testcases[test_num].symbol); + error_count++; + } + + if (strcmp(m_freq, testcases[test_num].freq) != 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("ERROR: Expected \"%s\" for Freq.\n", testcases[test_num].freq); + error_count++; + } + + if (strcmp(m_comment, testcases[test_num].comment) != 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("ERROR: Expected \"%s\" for Comment.\n", testcases[test_num].comment); + error_count++; + } + + snprintf (stemp, sizeof(stemp), "%.4f", m_latitude); + if (strcmp(stemp, testcases[test_num].lat) != 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("ERROR: Expected \"%s\" for Latitude.\n", testcases[test_num].lat); + error_count++; + } + + snprintf (stemp, sizeof(stemp), "%.4f", m_longitude); + if (strcmp(stemp, testcases[test_num].lon) != 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("ERROR: Expected \"%s\" for Longitude.\n", testcases[test_num].lon); + error_count++; + } + + if (strcmp(m_dao, testcases[test_num].dao) != 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("ERROR: Expected \"%s\" for DAO.\n", testcases[test_num].dao); + error_count++; + } +} int main (int argc, char *argv[]) { - char text[256], buttons[256]; - int n; - - dw_printf ("Hello, world!\n"); - aprs_tt_init (NULL); - //if (argc < 2) { - //dw_printf ("Supply text string on command line.\n"); - //exit (1); - //} + error_count = 0; -/* Callsigns & abbreviations. */ + for (test_num = 0; test_num < sizeof(testcases) / sizeof(testcases[0]); test_num++) { - aprs_tt_sequence (0, "A9A2B42A7A7C71#"); /* WB4APR/7 */ - aprs_tt_sequence (0, "A27773#"); /* abbreviated form */ - /* Example in http://www.aprs.org/aprstt/aprstt-coding24.txt has a bad checksum! */ - aprs_tt_sequence (0, "A27776#"); /* Expect error message. */ + text_color_set(DW_COLOR_INFO); + dw_printf ("\nTest case %d: %s\n", test_num, testcases[test_num].toneseq); - aprs_tt_sequence (0, "A2A7A7C71#"); /* Spelled suffix, overlay, checksum */ - aprs_tt_sequence (0, "A27773#"); /* Suffix digits, overlay, checksum */ + aprs_tt_sequence (0, testcases[test_num].toneseq); + } - aprs_tt_sequence (0, "A9A2B26C7D9D71#"); /* WB2OSZ/7 numeric overlay */ - aprs_tt_sequence (0, "A67979#"); /* abbreviated form */ + if (error_count != 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("\n\nTEST FAILED, Total of %d errors.\n", error_count); + return (EXIT_FAILURE); + } - aprs_tt_sequence (0, "A9A2B26C7D9D5A9#"); /* WB2OSZ/J letter overlay */ - aprs_tt_sequence (0, "A6795A7#"); /* abbreviated form */ - - aprs_tt_sequence (0, "A277#"); /* Tactical call "277" no overlay and no checksum */ - -/* Locations */ - - aprs_tt_sequence (0, "B01*A67979#"); - aprs_tt_sequence (0, "B988*A67979#"); - - /* expect about 52.79 +0.83 */ - aprs_tt_sequence (0, "B51000125*A67979#"); - - /* Try to get from Hilltop Tower to Archery & Target Range. */ - /* Latitude comes out ok, 37.9137 -> 55.82 min. */ - /* Longitude -81.1254 -> 8.20 min */ - - aprs_tt_sequence (0, "B5206070*A67979#"); - - aprs_tt_sequence (0, "B21234*A67979#"); - aprs_tt_sequence (0, "B533686*A67979#"); - - -/* Comments */ - - aprs_tt_sequence (0, "C1"); - aprs_tt_sequence (0, "C2"); - aprs_tt_sequence (0, "C146520"); - aprs_tt_sequence (0, "C7788444222550227776669660333666990122223333"); - -/* Macros */ - - aprs_tt_sequence (0, "88345"); - -/* 10 digit representation for callsign & satellite grid. WB4APR near 39.5, -77 */ - - aprs_tt_sequence (0, "AC9242771558*BA1819"); - aprs_tt_sequence (0, "18199242771558"); - - - return(0); + text_color_set(DW_COLOR_REC); + dw_printf ("\n\nAll tests passed.\n"); + return (EXIT_SUCCESS); } /* end main */ - - #endif /* end aprs_tt.c */ diff --git a/aprs_tt.h b/aprs_tt.h index f504569..01fed5c 100644 --- a/aprs_tt.h +++ b/aprs_tt.h @@ -176,7 +176,7 @@ void aprs_tt_dao_to_desc (char *dao, char *str); void aprs_tt_sequence (int chan, char *msg); -int dw_run_cmd (char *cmd, int oneline, char *result, int maxresult); +int dw_run_cmd (char *cmd, int oneline, char *result, size_t resultsiz); #endif diff --git a/atest.c b/atest.c index 28a5457..c869698 100644 --- a/atest.c +++ b/atest.c @@ -141,7 +141,11 @@ static int decimate = 0; /* Reduce that sampling rate if set. */ static struct audio_s my_audio_config; -static int error_if_less_than = 0; /* Exit with error status if this minimum not reached. */ +static int error_if_less_than = -1; /* Exit with error status if this minimum not reached. */ + /* Can be used to check that performance has not decreased. */ + +static int error_if_greater_than = -1; /* Exit with error status if this maximum exceeded. */ + /* Can be used to check that duplicate removal is not broken. */ @@ -161,6 +165,8 @@ extern float space_gain[MAX_SUBCHANS]; static void usage (void); +static int decode_only = 0; /* Set to 0 or 1 to decode only one channel. 2 for both. */ + int main (int argc, char *argv[]) { @@ -171,7 +177,6 @@ int main (int argc, char *argv[]) time_t start_time; - #if defined(EXPERIMENT_G) || defined(EXPERIMENT_H) int j; @@ -245,7 +250,7 @@ int main (int argc, char *argv[]) my_audio_config.achan[channel].space_freq = DEFAULT_SPACE_FREQ; my_audio_config.achan[channel].baud = DEFAULT_BAUD; - strcpy (my_audio_config.achan[channel].profiles, "E"); + strlcpy (my_audio_config.achan[channel].profiles, "E", sizeof(my_audio_config.achan[channel].profiles)); my_audio_config.achan[channel].num_freq = 1; my_audio_config.achan[channel].offset = 0; @@ -261,7 +266,7 @@ int main (int argc, char *argv[]) } while (1) { - int this_option_optind = optind ? optind : 1; + //int this_option_optind = optind ? optind : 1; int option_index = 0; static struct option long_options[] = { {"future1", 1, 0, 0}, @@ -272,7 +277,7 @@ int main (int argc, char *argv[]) /* ':' following option character means arg is required. */ - c = getopt_long(argc, argv, "B:P:D:F:e:", + c = getopt_long(argc, argv, "B:P:D:F:L:G:012", long_options, &option_index); if (c == -1) break; @@ -297,12 +302,13 @@ int main (int argc, char *argv[]) my_audio_config.achan[0].modem_type = MODEM_AFSK; my_audio_config.achan[0].mark_freq = 1600; my_audio_config.achan[0].space_freq = 1800; - strcpy (my_audio_config.achan[0].profiles, "D"); } + strlcpy (my_audio_config.achan[0].profiles, "D", sizeof(my_audio_config.achan[0].profiles)); + } else if (my_audio_config.achan[0].baud > 2400) { my_audio_config.achan[0].modem_type = MODEM_SCRAMBLE; my_audio_config.achan[0].mark_freq = 0; my_audio_config.achan[0].space_freq = 0; - strcpy (my_audio_config.achan[0].profiles, " "); // avoid getting default later. + strlcpy (my_audio_config.achan[0].profiles, " ", sizeof(my_audio_config.achan[0].profiles)); // avoid getting default later. dw_printf ("Using scrambled baseband signal rather than AFSK.\n"); } else { @@ -315,7 +321,7 @@ int main (int argc, char *argv[]) case 'P': /* -P for modem profile. */ dw_printf ("Demodulator profile set to \"%s\"\n", optarg); - strcpy (my_audio_config.achan[0].profiles, optarg); + strlcpy (my_audio_config.achan[0].profiles, optarg, sizeof(my_audio_config.achan[0].profiles)); break; case 'D': /* -D reduce sampling rate for lower CPU usage. */ @@ -343,11 +349,31 @@ int main (int argc, char *argv[]) } break; - case 'e': /* -e error if less than this number decoded. */ + case 'L': /* -L error if less than this number decoded. */ error_if_less_than = atoi(optarg); break; + case 'G': /* -G error if greater than this number decoded. */ + + error_if_greater_than = atoi(optarg); + break; + + case '0': /* channel 0, left from stereo */ + + decode_only = 0; + break; + + case '1': /* channel 1, right from stereo */ + + decode_only = 1; + break; + + case '2': /* decode both from stereo */ + + decode_only = 2; + break; + case '?': /* Unknown option message was already printed. */ @@ -363,6 +389,9 @@ int main (int argc, char *argv[]) } } + memcpy (&my_audio_config.achan[1], &my_audio_config.achan[0], sizeof(my_audio_config.achan[0])); + + if (optind >= argc) { text_color_set(DW_COLOR_ERROR); dw_printf ("Specify .WAV file name on command line.\n"); @@ -386,6 +415,7 @@ int main (int argc, char *argv[]) */ err= fread (&header, (size_t)12, (size_t)1, fp); + (void)(err); if (strncmp(header.riff, "RIFF", 4) != 0 || strncmp(header.wave, "WAVE", 4) != 0) { text_color_set(DW_COLOR_ERROR); @@ -421,6 +451,7 @@ int main (int argc, char *argv[]) exit(1); } + // TODO: Should have proper message, not abort. assert (format.nchannels == 1 || format.nchannels == 2); assert (format.wbitspersample == 8 || format.wbitspersample == 16); @@ -462,13 +493,10 @@ int main (int argc, char *argv[]) audio_sample = demod_get_sample (ACHAN2ADEV(c)); if (audio_sample >= 256 * 256) - e_o_f = 1; + e_o_f = 1; -#define ONE_CHAN 1 /* only use one audio channel. */ - -#if ONE_CHAN - if (c != 0) continue; -#endif + if (decode_only == 0 && c != 0) continue; + if (decode_only == 1 && c != 1) continue; multi_modem_process_sample(c,audio_sample); } @@ -495,9 +523,14 @@ int main (int argc, char *argv[]) #endif dw_printf ("%d packets decoded in %d seconds.\n", packets_decoded, (int)(time(NULL) - start_time)); - if (packets_decoded < error_if_less_than) { + if (error_if_less_than != -1 && packets_decoded < error_if_less_than) { text_color_set(DW_COLOR_ERROR); - dw_printf ("\n * * * TEST FAILED to achieve minimum of %d * * * \n", error_if_less_than); + dw_printf ("\n * * * TEST FAILED: number decoded is less than %d * * * \n", error_if_less_than); + exit (1); + } + if (error_if_greater_than != -1 && packets_decoded > error_if_greater_than) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("\n * * * TEST FAILED: number decoded is greater than %d * * * \n", error_if_greater_than); exit (1); } @@ -564,12 +597,11 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a unsigned char *pinfo; int info_len; int h; - char heard[20]; + char heard[AX25_MAX_ADDR_LEN]; char alevel_text[AX25_ALEVEL_TO_TEXT_SIZE]; packets_decoded++; - ax25_format_addrs (pp, stemp); info_len = ax25_get_info (pp, &pinfo); @@ -585,7 +617,7 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a if (ax25_get_num_addr(pp) == 0) { /* Not AX.25. No station to display below. */ h = -1; - strcpy (heard, ""); + strlcpy (heard, "", sizeof(heard)); } else { h = ax25_get_heard(pp); @@ -676,6 +708,10 @@ static void usage (void) { dw_printf (" -P m Select the demodulator type such as A, B, C, D (default for 300 baud),\n"); dw_printf (" E (default for 1200 baud), F, A+, B+, C+, D+, E+, F+.\n"); dw_printf ("\n"); + dw_printf (" -0 Use channel 0 (left) of stereo audio (default).\n"); + dw_printf (" -1 use channel 1 (right) of stereo audio.\n"); + dw_printf (" -1 decode both channels of stereo audio.\n"); + dw_printf ("\n"); dw_printf (" wav-file-in is a WAV format audio file.\n"); dw_printf ("\n"); dw_printf ("Examples:\n"); diff --git a/audio.c b/audio.c index 4e90c5a..47377e6 100644 --- a/audio.c +++ b/audio.c @@ -661,6 +661,16 @@ static int set_alsa_params (int a, snd_pcm_t *handle, struct audio_s *pa, char * dw_printf ("audio buffer size = %d (bytes per frame) x %d (frames per period) = %d \n", adev[a].bytes_per_frame, (int)fpp, buf_size_in_bytes); #endif + /* Version 1.3 - after a report of this situation for Mac OSX version. */ + if (buf_size_in_bytes < 256 || buf_size_in_bytes > 32768) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Audio buffer has unexpected extreme size of %d bytes.\n", buf_size_in_bytes); + dw_printf ("Detected at %s, line %d.\n", __FILE__, __LINE__); + dw_printf ("This might be caused by unusual audio device configuration values.\n"); + buf_size_in_bytes = 2048; + dw_printf ("Using %d to attempt recovery.\n", buf_size_in_bytes); + } + return (buf_size_in_bytes); @@ -787,9 +797,20 @@ static int set_oss_params (int fd, struct audio_s *pa) dw_printf ("audio_open(): using block size of %d\n", ossbuf_size_in_bytes); #endif +#if 0 + /* Original - dies without good explanation. */ assert (ossbuf_size_in_bytes >= 256 && ossbuf_size_in_bytes <= 32768); - - +#else + /* Version 1.3 - after a report of this situation for Mac OSX version. */ + if (ossbuf_size_in_bytes < 256 || ossbuf_size_in_bytes > 32768) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Audio buffer has unexpected extreme size of %d bytes.\n", ossbuf_size_in_bytes); + dw_printf ("Detected at %s, line %d.\n", __FILE__, __LINE__); + dw_printf ("This might be caused by unusual audio device configuration values.\n"); + ossbuf_size_in_bytes = 2048; + dw_printf ("Using %d to attempt recovery.\n", ossbuf_size_in_bytes); + } +#endif return (ossbuf_size_in_bytes); } /* end set_oss_params */ diff --git a/audio_portaudio.c b/audio_portaudio.c index dc8fd28..ead1ffc 100644 --- a/audio_portaudio.c +++ b/audio_portaudio.c @@ -714,6 +714,27 @@ int audio_open (struct audio_s *pa) * Finally allocate buffer for each direction. */ + /* Version 1.3 - Add sanity check on buffer size. */ + /* There was a reported case of assert failure on buffer size in audio_get(). */ + + if (adev[a].inbuf_size_in_bytes < 256 || adev[a].inbuf_size_in_bytes > 32768) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Audio input buffer has unexpected extreme size of %d bytes.\n", adev[a].inbuf_size_in_bytes); + dw_printf ("Detected at %s, line %d.\n", __FILE__, __LINE__); + dw_printf ("This might be caused by unusual audio device configuration values.\n"); + adev[a].inbuf_size_in_bytes = 2048; + dw_printf ("Using %d to attempt recovery.\n", adev[a].inbuf_size_in_bytes); + } + + if (adev[a].outbuf_size_in_bytes < 256 || adev[a].outbuf_size_in_bytes > 32768) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Audio output buffer has unexpected extreme size of %d bytes.\n", adev[a].outbuf_size_in_bytes); + dw_printf ("Detected at %s, line %d.\n", __FILE__, __LINE__); + dw_printf ("This might be caused by unusual audio device configuration values.\n"); + adev[a].outbuf_size_in_bytes = 2048; + dw_printf ("Using %d to attempt recovery.\n", adev[a].outbuf_size_in_bytes); + } + adev[a].inbuf_ptr = malloc(adev[a].inbuf_size_in_bytes); assert (adev[a].inbuf_ptr != NULL); adev[a].inbuf_len = 0; diff --git a/audio_win.c b/audio_win.c index cd4b999..f431363 100644 --- a/audio_win.c +++ b/audio_win.c @@ -113,6 +113,17 @@ static int calcbufsize(int rate, int chans, int bits) dw_printf ("audio_open: calcbufsize (rate=%d, chans=%d, bits=%d) calc size=%d, round up to %d\n", rate, chans, bits, size1, size2); #endif + + /* Version 1.3 - add a sanity check. */ + if (size2 < 256 || size2 > 32768) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Audio buffer has unexpected extreme size of %d bytes.\n", size2); + dw_printf ("Detected at %s, line %d.\n", __FILE__, __LINE__); + dw_printf ("This might be caused by unusual audio device configuration values.\n"); + size2 = 2048; + dw_printf ("Using %d to attempt recovery.\n", size2); + } + return (size2); } diff --git a/ax25_pad.c b/ax25_pad.c index fe6ebec..0d894ff 100644 --- a/ax25_pad.c +++ b/ax25_pad.c @@ -44,22 +44,27 @@ * Description: * * - * A UI frame starts with 2-10 addressses (14-70 octets): + * APRS uses only UI frames. + * Each starts with 2-10 addressses (14-70 octets): + * + * * Destination Address (note: opposite order in printed format) * - * * Destination Address * * Source Address - * * 0-8 Digipeater Addresses (Could there ever be more as a result of - * digipeaters inserting their own call - * and decrementing the remaining count in - * WIDEn-n, TRACEn-n, etc.? - * NO. The limit is 8 when transmitting AX.25 over the radio. - * However, communication with an IGate server could have - * a longer VIA path but that is only in text form, not here.) + * + * * 0-8 Digipeater Addresses (Could there ever be more as a result of + * digipeaters inserting their own call for + * the tracing feature? + * NO. The limit is 8 when transmitting AX.25 over the + * radio. + * Communication with an IGate server could + * have a longer VIA path but that is only in text form, + * not as an AX.25 frame.) * * Each address is composed of: * * * 6 upper case letters or digits, blank padded. - * These are shifted left one bit, leaving the the LSB always 0. + * These are shifted left one bit, leaving the LSB always 0. + * * * a 7th octet containing the SSID and flags. * The LSB is always 0 except for the last octet of the address field. * @@ -96,8 +101,12 @@ * have the "H" bit set to 1. * The "H" bit would be set to 1 in the repeated frame. * - * When monitoring, an asterisk is displayed after the last digipeater with - * the "H" bit set. No asterisk means the source is being heard directly. + * In standard monitoring format, an asterisk is displayed after the last + * digipeater with the "H" bit set. That indicates who you are hearing + * over the radio. + * (That is if digipeaters update the via path properly. Some don't so + * we don't know who we are hearing. This is discussed in the User Guide.) + * No asterisk means the source is being heard directly. * * Example, if we can hear all stations involved, * @@ -118,6 +127,10 @@ * * And, of course, the 2 byte CRC. * + * The descriptions above, for the C, H, and RR bits, are for APRS usage. + * When operating as a KISS TNC we just pass everything along and don't + * interpret or change them. + * * * Constructors: ax25_init - Clear everything. * ax25_from_text - Tear apart a text string @@ -569,11 +582,11 @@ packet_t ax25_from_frame_debug (unsigned char *fbuf, int flen, alevel_t alevel, packet_t ax25_from_frame (unsigned char *fbuf, int flen, alevel_t alevel) #endif { - unsigned char *pf; + //unsigned char *pf; packet_t this_p; - int a; - int addr_bytes; + //int a; + //int addr_bytes; /* * First make sure we have an acceptable length: @@ -908,7 +921,6 @@ void ax25_set_addr (packet_t this_p, int n, char *ad) void ax25_insert_addr (packet_t this_p, int n, char *ad) { - int k; int ssid_temp, heard_temp; char atemp[AX25_MAX_ADDR_LEN]; int i; @@ -979,7 +991,6 @@ void ax25_insert_addr (packet_t this_p, int n, char *ad) void ax25_remove_addr (packet_t this_p, int n) { - int k; int expect; assert (this_p->magic1 == MAGIC); @@ -1025,7 +1036,7 @@ void ax25_remove_addr (packet_t this_p, int n) int ax25_get_num_addr (packet_t this_p) { - unsigned char *pf; + //unsigned char *pf; int a; int addr_bytes; @@ -1178,7 +1189,6 @@ void ax25_get_addr_with_ssid (packet_t this_p, int n, char *station) void ax25_get_addr_no_ssid (packet_t this_p, int n, char *station) { - int ssid; int i; assert (this_p->magic1 == MAGIC); @@ -1801,23 +1811,23 @@ static void hex_dump (unsigned char *p, int len) // TODO: use ax25_frame_type() instead. -static void ctrl_to_text (int c, char *out) +static void ctrl_to_text (int c, char *out, size_t outsiz) { - if ((c & 1) == 0) { sprintf (out, "I frame: n(r)=%d, p=%d, n(s)=%d", (c>>5)&7, (c>>4)&1, (c>>1)&7); } - else if ((c & 0xf) == 0x01) { sprintf (out, "S frame RR: n(r)=%d, p/f=%d", (c>>5)&7, (c>>4)&1); } - else if ((c & 0xf) == 0x05) { sprintf (out, "S frame RNR: n(r)=%d, p/f=%d", (c>>5)&7, (c>>4)&1); } - else if ((c & 0xf) == 0x09) { sprintf (out, "S frame REJ: n(r)=%d, p/f=%d", (c>>5)&7, (c>>4)&1); } - else if ((c & 0xf) == 0x0D) { sprintf (out, "S frame sREJ: n(r)=%d, p/f=%d", (c>>5)&7, (c>>4)&1); } - else if ((c & 0xef) == 0x6f) { sprintf (out, "U frame SABME: p=%d", (c>>4)&1); } - else if ((c & 0xef) == 0x2f) { sprintf (out, "U frame SABM: p=%d", (c>>4)&1); } - else if ((c & 0xef) == 0x43) { sprintf (out, "U frame DISC: p=%d", (c>>4)&1); } - else if ((c & 0xef) == 0x0f) { sprintf (out, "U frame DM: f=%d", (c>>4)&1); } - else if ((c & 0xef) == 0x63) { sprintf (out, "U frame UA: f=%d", (c>>4)&1); } - else if ((c & 0xef) == 0x87) { sprintf (out, "U frame FRMR: f=%d", (c>>4)&1); } - else if ((c & 0xef) == 0x03) { sprintf (out, "U frame UI: p/f=%d", (c>>4)&1); } - else if ((c & 0xef) == 0xAF) { sprintf (out, "U frame XID: p/f=%d", (c>>4)&1); } - else if ((c & 0xef) == 0xe3) { sprintf (out, "U frame TEST: p/f=%d", (c>>4)&1); } - else { sprintf (out, "Unknown frame type for control = 0x%02x", c); } + if ((c & 1) == 0) { snprintf (out, outsiz, "I frame: n(r)=%d, p=%d, n(s)=%d", (c>>5)&7, (c>>4)&1, (c>>1)&7); } + else if ((c & 0xf) == 0x01) { snprintf (out, outsiz, "S frame RR: n(r)=%d, p/f=%d", (c>>5)&7, (c>>4)&1); } + else if ((c & 0xf) == 0x05) { snprintf (out, outsiz, "S frame RNR: n(r)=%d, p/f=%d", (c>>5)&7, (c>>4)&1); } + else if ((c & 0xf) == 0x09) { snprintf (out, outsiz, "S frame REJ: n(r)=%d, p/f=%d", (c>>5)&7, (c>>4)&1); } + else if ((c & 0xf) == 0x0D) { snprintf (out, outsiz, "S frame sREJ: n(r)=%d, p/f=%d", (c>>5)&7, (c>>4)&1); } + else if ((c & 0xef) == 0x6f) { snprintf (out, outsiz, "U frame SABME: p=%d", (c>>4)&1); } + else if ((c & 0xef) == 0x2f) { snprintf (out, outsiz, "U frame SABM: p=%d", (c>>4)&1); } + else if ((c & 0xef) == 0x43) { snprintf (out, outsiz, "U frame DISC: p=%d", (c>>4)&1); } + else if ((c & 0xef) == 0x0f) { snprintf (out, outsiz, "U frame DM: f=%d", (c>>4)&1); } + else if ((c & 0xef) == 0x63) { snprintf (out, outsiz, "U frame UA: f=%d", (c>>4)&1); } + else if ((c & 0xef) == 0x87) { snprintf (out, outsiz, "U frame FRMR: f=%d", (c>>4)&1); } + else if ((c & 0xef) == 0x03) { snprintf (out, outsiz, "U frame UI: p/f=%d", (c>>4)&1); } + else if ((c & 0xef) == 0xAF) { snprintf (out, outsiz, "U frame XID: p/f=%d", (c>>4)&1); } + else if ((c & 0xef) == 0xe3) { snprintf (out, outsiz, "U frame TEST: p/f=%d", (c>>4)&1); } + else { snprintf (out, outsiz, "Unknown frame type for control = 0x%02x", c); } } /* Text description of protocol id octet. */ @@ -1864,7 +1874,7 @@ void ax25_hex_dump (packet_t this_p) c = fptr[this_p->num_addr*7]; p = fptr[this_p->num_addr*7+1]; - ctrl_to_text (c, cp_text); // TODO: use ax25_frame_type() instead. + ctrl_to_text (c, cp_text, sizeof(cp_text)); // TODO: use ax25_frame_type() instead. if ( (c & 0x01) == 0 || /* I xxxx xxx0 */ c == 0x03 || c == 0x13) { /* UI 000x 0011 */ diff --git a/beacon.c b/beacon.c index 1583723..b4d0234 100644 --- a/beacon.c +++ b/beacon.c @@ -1,7 +1,3 @@ -//#define DEBUG 1 -//#define DEBUG_SIM 1 - - // // This file is part of Dire Wolf, an amateur radio packet TNC. // @@ -32,17 +28,17 @@ * *---------------------------------------------------------------*/ +//#define DEBUG 1 + + #include #include #include #include #include #include - #include -#if __WIN32__ -#include -#endif + #include "direwolf.h" #include "ax25_pad.h" @@ -51,7 +47,6 @@ #include "tq.h" #include "xmit.h" #include "config.h" -#include "digipeater.h" #include "version.h" #include "encode_aprs.h" #include "beacon.h" @@ -62,14 +57,25 @@ #include "aprs_tt.h" // for dw_run_cmd - should relocate someday. +#if __WIN32__ /* - * Are we using GPS data? - * Incremented if tracker beacons configured. - * Cleared if dwgps_init fails. + * Windows doesn't have localtime_r. + * It should have the equivalent localtime_s, with opposite parameter + * order, but I get undefined reference when trying to use it. */ -static int g_using_gps = 0; +struct tm *localtime_r(time_t *clock, struct tm *res) +{ + struct tm *tm; + + tm = localtime (clock); + memcpy (res, tm, sizeof(struct tm)); + return (res); +} + +#endif + /* * Save pointers to configuration settings. @@ -77,8 +83,6 @@ static int g_using_gps = 0; static struct audio_s *g_modem_config_p; static struct misc_config_s *g_misc_config_p; -static struct digi_config_s *g_digi_config_p; - #if __WIN32__ @@ -97,6 +101,11 @@ void beacon_tracker_set_debug (int level) g_tracker_debug_level = level; } +static time_t sb_calculate_next_time (time_t now, + float current_speed_mph, float current_course, + time_t last_xmit_time, float last_xmit_course); + +static void beacon_send (int j, dwgps_info_t *gpsinfo); /*------------------------------------------------------------------- @@ -105,26 +114,24 @@ void beacon_tracker_set_debug (int level) * * Purpose: Initialize the beacon process. * - * Inputs: pmodem - Aduio device and modem configuration. - * Used only to find valide channels. + * Inputs: pmodem - Audio device and modem configuration. + * Used only to find valid channels. + * * pconfig - misc. configuration from config file. - * pdigi - digipeater configuration from config file. - * TODO: Is this needed? * * * Outputs: Remember required information for future use. * - * Description: Initialize the queue to be empty and set up other - * mechanisms for sharing it between different threads. + * Description: Do some validity checking on the beacon configuration. * - * Start up xmit_thread to actually send the packets + * Start up beacon_thread to actually send the packets * at the appropriate time. * *--------------------------------------------------------------------*/ -void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct digi_config_s *pdigi) +void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig) { time_t now; int j; @@ -149,7 +156,6 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct */ g_modem_config_p = pmodem; g_misc_config_p = pconfig; - g_digi_config_p = pdigi; /* * Precompute the packet contents so any errors are @@ -164,7 +170,9 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct if (g_modem_config_p->achan[chan].valid) { - if (strlen(g_modem_config_p->achan[chan].mycall) > 0 && strcasecmp(g_modem_config_p->achan[chan].mycall, "NOCALL") != 0) { + if (strlen(g_modem_config_p->achan[chan].mycall) > 0 && + strcasecmp(g_modem_config_p->achan[chan].mycall, "N0CALL") != 0 && + strcasecmp(g_modem_config_p->achan[chan].mycall, "NOCALL") != 0) { switch (g_misc_config_p->beacon[j].btype) { @@ -194,14 +202,18 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct case BEACON_TRACKER: -#if defined(ENABLE_GPS) || defined(DEBUG_SIM) - g_using_gps++; -#else - text_color_set(DW_COLOR_ERROR); - dw_printf ("Config file, line %d: GPS tracker feature is not enabled.\n", g_misc_config_p->beacon[j].lineno); - g_misc_config_p->beacon[j].btype = BEACON_IGNORE; - continue; -#endif + { + dwgps_info_t gpsinfo; + dwfix_t fix; + + fix = dwgps_read (&gpsinfo); + if (fix == DWFIX_NOT_INIT) { + + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file, line %d: GPS must be configured to use TBEACON.\n", g_misc_config_p->beacon[j].lineno); + g_misc_config_p->beacon[j].btype = BEACON_IGNORE; + } + } break; case BEACON_CUSTOM: @@ -234,7 +246,7 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct } /* - * Calculate next time for each beacon. + * Calculate first time for each beacon from the 'delay' value. */ now = time(NULL); @@ -253,37 +265,6 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct } -/* - * Connect to GPS receiver if any tracker beacons are configured. - * If open fails, disable all tracker beacons. - */ - -#if DEBUG_SIM - - g_using_gps = 1; - -#elif ENABLE_GPS - - if (g_using_gps > 0) { - int err; - - err = dwgps_init(); - if (err != 0) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("All tracker beacons disabled.\n"); - g_using_gps = 0; - - for (j=0; jnum_beacons; j++) { - if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) { - g_misc_config_p->beacon[j].btype = BEACON_IGNORE; - } - } - } - - } -#endif - - /* * Start up thread for processing only if at least one is valid. */ @@ -342,18 +323,6 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct #define MIN(x,y) ((x) < (y) ? (x) : (y)) -/* Difference between two angles. */ - -static inline float heading_change (float a, float b) -{ - float diff; - - diff = fabs(a - b); - if (diff <= 180.) - return (diff); - else - return (360. - diff); -} #if __WIN32__ @@ -362,29 +331,17 @@ static unsigned __stdcall beacon_thread (void *arg) static void * beacon_thread (void *arg) #endif { - int j; + int j; /* Index into array of beacons. */ time_t earliest; - time_t now; + time_t now; /* Current time. */ + int number_of_tbeacons; /* Number of tracker beacons. */ -/* - * Information from GPS. - */ - int fix = 0; /* 0 = none, 2 = 2D, 3 = 3D */ - double my_lat = 0; /* degrees */ - double my_lon = 0; - float my_course = 0; /* degrees */ - float my_speed_knots = 0; - float my_speed_mph = 0; - float my_alt_m = G_UNKNOWN; /* meters */ - int my_alt_ft = G_UNKNOWN; /* * SmartBeaconing state. */ time_t sb_prev_time = 0; /* Time of most recent transmission. */ float sb_prev_course = 0; /* Most recent course reported. */ - //float sb_prev_speed_mph; /* Most recent speed reported. */ - int sb_every; /* Calculated time between transmissions. */ #if DEBUG @@ -392,30 +349,45 @@ static void * beacon_thread (void *arg) char hms[20]; now = time(NULL); + localtime_r (&now, &tm); + strftime (hms, sizeof(hms), "%H:%M:%S", &tm); text_color_set(DW_COLOR_DEBUG); dw_printf ("beacon_thread: started %s\n", hms); #endif + +/* + * See if any tracker beacons are configured. + * No need to obtain GPS data if none. + */ + + number_of_tbeacons = 0; + for (j=0; jnum_beacons; j++) { + if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) { + number_of_tbeacons++; + } + } + now = time(NULL); while (1) { - assert (g_misc_config_p->num_beacons >= 1); + dwgps_info_t gpsinfo; /* * Sleep until time for the earliest scheduled or * the soonest we could transmit due to corner pegging. */ - earliest = g_misc_config_p->beacon[0].next; - for (j=1; jnum_beacons; j++) { - if (g_misc_config_p->beacon[j].btype == BEACON_IGNORE) - continue; - earliest = MIN(g_misc_config_p->beacon[j].next, earliest); + earliest = now + 60 * 60; + for (j=0; jnum_beacons; j++) { + if (g_misc_config_p->beacon[j].btype != BEACON_IGNORE) { + earliest = MIN(g_misc_config_p->beacon[j].next, earliest); + } } - if (g_misc_config_p->sb_configured && g_using_gps) { + if (g_misc_config_p->sb_configured && number_of_tbeacons > 0) { earliest = MIN(now + g_misc_config_p->sb_turn_time, earliest); earliest = MIN(now + g_misc_config_p->sb_fast_rate, earliest); } @@ -442,128 +414,57 @@ static void * beacon_thread (void *arg) * beacon because corner pegging make it sooner. */ -#if DEBUG_SIM - FILE *fp; - char cs[40]; + if (number_of_tbeacons > 0) { - fp = fopen ("c:\\cygwin\\tmp\\cs", "r"); - if (fp != NULL) { - fscanf (fp, "%f %f", &my_course, &my_speed_knots); - fclose (fp); - } - else { - fprintf (stderr, "Can't read /tmp/cs.\n"); - } - fix = 3; - my_speed_mph = DW_KNOTS_TO_MPH(my_speed_knots); - my_lat = 42.99; - my_lon = 71.99; - my_alt_m = 100; -#else - if (g_using_gps) { - - fix = dwgps_read (&my_lat, &my_lon, &my_speed_knots, &my_course, &my_alt_m); - my_speed_mph = DW_KNOTS_TO_MPH(my_speed_knots); + dwfix_t fix = dwgps_read (&gpsinfo); + float my_speed_mph = DW_KNOTS_TO_MPH(gpsinfo.speed_knots); if (g_tracker_debug_level >= 1) { struct tm tm; char hms[20]; + localtime_r (&now, &tm); strftime (hms, sizeof(hms), "%H:%M:%S", &tm); text_color_set(DW_COLOR_DEBUG); if (fix == 3) { - dw_printf ("%s 3D, %.6f, %.6f, %.1f mph, %.0f\xc2\xb0, %.1f m\n", hms, my_lat, my_lon, my_speed_mph, my_course, my_alt_m); + dw_printf ("%s 3D, %.6f, %.6f, %.1f mph, %.0f\xc2\xb0, %.1f m\n", hms, gpsinfo.dlat, gpsinfo.dlon, my_speed_mph, gpsinfo.track, gpsinfo.altitude); } else if (fix == 2) { - dw_printf ("%s 2D, %.6f, %.6f, %.1f mph, %.0f\xc2\xb0\n", hms, my_lat, my_lon, my_speed_mph, my_course); + dw_printf ("%s 2D, %.6f, %.6f, %.1f mph, %.0f\xc2\xb0\n", hms, gpsinfo.dlat, gpsinfo.dlon, my_speed_mph, gpsinfo.track); } else { dw_printf ("%s No GPS fix\n", hms); } } - /* Transmit altitude only if 3D fix and user asked for it. */ - - my_alt_ft = G_UNKNOWN; - if (fix >= 3 && my_alt_m != G_UNKNOWN && g_misc_config_p->beacon[j].alt_m != G_UNKNOWN) { - my_alt_ft = DW_METERS_TO_FEET(my_alt_m); - } - /* Don't complain here for no fix. */ /* Possibly at the point where about to transmit. */ - } -#endif /* * Run SmartBeaconing calculation if configured and GPS data available. */ - if (g_misc_config_p->sb_configured && g_using_gps && fix >= 2) { + if (g_misc_config_p->sb_configured && fix >= DWFIX_2D) { - if (my_speed_mph > g_misc_config_p->sb_fast_speed) { - sb_every = g_misc_config_p->sb_fast_rate; - if (g_tracker_debug_level >= 2) { - text_color_set(DW_COLOR_DEBUG); - dw_printf ("my speed %.1f > fast %d mph, interval = %d sec\n", my_speed_mph, g_misc_config_p->sb_fast_speed, sb_every); - } - } - else if (my_speed_mph < g_misc_config_p->sb_slow_speed) { - sb_every = g_misc_config_p->sb_slow_rate; - if (g_tracker_debug_level >= 2) { - text_color_set(DW_COLOR_DEBUG); - dw_printf ("my speed %.1f < slow %d mph, interval = %d sec\n", my_speed_mph, g_misc_config_p->sb_slow_speed, sb_every); - } - } - else { - /* Can't divide by 0 assuming sb_slow_speed > 0. */ - sb_every = ( g_misc_config_p->sb_fast_rate * g_misc_config_p->sb_fast_speed ) / my_speed_mph; - if (g_tracker_debug_level >= 2) { - text_color_set(DW_COLOR_DEBUG); - dw_printf ("my speed %.1f mph, interval = %d sec\n", my_speed_mph, sb_every); - } - } + time_t tnext = sb_calculate_next_time (now, + DW_KNOTS_TO_MPH(gpsinfo.speed_knots), gpsinfo.track, + sb_prev_time, sb_prev_course); -#if DEBUG_SIM - text_color_set(DW_COLOR_DEBUG); - dw_printf ("SB: fast %d %d slow %d %d speed=%.1f every=%d\n", - g_misc_config_p->sb_fast_speed, g_misc_config_p->sb_fast_rate, - g_misc_config_p->sb_slow_speed, g_misc_config_p->sb_slow_rate, - my_speed_mph, sb_every); -#endif - -/* - * Test for "Corner Pegging" if moving. - */ - if (my_speed_mph >= 1.0) { - int turn_threshold = g_misc_config_p->sb_turn_angle + - g_misc_config_p->sb_turn_slope / my_speed_mph; - -#if DEBUG_SIM - text_color_set(DW_COLOR_DEBUG); - dw_printf ("SB-moving: course %.0f prev %.0f thresh %d\n", - my_course, sb_prev_course, turn_threshold); -#endif - if (heading_change(my_course, sb_prev_course) > turn_threshold && - now >= sb_prev_time + g_misc_config_p->sb_turn_time) { - - if (g_tracker_debug_level >= 2) { - text_color_set(DW_COLOR_DEBUG); - dw_printf ("heading change (%.0f, %.0f) > threshold %d and %d since last >= turn time %d\n", - my_course, sb_prev_course, turn_threshold, - (int)(now - sb_prev_time), g_misc_config_p->sb_turn_time); - } - - /* Send it now. */ - for (j=0; jnum_beacons; j++) { - if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) { - g_misc_config_p->beacon[j].next = now; + for (j=0; jnum_beacons; j++) { + if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) { + /* Haven't thought about the consequences of SmartBeaconing */ + /* and having more than one tbeacon configured. */ + if (tnext < g_misc_config_p->beacon[j].next) { + g_misc_config_p->beacon[j].next = tnext; } } - } /* significant change in direction */ - } /* is moving */ - } /* apply SmartBeaconing */ - - + } /* Update next time if sooner. */ + } /* apply SmartBeaconing */ + } /* tbeacon(s) configured. */ + +/* + * Send if the time has arrived. + */ for (j=0; jnum_beacons; j++) { if (g_misc_config_p->beacon[j].btype == BEACON_IGNORE) @@ -571,13 +472,199 @@ static void * beacon_thread (void *arg) if (g_misc_config_p->beacon[j].next <= now) { + /* Send the beacon. */ + + beacon_send (j, &gpsinfo); + + /* Calculate when the next one should be sent. */ + /* Easy for fixed interval. SmartBeaconing takes more effort. */ + + if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) { + + if (gpsinfo.fix < DWFIX_2D) { + /* Fix not available so beacon was not sent. */ + /* Try again in a couple seconds. */ + + g_misc_config_p->beacon[j].next = now + 2; + } + else if (g_misc_config_p->sb_configured) { + + /* Remember most recent tracker beacon. */ + /* Compute next time if not turning. */ + + sb_prev_time = now; + sb_prev_course = gpsinfo.track; + + g_misc_config_p->beacon[j].next = sb_calculate_next_time (now, + DW_KNOTS_TO_MPH(gpsinfo.speed_knots), gpsinfo.track, + sb_prev_time, sb_prev_course); + } + else { + g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every; + } + } + else { + /* non-tracker beacons are at fixed spacing. */ + + g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every; + } + + } /* if time to send it */ + + } /* for each configured beacon */ + + } /* do forever */ + +#if __WIN32__ + return(0); /* unreachable but warning if not here. */ +#else + return(NULL); +#endif + +} /* end beacon_thread */ + + +/*------------------------------------------------------------------- + * + * Name: sb_calculate_next_time + * + * Purpose: Calculate next transmission time using the SmartBeaconing algorithm. + * + * Inputs: now - Current time. + * + * current_speed_mph - Current speed from GPS. + * Not expecting G_UNKNOWN but should check for it. + * + * current_course - Current direction of travel. + * Could be G_UNKNOWN if stationary. + * + * last_xmit_time - Time of most recent transmission. + * + * last_xmit_course - Direction included in most recent transmission. + * + * Global In: g_misc_config_p-> + * sb_configured TRUE if SmartBeaconing is configured. + * sb_fast_speed MPH + * sb_fast_rate seconds + * sb_slow_speed MPH + * sb_slow_rate seconds + * sb_turn_time seconds + * sb_turn_angle degrees + * sb_turn_slope degrees * MPH + * + * Returns: Time of next transmission. + * Could vary from now to sb_slow_rate in the future. + * + * Caution: The algorithm is defined in MPH units. GPS uses knots. + * The caller must be careful about using the proper conversions. + * + *--------------------------------------------------------------------*/ + +/* Difference between two angles. */ + +static float heading_change (float a, float b) +{ + float diff; + + diff = fabs(a - b); + if (diff <= 180.) + return (diff); + else + return (360. - diff); +} + +static time_t sb_calculate_next_time (time_t now, + float current_speed_mph, float current_course, + time_t last_xmit_time, float last_xmit_course) +{ + int beacon_rate; + time_t next_time; + +/* + * Compute time between beacons for travelling in a straight line. + */ + + if (current_speed_mph == G_UNKNOWN) { + beacon_rate = (int)roundf((g_misc_config_p->sb_fast_rate + g_misc_config_p->sb_slow_rate) / 2.); + } + else if (current_speed_mph > g_misc_config_p->sb_fast_speed) { + beacon_rate = g_misc_config_p->sb_fast_rate; + } + else if (current_speed_mph < g_misc_config_p->sb_slow_speed) { + beacon_rate = g_misc_config_p->sb_slow_rate; + } + else { + /* Can't divide by 0 assuming sb_slow_speed > 0. */ + beacon_rate = (int)roundf(( g_misc_config_p->sb_fast_rate * g_misc_config_p->sb_fast_speed ) / current_speed_mph); + } + + if (g_tracker_debug_level >= 2) { + text_color_set(DW_COLOR_DEBUG); + dw_printf ("SmartBeaconing: Beacon Rate = %d seconds for %.1f MPH\n", beacon_rate, current_speed_mph); + } + + next_time = last_xmit_time + beacon_rate; + +/* + * Test for "Corner Pegging" if moving. + */ + if (current_speed_mph != G_UNKNOWN && current_speed_mph >= 1.0 && + current_course != G_UNKNOWN && last_xmit_course != G_UNKNOWN) { + + float change = heading_change(current_course, last_xmit_course); + float turn_threshold = g_misc_config_p->sb_turn_angle + + g_misc_config_p->sb_turn_slope / current_speed_mph; + + if (change > turn_threshold && + now >= last_xmit_time + g_misc_config_p->sb_turn_time) { + + if (g_tracker_debug_level >= 2) { + text_color_set(DW_COLOR_DEBUG); + dw_printf ("SmartBeaconing: Send now for heading change of %.0f\n", change); + } + + next_time = now; + } + } + + return (next_time); + +} /* end sb_calculate_next_time */ + + +/*------------------------------------------------------------------- + * + * Name: beacon_send + * + * Purpose: Transmit one beacon after it was determined to be time. + * + * Inputs: j Index into beacon configuration array below. + * + * gpsinfo Information from GPS. Used only for TBEACON. + * + * Global In: g_misc_config_p->beacon Array of beacon configurations. + * + * Outputs: Destination(s) specified: + * - Transmit queue. + * - IGate. + * - Simulated reception. + * + * Description: Prepare text in monitor format. + * Convert to packet object. + * Send to desired destination(s). + * + *--------------------------------------------------------------------*/ + +static void beacon_send (int j, dwgps_info_t *gpsinfo) +{ + + int strict = 1; /* Strict packet checking because they will go over air. */ char stemp[20]; char info[AX25_MAX_INFO_LEN]; char beacon_text[AX25_MAX_PACKET_LEN]; packet_t pp = NULL; char mycall[AX25_MAX_ADDR_LEN]; - int alt_ft; char super_comment[AX25_MAX_INFO_LEN]; // Fixed part + any dynamic part. @@ -600,7 +687,7 @@ static void * beacon_thread (void *arg) if (strlen(mycall) == 0 || strcmp(mycall, "NOCALL") == 0) { text_color_set(DW_COLOR_ERROR); dw_printf ("MYCALL not set for beacon in config file line %d.\n", g_misc_config_p->beacon[j].lineno); - continue; + return; } /* @@ -645,7 +732,7 @@ static void * beacon_thread (void *arg) /* Run given command to get variable part of comment. */ - k = dw_run_cmd (g_misc_config_p->beacon[j].commentcmd, 2, var_comment, (int)sizeof(var_comment)); + k = dw_run_cmd (g_misc_config_p->beacon[j].commentcmd, 2, var_comment, sizeof(var_comment)); if (k > 0) { strlcat (super_comment, var_comment, sizeof(super_comment)); } @@ -663,18 +750,16 @@ static void * beacon_thread (void *arg) case BEACON_POSITION: - alt_ft = DW_METERS_TO_FEET(g_misc_config_p->beacon[j].alt_m); - - encode_position (g_misc_config_p->beacon[j].messaging, - g_misc_config_p->beacon[j].compress, g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon, alt_ft, + encode_position (g_misc_config_p->beacon[j].messaging, g_misc_config_p->beacon[j].compress, + g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon, + (int)roundf(DW_METERS_TO_FEET(g_misc_config_p->beacon[j].alt_m)), g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir, - 0, 0, /* course, speed */ + G_UNKNOWN, G_UNKNOWN, /* course, speed */ g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset, super_comment, info, sizeof(info)); strlcat (beacon_text, info, sizeof(beacon_text)); - g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every; break; case BEACON_OBJECT: @@ -682,48 +767,43 @@ static void * beacon_thread (void *arg) encode_object (g_misc_config_p->beacon[j].objname, g_misc_config_p->beacon[j].compress, 0, g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon, g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir, - 0, 0, /* course, speed */ + G_UNKNOWN, G_UNKNOWN, /* course, speed */ g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset, super_comment, info, sizeof(info)); strlcat (beacon_text, info, sizeof(beacon_text)); - g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every; break; case BEACON_TRACKER: - if (fix >= 2) { - int coarse; /* APRS encoder wants 1 - 360. */ - /* 0 means none or unknown. */ + if (gpsinfo->fix >= DWFIX_2D) { - coarse = (int)roundf(my_course); - if (coarse == 0) { - coarse = 360; - } - encode_position (g_misc_config_p->beacon[j].messaging, - g_misc_config_p->beacon[j].compress, - my_lat, my_lon, my_alt_ft, + int coarse; /* Round to nearest integer. retaining unknown state. */ + int my_alt_ft; + + /* Transmit altitude only if user asked for it. */ + /* A positive altitude in the config file enables */ + /* transmission of altitude from GPS. */ + + my_alt_ft = G_UNKNOWN; + if (gpsinfo->fix >= 3 && gpsinfo->altitude != G_UNKNOWN && g_misc_config_p->beacon[j].alt_m > 0) { + my_alt_ft = (int)roundf(DW_METERS_TO_FEET(gpsinfo->altitude)); + } + + coarse = G_UNKNOWN; + if (gpsinfo->track != G_UNKNOWN) { + coarse = (int)roundf(gpsinfo->track); + } + + encode_position (g_misc_config_p->beacon[j].messaging, g_misc_config_p->beacon[j].compress, + gpsinfo->dlat, gpsinfo->dlon, my_alt_ft, g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir, - coarse, (int)roundf(my_speed_knots), + coarse, (int)roundf(gpsinfo->speed_knots), g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset, super_comment, info, sizeof(info)); strlcat (beacon_text, info, sizeof(beacon_text)); - /* Remember most recent tracker beacon. */ - - sb_prev_time = now; - sb_prev_course = my_course; - //sb_prev_speed_mph = my_speed_mph; - - /* Calculate time for next transmission. */ - if (g_misc_config_p->sb_configured) { - g_misc_config_p->beacon[j].next = now + sb_every; - } - else { - g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every; - } - /* Write to log file for testing. */ /* The idea is to run log2gpx and map the result rather than */ /* actually transmitting and relying on someone else to receive */ @@ -743,11 +823,11 @@ static void * beacon_thread (void *arg) strlcpy (A.g_src, mycall, sizeof(A.g_src)); A.g_symbol_table = g_misc_config_p->beacon[j].symtab; A.g_symbol_code = g_misc_config_p->beacon[j].symbol; - A.g_lat = my_lat; - A.g_lon = my_lon; - A.g_speed = DW_KNOTS_TO_MPH(my_speed_knots); + A.g_lat = gpsinfo->dlat; + A.g_lon = gpsinfo->dlon; + A.g_speed_mph = DW_KNOTS_TO_MPH(gpsinfo->speed_knots); A.g_course = coarse; - A.g_altitude = my_alt_ft; + A.g_altitude_ft = DW_METERS_TO_FEET(gpsinfo->altitude); /* Fake channel of 999 to distinguish from real data. */ memset (&alevel, 0, sizeof(alevel)); @@ -755,8 +835,7 @@ static void * beacon_thread (void *arg) } } else { - g_misc_config_p->beacon[j].next = now + 2; - continue; /* No fix. Try again in a couple seconds. */ + return; /* No fix. Skip this time. */ } break; @@ -770,12 +849,11 @@ static void * beacon_thread (void *arg) } else if (g_misc_config_p->beacon[j].custom_infocmd != NULL) { char info_part[AX25_MAX_INFO_LEN]; - char *p; int k; /* Run given command to obtain the info part for packet. */ - k = dw_run_cmd (g_misc_config_p->beacon[j].custom_infocmd, 2, info_part, (int)sizeof(info_part)); + k = dw_run_cmd (g_misc_config_p->beacon[j].custom_infocmd, 2, info_part, sizeof(info_part)); if (k > 0) { strlcat (beacon_text, info_part, sizeof(beacon_text)); } @@ -790,7 +868,6 @@ static void * beacon_thread (void *arg) dw_printf ("Internal error. custom_info is null. %s %d\n", __FILE__, __LINE__); strlcpy (beacon_text, "", sizeof(beacon_text)); // abort! } - g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every; break; case BEACON_IGNORE: @@ -803,7 +880,7 @@ static void * beacon_thread (void *arg) * Parse monitor format into form for transmission. */ if (strlen(beacon_text) == 0) { - continue; + return; } pp = ax25_from_text (beacon_text, strict); @@ -819,11 +896,9 @@ static void * beacon_thread (void *arg) case SENDTO_IGATE: - -#if 1 text_color_set(DW_COLOR_XMIT); dw_printf ("[ig] %s\n", beacon_text); -#endif + igate_send_rec_packet (0, pp); ax25_delete (pp); break; @@ -849,12 +924,7 @@ static void * beacon_thread (void *arg) dw_printf ("%s\n", beacon_text); } - } /* if time to send it */ +} /* end beacon_send */ - } /* for each configured beacon */ - - } /* do forever */ - -} /* end beacon_thread */ /* end beacon.c */ diff --git a/beacon.h b/beacon.h index 3e02873..1dce32b 100644 --- a/beacon.h +++ b/beacon.h @@ -1,6 +1,6 @@ /* beacon.h */ -void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct digi_config_s *pdigi); +void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig); void beacon_tracker_set_debug (int level); diff --git a/config.c b/config.c index 03a56ef..72e60c9 100644 --- a/config.c +++ b/config.c @@ -42,10 +42,8 @@ #include #include -#if __WIN32__ -//#include "pthreads/pthread.h" -#else -#include +#if ENABLE_GPSD +#include /* for DEFAULT_GPSD_PORT (2947) */ #endif #include "ax25_pad.h" @@ -184,13 +182,12 @@ static int alllettersorpm(char *p) /* Acceptable symbols to separate degrees & minutes. */ /* Degree symbol is not in ASCII so documentation says to use "^" instead. */ /* Some wise guy will try to use degree symbol. */ +/* UTF-8 is more difficult because it is a two byte sequence, c2 b0. */ #define DEG1 '^' #define DEG2 0xb0 /* ISO Latin1 */ #define DEG3 0xf8 /* Microsoft code page 437 */ -// TODO: recognize UTF-8 degree symbol. - enum parse_ll_which_e { LAT, LON }; @@ -484,7 +481,6 @@ static char *split (char *string, int rest_of_line) { static char cmd[MAXCMDLEN]; static char token[MAXCMDLEN]; - static char *nextp = NULL; static char *c; // current position in cmd. char *s, *t; int in_quotes; @@ -756,6 +752,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, //strlcpy (p_misc_config->nullmodem, DEFAULT_NULLMODEM, sizeof(p_misc_config->nullmodem)); strlcpy (p_misc_config->nullmodem, "", sizeof(p_misc_config->nullmodem)); + strlcpy (p_misc_config->gpsnmea_port, "", sizeof(p_misc_config->gpsnmea_port)); strlcpy (p_misc_config->nmea_port, "", sizeof(p_misc_config->nmea_port)); strlcpy (p_misc_config->logdir, "", sizeof(p_misc_config->logdir)); @@ -869,12 +866,12 @@ void config_init (char *fname, struct audio_s *p_audio_config, /* First channel of device is valid. */ p_audio_config->achan[ADEVFIRSTCHAN(adevice)].valid = 1; - strncpy (p_audio_config->adev[adevice].adevice_in, t, sizeof(p_audio_config->adev[adevice].adevice_in)-1); - strncpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out)-1); + strlcpy (p_audio_config->adev[adevice].adevice_in, t, sizeof(p_audio_config->adev[adevice].adevice_in)); + strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out)); t = split(NULL,0); if (t != NULL) { - strncpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out)-1); + strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out)); } } @@ -924,7 +921,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, /* First channel of device is valid. */ p_audio_config->achan[ADEVFIRSTCHAN(adevice)].valid = 1; - strncpy (p_audio_config->adev[adevice].adevice_in, t, sizeof(p_audio_config->adev[adevice].adevice_in)-1); + strlcpy (p_audio_config->adev[adevice].adevice_in, t, sizeof(p_audio_config->adev[adevice].adevice_in)); } else if (strcasecmp(t, "PAODEVICE") == 0) { adevice = 0; @@ -951,7 +948,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, /* First channel of device is valid. */ p_audio_config->achan[ADEVFIRSTCHAN(adevice)].valid = 1; - strncpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out)-1); + strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out)); } @@ -1074,7 +1071,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, char *p; - strncpy (p_audio_config->achan[c].mycall, t, sizeof(p_audio_config->achan[c].mycall)-1); + strlcpy (p_audio_config->achan[c].mycall, t, sizeof(p_audio_config->achan[c].mycall)); for (p = p_audio_config->achan[c].mycall; *p != '\0'; p++) { if (islower(*p)) { @@ -1082,6 +1079,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, } } // TODO: additional checks if valid. + // Should have a function to check for valid callsign[-ssid] } } } @@ -1231,7 +1229,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, } } - strncpy (p_audio_config->achan[channel].profiles, t, sizeof(p_audio_config->achan[channel].profiles)); + strlcpy (p_audio_config->achan[channel].profiles, t, sizeof(p_audio_config->achan[channel].profiles)); t = split(NULL,0); if (strlen(p_audio_config->achan[channel].profiles) > 1 && t != NULL) { text_color_set(DW_COLOR_ERROR); @@ -1328,7 +1326,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, else if (alllettersorpm(t)) { /* profile of letter(s) + - */ // Will be validated later. - strncpy (p_audio_config->achan[channel].profiles, t, sizeof(p_audio_config->achan[channel].profiles)); + strlcpy (p_audio_config->achan[channel].profiles, t, sizeof(p_audio_config->achan[channel].profiles)); } else if (*t == '/') { /* /div */ @@ -1354,7 +1352,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, /* A later place catches disallowed combination of + and @. */ /* A later place sets /n for 300 baud if not specified by user. */ - //printf ("debug: div = %d\n", p_audio_config->achan[channel].decimate); + //dw_printf ("debug: div = %d\n", p_audio_config->achan[channel].decimate); } } @@ -1440,7 +1438,6 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ else if (strcasecmp(t, "PTT") == 0 || strcasecmp(t, "DCD") == 0) { - //int n; int ot; char otname[8]; @@ -1729,7 +1726,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ else if (strcasecmp(t, "SPEECH") == 0) { - int n; + t = split(NULL,0); if (t == NULL) { text_color_set(DW_COLOR_ERROR); @@ -1895,8 +1892,6 @@ void config_init (char *fname, struct audio_s *p_audio_config, else if (strcasecmp(t, "regen") == 0) { int from_chan, to_chan; - int e; - char message[100]; t = split(NULL,0); @@ -1957,8 +1952,6 @@ void config_init (char *fname, struct audio_s *p_audio_config, else if (strcasecmp(t, "FILTER") == 0) { int from_chan, to_chan; - int e; - char message[100]; t = split(NULL,0); @@ -2036,7 +2029,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ else if (strcasecmp(t, "TTCORRAL") == 0) { - //int n; + t = split(NULL,0); if (t == NULL) { text_color_set(DW_COLOR_ERROR); @@ -2371,8 +2364,6 @@ void config_init (char *fname, struct audio_s *p_audio_config, struct ttloc_s *tl; int j; - int znum; - char *zlet; double dlat, dlon; long lerr; @@ -2485,8 +2476,6 @@ void config_init (char *fname, struct audio_s *p_audio_config, struct ttloc_s *tl; int j; - int znum; - char *zlet; int num_x, num_y; double lat, lon; long lerr; @@ -2679,7 +2668,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, p_tt_config->ttloc_len--; continue; } - if (tt_mhead_to_text(t, 0, mh) != 0) { + if (tt_mhead_to_text(t, 0, mh, sizeof(mh)) != 0) { text_color_set(DW_COLOR_ERROR); dw_printf ("Line %d: TTMHEAD prefix not a valid DTMF sequence.\n", line); p_tt_config->ttloc_len--; @@ -2802,7 +2791,6 @@ void config_init (char *fname, struct audio_s *p_audio_config, struct ttloc_s *tl; int j; - //char ch; int p_count[3], d_count[3]; int tt_error = 0; @@ -2959,8 +2947,6 @@ void config_init (char *fname, struct audio_s *p_audio_config, if (*pi == '}') { char symtab; char symbol; - char overlay; - char symdest[8]; *ps = '\0'; @@ -2975,7 +2961,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, // Convert symtab(overlay) & symbol to tone sequence. - symbols_to_tones (symtab, symbol, ttemp); + symbols_to_tones (symtab, symbol, ttemp, sizeof(ttemp)); //text_color_set(DW_COLOR_DEBUG); //dw_printf ("DEBUG config file Line %d: AB{%s} -> %s\n", line, stemp, ttemp); @@ -3129,7 +3115,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, if (t != NULL) { // TODO: Should do some validity checking on the path. - strncpy (p_tt_config->obj_xmit_via, t, sizeof(p_tt_config->obj_xmit_via)); + strlcpy (p_tt_config->obj_xmit_via, t, sizeof(p_tt_config->obj_xmit_via)); } } @@ -3246,9 +3232,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, while (*t == ' ' || *t == '\t') t++; // remove leading white space. - strncpy (p_tt_config->status[status_num], t, TT_MTEXT_LEN); - p_tt_config->status[status_num][TT_MTEXT_LEN-1] = '\0'; - + strlcpy (p_tt_config->status[status_num], t, sizeof(p_tt_config->status[status_num])); } @@ -3260,7 +3244,6 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ else if (strcasecmp(t, "TTCMD") == 0) { - int status_num; t = split(NULL,1); if (t == NULL) { @@ -3269,9 +3252,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } - strncpy (p_tt_config->ttcmd, t, sizeof(p_tt_config->ttcmd)); - p_tt_config->ttcmd[sizeof(p_tt_config->ttcmd)-1] = '\0'; - + strlcpy (p_tt_config->ttcmd, t, sizeof(p_tt_config->ttcmd)); } @@ -3294,7 +3275,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, dw_printf ("Line %d: Missing IGate server name for IGSERVER command.\n", line); continue; } - strncpy (p_igate_config->t2_server_name, t, sizeof(p_igate_config->t2_server_name)-1); + strlcpy (p_igate_config->t2_server_name, t, sizeof(p_igate_config->t2_server_name)); /* If there is a : in the name, split it out as the port number. */ @@ -3329,7 +3310,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, line, p_igate_config->t2_server_port); } } - //printf ("DEBUG server=%s port=%d\n", p_igate_config->t2_server_name, p_igate_config->t2_server_port); + //dw_printf ("DEBUG server=%s port=%d\n", p_igate_config->t2_server_name, p_igate_config->t2_server_port); //exit (0); } @@ -3347,7 +3328,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } // TODO: Wouldn't hurt to do validity checking of format. - strncpy (p_igate_config->t2_login, t, sizeof(p_igate_config->t2_login)-1); + strlcpy (p_igate_config->t2_login, t, sizeof(p_igate_config->t2_login)); t = split(NULL,0); if (t == NULL) { @@ -3355,7 +3336,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, dw_printf ("Line %d: Missing passcode for IGLOGIN command.\n", line); continue; } - strncpy (p_igate_config->t2_passcode, t, sizeof(p_igate_config->t2_passcode)-1); + strlcpy (p_igate_config->t2_passcode, t, sizeof(p_igate_config->t2_passcode)); } /* @@ -3387,7 +3368,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, if (t != NULL) { char *p; p_igate_config->tx_via[0] = ','; - strncpy (p_igate_config->tx_via + 1, t, sizeof(p_igate_config->tx_via)-2); + strlcpy (p_igate_config->tx_via + 1, t, sizeof(p_igate_config->tx_via)-1); for (p = p_igate_config->tx_via; *p != '\0'; p++) { if (islower(*p)) { *p = toupper(*p); /* silently force upper case. */ @@ -3403,7 +3384,6 @@ void config_init (char *fname, struct audio_s *p_audio_config, */ else if (strcasecmp(t, "IGFILTER") == 0) { - //int n; t = split(NULL,1); /* Take rest of line as one string. */ @@ -3524,12 +3504,74 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } else { - strncpy (p_misc_config->nullmodem, t, sizeof(p_misc_config->nullmodem)-1); + strlcpy (p_misc_config->nullmodem, t, sizeof(p_misc_config->nullmodem)); } } +/* + * GPSNMEA - Device name for reading from GPS receiver. + */ + else if (strcasecmp(t, "gpsnmea") == 0) { + t = split(NULL,0); + if (t == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file, line %d: Missing serial port name for GPS receiver.\n", line); + continue; + } + else { + strlcpy (p_misc_config->gpsnmea_port, t, sizeof(p_misc_config->gpsnmea_port)); + } + } + +/* + * GPSD - Use GPSD server. + * + * GPSD [ host [ port ] ] + */ + else if (strcasecmp(t, "gpsd") == 0) { + +#if __WIN32__ + + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file, line %d: The GPSD interface is not available for Windows.\n", line); + continue; + +#elif ENABLE_GPSD + + strlcpy (p_misc_config->gpsd_host, "localhost", sizeof(p_misc_config->gpsd_host)); + p_misc_config->gpsd_port = atoi(DEFAULT_GPSD_PORT); + + t = split(NULL,0); + if (t != NULL) { + strlcpy (p_misc_config->gpsd_host, t, sizeof(p_misc_config->gpsd_host)); + + t = split(NULL,0); + if (t != NULL) { + + int n = atoi(t); + if ((n >= MIN_IP_PORT_NUMBER && n <= MAX_IP_PORT_NUMBER) || n == 0) { + p_misc_config->gpsd_port = n; + } + else { + p_misc_config->gpsd_port = atoi(DEFAULT_GPSD_PORT); + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: Invalid port number for GPSD Socket Interface. Using default of %d.\n", + line, p_misc_config->gpsd_port); + } + } + } +#else + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file, line %d: The GPSD interface has not been enabled.\n", line); + dw_printf ("Install gpsd and libgps-dev packages then rebuild direwolf.\n"); + continue; +#endif + + } + /* * NMEA - Device name for communication with NMEA device. + * Wasn't documented will probably use WAYPOINT instead. */ else if (strcasecmp(t, "nmea") == 0) { t = split(NULL,0); @@ -3539,7 +3581,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } else { - strncpy (p_misc_config->nmea_port, t, sizeof(p_misc_config->nmea_port)-1); + strlcpy (p_misc_config->nmea_port, t, sizeof(p_misc_config->nmea_port)); } } @@ -3554,7 +3596,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, continue; } else { - strncpy (p_misc_config->logdir, t, sizeof(p_misc_config->logdir)-1); + strlcpy (p_misc_config->logdir, t, sizeof(p_misc_config->logdir)); } t = split(NULL,0); if (t != NULL) { @@ -3591,21 +3633,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, strcasecmp(t, "OBEACON") == 0 || strcasecmp(t, "TBEACON") == 0 || strcasecmp(t, "CBEACON") == 0) { -#if __WIN32__ - if (strcasecmp(t, "TBEACON") == 0) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Config file, line %d: TBEACON is available only in Linux version.\n", line); - continue; - } -#endif -#ifndef ENABLE_GPS - if (strcasecmp(t, "TBEACON") == 0) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Config file, line %d: Rebuild with GPS support for TBEACON to be available.\n", line); - continue; - } -#endif if (p_misc_config->num_beacons < MAX_BEACONS) { memset (&(p_misc_config->beacon[p_misc_config->num_beacons]), 0, sizeof(struct beacon_s)); @@ -3638,7 +3666,9 @@ void config_init (char *fname, struct audio_s *p_audio_config, /* - * SMARTBEACONING fast_speed fast_rate slow_speed slow_rate turn_time turn_angle turn_slope + * SMARTBEACONING [ fast_speed fast_rate slow_speed slow_rate turn_time turn_angle turn_slope ] + * + * Parameters must be all or nothing. */ else if (strcasecmp(t, "SMARTBEACON") == 0 || @@ -3647,8 +3677,12 @@ void config_init (char *fname, struct audio_s *p_audio_config, int n; #define SB_NUM(name,sbvar,minn,maxx,unit) \ - t = split(NULL,0); \ + t = split(NULL,0); \ if (t == NULL) { \ + if (strcmp(name, "fast speed") == 0) { \ + p_misc_config->sb_configured = 1; \ + continue; \ + } \ text_color_set(DW_COLOR_ERROR); \ dw_printf ("Line %d: Missing %s for SmartBeaconing.\n", line, name); \ continue; \ @@ -3664,7 +3698,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, } #define SB_TIME(name,sbvar,minn,maxx,unit) \ - t = split(NULL,0); \ + t = split(NULL,0); \ if (t == NULL) { \ text_color_set(DW_COLOR_ERROR); \ dw_printf ("Line %d: Missing %s for SmartBeaconing.\n", line, name); \ @@ -3787,11 +3821,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_s *p_audio_config) { - char options[1000]; - char *o; char *t; - char *p; - int q; char temp_symbol[100]; int ok; char zone[8]; @@ -3901,7 +3931,7 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_ b->custom_infocmd = strdup(value); } else if (strcasecmp(keyword, "OBJNAME") == 0) { - strncpy(b->objname, value, 9); + strlcpy(b->objname, value, sizeof(b->objname)); } else if (strcasecmp(keyword, "LAT") == 0) { b->lat = parse_ll (value, LAT, line); @@ -3913,7 +3943,7 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_ b->alt_m = atof(value); } else if (strcasecmp(keyword, "ZONE") == 0) { - strncpy(zone, value, sizeof(zone)); + strlcpy(zone, value, sizeof(zone)); } else if (strcasecmp(keyword, "EAST") == 0 || strcasecmp(keyword, "EASTING") == 0) { easting = atof(value); @@ -3944,7 +3974,7 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_ b->gain = atoi(value); } else if (strcasecmp(keyword, "DIR") == 0 || strcasecmp(keyword, "DIRECTION") == 0) { - strncpy(b->dir, value, 2); + strlcpy(b->dir, value, sizeof(b->dir)); } else if (strcasecmp(keyword, "FREQ") == 0) { b->freq = atof(value); diff --git a/config.h b/config.h index be65cd7..4ea2eda 100644 --- a/config.h +++ b/config.h @@ -38,11 +38,21 @@ struct misc_config_s { /* Want this to be off by default because it hangs */ /* after a while if nothing is reading from other end. */ - char nullmodem[40]; /* Serial port name for our end of the */ + char nullmodem[20]; /* Serial port name for our end of the */ /* virtual null modem for native Windows apps. */ - char nmea_port[40]; /* Serial port name for NMEA communication with GPS */ - /* receiver and/or mapping application. */ + char gpsnmea_port[20]; /* Serial port name for reading NMEA sentences from GPS. */ + /* e.g. COM22, /dev/ttyACM0 */ + + char gpsd_host[20]; /* Host for gpsd server. */ + /* e.g. localhost, 192.168.1.2 */ + + int gpsd_port; /* Port number for gpsd server. */ + /* Default is 2947. */ + + /* e.g. COM22, /dev/ttyACM0 */ + char nmea_port[20]; /* Serial port name for NMEA communication with GPS */ + /* receiver and/or mapping application. Change this. */ char logdir[80]; /* Directory for saving activity logs. */ diff --git a/decode_aprs.c b/decode_aprs.c index 282c016..807dcd9 100644 --- a/decode_aprs.c +++ b/decode_aprs.c @@ -53,7 +53,7 @@ #include "textcolor.h" #include "symbols.h" #include "latlong.h" -//#include "nmea.h" +#include "dwgpsnmea.h" #include "decode_aprs.h" #include "telemetry.h" @@ -143,7 +143,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen); * * Outputs: A-> g_symbol_table, g_symbol_code, * g_lat, g_lon, - * g_speed, g_course, g_altitude, + * g_speed_mph, g_course, g_altitude_ft, * g_comment * ... and many others... * @@ -174,7 +174,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet) A->g_lat = G_UNKNOWN; A->g_lon = G_UNKNOWN; - A->g_speed = G_UNKNOWN; + A->g_speed_mph = G_UNKNOWN; A->g_course = G_UNKNOWN; A->g_power = G_UNKNOWN; @@ -182,7 +182,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet) A->g_gain = G_UNKNOWN; A->g_range = G_UNKNOWN; - A->g_altitude = G_UNKNOWN; + A->g_altitude_ft = G_UNKNOWN; A->g_freq = G_UNKNOWN; A->g_tone = G_UNKNOWN; A->g_dcs = G_UNKNOWN; @@ -416,6 +416,11 @@ void decode_aprs_print (decode_aprs_t *A) { if (A->g_power > 0) { char phg[100]; + /* Protcol spec doesn't mention whether this is dBd or dBi. */ + /* Clarified later. */ + /* http://eng.usna.navy.mil/~bruninga/aprs/aprs11.html */ + /* "The Antenna Gain in the PHG format on page 28 is in dBi." */ + snprintf (phg, sizeof(phg), ", %d W height=%d %ddBi %s", A->g_power, A->g_height, A->g_gain, A->g_directivity); strlcat (stemp, phg, sizeof(stemp)); } @@ -507,11 +512,11 @@ void decode_aprs_print (decode_aprs_t *A) { strlcat (stemp, A->g_aprstt_loc, sizeof(stemp)); }; - if (A->g_speed != G_UNKNOWN) { + if (A->g_speed_mph != G_UNKNOWN) { char spd[20]; if (strlen(stemp) > 0) strlcat (stemp, ", ", sizeof(stemp)); - snprintf (spd, sizeof(spd), "%.0f MPH", A->g_speed); + snprintf (spd, sizeof(spd), "%.0f MPH", A->g_speed_mph); strlcat (stemp, spd, sizeof(stemp)); }; @@ -523,11 +528,11 @@ void decode_aprs_print (decode_aprs_t *A) { strlcat (stemp, cse, sizeof(stemp)); }; - if (A->g_altitude != G_UNKNOWN) { + if (A->g_altitude_ft != G_UNKNOWN) { char alt[20]; if (strlen(stemp) > 0) strlcat (stemp, ", ", sizeof(stemp)); - snprintf (alt, sizeof(alt), "alt %.0f ft", A->g_altitude); + snprintf (alt, sizeof(alt), "alt %.0f ft", A->g_altitude_ft); strlcat (stemp, alt, sizeof(stemp)); }; @@ -662,7 +667,7 @@ void decode_aprs_print (decode_aprs_t *A) { * Inputs: info - Pointer to Information field. * ilen - Information field length. * - * Outputs: A->g_lat, A->g_lon, A->g_symbol_table, A->g_symbol_code, A->g_speed, A->g_course, A->g_altitude. + * Outputs: A->g_lat, A->g_lon, A->g_symbol_table, A->g_symbol_code, A->g_speed_mph, A->g_course, A->g_altitude_ft. * * Description: Type identifier '=' has APRS messaging. * Type identifier '!' does not have APRS messaging. @@ -758,7 +763,7 @@ static void aprs_ll_pos (decode_aprs_t *A, unsigned char *info, int ilen) * Inputs: info - Pointer to Information field. * ilen - Information field length. * - * Outputs: A->g_lat, A->g_lon, A->g_symbol_table, A->g_symbol_code, A->g_speed, A->g_course, A->g_altitude. + * Outputs: A->g_lat, A->g_lon, A->g_symbol_table, A->g_symbol_code, A->g_speed_mph, A->g_course, A->g_altitude_ft. * * Description: Type identifier '@' has APRS messaging. * Type identifier '/' does not have APRS messaging. @@ -850,6 +855,7 @@ static void aprs_ll_pos_time (decode_aprs_t *A, unsigned char *info, int ilen) } } + (void)(ts); // suppress 'set but not used' warning. } @@ -862,7 +868,7 @@ static void aprs_ll_pos_time (decode_aprs_t *A, unsigned char *info, int ilen) * Inputs: info - Pointer to Information field. * ilen - Information field length. * - * Outputs: ??? TBD + * Outputs: A-> ... * * Description: APRS recognizes raw ASCII data strings conforming to the NMEA 0183 * Version 2.0 specification, originating from navigation equipment such @@ -875,286 +881,37 @@ static void aprs_ll_pos_time (decode_aprs_t *A, unsigned char *info, int ilen) * VTG Velocity and Track Data * WPL Way Point Location * + * We presently recognize only RMC and GGA. + * * Examples: $GPGGA,102705,5157.9762,N,00029.3256,W,1,04,2.0,75.7,M,47.6,M,,*62 * $GPGLL,2554.459,N,08020.187,W,154027.281,A * $GPRMC,063909,A,3349.4302,N,11700.3721,W,43.022,89.3,291099,13.6,E*52 * $GPVTG,318.7,T,,M,35.1,N,65.0,K*69 * - * *------------------------------------------------------------------*/ -static void nmea_checksum (decode_aprs_t *A, char *sent) -{ - char *p; - char *next; - unsigned char cs; - - -// Do we have valid checksum? - - cs = 0; - for (p = sent+1; *p != '*' && *p != '\0'; p++) { - cs ^= *p; - } - - p = strchr (sent, '*'); - if (p == NULL) { - if ( ! A->g_quiet) { - text_color_set (DW_COLOR_INFO); - dw_printf("Missing GPS checksum.\n"); - } - return; - } - if (cs != strtoul(p+1, NULL, 16)) { - if ( ! A->g_quiet) { - text_color_set (DW_COLOR_ERROR); - dw_printf("GPS checksum error. Expected %02x but found %s.\n", cs, p+1); - } - return; - } - *p = '\0'; // Remove the checksum. -} static void aprs_raw_nmea (decode_aprs_t *A, unsigned char *info, int ilen) { - char stemp[256]; - char *ptype; - char *next; - - - strlcpy (A->g_msg_type, "Raw NMEA", sizeof(A->g_msg_type)); - - strlcpy (stemp, (char *)info, sizeof(stemp)); - nmea_checksum (A, stemp); - - next = stemp; - ptype = strsep(&next, ","); - - if (strcmp(ptype, "$GPGGA") == 0) + if (strncmp((char*)info, "$GPRMC,", 7) == 0) { - char *ptime; /* Time, hhmmss[.sss] */ - char *plat; /* Latitude */ - char *pns; /* North/South */ - char *plon; /* Longitude */ - char *pew; /* East/West */ - char *pquality; /* Fix Quality: 0=invalid, 1=GPS, 2=DGPS */ - char *pnsat; /* Number of satellites. */ - char *phdop; /* Horizontal dilution of precision. */ - char *paltitude; /* Altitude, meters above mean sea level. */ - char *pm; /* "M" = meters */ - /* Various other stuff... */ - - - ptime = strsep(&next, ","); - plat = strsep(&next, ","); - pns = strsep(&next, ","); - plon = strsep(&next, ","); - pew = strsep(&next, ","); - pquality = strsep(&next, ","); - pnsat = strsep(&next, ","); - phdop = strsep(&next, ","); - paltitude = strsep(&next, ","); - pm = strsep(&next, ","); - - /* Process time??? */ - - if (plat != NULL && strlen(plat) > 0 && pns != NULL && strlen(pns) > 0) { - A->g_lat = latitude_from_nmea(plat, pns); - } - else { - text_color_set (DW_COLOR_ERROR); - dw_printf("Incomplete latitude in sentence.\n"); - } - - if (plon != NULL && strlen(plon) > 0 && pew != NULL && strlen(pew) > 0) { - A->g_lon = longitude_from_nmea(plon, pew); - } - else { - text_color_set (DW_COLOR_ERROR); - dw_printf("Incomplete longitude in sentence.\n"); - } - - if (paltitude != NULL && strlen(paltitude) > 0) { - A->g_altitude = DW_METERS_TO_FEET(atof(paltitude)); - } - else { - text_color_set (DW_COLOR_ERROR); - dw_printf("Incomplete altitude in sentence.\n"); - } + float speed_knots = G_UNKNOWN; + (void) dwgpsnmea_gprmc ((char*)info, A->g_quiet, &(A->g_lat), &(A->g_lon), &speed_knots, &(A->g_course)); + A->g_speed_mph = DW_KNOTS_TO_MPH(speed_knots); } - else if (strcmp(ptype, "$GPGLL") == 0) + else if (strncmp((char*)info, "$GPGGA,", 7) == 0) { - char *plat; /* Latitude */ - char *pns; /* North/South */ - char *plon; /* Longitude */ - char *pew; /* East/West */ - /* optional Time hhmmss[.sss] */ - /* optional 'A' for data valid */ - - plat = strsep(&next, ","); - pns = strsep(&next, ","); - plon = strsep(&next, ","); - pew = strsep(&next, ","); - - if (plat != NULL && strlen(plat) > 0 && pns != NULL && strlen(pns) > 0) { - A->g_lat = latitude_from_nmea(plat, pns); - } - else { - text_color_set (DW_COLOR_ERROR); - dw_printf("Incomplete latitude in sentence.\n"); - } - - if (plon != NULL && strlen(plon) > 0 && pew != NULL && strlen(pew) > 0) { - A->g_lon = longitude_from_nmea(plon, pew); - } - else { - text_color_set (DW_COLOR_ERROR); - dw_printf("Incomplete longitude in sentence.\n"); - } + float alt_meters = G_UNKNOWN; + int num_sat = 0; + (void) dwgpsnmea_gpgga ((char*)info, A->g_quiet, &(A->g_lat), &(A->g_lon), &alt_meters, &num_sat); + A->g_altitude_ft = DW_METERS_TO_FEET(alt_meters); } - else if (strcmp(ptype, "$GPRMC") == 0) - { - //char *ptime, *pstatus, *plat, *pns, *plon, *pew, *pspeed, *ptrack, *pdate; - char *ptime; /* Time, hhmmss[.sss] */ - char *pstatus; /* Status, A=Active (valid position), V=Void */ - char *plat; /* Latitude */ - char *pns; /* North/South */ - char *plon; /* Longitude */ - char *pew; /* East/West */ - char *pknots; /* Speed over ground, knots. */ - char *pcourse; /* True course, degrees. */ - char *pdate; /* Date, ddmmyy */ - /* Magnetic variation */ - /* In version 3.00, mode is added: A D E N (see below) */ - /* Checksum */ + // TODO (low): add a few other sentence types. - ptime = strsep(&next, ","); - pstatus = strsep(&next, ","); - plat = strsep(&next, ","); - pns = strsep(&next, ","); - plon = strsep(&next, ","); - pew = strsep(&next, ","); - pknots = strsep(&next, ","); - pcourse = strsep(&next, ","); - pdate = strsep(&next, ","); - - /* process time ??? date ??? */ - - if (plat != NULL && strlen(plat) > 0 && pns != NULL && strlen(pns) > 0) { - A->g_lat = latitude_from_nmea(plat, pns); - } - else { - text_color_set (DW_COLOR_ERROR); - dw_printf("Incomplete latitude in sentence.\n"); - } - - if (plon != NULL && strlen(plon) > 0 && pew != NULL && strlen(pew) > 0) { - A->g_lon = longitude_from_nmea(plon, pew); - } - else { - text_color_set (DW_COLOR_ERROR); - dw_printf("Incomplete longitude in sentence.\n"); - } - - if (pknots != NULL && strlen(pknots) > 0) { - A->g_speed = DW_KNOTS_TO_MPH(atof(pknots)); - } - else { - text_color_set (DW_COLOR_ERROR); - dw_printf("Incomplete speed in sentence.\n"); - } - - if (pcourse != NULL && strlen(pcourse) > 0) { - A->g_course = atof(pcourse); - } - else { - text_color_set (DW_COLOR_ERROR); - dw_printf("Incomplete course in sentence.\n"); - } - - } - else if (strcmp(ptype, "$GPVTG") == 0) - { - - /* Speed and direction but NO location! */ - - char *ptcourse; /* True course, degrees. */ - char *pt; /* "T" */ - char *pmcourse; /* Magnetic course, degrees. */ - char *pm; /* "M" */ - char *pknots; /* Ground speed, knots. */ - char *pn; /* "N" = Knots */ - char *pkmh; /* Ground speed, km/hr */ - char *pk; /* "K" = Kilometers per hour */ - char *pmode; /* New in NMEA 0183 version 3.0 */ - /* Mode: A=Autonomous, D=Differential, */ - - ptcourse = strsep(&next, ","); - pt = strsep(&next, ","); - pmcourse = strsep(&next, ","); - pm = strsep(&next, ","); - pknots = strsep(&next, ","); - pn = strsep(&next, ","); - pkmh = strsep(&next, ","); - pk = strsep(&next, ","); - pmode = strsep(&next, ","); - - if (pknots != NULL && strlen(pknots) > 0) { - A->g_speed = DW_KNOTS_TO_MPH(atof(pknots)); - } - else { - text_color_set (DW_COLOR_ERROR); - dw_printf("Incomplete speed in sentence.\n"); - } - - if (ptcourse != NULL && strlen(ptcourse) > 0) { - A->g_course = atof(ptcourse); - } - else { - text_color_set (DW_COLOR_ERROR); - dw_printf("Incomplete course in sentence.\n"); - } - - } - else if (strcmp(ptype, "$GPWPL") == 0) - { - - char *plat; /* Latitude */ - char *pns; /* North/South */ - char *plon; /* Longitude */ - char *pew; /* East/West */ - char *pident; /* Identifier for Waypoint. rules??? */ - /* checksum */ - - plat = strsep(&next, ","); - pns = strsep(&next, ","); - plon = strsep(&next, ","); - pew = strsep(&next, ","); - pident = strsep(&next, ","); - - if (plat != NULL && strlen(plat) > 0 && pns != NULL && strlen(pns) > 0) { - A->g_lat = latitude_from_nmea(plat, pns); - } - else { - text_color_set (DW_COLOR_ERROR); - dw_printf("Incomplete latitude in sentence.\n"); - } - - if (plon != NULL && strlen(plon) > 0 && pew != NULL && strlen(pew) > 0) { - A->g_lon = longitude_from_nmea(plon, pew); - } - else { - text_color_set (DW_COLOR_ERROR); - dw_printf("Incomplete longitude in sentence.\n"); - } - - /* do something with identifier? */ - - } -} +} /* end aprs_raw_nmea */ @@ -1535,7 +1292,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int n = ((p->speed_course[0] - 28) * 10) + ((p->speed_course[1] - 28) / 10); if (n >= 800) n -= 800; - A->g_speed = DW_KNOTS_TO_MPH(n); + A->g_speed_mph = DW_KNOTS_TO_MPH(n); n = ((p->speed_course[1] - 28) % 10) * 100 + (p->speed_course[2] - 28); if (n >= 400) n -= 400; @@ -1627,16 +1384,16 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int if (plast > pfirst && pfirst[3] == '}') { - A->g_altitude = DW_METERS_TO_FEET((pfirst[0]-33)*91*91 + (pfirst[1]-33)*91 + (pfirst[2]-33) - 10000); + A->g_altitude_ft = DW_METERS_TO_FEET((pfirst[0]-33)*91*91 + (pfirst[1]-33)*91 + (pfirst[2]-33) - 10000); if ( ! isdigit91(pfirst[0]) || ! isdigit91(pfirst[1]) || ! isdigit91(pfirst[2])) { if ( ! A->g_quiet) { text_color_set(DW_COLOR_ERROR); dw_printf("Invalid character in MIC-E altitude. Must be in range of '!' to '{'.\n"); - dw_printf("Bogus altitude of %.0f changed to unknown.\n", A->g_altitude); + dw_printf("Bogus altitude of %.0f changed to unknown.\n", A->g_altitude_ft); } - A->g_altitude = G_UNKNOWN; + A->g_altitude_ft = G_UNKNOWN; } pfirst += 4; @@ -1776,7 +1533,7 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q * Inputs: info - Pointer to Information field. * ilen - Information field length. * - * Outputs: A->g_object_name, A->g_lat, A->g_lon, A->g_symbol_table, A->g_symbol_code, A->g_speed, A->g_course, A->g_altitude. + * Outputs: A->g_object_name, A->g_lat, A->g_lon, A->g_symbol_table, A->g_symbol_code, A->g_speed_mph, A->g_course, A->g_altitude_ft. * * Description: Message has a 9 character object name which could be quite different than * the source station. @@ -1878,7 +1635,9 @@ static void aprs_object (decode_aprs_t *A, unsigned char *info, int ilen) } } -} + (void)(ts); + +} /* end aprs_object */ /*------------------------------------------------------------------ @@ -1890,7 +1649,7 @@ static void aprs_object (decode_aprs_t *A, unsigned char *info, int ilen) * Inputs: info - Pointer to Information field. * ilen - Information field length. * - * Outputs: A->g_object_name, A->g_lat, A->g_lon, A->g_symbol_table, A->g_symbol_code, A->g_speed, A->g_course, A->g_altitude. + * Outputs: A->g_object_name, A->g_lat, A->g_lon, A->g_symbol_table, A->g_symbol_code, A->g_speed_mph, A->g_course, A->g_altitude_ft. * * Description: An "item" is very much like an "object" except * @@ -1928,13 +1687,13 @@ static void aprs_item (decode_aprs_t *A, unsigned char *info, int ilen) } *q; - time_t ts = 0; int i; char *ppos; p = (struct aprs_item_s *)info; q = (struct aprs_compressed_item_s *)info; + (void)(q); memset (A->g_name, 0, sizeof(A->g_name)); i = 0; @@ -2209,10 +1968,13 @@ static void aprs_status_report (decode_aprs_t *A, char *info, int ilen) erp = (p - '0') * (p - '0') * 10; } - // TODO: put result somewhere. + // TODO (low): put result somewhere. // could use A->g_directivity and need new variable for erp. *hp = '\0'; + + (void)(beam); + (void)(erp); } } @@ -2406,10 +2168,10 @@ static void aprs_general_query (decode_aprs_t *A, char *info, int ilen, int quie static void aprs_directed_station_query (decode_aprs_t *A, char *addressee, char *query, int quiet) { - char query_type[20]; /* Does the query type always need to be exactly 5 characters? */ + //char query_type[20]; /* Does the query type always need to be exactly 5 characters? */ /* If not, how would we know where the extra optional information starts? */ - char callsign[AX25_MAX_ADDR_LEN]; + //char callsign[AX25_MAX_ADDR_LEN]; //if (strlen(query) < 5) ... @@ -2428,7 +2190,8 @@ static void aprs_directed_station_query (decode_aprs_t *A, char *addressee, char * ilen - Information field length. * quiet - suppress error messages. * - * Outputs: ??? + * Outputs: A->g_telemetry + * A->g_comment * * Description: TBD. * @@ -2444,7 +2207,7 @@ static void aprs_telemetry (decode_aprs_t *A, char *info, int ilen, int quiet) strlcpy (A->g_msg_type, "Telemetry", sizeof(A->g_msg_type)); - telemetry_data_original (A->g_src, info, quiet, A->g_telemetry, A->g_comment); + telemetry_data_original (A->g_src, info, quiet, A->g_telemetry, sizeof(A->g_telemetry), A->g_comment, sizeof(A->g_comment)); } /* end aprs_telemetry */ @@ -2542,7 +2305,7 @@ static void aprs_positionless_weather_report (decode_aprs_t *A, unsigned char *i strlcpy (A->g_msg_type, "Positionless Weather Report", sizeof(A->g_msg_type)); - time_t ts = 0; + //time_t ts = 0; p = (struct aprs_positionless_weather_s *)info; @@ -2569,7 +2332,7 @@ static void aprs_positionless_weather_report (decode_aprs_t *A, unsigned char *i * TODO: call this context instead and have 3 enumerated values. * * Global In: A->g_course - Wind info for compressed location. - * A->g_speed + * A->g_speed_mph * * Outputs: A->g_weather * @@ -2578,7 +2341,7 @@ static void aprs_positionless_weather_report (decode_aprs_t *A, unsigned char *i * For human-readable locations, we expect wind direction * and speed in a format like this: 999/999. * For compressed location, this has already been - * processed and put in A->g_course and A->g_speed. + * processed and put in A->g_course and A->g_speed_mph. * Otherwise, for positionless weather data, the * wind is in the form c999s999. * @@ -2661,11 +2424,11 @@ static void weather_data (decode_aprs_t *A, char *wdata, int wind_prefix) } if (sscanf (wp+4, "%3d", &n)) { - A->g_speed = DW_KNOTS_TO_MPH(n); /* yes, in knots */ + A->g_speed_mph = DW_KNOTS_TO_MPH(n); /* yes, in knots */ } wp += 7; } - else if ( A->g_speed == G_UNKNOWN) { + else if ( A->g_speed_mph == G_UNKNOWN) { if ( ! getwdata (&wp, 'c', 3, &A->g_course)) { if ( ! A->g_quiet) { @@ -2673,7 +2436,7 @@ static void weather_data (decode_aprs_t *A, char *wdata, int wind_prefix) dw_printf("Didn't find wind direction in form c999.\n"); } } - if ( ! getwdata (&wp, 's', 3, &A->g_speed)) { /* MPH here */ + if ( ! getwdata (&wp, 's', 3, &A->g_speed_mph)) { /* MPH here */ if ( ! A->g_quiet) { text_color_set(DW_COLOR_ERROR); dw_printf("Didn't find wind speed in form s999.\n"); @@ -2684,9 +2447,9 @@ static void weather_data (decode_aprs_t *A, char *wdata, int wind_prefix) // At this point, we should have the wind direction and speed // from one of three methods. - if (A->g_speed != G_UNKNOWN) { + if (A->g_speed_mph != G_UNKNOWN) { - snprintf (A->g_weather, sizeof(A->g_weather), "wind %.1f mph", A->g_speed); + snprintf (A->g_weather, sizeof(A->g_weather), "wind %.1f mph", A->g_speed_mph); if (A->g_course != G_UNKNOWN) { char ctemp[40]; snprintf (ctemp, sizeof(ctemp), ", direction %.0f", A->g_course); @@ -2695,7 +2458,7 @@ static void weather_data (decode_aprs_t *A, char *wdata, int wind_prefix) } /* We don't want this to show up on the location line. */ - A->g_speed = G_UNKNOWN; + A->g_speed_mph = G_UNKNOWN; A->g_course = G_UNKNOWN; /* @@ -3041,7 +2804,6 @@ static void aprs_ultimeter (decode_aprs_t *A, char *info, int ilen) static void third_party_header (decode_aprs_t *A, char *info, int ilen) { - int n; strlcpy (A->g_msg_type, "Third Party Header", sizeof(A->g_msg_type)); @@ -3095,7 +2857,7 @@ static void decode_position (decode_aprs_t *A, position_t *ppos) * * One of the following: * A->g_course & A->g_speeed - * A->g_altitude + * A->g_altitude_ft * A->g_range * * Description: The compressed position provides resolution of around ??? @@ -3169,7 +2931,7 @@ static void decode_compressed_position (decode_aprs_t *A, compressed_position_t ; /* ignore other two bytes */ } else if (((pcpos->t - 33) & 0x18) == 0x10) { - A->g_altitude = pow(1.002, (pcpos->c - 33) * 91 + pcpos->s - 33); + A->g_altitude_ft = pow(1.002, (pcpos->c - 33) * 91 + pcpos->s - 33); } else if (pcpos->c == '{') { @@ -3179,7 +2941,7 @@ static void decode_compressed_position (decode_aprs_t *A, compressed_position_t { /* For a weather station, this is wind information. */ A->g_course = (pcpos->c - 33) * 4; - A->g_speed = DW_KNOTS_TO_MPH(pow(1.08, pcpos->s - 33) - 1.0); + A->g_speed_mph = DW_KNOTS_TO_MPH(pow(1.08, pcpos->s - 33) - 1.0); } } @@ -3720,7 +3482,7 @@ int get_maidenhead (decode_aprs_t *A, char *p) * Outputs: One or more of the following, depending the data found: * * A->g_course - * A->g_speed + * A->g_speed_mph * A->g_power * A->g_height * A->g_gain @@ -3771,7 +3533,7 @@ static int data_extension_comment (decode_aprs_t *A, char *pdext) } if (sscanf (pdext+4, "%3d", &n)) { - A->g_speed = DW_KNOTS_TO_MPH(n); + A->g_speed_mph = DW_KNOTS_TO_MPH(n); } /* Bearing and Number/Range/Quality? */ @@ -3878,8 +3640,11 @@ static const char *search_locations[] = { (const char *) NULL }; -static int tocall_cmp (const struct tocalls_s *x, const struct tocalls_s *y) +static int tocall_cmp (const void *px, const void *py) { + const struct tocalls_s *x = (struct tocalls_s *)px; + const struct tocalls_s *y = (struct tocalls_s *)py; + if (x->len != y->len) return (y->len - x->len); return (strcmp(x->prefix, y->prefix)); } @@ -3933,7 +3698,7 @@ static void decode_tocall (decode_aprs_t *A, char *dest) *p-- = '\0'; } - // printf("debug: %s\n", stuff); + // dw_printf("debug: %s\n", stuff); if (stuff[0] == ' ' && stuff[4] == ' ' && @@ -4065,7 +3830,7 @@ static void substr_se (char *dest, const char *src, int start, int endp1) * clen - Length of comment or -1 to take it all. * * Outputs: A->g_telemetry - Base 91 telemetry |ss1122| - * A->g_altitude - from /A=123456 + * A->g_altitude_ft - from /A=123456 * A->g_lat - Might be adjusted from !DAO! * A->g_lon - Might be adjusted from !DAO! * A->g_aprstt_loc - Private extension to !DAO! @@ -4381,7 +4146,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen) } else if (regexec (&std_toff_re, A->g_comment, MAXMATCH, match, 0) == 0) { - printf ("NO tone\n"); + dw_printf ("NO tone\n"); A->g_tone = 0; strlcpy (temp, A->g_comment + match[0].rm_eo, sizeof(temp)); @@ -4390,8 +4155,6 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen) else if (regexec (&std_dcs_re, A->g_comment, MAXMATCH, match, 0) == 0) { char sttemp[10]; /* three octal digits */ - int f; - int i; substr_se (sttemp, A->g_comment, match[1].rm_so, match[1].rm_eo); @@ -4403,7 +4166,6 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen) else if (regexec (&std_offset_re, A->g_comment, MAXMATCH, match, 0) == 0) { char sttemp[10]; /* includes leading sign */ - int f; substr_se (sttemp, A->g_comment, match[1].rm_so, match[1].rm_eo); @@ -4416,7 +4178,6 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen) char sttemp[10]; /* should be two digits */ char sutemp[10]; /* m for miles or k for km */ - int f; substr_se (sttemp, A->g_comment, match[1].rm_so, match[1].rm_eo); substr_se (sutemp, A->g_comment, match[2].rm_so, match[2].rm_eo); @@ -4453,7 +4214,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen) //dw_printf("compressed telemetry data = \"%s\"\n", tdata); - telemetry_data_base91 (A->g_src, tdata, A->g_telemetry); + telemetry_data_base91 (A->g_src, tdata, A->g_telemetry, sizeof(A->g_telemetry)); strlcpy (temp, A->g_comment + match[0].rm_eo, sizeof(temp)); strlcpy (A->g_comment + match[0].rm_so, temp, sizeof(A->g_comment)-match[0].rm_so); @@ -4558,7 +4319,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen) strlcpy (temp, A->g_comment + match[0].rm_eo, sizeof(temp)); A->g_comment[match[0].rm_eo] = '\0'; - A->g_altitude = atoi(A->g_comment + match[0].rm_so + 3); + A->g_altitude_ft = atoi(A->g_comment + match[0].rm_so + 3); strlcpy (A->g_comment + match[0].rm_so, temp, sizeof(A->g_comment)-match[0].rm_so); } @@ -4586,7 +4347,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen) (x >= 902 && x <= 928)) { if ( ! A->g_quiet) { - sprintf (good, "%07.3fMHz", x); + snprintf (good, sizeof(good), "%07.3fMHz", x); text_color_set(DW_COLOR_ERROR); dw_printf("\"%s\" in comment looks like a frequency in non-standard format.\n", bad); dw_printf("For most systems to recognize it, use exactly this form \"%s\" at beginning of comment.\n", good); @@ -4680,7 +4441,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen) * * Description: Compile like this to make a standalone test program. * - * gcc -o decode_aprs -DTEST decode_aprs.c ax25_pad.c + * gcc -o decode_aprs -DDECAMAIN decode_aprs.c ax25_pad.c ... * * ./decode_aprs < decode_aprs.txt * @@ -4709,7 +4470,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen) * *------------------------------------------------------------------*/ -#if TEST +#if DECAMAIN /* Stub for stand-alone decoder. */ @@ -4809,6 +4570,6 @@ int main (int argc, char *argv[]) return (0); } -#endif /* TEST */ +#endif /* DECAMAIN */ /* end decode_aprs.c */ diff --git a/decode_aprs.h b/decode_aprs.h index 6e0ce1f..a767ed2 100644 --- a/decode_aprs.h +++ b/decode_aprs.h @@ -64,7 +64,7 @@ typedef struct decode_aprs_s { /* Also for Directed Station Query which is a */ /* special case of message. */ - float g_speed; /* Speed in MPH. */ + float g_speed_mph; /* Speed in MPH. */ float g_course; /* 0 = North, 90 = East, etc. */ @@ -78,7 +78,7 @@ typedef struct decode_aprs_s { float g_range; /* Precomputed radio range in miles. */ - float g_altitude; /* Feet above median sea level. */ + float g_altitude_ft; /* Feet above median sea level. */ char g_mfr[80]; /* Manufacturer or application. */ diff --git a/demod.c b/demod.c index b11a600..8560e71 100644 --- a/demod.c +++ b/demod.c @@ -98,7 +98,7 @@ static int sample_count[MAX_CHANS][MAX_SUBCHANS]; int demod_init (struct audio_s *pa) { - int j; + //int j; int chan; /* Loop index over number of radio channels. */ char profile; @@ -184,7 +184,7 @@ int demod_init (struct audio_s *pa) /* This has been optimized for 300 baud. */ - strcpy (just_letters, "D"); + strlcpy (just_letters, "D", sizeof(just_letters)); } else { @@ -193,7 +193,7 @@ int demod_init (struct audio_s *pa) /* Previously we would use F if possible otherwise fall back to A. */ /* In version 1.2, new default is E+ /3. */ - strcpy (just_letters, "E"); // version 1.2 now E. + strlcpy (just_letters, "E", sizeof(just_letters)); // version 1.2 now E. if (have_plus != -1) have_plus = 1; // Add as default for version 1.2 // If not explicitly turned off. if (save_audio_config_p->achan[chan].decimate == 0) { @@ -202,7 +202,7 @@ int demod_init (struct audio_s *pa) } } #else - strcpy (just_letters, "E"); // version 1.2 changed C to E. + strlcpy (just_letters, "E", sizeof(just_letters)); // version 1.2 changed C to E. if (have_plus != -1) have_plus = 1; // Add as default for version 1.2 // If not explicitly turned off. #endif @@ -223,12 +223,12 @@ int demod_init (struct audio_s *pa) if (have_plus == -1) have_plus = 0; - strcpy (save_audio_config_p->achan[chan].profiles, just_letters); + strlcpy (save_audio_config_p->achan[chan].profiles, just_letters, sizeof(save_audio_config_p->achan[chan].profiles)); assert (strlen(save_audio_config_p->achan[chan].profiles) >= 1); if (have_plus) { - strcat (save_audio_config_p->achan[chan].profiles, "+"); + strlcat (save_audio_config_p->achan[chan].profiles, "+", sizeof(save_audio_config_p->achan[chan].profiles)); } /* These can be increased later for the multi-frequency case. */ @@ -250,7 +250,7 @@ int demod_init (struct audio_s *pa) text_color_set(DW_COLOR_ERROR); dw_printf ("Channel %d: Demodulator + option can't be combined with multiple letters.\n", chan); - strcpy (save_audio_config_p->achan[chan].profiles, "C+"); // Reduce to one letter. + strlcpy (save_audio_config_p->achan[chan].profiles, "C+", sizeof(save_audio_config_p->achan[chan].profiles)); // Reduce to one letter. num_letters = 1; save_audio_config_p->achan[chan].num_demod = 1; save_audio_config_p->achan[chan].num_subchan = 1; // Will be set higher later. @@ -478,7 +478,7 @@ int demod_init (struct audio_s *pa) /* Need to take a look at CPU usage and performance difference. */ #ifndef __arm__ - strcpy (save_audio_config_p->achan[chan].profiles, "+"); + strlcpy (save_audio_config_p->achan[chan].profiles, "+", sizeof(save_audio_config_p->achan[chan].profiles)); #endif } @@ -641,7 +641,8 @@ int demod_get_sample (int a) __attribute__((hot)) void demod_process_sample (int chan, int subchan, int sam) { - float fsam, abs_fsam; + float fsam; + //float abs_fsam; int k; @@ -650,8 +651,8 @@ void demod_process_sample (int chan, int subchan, int sam) static int seq = 0; /* for log file name */ #endif - int j; - int demod_data; + //int j; + //int demod_data; struct demodulator_state_s *D; assert (chan >= 0 && chan < MAX_CHANS); @@ -786,7 +787,6 @@ alevel_t demod_get_audio_level (int chan, int subchan) { struct demodulator_state_s *D; alevel_t alevel; - int pk; assert (chan >= 0 && chan < MAX_CHANS); assert (subchan >= 0 && subchan < MAX_SUBCHANS); diff --git a/demod_9600.c b/demod_9600.c index 264cf20..e4ebfa6 100644 --- a/demod_9600.c +++ b/demod_9600.c @@ -56,7 +56,7 @@ static float slice_point[MAX_SUBCHANS]; /* Add sample to buffer and shift the rest down. */ -__attribute__((hot)) +__attribute__((hot)) __attribute__((always_inline)) static inline void push_sample (float val, float *buff, int size) { memmove(buff+1,buff,(size-1)*sizeof(float)); @@ -66,7 +66,7 @@ static inline void push_sample (float val, float *buff, int size) /* FIR filter kernel. */ -__attribute__((hot)) +__attribute__((hot)) __attribute__((always_inline)) static inline float convolve (const float *__restrict__ data, const float *__restrict__ filter, int filter_size) { float sum = 0.0f; @@ -81,10 +81,12 @@ static inline float convolve (const float *__restrict__ data, const float *__res float *d = __builtin_assume_aligned(data, 16); float *f = __builtin_assume_aligned(filter, 16); +#pragma GCC ivdep for (j=0; j= *ppeak) { @@ -264,7 +266,7 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D { float fsam; - float abs_fsam; + //float abs_fsam; float amp; float demod_out; @@ -273,7 +275,7 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D static int seq = 0; /* for log file name */ #endif - int j; + //int j; int subchan = 0; int demod_data; /* Still scrambled. */ @@ -397,7 +399,6 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D __attribute__((hot)) static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator_state_s *D) { - int descram; /* Data bit de-scrambled. */ /* * Next, a PLL is used to sample near the centers of the data bits. @@ -439,22 +440,12 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator * * http://www.amsat.org/amsat/articles/g3ruh/109/fig03.gif */ + // Warning: 'descram' set but not used. + // It's used in conditional debug code below. + // descram = + descramble (demod_data, &(D->slicer[subchan].lfsr)); - //assert (modem.modem_type[chan] == MODEM_SCRAMBLE); - - //if (modem.modem_type[chan] == MODEM_SCRAMBLE) { - - - descram = descramble (demod_data, &(D->slicer[subchan].lfsr)); - - hdlc_rec_bit (chan, subchan, demod_data, 1, D->slicer[subchan].lfsr); - - //D->prev_descram = descram; - //} - //else { - /* Baseband signal for completeness - not in common use. */ - //hdlc_rec_bit (chan, subchan, demod_data); - //} + hdlc_rec_bit (chan, subchan, demod_data, 1, D->slicer[subchan].lfsr); } if (demod_data != D->slicer[subchan].prev_demod_data) { @@ -480,7 +471,7 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator if (demod_log_fp == NULL) { seq++; - sprintf (fname, "demod96/%04d.csv", seq); + snprintf (fname, sizeof(fname), "demod96/%04d.csv", seq); if (seq == 1) mkdir ("demod96" #ifndef __WIN32__ , 0777 diff --git a/demod_afsk.c b/demod_afsk.c index 5214f8f..725dcb1 100644 --- a/demod_afsk.c +++ b/demod_afsk.c @@ -72,7 +72,7 @@ /* Should help with microcomputer platform. */ -__attribute__((hot)) +__attribute__((hot)) __attribute__((always_inline)) static inline float z (float x, float y) { x = fabsf(x); @@ -88,7 +88,7 @@ static inline float z (float x, float y) /* Add sample to buffer and shift the rest down. */ -__attribute__((hot)) +__attribute__((hot)) __attribute__((always_inline)) static inline void push_sample (float val, float *buff, int size) { memmove(buff+1,buff,(size-1)*sizeof(float)); @@ -98,7 +98,7 @@ static inline void push_sample (float val, float *buff, int size) /* FIR filter kernel. */ -__attribute__((hot)) +__attribute__((hot)) __attribute__((always_inline)) static inline float convolve (const float *__restrict__ data, const float *__restrict__ filter, int filter_size) { float sum = 0.0f; @@ -113,10 +113,12 @@ static inline float convolve (const float *__restrict__ data, const float *__res float *d = __builtin_assume_aligned(data, 16); float *f = __builtin_assume_aligned(filter, 16); +#pragma GCC ivdep for (j=0; j= *ppeak) { @@ -795,7 +797,8 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator __attribute__((hot)) void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulator_state_s *D) { - float fsam, abs_fsam; + float fsam; + //float abs_fsam; float m_sum1, m_sum2, s_sum1, s_sum2; float m_amp, s_amp; float m_norm, s_norm; @@ -806,7 +809,7 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat #endif - int j; + //int j; int demod_data; @@ -827,7 +830,7 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat fsam = sam / 16384.0f; - abs_fsam = fsam >= 0.0f ? fsam : -fsam; + //abs_fsam = fsam >= 0.0f ? fsam : -fsam; /* @@ -1049,7 +1052,7 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat if (demod_log_fp == NULL) { seq++; - sprintf (fname, "demod/%04d.csv", seq); + snprintf (fname, sizeof(fname), "demod/%04d.csv", seq); if (seq == 1) mkdir ("demod", 0777); demod_log_fp = fopen (fname, "w"); diff --git a/digipeater.c b/digipeater.c index 19d1c46..7e90d91 100644 --- a/digipeater.c +++ b/digipeater.c @@ -532,6 +532,9 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch * Returns: None. * * Description: TODO... + * + * Initial reports were favorable. + * Should document what this is all about if there is still interest... * *------------------------------------------------------------------------------*/ @@ -569,7 +572,7 @@ void digi_regen (int from_chan, packet_t pp) * *------------------------------------------------------------------------*/ -#if TEST +#if DIGITEST static char mycall[] = "WB2OSZ-9"; @@ -594,6 +597,7 @@ static void test (char *in, char *out) int info_len; unsigned char frame[AX25_MAX_PACKET_LEN]; int frame_len; + alevel_t alevel; dw_printf ("\n"); @@ -606,7 +610,7 @@ static void test (char *in, char *out) ax25_format_addrs (pp, rec); info_len = ax25_get_info (pp, &pinfo); - strcat (rec, (char*)pinfo); + strlcat (rec, (char*)pinfo, sizeof(rec)); if (strcmp(in, rec) != 0) { text_color_set(DW_COLOR_ERROR); @@ -621,11 +625,15 @@ static void test (char *in, char *out) frame_len = ax25_pack (pp, frame); ax25_delete (pp); - pp = ax25_from_frame (frame, frame_len, 50); + alevel.rec = 50; + alevel.mark = 50; + alevel.space = 50; + + pp = ax25_from_frame (frame, frame_len, alevel); assert (pp != NULL); ax25_format_addrs (pp, rec); info_len = ax25_get_info (pp, &pinfo); - strcat (rec, (char*)pinfo); + strlcat (rec, (char*)pinfo, sizeof(rec)); if (strcmp(in, rec) != 0) { text_color_set(DW_COLOR_ERROR); @@ -648,11 +656,11 @@ static void test (char *in, char *out) dedupe_remember (result, 0); ax25_format_addrs (result, xmit); info_len = ax25_get_info (result, &pinfo); - strcat (xmit, (char*)pinfo); + strlcat (xmit, (char*)pinfo, sizeof(xmit)); ax25_delete (result); } else { - strcpy (xmit, ""); + strlcpy (xmit, "", sizeof(xmit)); } text_color_set(DW_COLOR_XMIT); @@ -894,7 +902,7 @@ int main (int argc, char *argv[]) * Filtering integrated with rest of process... */ - strcpy (typefilter, "w"); + strlcpy (typefilter, "w", sizeof(typefilter)); test ( "N8VIM>APN391,WIDE2-1:$ULTW00000000010E097D2884FFF389DC000102430002033400000000", "N8VIM>APN391,WB2OSZ-9*:$ULTW00000000010E097D2884FFF389DC000102430002033400000000"); @@ -902,7 +910,7 @@ int main (int argc, char *argv[]) test ( "AB1OC-10>APWW10,WIDE1-1,WIDE2-1:>FN42er/# Hollis, NH iGate Operational", ""); - strcpy (typefilter, "s"); + strlcpy (typefilter, "s", sizeof(typefilter)); test ( "AB1OC-10>APWW10,WIDE1-1,WIDE2-1:>FN42er/# Hollis, NH iGate Operational", "AB1OC-10>APWW10,WB2OSZ-9*,WIDE2-1:>FN42er/# Hollis, NH iGate Operational"); @@ -910,12 +918,12 @@ int main (int argc, char *argv[]) test ( "K1ABC-9>TR4R8R,WIDE1-1:`c6LlIb>/`\"4K}_%", ""); - strcpy (typefilter, "up"); + strlcpy (typefilter, "up", sizeof(typefilter)); test ( "K1ABC-9>TR4R8R,WIDE1-1:`c6LlIb>/`\"4K}_%", "K1ABC-9>TR4R8R,WB2OSZ-9*:`c6LlIb>/`\"4K}_%"); - strcpy (typefilter, ""); + strlcpy (typefilter, "", sizeof(typefilter)); #endif /* @@ -934,6 +942,6 @@ int main (int argc, char *argv[]) } /* end main */ -#endif /* if TEST */ +#endif /* if DIGITEST */ /* end digipeater.c */ diff --git a/direwolf.c b/direwolf.c index 1390bb9..51e531e 100644 --- a/direwolf.c +++ b/direwolf.c @@ -164,7 +164,7 @@ static struct misc_config_s misc_config; int main (int argc, char *argv[]) { int err; - int eof; + //int eof; int j; char config_file[100]; int xmit_calibrate_option = 0; @@ -180,6 +180,7 @@ int main (int argc, char *argv[]) int d_k_opt = 0; /* "-d k" option for serial port KISS. Can be repeated for more detail. */ int d_n_opt = 0; /* "-d n" option for Network KISS. Can be repeated for more detail. */ int d_t_opt = 0; /* "-d t" option for Tracker. Can be repeated for more detail. */ + int d_g_opt = 0; /* "-d g" option for GPS. Can be repeated for more detail. */ int d_o_opt = 0; /* "-d o" option for output control such as PTT and DCD. */ int a_opt = 0; /* "-a n" interval, in seconds, for audio statistics report. 0 for none. */ @@ -199,12 +200,6 @@ int main (int argc, char *argv[]) //Restore on exit? oldcp = GetConsoleOutputCP(); SetConsoleOutputCP(CP_UTF8); -#elif __CYGWIN__ - -/* - * Without this, the ISO Latin 1 characters are displayed as gray boxes. - */ - //setenv ("LANG", "C.ISO-8859-1", 1); #else /* @@ -230,16 +225,17 @@ int main (int argc, char *argv[]) } // TODO: control development/beta/release by version.h instead of changing here. + // Print platform. This will provide more information when people send a copy the information displayed. text_color_init(t_opt); text_color_set(DW_COLOR_INFO); //dw_printf ("Dire Wolf version %d.%d (%s) Beta Test\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); - dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "F", __DATE__); + dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "G", __DATE__); //dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION); #if __WIN32__ - SetConsoleCtrlHandler (cleanup_win, TRUE); + SetConsoleCtrlHandler ((PHANDLER_ROUTINE)cleanup_win, TRUE); #else setlinebuf (stdout); signal (SIGINT, cleanup_linux); @@ -278,19 +274,7 @@ int main (int argc, char *argv[]) text_color_set(DW_COLOR_INFO); #endif -/* - * This has not been very well tested in 64 bit mode. - */ -#if 0 - if (sizeof(int) != 4 || sizeof(long) != 4 || sizeof(char *) != 4) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("------------------------------------------------------------------\n"); - dw_printf ("This might not work properly when compiled for a 64 bit target.\n"); - dw_printf ("It is recommended that you rebuild it with gcc -m32 option.\n"); - dw_printf ("------------------------------------------------------------------\n"); - } -#endif /* * Default location of configuration file is current directory. @@ -307,7 +291,7 @@ int main (int argc, char *argv[]) strlcpy (input_file, "", sizeof(input_file)); while (1) { - int this_option_optind = optind ? optind : 1; + //int this_option_optind = optind ? optind : 1; int option_index = 0; int c; char *p; @@ -448,6 +432,7 @@ int main (int argc, char *argv[]) // separate out gps & waypoints. + case 'g': d_g_opt++; break; case 't': d_t_opt++; beacon_tracker_set_debug (d_t_opt); break; case 'w': nmea_set_debug (1); break; // not documented yet. @@ -685,7 +670,9 @@ int main (int argc, char *argv[]) /* * Open port for communication with GPS. */ - nmea_init (&misc_config); + dwgps_init (&misc_config, d_g_opt); + + nmea_init (&misc_config); // TODO: revisit. /* * Create thread for trying to salvage frames with bad FCS. @@ -695,8 +682,8 @@ int main (int argc, char *argv[]) /* * Enable beaconing. */ - beacon_init (&audio_config, &misc_config, &digi_config); + beacon_init (&audio_config, &misc_config); log_init(misc_config.logdir); @@ -920,7 +907,7 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel if (A.g_lat != G_UNKNOWN && A.g_lon != G_UNKNOWN) { nmea_send_waypoint (strlen(A.g_name) > 0 ? A.g_name : A.g_src, A.g_lat, A.g_lon, A.g_symbol_table, A.g_symbol_code, - DW_FEET_TO_METERS(A.g_altitude), A.g_course, DW_MPH_TO_KNOTS(A.g_speed), + DW_FEET_TO_METERS(A.g_altitude_ft), A.g_course, DW_MPH_TO_KNOTS(A.g_speed_mph), A.g_comment); } } @@ -947,7 +934,7 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel */ if (subchan == -1) { if (tt_config.gateway_enabled && info_len >= 2) { - aprs_tt_sequence (chan, pinfo+1); + aprs_tt_sequence (chan, (char*)(pinfo+1)); } } else { @@ -1048,7 +1035,8 @@ static void usage (char **argv) dw_printf (" n n = KISS network client.\n"); dw_printf (" u u = Display non-ASCII text in hexadecimal.\n"); dw_printf (" p p = dump Packets in hexadecimal.\n"); - dw_printf (" t t = gps Tracker.\n"); + dw_printf (" g g = GPS interface.\n"); + dw_printf (" t t = Tracker beacon.\n"); dw_printf (" o o = output controls such as PTT and DCD.\n"); dw_printf (" -q Quiet (suppress output) options:\n"); dw_printf (" h h = Heard line with the audio level.\n"); diff --git a/direwolf.conf b/direwolf.conf index b564b3c..1e1af46 100644 --- a/direwolf.conf +++ b/direwolf.conf @@ -66,7 +66,7 @@ # To use something other than the default, generally use plughw # and a card number reported by "arecord -l" command. Example: -# ADEVICE plughw:1,0 +ADEVICE "Realtek High" # Starting with version 1.0, you can also use "-" or "stdin" to # pipe stdout from some other application such as a software defined @@ -93,8 +93,7 @@ ACHANNELS 1 # # ############################################################# -#ADEVICE1 ... - +#ADEVICE1 USB ############################################################# # # @@ -129,7 +128,7 @@ CHANNEL 0 # Example (don't use this unless you are me): MYCALL WB2OSZ-5 # -MYCALL N0CALL +MYCALL WB2OSZ-14 # # Pick a suitable modem speed based on your situation. @@ -166,7 +165,7 @@ MODEM 1200 # Uncomment line below to enable the DTMF decoder for this channel. # -#DTMF +DTMF # # If not using a VOX circuit, the transmitter Push to Talk (PTT) @@ -181,7 +180,7 @@ MODEM 1200 # #PTT COM1 RTS -#PTT COM1 RTS -DTR +PTT COM1 RTS -DTR #PTT /dev/ttyUSB0 RTS # @@ -213,6 +212,8 @@ MODEM 1200 # Specify MYCALL, MODEM, PTT, etc. configuration items for # CHANNEL 1. Repeat for any other channels. +#CHANNEL 2 +#DTMF ############################################################# # # @@ -260,6 +261,11 @@ KISSPORT 8001 # # ############################################################# +GPSNMEA COM22 + +TBEACON delay=0:30 every=0:20 SYMBOL=car FREQ=146.955 OFFSET=-0.600 TONE=74.4 + + # # Beaconing is configured with these two commands: diff --git a/direwolf.h b/direwolf.h index 07022a9..90dae23 100644 --- a/direwolf.h +++ b/direwolf.h @@ -3,7 +3,6 @@ #define DIREWOLF_H 1 - /* * Previously, we could handle only a single audio device. * This meant we could have only two radio channels. @@ -75,6 +74,10 @@ /* Prefix with DW_ because /usr/include/gps.h uses a couple of these names. */ +#ifndef G_UNKNOWN +#include "latlong.h" +#endif + #define DW_METERS_TO_FEET(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 3.2808399) #define DW_FEET_TO_METERS(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.3048) @@ -163,8 +166,10 @@ typedef pthread_mutex_t dw_mutex_t; /* Platform differences for string functions. */ + #if __WIN32__ char *strsep(char **stringp, const char *delim); +char *strtok_r(char *str, const char *delim, char **saveptr); #endif //#if __WIN32__ diff --git a/dlq.c b/dlq.c index f1703d9..c08294b 100644 --- a/dlq.c +++ b/dlq.c @@ -276,9 +276,9 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a pnew->alevel = alevel; pnew->retries = retries; if (spectrum == NULL) - strcpy(pnew->spectrum, ""); + strlcpy(pnew->spectrum, "", sizeof(pnew->spectrum)); else - strcpy(pnew->spectrum, spectrum); + strlcpy(pnew->spectrum, spectrum, sizeof(pnew->spectrum)); #if DEBUG1 text_color_set(DW_COLOR_DEBUG); @@ -524,7 +524,7 @@ void dlq_wait_while_empty (void) * *--------------------------------------------------------------------*/ -int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum) +int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum, size_t spectrumsize) { struct dlq_item_s *phead; @@ -562,7 +562,7 @@ int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_ memset (alevel, 0xff, sizeof(*alevel)); *retries = -1; - strcpy(spectrum,""); + strlcpy(spectrum, "", spectrumsize); result = 0; } else { @@ -576,7 +576,7 @@ int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_ *pp = phead->pp; *alevel = phead->alevel; *retries = phead->retries; - strcpy (spectrum, phead->spectrum); + strlcpy (spectrum, phead->spectrum, spectrumsize); result = 1; } diff --git a/dlq.h b/dlq.h index a80dc32..089f8e7 100644 --- a/dlq.h +++ b/dlq.h @@ -22,7 +22,7 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a void dlq_wait_while_empty (void); -int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum); +int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum, size_t spectrumsize); #endif diff --git a/doc/APRS-Telemetry-Toolkit.pdf b/doc/APRS-Telemetry-Toolkit.pdf index 4a41b72..dc53a2f 100644 Binary files a/doc/APRS-Telemetry-Toolkit.pdf and b/doc/APRS-Telemetry-Toolkit.pdf differ diff --git a/doc/APRStt-Implementation-Notes.pdf b/doc/APRStt-Implementation-Notes.pdf index 40004c9..db09920 100644 Binary files a/doc/APRStt-Implementation-Notes.pdf and b/doc/APRStt-Implementation-Notes.pdf differ diff --git a/doc/Raspberry-Pi-APRS-Tracker.pdf b/doc/Raspberry-Pi-APRS-Tracker.pdf index eefd30f..c684820 100644 Binary files a/doc/Raspberry-Pi-APRS-Tracker.pdf and b/doc/Raspberry-Pi-APRS-Tracker.pdf differ diff --git a/doc/Raspberry-Pi-APRS.pdf b/doc/Raspberry-Pi-APRS.pdf index 655de1b..13e21a5 100644 Binary files a/doc/Raspberry-Pi-APRS.pdf and b/doc/Raspberry-Pi-APRS.pdf differ diff --git a/doc/User-Guide.pdf b/doc/User-Guide.pdf index f0757b0..b768cbd 100644 Binary files a/doc/User-Guide.pdf and b/doc/User-Guide.pdf differ diff --git a/dtime_now.c b/dtime_now.c index 13c8835..c78fba8 100644 --- a/dtime_now.c +++ b/dtime_now.c @@ -1,5 +1,5 @@ - +#include "direwolf.h" #include "textcolor.h" #include "dtime_now.h" @@ -17,9 +17,7 @@ #include #endif -#if __WIN32__ -#include -#endif + diff --git a/dwgps.c b/dwgps.c index 8f7533a..f8cc37e 100644 --- a/dwgps.c +++ b/dwgps.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2013, 2014 John Langner, WB2OSZ +// Copyright (C) 2015 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,69 +22,67 @@ * * Module: dwgps.c * - * Purpose: Interface to location data, i.e. GPS receiver. + * Purpose: Interface for obtaining location from GPS. * - * Description: Tracker beacons need to know the current location. - * At this time, I can't think of any other reason why - * we would need this information. + * Description: This is a wrapper for two different implementations: * - * For Linux, we use gpsd and libgps. - * This has the extra benefit that the system clock can - * be set from the GPS signal. + * (1) Read NMEA sentences from a serial port (or USB + * that looks line one). Available for all platforms. * - * Not yet implemented for Windows. Not sure how yet. - * The Windows location API is new in Windows 7. - * At the end of 2013, about 1/3 of Windows users are - * still using XP so that still needs to be supported. + * (2) Read from gpsd. Not available for Windows. + * Including this is optional because it depends + * on another external software component. * - * Reference: + * + * API: dwgps_init Connect to data stream at start up time. + * + * dwgps_read Return most recent location to application. + * + * dwgps_print Print contents of structure for debugging. + * + * dwgps_term Shutdown on exit. + * + * + * from below: dwgps_set_data Called from other two implementations to + * save data until it is needed. * *---------------------------------------------------------------*/ -#if TEST -#define ENABLE_GPS 1 -#endif - - #include #include #include -#include #include -#include -#include - -#if __WIN32__ -#include -#else -#if ENABLE_GPS -#include - -#if GPSD_API_MAJOR_VERSION != 5 -#error libgps API version might be incompatible. -#endif - -#endif -#endif +#include #include "direwolf.h" #include "textcolor.h" #include "dwgps.h" +#include "dwgpsnmea.h" +#include "dwgpsd.h" -/* Was init successful? */ +static int s_dwgps_debug = 0; /* Enable debug output. */ + /* >= 2 show updates from GPS. */ + /* >= 1 show results from dwgps_read. */ -static enum { INIT_NOT_YET, INIT_SUCCESS, INIT_FAILED } init_status = INIT_NOT_YET; +/* + * The GPS reader threads deposit current data here when it becomes available. + * dwgps_read returns it to the requesting application. + * + * A critical region to avoid inconsistency between fields. + */ -#if __WIN32__ -#include -#else -#if ENABLE_GPS +static dwgps_info_t s_dwgps_info = { + .timestamp = 0, + .fix = DWFIX_NOT_INIT, /* to detect read without init. */ + .dlat = G_UNKNOWN, + .dlon = G_UNKNOWN, + .speed_knots = G_UNKNOWN, + .track = G_UNKNOWN, + .altitude = G_UNKNOWN +}; -static struct gps_data_t gpsdata; - -#endif -#endif +static dw_mutex_t s_gps_mutex; /*------------------------------------------------------------------- @@ -93,223 +91,125 @@ static struct gps_data_t gpsdata; * * Purpose: Intialize the GPS interface. * - * Inputs: none. - * - * Returns: 0 = success - * -1 = failure + * Inputs: pconfig Configuration settings. This might include + * serial port name for direct connect and host + * name or address for network connection. * - * Description: For Linux, this maps into gps_open. - * Not yet implemented for Windows. + * debug - If >= 1, print results when dwgps_read is called. + * (In this file.) + * + * If >= 2, location updates are also printed. + * (In other two related files.) + * + * Returns: none + * + * Description: Call corresponding functions for implementations. + * Normally we would expect someone to use either GPSNMEA or + * GPSD but there is nothing to prevent use of both at the + * same time. * *--------------------------------------------------------------------*/ -int dwgps_init (void) + +void dwgps_init (struct misc_config_s *pconfig, int debug) { -#if __WIN32__ + s_dwgps_debug = debug; -/* - * Windows version. Not implemented yet. - */ + dw_mutex_init (&s_gps_mutex); - text_color_set(DW_COLOR_ERROR); - dw_printf ("GPS interface not yet available in Windows version.\n"); - init_status = INIT_FAILED; - return (-1); + dwgpsnmea_init (pconfig, debug); -#elif ENABLE_GPS +#if ENABLE_GPSD - int err; - -#if USE_GPS_SHM - -/* - * Linux - Shared memory interface to gpsd. - * - * I wanted to use this method because it is simpler and more efficient. - * - * The current version of gpsd, supplied with Raspian, is 3.6 from back in - * May 2012, is missing support for the shared memory interface. - * https://github.com/raspberrypi/linux/issues/523 - * - * I tried to download a newer source and build with shared memory support - * but ran into a couple other issues. - * - * sudo apt-get install libncurses5-dev - * sudo apt-get install scons - * cd ~ - * wget http://download-mirror.savannah.gnu.org/releases/gpsd/gpsd-3.11.tar.gz - * tar xfz gpsd-3.11.tar.gz - * cd gpsd-3.11 - * scons prefix=/usr libdir=lib/arm-linux-gnueabihf shm_export=True python=False - * sudo scons udev-install - * - * For now, we will use the socket interface. - * Maybe get back to this again someday. - */ - - err = gps_open (GPSD_SHARED_MEMORY, NULL, &gpsdata); - if (err != 0) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Unable to connect to GPSD shared memory interface, status=%d.\n", err); - if (err == NL_NOHOST) { - // I don't think this is right but we are not using it anyhow. - dw_printf ("Shared memory interface is not enabled in libgps.\n"); - dw_printf ("Download the gpsd source and build with 'shm_export=True' option.\n"); - } - else { - dw_printf ("%s\n", gps_errstr(errno)); - } - init_status = INIT_FAILED; - return (-1); - } - init_status = INIT_SUCCESS; - return (0); - -#else - -/* - * Linux - Socket interface to gpsd. - */ - - err = gps_open ("localhost", DEFAULT_GPSD_PORT, &gpsdata); - if (err != 0) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Unable to connect to GPSD stream, status%d.\n", err); - dw_printf ("%s\n", gps_errstr(errno)); - init_status = INIT_FAILED; - return (-1); - } - - gps_stream(&gpsdata, WATCH_ENABLE | WATCH_JSON, NULL); - - init_status = INIT_SUCCESS; - return (0); - -#endif - -#else /* end ENABLE_GPS */ - - text_color_set(DW_COLOR_ERROR); - dw_printf ("GPS interface not enabled in this version.\n"); - dw_printf ("See documentation on how to rebuild with ENABLE_GPS.\n"); - init_status = INIT_FAILED; - return (-1); + dwgpsd_init (pconfig, debug); #endif -} /* end dwgps_init */ + SLEEP_MS(500); /* So receive thread(s) can clear the */ + /* not init status before it gets checked. */ +} /* end dwgps_init */ + + +/*------------------------------------------------------------------- + * + * Name: dwgps_clear + * + * Purpose: Clear the gps info structure. + * + *--------------------------------------------------------------------*/ + +void dwgps_clear (dwgps_info_t *gpsinfo) +{ + gpsinfo->timestamp = 0; + gpsinfo->fix = DWFIX_NOT_SEEN; + gpsinfo->dlat = G_UNKNOWN; + gpsinfo->dlon = G_UNKNOWN; + gpsinfo->speed_knots = G_UNKNOWN; + gpsinfo->track = G_UNKNOWN; + gpsinfo->altitude = G_UNKNOWN; +} /*------------------------------------------------------------------- * * Name: dwgps_read * - * Purpose: Obtain current location from GPS receiver. + * Purpose: Return most recent location data available. * - * Outputs: *plat - Latitude. - * *plon - Longitude. - * *pspeed - Speed, knots. - * *pcourse - Course over ground, degrees. - * *palt - Altitude, meters. + * Outputs: gpsinfo - Structure with latitude, longitude, etc. + * + * Returns: Position fix quality. Same as in structure. * - * Returns: -1 = error - * 0 = location currently not available (no fix) - * 2 = 2D fix, lat/lon, speed, and course are set. - * 3 - 3D fix, altitude is also set. * *--------------------------------------------------------------------*/ -int dwgps_read (double *plat, double *plon, float *pspeed, float *pcourse, float *palt) +dwfix_t dwgps_read (dwgps_info_t *gpsinfo) { -#if __WIN32__ - text_color_set(DW_COLOR_ERROR); - dw_printf ("Internal error, dwgps_read, shouldn't be here.\n"); - return (-1); + dw_mutex_lock (&s_gps_mutex); -#elif ENABLE_GPS + memcpy (gpsinfo, &s_dwgps_info, sizeof(*gpsinfo)); - int err; + dw_mutex_unlock (&s_gps_mutex); - if (init_status != INIT_SUCCESS) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Internal error, dwgps_read without successful init.\n"); - return (-1); + if (s_dwgps_debug >= 1) { + text_color_set (DW_COLOR_DEBUG); + dwgps_print ("gps_read: ", gpsinfo); } -#if USE_GPS_SHM + // TODO: Should we check timestamp and complain if very stale? + // or should we leave that up to the caller? -/* - * Shared memory version. - */ + return (s_dwgps_info.fix); +} - err = gps_read (&gpsdata); -#if DEBUG - dw_printf ("gps_read returns %d bytes\n", err); -#endif +/*------------------------------------------------------------------- + * + * Name: dwgps_print + * + * Purpose: Print gps information for debugging. + * + * Inputs: msg - Message for prefix on line. + * gpsinfo - Structure with latitude, longitude, etc. + * + * Description: Caller is responsible for setting text color. + * + *--------------------------------------------------------------------*/ -#else +void dwgps_print (char *msg, dwgps_info_t *gpsinfo) +{ -/* - * Socket version. - */ + dw_printf ("%stime=%d fix=%d lat=%.6f lon=%.6f trk=%.0f spd=%.1f alt=%.0f\n", + msg, + (int)gpsinfo->timestamp, (int)gpsinfo->fix, + gpsinfo->dlat, gpsinfo->dlon, + gpsinfo->track, gpsinfo->speed_knots, + gpsinfo->altitude); - // Wait for up to 1000 milliseconds. - // This should only happen in the beaconing thread so - // I'm not worried about other functions hanging. - - if (gps_waiting(&gpsdata, 1000)) { - - err = gps_read (&gpsdata); - } - else { - gps_stream(&gpsdata, WATCH_ENABLE | WATCH_JSON, NULL); - sleep (1); - } - -#endif - - if (err > 0) { - /* Data is available. */ - - if (gpsdata.status >= STATUS_FIX && gpsdata.fix.mode >= MODE_2D) { - - *plat = gpsdata.fix.latitude; - *plon = gpsdata.fix.longitude; - *pcourse = gpsdata.fix.track; - *pspeed = MPS_TO_KNOTS * gpsdata.fix.speed; /* libgps uses meters/sec */ - - if (gpsdata.fix.mode >= MODE_3D) { - *palt = gpsdata.fix.altitude; - return (3); - } - return (2); - } - - /* No fix. Probably temporary condition. */ - return (0); - } - else if (err == 0) { - - /* No data available at the present time. */ - return (0); - } - else { - - /* More serious error. */ - return (-1); - } -#else - - text_color_set(DW_COLOR_ERROR); - dw_printf ("Internal error, dwgps_read, shouldn't be here.\n"); - return (-1); -#endif - -} /* end dwgps_read */ +} /* end dwgps_set_data */ /*------------------------------------------------------------------- @@ -326,19 +226,10 @@ int dwgps_read (double *plat, double *plon, float *pspeed, float *pcourse, float void dwgps_term (void) { -#if __WIN32__ - -#elif ENABLE_GPS - - if (init_status == INIT_SUCCESS) { - -#ifndef USE_GPS_SHM - gps_stream(&gpsdata, WATCH_DISABLE, NULL); -#endif - gps_close (&gpsdata); - } -#else + dwgpsnmea_term (); +#if ENABLE_GPSD + dwgpsd_term (); #endif } /* end dwgps_term */ @@ -348,69 +239,27 @@ void dwgps_term (void) { /*------------------------------------------------------------------- * - * Name: main + * Name: dwgps_set_data * - * Purpose: Simple unit test for other functions in this file. + * Purpose: Called by the GPS interfaces when new data is available. * - * Description: Compile with -DTEST option. - * - * gcc -DTEST dwgps.c textcolor.c -lgps - * ./a.out + * Inputs: gpsinfo - Structure with latitude, longitude, etc. * *--------------------------------------------------------------------*/ -#if TEST - -int main (int argc, char *argv[]) +void dwgps_set_data (dwgps_info_t *gpsinfo) { -#if __WIN32__ + /* Debug print is handled by the two callers so */ + /* we can distinguish the source. */ - printf ("Not in win32 version yet.\n"); + dw_mutex_lock (&s_gps_mutex); -#elif ENABLE_GPS - int err; - int fix; - double lat; - double lon; - float speed; - float course; - float alt; + memcpy (&s_dwgps_info, gpsinfo, sizeof(s_dwgps_info)); - err = dwgps_init (); - - if (err != 0) exit(1); - - while (1) { - fix = dwgps_read (&lat, &lon, &speed, &course, &alt) ; - switch (fix) { - case 3: - case 2: - dw_printf ("%.6f %.6f", lat, lon); - dw_printf (" %.1f knots %.0f degrees", speed, course); - if (fix==3) dw_printf (" altitude = %.1f meters", alt); - dw_printf ("\n"); - break; - case 0: - dw_printf ("location currently not available.\n"); - break; - default: - dw_printf ("ERROR getting GPS information.\n"); - } - sleep (3); - } - - -#else - - printf ("Test: Shouldn't be here.\n"); -#endif - -} /* end main */ - - -#endif + dw_mutex_unlock (&s_gps_mutex); +} /* end dwgps_set_data */ /* end dwgps.c */ diff --git a/dwgps.h b/dwgps.h index 90aa342..78f821f 100644 --- a/dwgps.h +++ b/dwgps.h @@ -1,13 +1,59 @@ /* dwgps.h */ +#ifndef DWGPS_H +#define DWGPS_H 1 -int dwgps_init (void); -int dwgps_read (double *plat, double *plon, float *pspeed, float *pcourse, float *palt); +#include +#include "config.h" /* for struct misc_config_s */ + + +/* + * Values for fix, equivalent to values from libgps. + * -2 = not initialized. + * -1 = error communicating with GPS receiver. + * 0 = nothing heard yet. + * 1 = had signal but lost it. + * 2 = 2D. + * 3 = 3D. + * + * Undefined float & double values are set to G_UNKNOWN. + * + */ + +enum dwfix_e { DWFIX_NOT_INIT= -2, DWFIX_ERROR= -1, DWFIX_NOT_SEEN=0, DWFIX_NO_FIX=1, DWFIX_2D=2, DWFIX_3D=3 }; + +typedef enum dwfix_e dwfix_t; + +typedef struct dwgps_info_s { + time_t timestamp; /* When last updated. System time. */ + dwfix_t fix; /* Quality of position fix. */ + double dlat; /* Latitude. Valid if fix >= 2. */ + double dlon; /* Longitude. Valid if fix >= 2. */ + float speed_knots; /* libgps uses meters/sec but we use GPS usual knots. */ + float track; /* What is difference between track and course? */ + float altitude; /* meters above mean sea level. Valid if fix == 3. */ +} dwgps_info_t; + + + + + +void dwgps_init (struct misc_config_s *pconfig, int debug); + +void dwgps_clear (dwgps_info_t *gpsinfo); + +dwfix_t dwgps_read (dwgps_info_t *gpsinfo); + +void dwgps_print (char *msg, dwgps_info_t *gpsinfo); void dwgps_term (void); +void dwgps_set_data (dwgps_info_t *gpsinfo); + + +#endif /* DWGPS_H 1 */ /* end dwgps.h */ diff --git a/dwgpsd.c b/dwgpsd.c new file mode 100644 index 0000000..a3f4835 --- /dev/null +++ b/dwgpsd.c @@ -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 . +// + + +/*------------------------------------------------------------------ + * + * 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 +#include +#include +#include +#include +#include +#include + +#if __WIN32__ +#error Not for Windows +#endif + +#if ENABLE_GPSD +#include + +// 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 */ + + + diff --git a/dwgpsd.h b/dwgpsd.h new file mode 100644 index 0000000..4c0e0fd --- /dev/null +++ b/dwgpsd.h @@ -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 */ + + + diff --git a/dwgpsnmea.c b/dwgpsnmea.c new file mode 100644 index 0000000..0de0512 --- /dev/null +++ b/dwgpsnmea.c @@ -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 . +// + + +/*------------------------------------------------------------------ + * + * Module: dwgpsnmea.c + * + * Purpose: process NMEA sentences from a GPS receiver. + * + * Description: This version is available for all operating systems. + * + *---------------------------------------------------------------*/ + + +#include +#include +#include +#include +#include +#include +#include + + +#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 */ + + + diff --git a/dwgpsnmea.h b/dwgpsnmea.h new file mode 100644 index 0000000..a126a50 --- /dev/null +++ b/dwgpsnmea.h @@ -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 */ + + + diff --git a/encode_aprs.c b/encode_aprs.c index 792c0b8..e028738 100644 --- a/encode_aprs.c +++ b/encode_aprs.c @@ -116,7 +116,8 @@ static int set_norm_position (char symtab, char symbol, double dlat, double dlon * height - Feet. * 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. * * @@ -130,6 +131,9 @@ static int set_norm_position (char symtab, char symbol, double dlat, double dlon * radio range - calculated from PHG * 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. */ @@ -198,13 +202,18 @@ static int set_comp_position (char symtab, char symbol, double dlat, double dlon * When c is '{', s is range ... */ - if (course || speed) { + if (speed > 0) { int c; int s; - - c = (course + 1) / 4; - if (c < 0) c += 90; - if (c >= 90) c -= 90; + + if (course != G_UNKNOWN) { + c = (course + 2) / 4; + if (c < 0) c += 90; + if (c >= 90) c -= 90; + } + else { + c = 0; + } presult->c = c + '!'; 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. * 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. * * 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 { 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. * - * Inputs: course - Degress, 1 - 360. + * Inputs: course - Degress, 0 - 360 (360 equiv. to 0). + * Use G_UNKNOWN for none or unknown. + * * speed - knots. * * Outputs: presult - Stored here. * * 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]; int x; - x = course; - if (x < 0) x = 0; - if (x > 360) x = 360; + if (course != G_UNKNOWN) { + x = course; + while (x < 1) 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); memcpy (r->cse, stemp, 3); r->slash = '/'; x = speed; - if (x < 0) x = 0; + if (x < 0) x = 0; // would include G_UNKNOWN if (x > 999) x = 999; snprintf (stemp, sizeof(stemp), "%03d", x); 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. * dir - Directivity: N, NE, etc., omni. * - * course - Degress, 1 - 360. 0 means none or unknown. - * speed - knots. + * course - Degress, 0 - 360 (360 equiv. to 0). + * Use G_UNKNOWN for none or unknown. + * speed - knots. // TODO: should distinguish unknown(not revevant) vs. known zero. * * freq - MHz. * tone - Hz. @@ -487,6 +518,7 @@ static int frequency_spec (float freq, float tone, float offset, char *presult) * *----------------------------------------------------------------*/ + typedef struct aprs_ll_pos_s { char dti; /* ! or = */ position_t pos; @@ -533,10 +565,10 @@ int encode_position (int messaging, int compressed, double lat, double lon, int /* Optional data extension. (singular) */ /* 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); } - 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); } } @@ -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. * 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. * * freq - MHz. @@ -614,7 +647,7 @@ int encode_position (int messaging, int compressed, double lat, double lon, int * 36 for fixed part, * 7 for optional extended data, * ~20 for freq, etc., - * comment ... + * comment could be very long... * * 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) */ /* 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); } - 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); } } @@ -746,93 +779,97 @@ int encode_object (char *name, int compressed, time_t thyme, double lat, double int main (int argc, char *argv[]) { char result[100]; - + int errors = 0; /*********** Position ***********/ - encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&', - 0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result, sizeof(result)); + encode_position (0, 0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&', + 0, 0, 0, NULL, G_UNKNOWN, 0, 0, 0, 0, NULL, result, sizeof(result)); dw_printf ("%s\n", result); - if (strcmp(result, "!4234.61ND07126.47W&") != 0) dw_printf ("ERROR! line %d\n", __LINE__); + if (strcmp(result, "!4234.61ND07126.47W&") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; } /* 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', '&', - 50, 100, 6, "N", 0, 0, 0, 0, 0, NULL, result, sizeof(result)); + encode_position (0, 0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&', + 50, 100, 6, "N", G_UNKNOWN, 0, 0, 0, 0, NULL, result, sizeof(result)); dw_printf ("%s\n", result); - if (strcmp(result, "!4234.61ND07126.47W&PHG7368") != 0) dw_printf ("ERROR! line %d\n", __LINE__); + if (strcmp(result, "!4234.61ND07126.47W&PHG7368") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; } /* with freq. */ - encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&', - 0, 0, 0, NULL, 0, 0, 146.955, 74.4, -0.6, NULL, result, sizeof(result)); + encode_position (0, 0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&', + 0, 0, 0, NULL, G_UNKNOWN, 0, 146.955, 74.4, -0.6, NULL, result, sizeof(result)); dw_printf ("%s\n", result); - if (strcmp(result, "!4234.61ND07126.47W&146.955MHz T074 -060 ") != 0) dw_printf ("ERROR! line %d\n", __LINE__); + 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! */ - 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)); 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 */ - 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)); 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 */ - 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)); 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. ***********/ - encode_position (1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&', - 0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result, sizeof(result)); + encode_position (0, 1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&', + 0, 0, 0, NULL, G_UNKNOWN, 0, 0, 0, 0, NULL, result, sizeof(result)); dw_printf ("%s\n", result); - if (strcmp(result, "!D8yKCB:!D8yKCB:!D8yKC 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 */ diff --git a/encode_aprs.h b/encode_aprs.h index 942bf53..e26ab5a 100644 --- a/encode_aprs.h +++ b/encode_aprs.h @@ -2,7 +2,7 @@ int encode_position (int messaging, int compressed, double lat, double lon, int alt_ft, char symtab, char symbol, 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, 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, char symtab, char symbol, 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, char *presult, size_t result_size); diff --git a/gen_packets.c b/gen_packets.c index 8a1f12d..78ad964 100644 --- a/gen_packets.c +++ b/gen_packets.c @@ -118,7 +118,7 @@ static void send_packet (char *str) int main(int argc, char **argv) { int c; - int digit_optind = 0; + //int digit_optind = 0; int err; int packet_count = 0; int i; @@ -156,14 +156,14 @@ int main(int argc, char **argv) char output_file[256]; /* -o option */ 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. */ while (1) { - int this_option_optind = optind ? optind : 1; + //int this_option_optind = optind ? optind : 1; int option_index = 0; static struct option long_options[] = { {"future1", 1, 0, 0}, @@ -333,7 +333,7 @@ int main(int argc, char **argv) case 'o': /* -o for Output file */ - strcpy (output_file, optarg); + strlcpy (output_file, optarg, sizeof(output_file)); text_color_set(DW_COLOR_INFO); dw_printf ("Output file set to %s\n", output_file); 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].samples_per_sec >= MIN_SAMPLES_PER_SEC && modem.adev[0].samples_per_sec <= MAX_SAMPLES_PER_SEC); - - - /* * Get user packets(s) from file or stdin if specified. * "-n" option is ignored in this case. @@ -472,12 +469,16 @@ int main(int argc, char **argv) if (modem.achan[0].modem_type == MODEM_SCRAMBLE) { 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 { /* About 2/3 should be decoded properly. */ 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); @@ -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) { @@ -711,8 +720,13 @@ int audio_put (int a, int c) /* Add random noise to the signal. */ /* 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; diff --git a/gen_tone.c b/gen_tone.c index 775ca39..63f36f9 100644 --- a/gen_tone.c +++ b/gen_tone.c @@ -426,8 +426,8 @@ int main () /* one channel. 2 times: one second of each tone. */ memset (&my_audio_config, 0, sizeof(my_audio_config)); - strcpy (my_audio_config.adev[0].adevice_in, DEFAULT_ADEVICE); - strcpy (my_audio_config.adev[0].adevice_out, DEFAULT_ADEVICE); + strlcpy (my_audio_config.adev[0].adevice_in, DEFAULT_ADEVICE, sizeof(my_audio_config.adev[0].adevice_in)); + strlcpy (my_audio_config.adev[0].adevice_out, DEFAULT_ADEVICE, sizeof(my_audio_config.adev[0].adevice_out)); audio_open (&my_audio_config); gen_tone_init (&my_audio_config, 100); @@ -448,8 +448,8 @@ int main () /* Now try stereo. */ memset (&my_audio_config, 0, sizeof(my_audio_config)); - strcpy (my_audio_config.adev[0].adevice_in, DEFAULT_ADEVICE); - strcpy (my_audio_config.adev[0].adevice_out, DEFAULT_ADEVICE); + strlcpy (my_audio_config.adev[0].adevice_in, DEFAULT_ADEVICE, sizeof(my_audio_config.adev[0].adevice_in)); + 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; audio_open (&my_audio_config); diff --git a/geotranz/polarst.c b/geotranz/polarst.c index 0cafaa4..d32f9e8 100644 --- a/geotranz/polarst.c +++ b/geotranz/polarst.c @@ -178,7 +178,7 @@ long Set_Polar_Stereographic_Parameters (double a, double essin; double one_PLUS_es, one_MINUS_es; double pow_es; - double temp, temp_northing; + double temp, temp_northing = 0; double inv_f = 1 / f; double mc; // const double epsilon = 1.0e-2; diff --git a/hdlc_rec.c b/hdlc_rec.c index 249f5e3..975c26c 100644 --- a/hdlc_rec.c +++ b/hdlc_rec.c @@ -634,8 +634,6 @@ void dcd_change (int chan, int subchan, int state) int hdlc_rec_data_detect_any (int chan) { - int subchan; - assert (chan >= 0 && chan < MAX_CHANS); return (composite_dcd[chan] != 0); diff --git a/hdlc_rec2.c b/hdlc_rec2.c index e6553e9..31a0bf6 100644 --- a/hdlc_rec2.c +++ b/hdlc_rec2.c @@ -226,7 +226,6 @@ void hdlc_rec2_block (rrbb_t block) retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits; int passall = save_audio_config_p->achan[chan].passall; int ok; - int n; #if DEBUGx 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 len, i,j; 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); @@ -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 i; unsigned int raw; /* From demodulator. */ +#if DEBUGx int crc_failed = 1; +#endif int retry_conf_mode = retry_conf.mode; int retry_conf_type = retry_conf.type; 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; } } else { +#if DEBUGx crc_failed = 0; +#endif goto failure; } failure: @@ -1151,8 +1154,8 @@ failure: } dw_printf ("\n"); } -#endif end: +#endif return 0; /* failure. */ } /* end try_decode */ diff --git a/igate.c b/igate.c index 1f019d6..b126a9b 100644 --- a/igate.c +++ b/igate.c @@ -327,7 +327,7 @@ static int stats_rf_xmit_packets; /* Number of packets passed along to radio */ The APRS Protocol Reference ( http://www.aprs.org/doc/APRS101.PDF ), section 15, briefly discusses station capabilities and gives the example - IGATE,MSG_CNT=n,LOC_CNT=n + IGATE,MSG_CNT=n,LOC_CNT=n IGate Design ( http://www.aprs-is.net/IGating.aspx ) barely mentions nullmodem); 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); } #endif @@ -374,7 +374,7 @@ static MYFDTYPE kiss_open_pt (void) return (MYFDERROR); } - strcpy (pt_slave_name, pts); + strlcpy (pt_slave_name, pts, sizeof(pt_slave_name)); e = tcgetattr (fd, &ts); 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. // http://support.microsoft.com/kb/115831 - strcpy (bettername, devicename); + strlcpy (bettername, devicename, sizeof(bettername)); if (strncasecmp(devicename, "COM", 3) == 0) { int n; n = atoi(devicename+3); if (n >= 10) { - strcpy (bettername, "\\\\.\\"); - strcat (bettername, devicename); + strlcpy (bettername, "\\\\.\\", sizeof(bettername)); + strlcat (bettername, devicename, sizeof(bettername)); } } @@ -667,7 +667,7 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen) if (kiss_debug) { 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); } else { diff --git a/kiss_frame.c b/kiss_frame.c index d5da290..f988412 100644 --- a/kiss_frame.c +++ b/kiss_frame.c @@ -70,10 +70,8 @@ #include #include - #include #include - #include #include @@ -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); -#if TEST +#if KISSTEST #define dw_printf printf @@ -275,7 +273,7 @@ static int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out) } /* 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)) { - //printf ("kiss_frame ( %c %02x ) \n", ch, ch); + //dw_printf ("kiss_frame ( %c %02x ) \n", ch, ch); 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 */ -// $ gcc -DTEST kiss_frame.c ; ./a +// $ gcc -DKISSTEST kiss_frame.c ; ./a // Quick KISS test passed OK. -#if TEST +#if KISSTEST main () @@ -661,7 +659,8 @@ main () assert (dlen == 512); 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 diff --git a/kissnet.c b/kissnet.c index 90c9f97..357c582 100644 --- a/kissnet.c +++ b/kissnet.c @@ -275,7 +275,7 @@ static void * connect_listen_thread (void *arg) SOCKET listen_sock; WSADATA wsadata; - sprintf (kiss_port_str, "%d", (int)(long)arg); + snprintf (kiss_port_str, sizeof(kiss_port_str), "%d", (int)(long)arg); #if DEBUG text_color_set(DW_COLOR_DEBUG); 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) { 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); } else { diff --git a/latlong.c b/latlong.c index 98875bb..c984ef1 100644 --- a/latlong.c +++ b/latlong.c @@ -55,6 +55,7 @@ * ambiguity - If 1, 2, 3, or 4, blank out that many trailing digits. * * Outputs: slat - String in format ddmm.mm[NS] + * Should always be exactly 8 characters + NUL. * * Returns: None * @@ -89,7 +90,7 @@ void latitude_to_str (double dlat, int ambiguity, char *slat) ideg = (int)dlat; 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" */ if (smin[0] == '6') { 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. * * Outputs: slat - String in format dddmm.mm[NS] + * Should always be exactly 9 characters + NUL. * * Returns: None * @@ -158,7 +160,7 @@ void longitude_to_str (double dlong, int ambiguity, char *slong) ideg = (int)dlong; 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" */ if (smin[0] == '6') { smin[0] = '0'; @@ -197,6 +199,7 @@ void longitude_to_str (double dlong, int ambiguity, char *slong) * Inputs: dlat - Floating point degrees. * * 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. * * 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; 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" */ if (smin[0] == '6') { smin[0] = '0'; @@ -391,7 +395,7 @@ void longitude_to_nmea (double dlong, char *slong, char *hemi) ideg = (int)dlong; 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" */ if (smin[0] == '6') { smin[0] = '0'; @@ -426,7 +430,6 @@ void longitude_to_nmea (double dlong, char *slong, char *hemi) * Bugs: Very little validation of data. * * 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. * * 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. * - * 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. * Original values unchanged if error. * * Returns: 1 for success, 0 if error. * - * Bug: This does not check for invalid values. - * - * Reference: A good converter for spot checking: + * Reference: A good converter for spot checking. Only handles 4 or 6 characters :-( * http://home.arcor.de/waldemar.kebsch/The_Makrothen_Contest/fmaidenhead.html * * 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? * * Using this calculator, http://www.earthpoint.us/Convert.aspx + * It gives lower left corner of square rather than the middle. :-( * * FN42MA00 --> 19T 334361mE 4651711mN * FN42MA11 --> 19T 335062mE 4652157mN * ------ ------- * 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) { double lat, lon; @@ -687,6 +760,142 @@ int ll_from_grid_square (char *maidenhead, double *dlat, double *dlon) return (1); } +#endif + /* 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 */ \ No newline at end of file diff --git a/log.c b/log.c index 238f875..f5448e7 100644 --- a/log.c +++ b/log.c @@ -54,7 +54,7 @@ * 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; char *q = out; 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) { *q++ = '"'; for (p = in; *p != '\0'; p++) { @@ -78,7 +80,7 @@ static void quote_for_csv (char *out, const char *in) { *q = '\0'; } else { - strcpy (out, in); + strlcpy (out, in, outsize); } } @@ -108,9 +110,9 @@ void log_init (char *path) { struct stat st; - strcpy (g_log_dir, ""); + strlcpy (g_log_dir, "", sizeof(g_log_dir)); g_log_fp = NULL; - strcpy (g_open_fname, ""); + strlcpy (g_open_fname, "", sizeof(g_open_fname)); if (strlen(path) == 0) { return; @@ -120,13 +122,13 @@ void log_init (char *path) // Exists, but is it a directory? if (S_ISDIR(st.st_mode)) { // Specified directory exists. - strcpy (g_log_dir, path); + strlcpy (g_log_dir, path, sizeof(g_log_dir)); } else { text_color_set(DW_COLOR_ERROR); dw_printf ("Log file location \"%s\" is not a directory.\n", path); dw_printf ("Using current working directory \".\" instead.\n"); - strcpy (g_log_dir, "."); + strlcpy (g_log_dir, ".", sizeof(g_log_dir)); } } else { @@ -141,14 +143,14 @@ void log_init (char *path) // Success. text_color_set(DW_COLOR_INFO); 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 { text_color_set(DW_COLOR_ERROR); dw_printf ("Failed to create log file location \"%s\".\n", path); dw_printf ("%s\n", strerror(errno)); dw_printf ("Using current working directory \".\" instead.\n"); - 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. now = time(NULL); - gmtime_r (&now, &tm); + (void)gmtime_r (&now, &tm); // 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; int already_there; - strcpy (full_path, g_log_dir); + strlcpy (full_path, g_log_dir, sizeof(full_path)); #if __WIN32__ - strcat (full_path, "\\"); + strlcat (full_path, "\\", sizeof(full_path)); #else - strcat (full_path, "/"); + strlcat (full_path, "/", sizeof(full_path)); #endif - strcat (full_path, fname); + strlcat (full_path, fname, sizeof(full_path)); // See if it already exists. // 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"); if (g_log_fp != NULL) { - strcpy (g_open_fname, fname); + strlcpy (g_open_fname, fname, sizeof(g_open_fname)); } else { text_color_set(DW_COLOR_ERROR); dw_printf("Can't open log file \"%s\" for write.\n", full_path); dw_printf ("%s\n", strerror(errno)); - strcpy (g_open_fname, ""); + strlcpy (g_open_fname, "", sizeof(g_open_fname)); 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? */ /* Similar code in direwolf.c. Combine into one function? */ - strcpy(heard, ""); + strlcpy(heard, "", sizeof(heard)); if (pp != NULL) { if (ax25_get_num_addr(pp) == 0) { /* Not AX.25. No station to display below. */ h = -1; - strcpy (heard, ""); + strlcpy (heard, "", sizeof(heard)); } else { h = ax25_get_heard(pp); @@ -285,7 +287,7 @@ void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_ heard[5] == '\0') { 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. - strcpy(sdti, ""); + strlcpy(sdti, "", sizeof(sdti)); if (pp != NULL) { stemp[0] = ax25_get_dti(pp); 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[1] = A->g_symbol_code; 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 (sstatus, A->g_mic_e_status); - quote_for_csv (stelemetry, A->g_telemetry); - quote_for_csv (scomment, A->g_comment); + quote_for_csv (smfr, sizeof(smfr), A->g_mfr); + quote_for_csv (sstatus, sizeof(sstatus), A->g_mic_e_status); + quote_for_csv (stelemetry, sizeof(stelemetry), A->g_telemetry); + quote_for_csv (scomment, sizeof(scomment), A->g_comment); - strcpy (slat, ""); if (A->g_lat != G_UNKNOWN) sprintf (slat, "%.6f", A->g_lat); - strcpy (slon, ""); if (A->g_lon != G_UNKNOWN) sprintf (slon, "%.6f", A->g_lon); - strcpy (sspd, ""); if (A->g_speed != G_UNKNOWN) sprintf (sspd, "%.1f", DW_MPH_TO_KNOTS(A->g_speed)); - strcpy (scse, ""); if (A->g_course != G_UNKNOWN) sprintf (scse, "%.1f", A->g_course); - strcpy (salt, ""); if (A->g_altitude != G_UNKNOWN) sprintf (salt, "%.1f", DW_FEET_TO_METERS(A->g_altitude)); + strlcpy (slat, "", sizeof(slat)); if (A->g_lat != G_UNKNOWN) snprintf (slat, sizeof(slat), "%.6f", A->g_lat); + strlcpy (slon, "", sizeof(slon)); if (A->g_lon != G_UNKNOWN) snprintf (slon, sizeof(slon), "%.6f", A->g_lon); + strlcpy (sspd, "", sizeof(sspd)); if (A->g_speed_mph != G_UNKNOWN) snprintf (sspd, sizeof(sspd), "%.1f", DW_MPH_TO_KNOTS(A->g_speed_mph)); + strlcpy (scse, "", sizeof(scse)); if (A->g_course != G_UNKNOWN) snprintf (scse, sizeof(scse), "%.1f", A->g_course); + 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); - strcpy (soffs, ""); if (A->g_offset != G_UNKNOWN) sprintf (soffs, "%+d", A->g_offset); - strcpy (stone, ""); if (A->g_tone != G_UNKNOWN) sprintf (stone, "%.1f", A->g_tone); - if (A->g_dcs != G_UNKNOWN) sprintf (stone, "D%03o", A->g_dcs); + strlcpy (sfreq, "", sizeof(sfreq)); if (A->g_freq != G_UNKNOWN) snprintf (sfreq, sizeof(sfreq), "%.3f", A->g_freq); + strlcpy (soffs, "", sizeof(soffs)); if (A->g_offset != G_UNKNOWN) snprintf (soffs, sizeof(soffs), "%+d", A->g_offset); + strlcpy (stone, "", sizeof(stone)); if (A->g_tone != G_UNKNOWN) snprintf (stone, sizeof(stone), "%.1f", A->g_tone); + 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", chan, (int)now, itime, @@ -359,7 +361,7 @@ void log_term (void) fclose (g_log_fp); g_log_fp = NULL; - strcpy (g_open_fname, ""); + strlcpy (g_open_fname, "", sizeof(g_open_fname)); } } /* end log_term */ diff --git a/log2gpx.c b/log2gpx.c index 24c1b39..c8f47e0 100644 --- a/log2gpx.c +++ b/log2gpx.c @@ -27,6 +27,8 @@ char *strsep(char **stringp, const char *delim); #endif +#include "direwolf.h" + /* * 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? */ 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. */ @@ -265,42 +281,42 @@ static void read_csv(FILE *fp) if (pfreq != NULL && strlen(pfreq) > 0) { freq = atof(pfreq); - sprintf (desc, "%.3f MHz", freq); + snprintf (desc, sizeof(desc), "%.3f MHz", freq); } else { - strcpy (desc, ""); + strlcpy (desc, "", sizeof(desc)); } if (poffset != NULL && strlen(poffset) > 0) { offset = atoi(poffset); if (offset != 0 && offset % 1000 == 0) { - sprintf (stemp, "%+dM", offset / 1000); + snprintf (stemp, sizeof(stemp), "%+dM", offset / 1000); } else { - sprintf (stemp, "%+dk", offset); + snprintf (stemp, sizeof(stemp), "%+dk", offset); } - if (strlen(desc) > 0) strcat (desc, " "); - strcat (desc, stemp); + if (strlen(desc) > 0) strlcat (desc, " ", sizeof(desc)); + strlcat (desc, stemp, sizeof(desc)); } if (ptone != NULL && strlen(ptone) > 0) { if (*ptone == 'D') { - sprintf (stemp, "DCS %s", ptone+1); + snprintf (stemp, sizeof(stemp), "DCS %s", ptone+1); } else { - sprintf (stemp, "PL %s", ptone); + snprintf (stemp, sizeof(stemp), "PL %s", ptone); } - if (strlen(desc) > 0) strcat (desc, " "); - strcat (desc, stemp); + if (strlen(desc) > 0) strlcat (desc, " ", sizeof(desc)); + strlcat (desc, stemp, sizeof(desc)); } - strcpy (comment, ""); + strlcpy (comment, "", sizeof(comment)); if (pstatus != NULL && strlen(pstatus) > 0) { - strcpy (comment, pstatus); + strlcpy (comment, pstatus, sizeof(comment)); } if (pcomment != NULL && strlen(pcomment) > 0) { - if (strlen(comment) > 0) strcat (comment, ", "); - strcat (comment, pcomment); + if (strlen(comment) > 0) strlcat (comment, ", ", sizeof(comment)); + strlcat (comment, pcomment, sizeof(comment)); } if (num_things == max_things) { diff --git a/man1/direwolf.1 b/man1/direwolf.1 index 750941a..b78f70a 100644 --- a/man1/direwolf.1 +++ b/man1/direwolf.1 @@ -77,7 +77,9 @@ u = Display non-ASCII text in hexadecimal. .P p = Packet dump in hexadecimal. .P -t = GPS Tracker. +g = GPS interface. +.P +t = Tracker beacon. .P o = Output controls such as PTT and DCD. .RE diff --git a/morse.c b/morse.c index 0c490ef..8598fcc 100644 --- a/morse.c +++ b/morse.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include diff --git a/nmea.c b/nmea.c index 55d2e3a..32e487e 100644 --- a/nmea.c +++ b/nmea.c @@ -1,7 +1,7 @@ // // 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 // it under the terms of the GNU General Public License as published by @@ -20,6 +20,9 @@ //#define DEBUG 1 + +// TODO: rename this to waypoint & integrate. + /*------------------------------------------------------------------ * * Module: nmea.c @@ -73,13 +76,9 @@ static MYFDTYPE nmea_port_fd = MYFDERROR; 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. */ @@ -102,7 +101,6 @@ void nmea_set_debug (int n) * * * 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) { -#if __WIN32__ - HANDLE nmea_listen_th; -#else - pthread_t nmea_listen_tid; -#endif - /* * Open serial port connection. * 4800 baud is standard for GPS. @@ -125,24 +117,7 @@ void nmea_init (struct misc_config_s *mc) 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 - } + } @@ -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 scourse[12]; /* course as string, empty if unknown */ int grm_sym; /* Garmin symbol code. */ - char sicon[4]; /* Magellan icon string */ + char sicon[5]; /* Magellan icon string */ char stime[8]; char sdate[8]; char *p; @@ -265,7 +240,7 @@ void nmea_send_waypoint (char *wname_in, double dlat, double dlong, char symtab, * *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); nmea_send_sentence (sentence); @@ -291,11 +266,11 @@ void nmea_send_waypoint (char *wname_in, double dlat, double dlong, char symtab, strcpy (salt, ""); } else { - sprintf (salt, "%.1f", alt); + snprintf (salt, sizeof(salt), "%.1f", alt); } 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); nmea_send_sentence (sentence); @@ -319,8 +294,8 @@ void nmea_send_waypoint (char *wname_in, double dlat, double dlong, char symtab, // TODO: icon - sprintf (sicon, "??"); - sprintf (sentence, "$PMGNWPL,%s,%s,%s,%s,%s,M,%s,%s,%s", + snprintf (sicon, sizeof(sicon), "??"); + snprintf (sentence, sizeof(sentence), "$PMGNWPL,%s,%s,%s,%s,%s,M,%s,%s,%s", slat, slat_ns, slong, slong_ew, salt, wname, comment, sicon); append_checksum (sentence); nmea_send_sentence (sentence); @@ -357,13 +332,13 @@ void nmea_send_waypoint (char *wname_in, double dlat, double dlong, char symtab, strcpy (sspeed, ""); } else { - sprintf (sspeed, "%.1f", speed); + snprintf (sspeed, sizeof(sspeed), "%.1f", speed); } if (course == G_UNKNOWN) { strcpy (scourse, ""); } else { - sprintf (scourse, "%.1f", course); + snprintf (scourse, sizeof(scourse), "%.1f", course); } // 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 (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, sspeed, scourse, sdate, salt, wname, symtab, symbol); append_checksum (sentence); @@ -434,7 +409,7 @@ http://gpsbabel.sourcearchive.com/documentation/1.3.7~cvs1/magproto_8c-source.ht &lngdeg,&lngdir, &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', lon, ilon < 0 ? 'W' : 'E', 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 */ diff --git a/pfilter.c b/pfilter.c index 0578890..4178f91 100644 --- a/pfilter.c +++ b/pfilter.c @@ -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. * - * 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 () { + 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 (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); @@ -1057,12 +1060,12 @@ int main () if (error_count > 0) { text_color_set (DW_COLOR_ERROR); - dw_printf ("Packet Filtering Test - FAILED!\n"); - exit (1); + dw_printf ("\nPacket Filtering Test - FAILED!\n"); + exit (EXIT_FAILURE); } - text_color_set (DW_COLOR_DEBUG ); - dw_printf ("Packet Filtering Test - SUCCESS!\n"); - exit (0); + text_color_set (DW_COLOR_REC); + dw_printf ("\nPacket Filtering Test - SUCCESS!\n"); + exit (EXIT_SUCCESS); } diff --git a/ptt.c b/ptt.c index 7752f01..b76775a 100644 --- a/ptt.c +++ b/ptt.c @@ -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 #include #include #include #include -#include +#include #if __WIN32__ #include @@ -164,7 +200,7 @@ static char otnames[NUM_OCTYPES][8]; void ptt_init (struct audio_s *audio_config_p) { int ch; - HANDLE fd; + HANDLE fd = INVALID_HANDLE_VALUE; #if __WIN32__ #else int using_gpio; @@ -177,9 +213,9 @@ void ptt_init (struct audio_s *audio_config_p) save_audio_config_p = audio_config_p; - strcpy (otnames[OCTYPE_PTT], "PTT"); - strcpy (otnames[OCTYPE_DCD], "DCD"); - strcpy (otnames[OCTYPE_FUTURE], "FUTURE"); + strlcpy (otnames[OCTYPE_PTT], "PTT", sizeof(otnames[OCTYPE_PTT])); + strlcpy (otnames[OCTYPE_DCD], "DCD", sizeof(otnames[OCTYPE_DCD])); + strlcpy (otnames[OCTYPE_FUTURE], "FUTURE", sizeof(otnames[OCTYPE_FUTURE])); 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); dw_printf ("Converted %s device '%s'", audio_config_p->achan[ch].octrl[ot].ptt_device, otnames[ot]); 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); } #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. // 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) { int n; n = atoi(bettername+3); if (n >= 10) { - strcpy (bettername, "\\\\.\\"); - strcat (bettername, audio_config_p->achan[ch].octrl[ot].ptt_device); + strlcpy (bettername, "\\\\.\\", sizeof(bettername)); + strlcat (bettername, audio_config_p->achan[ch].octrl[ot].ptt_device, sizeof(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"); 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)) { int e = errno; /* Ignore EBUSY error which seems to mean */ @@ -424,16 +460,36 @@ void ptt_init (struct audio_s *audio_config_p) } 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 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); - 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); - 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) { int e = errno; @@ -458,7 +514,7 @@ void ptt_init (struct audio_s *audio_config_p) * 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); if (fd < 0) { int e = errno; @@ -470,10 +526,10 @@ void ptt_init (struct audio_s *audio_config_p) char hilo[8]; if (audio_config_p->achan[ch].octrl[ot].ptt_invert) { - strcpy (hilo, "high"); + strlcpy (hilo, "high", sizeof(hilo)); } else { - strcpy (hilo, "low"); + strlcpy (hilo, "low", sizeof(hilo)); } if (write (fd, hilo, strlen(hilo)) != strlen(hilo)) { int e = errno; @@ -702,7 +758,7 @@ void ptt_set (int ot, int chan, int ptt_signal) int fd; 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); if (fd < 0) { @@ -713,7 +769,7 @@ void ptt_set (int ot, int chan, int ptt_signal) return; } - sprintf (stemp, "%d", ptt); + snprintf (stemp, sizeof(stemp), "%d", ptt); if (write (fd, stemp, 1) != 1) { int e = errno; @@ -840,14 +896,14 @@ main () my_audio_config.valid[0] = 1; my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_SERIAL; - //strcpy (my_audio_config.ptt_device, "COM1"); - strcpy (my_audio_config.ptt_device, "/dev/ttyUSB0"); + //strlcpy (my_audio_config.ptt_device, "COM1", sizeof(my_audio_config.ptt_device)); + strlcpy (my_audio_config.ptt_device, "/dev/ttyUSB0", sizeof(my_audio_config.ptt_device)); my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_RTS; my_audio_config.valid[1] = 1; 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"); - strcpy (my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device, "/dev/ttyUSB0"); + //strlcpy (my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device, "COM1", sizeof(my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device)); + strlcpy (my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device, "/dev/ttyUSB0", sizeof(my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device)); my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_DTR; diff --git a/recv.c b/recv.c index 1892562..7972a8d 100644 --- a/recv.c +++ b/recv.c @@ -302,7 +302,7 @@ void recv_process (void) #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 text_color_set(DW_COLOR_DEBUG); dw_printf ("recv_process: dlq_remove() returned ok=%d, type=%d, chan=%d, pp=%p\n", diff --git a/redecode.c b/redecode.c index d975adf..c54d435 100644 --- a/redecode.c +++ b/redecode.c @@ -46,7 +46,6 @@ #include #include -//#include #include #if __WIN32__ diff --git a/serial_port.c b/serial_port.c index 799f4fc..2a71010 100644 --- a/serial_port.c +++ b/serial_port.c @@ -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. // http://support.microsoft.com/kb/115831 - strcpy (bettername, devicename); + strlcpy (bettername, devicename, sizeof(bettername)); if (strncasecmp(devicename, "COM", 3) == 0) { int n; n = atoi(devicename+3); if (n >= 10) { - strcpy (bettername, "\\\\.\\"); - strcat (bettername, devicename); + strlcpy (bettername, "\\\\.\\", sizeof(bettername)); + strlcat (bettername, devicename, sizeof(bettername)); } } @@ -201,14 +201,14 @@ MYFDTYPE serial_port_open (char *devicename, int baud) /* Translate Windows device name into Linux name. */ /* COM1 -> /dev/ttyS0, etc. */ - strcpy (linuxname, devicename); + strlcpy (linuxname, devicename, sizeof(linuxname)); if (strncasecmp(devicename, "COM", 3) == 0) { int n = atoi (devicename + 3); text_color_set(DW_COLOR_INFO); dw_printf ("Converted serial port name '%s'", devicename); if (n < 1) n = 1; - sprintf (linuxname, "/dev/ttyS%d", n-1); + snprintf (linuxname, sizeof(linuxname), "/dev/ttyS%d", n-1); dw_printf (" to Linux equivalent '%s'\n", linuxname); } diff --git a/server.c b/server.c index 68c5703..85b9e1b 100644 --- a/server.c +++ b/server.c @@ -364,8 +364,8 @@ void server_init (struct audio_s *audio_config_p, struct misc_config_s *mc) #else pthread_t connect_listen_tid; pthread_t cmd_listen_tid[MAX_NET_CLIENTS]; -#endif int e; +#endif 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) { text_color_set(DW_COLOR_ERROR); 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) { @@ -483,7 +483,7 @@ static THREAD_F connect_listen_thread (void *arg) dw_printf("Could not find a usable version of Winsock.dll\n"); WSACleanup(); //sleep (1); - return (NULL); // TODO: what should this be for Windows? + return (0); } memset (&hints, 0, sizeof(hints)); @@ -498,14 +498,14 @@ static THREAD_F connect_listen_thread (void *arg) dw_printf("getaddrinfo failed: %d\n", err); //sleep (1); WSACleanup(); - return (NULL); // TODO: what should this be for Windows? + return (0); } listen_sock= socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (listen_sock == INVALID_SOCKET) { text_color_set(DW_COLOR_ERROR); dw_printf ("connect_listen_thread: Socket creation failed, err=%d", WSAGetLastError()); - return (NULL); // TODO: what should this be for Windows? + return (0); } #if DEBUG @@ -522,7 +522,7 @@ static THREAD_F connect_listen_thread (void *arg) freeaddrinfo(ai); closesocket(listen_sock); WSACleanup(); - return (NULL); // TODO: what should this be for Windows? + return (0); } freeaddrinfo(ai); @@ -553,7 +553,7 @@ static THREAD_F connect_listen_thread (void *arg) { text_color_set(DW_COLOR_ERROR); 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); @@ -566,7 +566,7 @@ static THREAD_F connect_listen_thread (void *arg) dw_printf("Accept failed with error: %d\n", WSAGetLastError()); closesocket(listen_sock); WSACleanup(); - return (NULL); // TODO: what should this be for Windows? + return (0); } 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; clock = time(NULL); - tm = localtime(&clock); + tm = localtime(&clock); // TODO: should use localtime_r 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]); #endif client_sock[client] = -1; - return NULL; // TODO: what should this be for Windows? + return (0); } cmd.data[0] = '\0'; @@ -1035,7 +1035,7 @@ static THREAD_F cmd_listen_thread (void *arg) close (client_sock[client]); #endif client_sock[client] = -1; - return NULL; + return (0); } if (n > 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. */ { -#if 0 +#if 0 /* This information is not being collected. */ struct { struct agwpe_s hdr; char info[100]; @@ -1239,23 +1239,21 @@ static THREAD_F cmd_listen_thread (void *arg) break; - case 'V': /* Transmit UI data frame */ + case 'V': /* Transmit UI data frame (with digipeater path) */ { // Data format is: // 1 byte for number of digipeaters. // 10 bytes for each digipeater. // data part of message. - char stemp[512]; + char stemp[AX25_MAX_PACKET_LEN+2]; char *p; int ndigi; int k; 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)); strlcat (stemp, ">", sizeof(stemp)); @@ -1396,13 +1394,13 @@ static THREAD_F cmd_listen_thread (void *arg) 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. - None of the other tested applications use it. - <<< Send UNPROTO Information from AGWPE client application 0, total length = 253 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 = 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 !"", sizeof(stemp)); + strlcat (stemp, cmd.hdr.call_to, sizeof(stemp)); - if (pp != NULL) { - tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp); - ax25_set_pid (pp, pid); - } - else { + cmd.data[cmd.hdr.data_len] = '\0'; + + strlcat (stemp, ":", sizeof(stemp)); + strlcat (stemp, cmd.data, sizeof(stemp)); + + //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); dw_printf ("Failed to create frame from AGW 'M' message.\n"); } + else { + tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp); + } } break; -#endif - case 'y': /* Ask Outstanding frames waiting on a Port */ diff --git a/symbols-new.txt b/symbols-new.txt index be0a558..cce060e 100644 --- a/symbols-new.txt +++ b/symbols-new.txt @@ -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) 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 -different web pages and links. THIS file is now assumed to be the -CORRECT one. - +Update 29 Oct 2015: Reorgainized list to Alphabetical Order. + + Added many new Balloons (due to lost DoD radar Blimp yesterday) + + 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: @@ -26,12 +29,16 @@ UPDATES/REVISIONS/CORRECTIONS: 04 Jan 10 added #A to the table (correcting earlier omission) 12 Oct 09 Added W0 for Yaesu WIRES nodes 09 Apr 09 Changed APRStt symbol to overlayed BOX (#A) -21 Aug 08 Added RFID R=, Stroller B], Radios#Y, & skull&Xbones (XH) -27 Apr 08 Added some definitions of the numbered circle #0. -25 Mar 08 Added these new definitions of overlays: +21 Aug 08 Added RFID R=, Babystroller B], Radio#Y, skull&Xbones XH -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) \' - Was Crash Site. Now expanded to be INCIDENT sites \% - 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 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 \' to crash & incident sites -change \D to DEPOT family -change overlayed car to generic with (1-9 overlays) +Update #' name to crash & incident sites +Update \D (was available) to DEPOT family +change overlayed car to generic Vehicle with (1-9 overlays) + +ADVISORIES: #< (new expansion possibilities) +/< = motorcycle +\< = Advisory (single gale flag) AIRCRAFT /^ = LARGE Aircraft \^ = top-view originally intended to point in direction of flight +A^ = Autonomous (2015) 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) J^ = JET (new may 2014) M^ = Missle (new may 2014) P^ = Prop (new Aug 2014) +R^ = Remotely Piloted (new 2015) +S^ = Solar Powered (new 2015) V^ = Vertical takeoff (new may 2014) X^ = Experimental (new Aug 2014) @@ -138,6 +153,60 @@ U$ = US dollars L$ = Brittish Pound 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 /D = was originally undefined \D = was drizzle (moved to ' ovlyD) @@ -155,17 +224,14 @@ EMERGENCY: #! E! = ELT or EPIRB (new Aug 2014) V! = Volcanic Eruption or Lava (new Aug 2014) -POWER PLANT: #% -/% = DX cluster <= the original primary table definition -C% = Coal -E% = Emergency (new Aug 2014) -G% = Geothermal -H% = Hydroelectric -N% = Nuclear -P% = Portable (new Aug 2014) -S% = Solar -T% = Turbine -W% = Wind +EYEBALL (EVENT) 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 \{ GATEWAYS: #& /& = 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) 2& = TX igate with path set to 2 hops (not generally good idea) -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 +GPS devices: #\ +/\ = Triangle DF primary symbol +\\ = was undefined alternate symbol +A\ = Avmap G5 * <= Recommend special symbol + +HAZARDS: #H +/H = hotel +\H = Haze +RH = Radiation detector (new mar 2011) +WH = Hazardous Waste +XH = Skull&Crossbones HUMAN SYMBOL: #[ /[ = Human @@ -205,6 +274,15 @@ O- = Operator Present S- = Solar 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 E0 = Echolink Node (E0) I0 = IRLP repeater (I0) @@ -223,48 +301,17 @@ I; = Islands on the air S; = Summits on the air W; = WOTA -ADVISORIES: #< (new expansion possibilities) -/< = motorcycle -\< = Advisory (single gale flag) - -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 - -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 +POWER or ENERGY: #% +/% = DX cluster <= the original primary table definition +C% = Coal +E% = Emergency (new Aug 2014) +G% = Geothermal +H% = Hydroelectric +N% = Nuclear +P% = Portable (new Aug 2014) +S% = Solar +T% = Turbine +W% = Wind RESTAURANTS: #R \R = Restaurant (generic) @@ -282,35 +329,6 @@ IY = Icom KY = Kenwood * <= 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 /k = truck @@ -318,6 +336,14 @@ SPECIAL VEHICLES: #k 4k = 4x4 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 /s = Power boat (ship) side view \s = Overlay Boat (Top view) @@ -341,11 +367,10 @@ Ws = Wing-in-Ground effect (or Hovercraft) Xs = Passenger (paX)(ferry) Ys = Sailing (large ship) - TRUCKS: #u /u = Truck (18 wheeler) \u = truck with overlay -Bu = Buldozer/construction (new Aug 2014) +Bu = Buldozer/construction/Backhoe (new Aug 2014) Gu = Gas Pu = Plow or SnowPlow (new Aug 2014) Tu = Tanker diff --git a/symbols.c b/symbols.c index 16a0aad..e4d51af 100644 --- a/symbols.c +++ b/symbols.c @@ -35,6 +35,8 @@ #include "direwolf.h" #include "textcolor.h" #include "symbols.h" +#include "tt_text.h" + //#if __WIN32__ char *strcasestr(const char *S, const char *FIND); @@ -460,7 +462,7 @@ void symbols_list (void) int symbol = new_sym_ptr[n].symbol; char tones[12]; - symbols_to_tones (overlay, symbol, tones); + symbols_to_tones (overlay, symbol, tones, sizeof(tones)); if (overlay == '/') { @@ -900,6 +902,7 @@ int symbols_code_from_description (char overlay, char *description, char *symtab * * Inputs: symtab/overlay * symbol + * tonessiz - Amount of space available for result. * * 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 == '/') { - // TODO: potential buffer overflow. - sprintf (tones, "AB1%02d", symbol - ' '); + snprintf (tones, tonessiz, "AB1%02d", symbol - ' '); } 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); - sprintf (tones, "AB0%02d%s", symbol - ' ', tt); + snprintf (tones, tonessiz, "AB0%02d%s", symbol - ' ', tt); } else { - sprintf (tones, "AB2%02d", symbol - ' '); + snprintf (tones, tonessiz, "AB2%02d", symbol - ' '); } } /* end symbols_to_tones */ diff --git a/symbols.h b/symbols.h index 2e3731b..5ed91ad 100644 --- a/symbols.h +++ b/symbols.h @@ -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); -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 */ diff --git a/symbolsX.txt b/symbolsX.txt index f97adff..2c82ce4 100644 --- a/symbolsX.txt +++ b/symbolsX.txt @@ -1,4 +1,4 @@ -APRS SYMBOLS (Icons) 28 Aug 2014 +APRS SYMBOLS (Icons) 23 Jun 2015 ----------------------------------------------------------------------- WB4APR @@ -28,6 +28,7 @@ http://aprs.org/symbols/symbols-background.txt 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) Old WX versions of these: Bb{*:DFegJp were moved to ovlays 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) /% BF DX CLUSTER \% OFO Power Plant with overlay /& 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) /) BJ Wheelchair (handicapped) \) OJO Firenet MEO, MODIS Earth Obs. /* 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) /: MR FIRE \: NR AVAIL (Hail ==> ` ovly H) /; 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) -/> 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 ?) /@ MX HC FUTURE predict (dot) \@ NX HURICANE/Trop-Storm /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 /M PM MacAPRS \M AMO MARS (A=Army,N=Navy,F=AF) /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 /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 /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 /W PW National WX Service Site \W AW# # NWS site (NWS options) -/X PX HELO (SSID = 6) \X AX Pharmacy Rx (Apothicary) -/Y PY YACHT (sail) (SSID = 5) \Y AYO Radios and devices +/X PX HELO (SSID-6) \X AX Pharmacy Rx (Apothicary) +/Y PY YACHT (sail) (SSID-5) \Y AYO Radios and devices /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 /] HU MAIL/PostOffice(was PBBS) \] DU AVAIL /^ 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 (\) -- --- ------------------------ -- --- -------------------------- -/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) +/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) /c LC Incident Command Post \c SC#O CD triangle RACES/SATERN/etc /d LD Fire dept \d SD DX spot by callsign /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 /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 /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) /m LM Mic-E Repeater \m SM Value Sign (3 digit display) /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 /t LT TRUCK STOP \t ST Tornado /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) /x LX xAPRS (Unix) \x SX Wreck or Obstruction ->X<- /y LY YAGI @ QTH \y SY Skywarn diff --git a/telemetry.c b/telemetry.c index 4df40b9..68cd585 100644 --- a/telemetry.c +++ b/telemetry.c @@ -57,10 +57,6 @@ #include #include -#if __WIN32__ -char *strsep(char **stringp, const char *delim); -#endif - #include "direwolf.h" #include "ax25_pad.h" // for packet_t, AX25_MAX_ADDR_LEN #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 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) { struct t_metadata_s *p; - int n, j; + int n; #if DEBUG3 text_color_set(DW_COLOR_DEBUG); @@ -158,13 +154,13 @@ static struct t_metadata_s * t_get_metadata (char *station) 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++) { - 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++) { - 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++) { @@ -255,11 +251,11 @@ static int t_ndp (char *str) * KB1GKN-10>APRX27,UNCAN,WIDE1*:T#491,4.9,0.3,25.0,0.0,1.0,00000000 * * Not integers. Not fixed width fields. - * We will accept these but issue a warning that others might not. + * We will accept these but issue a warning that others might not. * *--------------------------------------------------------------------*/ -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 seq; @@ -280,6 +276,9 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output dw_printf ("\n%s\n\n", info); #endif + strlcpy (output, "", outputsize); + strlcpy (comment, "", commentsize); + pm = t_get_metadata(station); 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. */ - memset (stemp, 0, sizeof(stemp)); - strncpy (stemp, info+2, sizeof(stemp)-1); + strlcpy (stemp, info+2, sizeof(stemp)); for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) { *p = '\0'; @@ -352,7 +350,7 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output // TODO: test this! if (strlen(next) > 8) { - strlcpy (comment, next+8, sizeof(comment)); + strlcpy (comment, next+8, commentsize); next[8] = '\0'; } for (k = 0; k < strlen(next); k++) { @@ -394,7 +392,7 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output #endif - t_data_process (pm, seq, araw, ndp, draw, output); + t_data_process (pm, seq, araw, ndp, draw, output, outputsize); } /* end telemtry_data_original */ @@ -413,7 +411,7 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output * Description: We are expecting from 2 to 7 pairs of base 91 digits. * The first pair is the sequence number. * Next we have 1 to 5 analog values. - * If digital values are present, all 5 analog values must be present. + * If digital values are present, all 5 analog values must be present. * *--------------------------------------------------------------------*/ @@ -436,6 +434,7 @@ static int two_base91_to_i (char *c) else { text_color_set(DW_COLOR_DEBUG); dw_printf ("\"%c\" is not a valid character for base 91 telemetry data.\n", c[0]); + return (G_UNKNOWN); } if (isdigit91(c[1])) { @@ -444,11 +443,12 @@ static int two_base91_to_i (char *c) else { text_color_set(DW_COLOR_DEBUG); dw_printf ("\"%c\" is not a valid character for base 91 telemetry data.\n", c[1]); + return (G_UNKNOWN); } 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 seq; @@ -465,6 +465,8 @@ void telemetry_data_base91 (char *station, char *cdata, char *output) dw_printf ("\n%s\n\n", cdata); #endif + strlcpy (output, "", outputsize); + pm = t_get_metadata(station); assert (pm->magic1 == MAGIC1); @@ -516,7 +518,7 @@ void telemetry_data_base91 (char *station, char *cdata, char *output) #endif - t_data_process (pm, seq, araw, ndp, draw, output); + t_data_process (pm, seq, araw, ndp, draw, output, outputsize); } /* end telemtry_data_base91 */ @@ -536,7 +538,7 @@ void telemetry_data_base91 (char *station, char *cdata, char *output) * Outputs: Stored for future use when data values are received. * * Description: The first 5 characters of the message are "PARM." and the - * rest is a variable length list of comma separated names. + * rest is a variable length list of comma separated names. * * The original spec has different maximum lengths for different * fields which we will ignore. @@ -566,8 +568,7 @@ void telemetry_name_message (char *station, char *msg) * Remove any trailing CR LF. */ - memset (stemp, 0, sizeof(stemp)); - strncpy (stemp, msg, sizeof(stemp)-1); + strlcpy (stemp, msg, sizeof(stemp)); for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) { *p = '\0'; @@ -583,8 +584,7 @@ void telemetry_name_message (char *station, char *msg) while ((p = strsep(&next,",")) != NULL) { if (n < T_NUM_ANALOG + T_NUM_DIGITAL) { if (strlen(p) > 0 && strcmp(p,"-") != 0) { - memset (pm->name[n], 0, T_STR_LEN); - strncpy (pm->name[n], p, T_STR_LEN-1); + strlcpy (pm->name[n], p, sizeof(pm->name[n])); } n++; } @@ -617,7 +617,7 @@ void telemetry_name_message (char *station, char *msg) * Outputs: Stored for future use when data values are received. * * Description: The first 5 characters of the message are "UNIT." and the - * rest is a variable length list of comma separated units/labels. + * rest is a variable length list of comma separated units/labels. * * The original spec has different maximum lengths for different * fields which we will ignore. @@ -644,8 +644,7 @@ void telemetry_unit_label_message (char *station, char *msg) * Remove any trailing CR LF. */ - memset (stemp, 0, sizeof(stemp)); - strncpy (stemp, msg, sizeof(stemp)-1); + strlcpy (stemp, msg, sizeof(stemp)); for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) { *p = '\0'; @@ -661,8 +660,7 @@ void telemetry_unit_label_message (char *station, char *msg) while ((p = strsep(&next,",")) != NULL) { if (n < T_NUM_ANALOG + T_NUM_DIGITAL) { if (strlen(p) > 0) { - memset (pm->unit[n], 0, T_STR_LEN); - strncpy (pm->unit[n], p, T_STR_LEN-1); + strlcpy (pm->unit[n], p, sizeof(pm->unit[n])); } n++; } @@ -696,7 +694,7 @@ void telemetry_unit_label_message (char *station, char *msg) * Outputs: Stored for future use when data values are received. * * Description: The first 5 characters of the message are "EQNS." and the - * rest is a comma separated list of 15 floating point values. + * rest is a comma separated list of 15 floating point values. * * The spec appears to require all 15 so we will issue an * error if fewer found. @@ -723,8 +721,7 @@ void telemetry_coefficents_message (char *station, char *msg, int quiet) * Remove any trailing CR LF. */ - memset (stemp, 0, sizeof(stemp)); - strncpy (stemp, msg, sizeof(stemp)-1); + strlcpy (stemp, msg, sizeof(stemp)); for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) { *p = '\0'; @@ -841,7 +838,7 @@ void telemetry_bit_sense_message (char *station, char *msg, int quiet) if (msg[n] == ',') n++; - strncpy (pm->project, msg+n, sizeof(pm->project)-1); + strlcpy (pm->project, msg+n, sizeof(pm->project)); #if DEBUG3 text_color_set(DW_COLOR_DEBUG); @@ -879,7 +876,7 @@ void telemetry_bit_sense_message (char *station, char *msg, int quiet) * Outputs: output - Decoded telemetry in human readable format. * * Description: Process raw data according to any metadata available - * and put into human readable form. + * and put into human readable form. * *--------------------------------------------------------------------*/ @@ -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; 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->magic2 == MAGIC2); - strcpy (output, ""); + strlcpy (output, "", outputsize); if (strlen(pm->project) > 0) { - strcpy (output, pm->project); - strcat (output, ": "); + strlcpy (output, pm->project, outputsize); + strlcat (output, ": ", outputsize); } ival_to_str (seq, val_str); - strcat (output, "Seq="); - strcat (output, val_str); + strlcat (output, "Seq=", outputsize); + strlcat (output, val_str, outputsize); 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; int fndp; - strcat (output, ", "); + strlcat (output, ", ", outputsize); - strcat (output, pm->name[n]); - strcat (output, "="); + strlcat (output, pm->name[n], outputsize); + strlcat (output, "=", outputsize); // 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])); } fval_to_str (fval, fndp, val_str); - strcat (output, val_str); + strlcat (output, val_str, outputsize); if (strlen(pm->unit[n]) > 0) { - strcat (output, " "); - strcat (output, pm->unit[n]); + strlcat (output, " ", outputsize); + 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) { int dval; - strcat (output, ", "); + strlcat (output, ", ", outputsize); - strcat (output, pm->name[T_NUM_ANALOG+n]); - strcat (output, "="); + strlcat (output, pm->name[T_NUM_ANALOG+n], outputsize); + strlcat (output, "=", outputsize); // 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); if (strlen(pm->unit[T_NUM_ANALOG+n]) > 0) { - strcat (output, " "); - strcat (output, pm->unit[T_NUM_ANALOG+n]); + strlcat (output, " ", outputsize); + 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: * - * 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 - int main ( ) { - char result[256]; - char comment[256]; + char result[120]; + char comment[40]; + int errors = 0; - strcpy (result, ""); - strcpy (comment, ""); + strlcpy (result, "", sizeof(result)); + strlcpy (comment, "", sizeof(comment)); text_color_set(DW_COLOR_INFO); @@ -1039,24 +1037,69 @@ int main ( ) // 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. - telemetry_data_original ("WB2OSZ", "T#005,199,000,255,073,123,01101001Comment,with,commas", 0, result, comment); - strcpy (comment, ""); + telemetry_data_original ("WB2OSZ", "T#005,199,000,255,073,123,01101001Comment,with,commas", 0, result, sizeof(result), comment, sizeof(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. - 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 @@ -1067,43 +1110,187 @@ int main ( ) // From protocol spec. - telemetry_data_base91 ("WB2OSZ", "ss11", result); - dw_printf ("expect 7544: 1472 above.\n"); + telemetry_data_base91 ("WB2OSZ", "ss11", result, sizeof(result)); - telemetry_data_base91 ("WB2OSZ", "ss11223344{{!\"", result); - dw_printf ("expect 7544: 1472, 1564, 1656, 1748, 8280, 10000000 above.\n"); + if (strcmp(result, "Seq=7544, A1=1472") != 0) { + 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 // should be called only with valid data that matches the pattern. - telemetry_data_base91 ("WB2OSZ", "ss11223344{{!\"x", result); - telemetry_data_base91 ("WB2OSZ", "ss1", result); - telemetry_data_base91 ("WB2OSZ", "ss11223344{{!", result); - telemetry_data_base91 ("WB2OSZ", "s |1", result); + telemetry_data_base91 ("WB2OSZ", "ss11223344{{!\"x", result, sizeof(result)); + + if (strcmp(result, "") != 0) { + 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 #if DEBUG3 + text_color_set(DW_COLOR_INFO); dw_printf ("part 3\n"); 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"); + 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); + 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. + // 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); + + 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); + 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); + 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. 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); + 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 text_color_set(DW_COLOR_INFO); @@ -1114,15 +1301,46 @@ int main ( ) telemetry_name_message ("M0XER-3", "Vbat,Vsolar,Temp,Sat"); telemetry_unit_label_message ("M0XER-3", "V,V,C,,m"); - telemetry_data_base91 ("M0XER-3", "DyR.&^b!+", result); - telemetry_data_base91 ("M0XER-3", "x&G=!(8s!,", result); + telemetry_data_base91 ("M0XER-3", "DyR.&^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); } /* diff --git a/telemetry.h b/telemetry.h index d2b56e7..4ef9b62 100644 --- a/telemetry.h +++ b/telemetry.h @@ -2,9 +2,9 @@ /* 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); diff --git a/tocalls.txt b/tocalls.txt index 9b4dfba..19693b1 100644 --- a/tocalls.txt +++ b/tocalls.txt @@ -1,6 +1,9 @@ -APRS TO-CALL VERSION NUMBERS 27 Apr 2015 +APRS TO-CALL VERSION NUMBERS 26 Oct 2015 ------------------------------------------------------------------- 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 21 Apr 15 added APB2MF & APR2MF DL2MF - MF2APRS Radiosonde 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 11 Aug 14 added APCWP8 John GM7HHB, WinphoneAPRS 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 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 APH APHKxx for LA1BR tracker/digipeater APHAXn SM2APRS by PY2UEP + APHTxx HMTracker by IU0AAC API APICQx for ICQ APICxx for HA9MCQ Pic IGate APJ APJAxx JavAPRS @@ -109,6 +94,7 @@ a TOCALL number series: APM APMxxx MacAPRS, APMGxx MiniGate - Alex, AB0TJ APMIxx SQ3PLX http://microsat.com.pl/ + APMTxx LZ1PPL for tracker APN APNxxx Network nodes, digis, etc APN3xx Kantronics KPC-3 rom versions APN9xx Kantronics KPC-9612 Roms @@ -180,6 +166,7 @@ a TOCALL number series: APY350 Yaesu FTM-350 series APYTxx for YagTracker APZ APZxxx Experimental + APZ247 for UPRS NR0Q APZ0xx Xastir (old versions. See APX) APZMAJ Martyn M1MAJ DeLorme inReach Tracker APZMDR for HaMDR trackers - hessu * hes.iki.fi] diff --git a/tt_text.c b/tt_text.c index be2a96f..2b82f54 100644 --- a/tt_text.c +++ b/tt_text.c @@ -162,6 +162,7 @@ static const char grid[10][10][3] = #include #include +#include "direwolf.h" #include "textcolor.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 c; 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 c; int row, col; @@ -377,11 +378,12 @@ int tt_text_to_two_key (char *text, int quiet, char *buttons) * Inputs: c - One letter. * * quiet - True to suppress error messages. - * + * * Outputs: buttons - Sequence of two buttons to press. * "00" for error because this is probably * being used to build up a fixed length * string where positions are signficant. + * Must be at least 3 bytes. * * 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. -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 errors = 0; int found; - *b = '\0'; + strlcpy(buttons, "", 3); if (islower(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); dw_printf ("Letter to two digits: \"%c\" found where a letter is required.\n", c); } - strcpy (buttons, "00"); + strlcpy (buttons, "00", 3); 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 (col=0; col<4 && ! found; col++) { if (c == translate[row][col]) { - *b++ = '0' + row; - *b++ = '1' + col; - *b = '\0'; + buttons[0] = '0' + row; + buttons[1] = '1' + col; + buttons[2] = '\0'; found = 1; } } @@ -431,7 +432,7 @@ int tt_letter_to_two_digits (char c, int quiet, char *buttons) errors++; text_color_set (DW_COLOR_ERROR); dw_printf ("Letter to two digits: INTERNAL ERROR. Should not be here.\n"); - strcpy (buttons, "00"); + strlcpy (buttons, "00", 3); } 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 c; 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. */ - sprintf (stemp, "%04d", packed); + snprintf (stemp, sizeof(stemp), "%04d", packed); strcat (buttons, stemp); 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; @@ -577,7 +578,7 @@ int tt_text_to_satsq (char *text, int quiet, char *buttons) char uc[3]; - strcpy (buttons, ""); + strlcpy (buttons, "", buttonsize); /* 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 (col=0; col<10 && ! found; col++) { if (strcmp(uc,grid[row][col]) == 0) { - buttons[0] = row + '0'; - buttons[1] = col + '0'; - buttons[2] = text[2]; - buttons[3] = text[3]; - buttons[4] = '\0'; + + char btemp[8]; + + btemp[0] = row + '0'; + btemp[1] = col + '0'; + btemp[2] = text[2]; + btemp[3] = text[3]; + btemp[4] = '\0'; + + strlcpy (buttons, btemp, buttonsize); 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 c; 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 c; int row, col; @@ -846,25 +852,25 @@ int tt_two_key_to_text (char *buttons, int quiet, char *text) * Should contain exactly two digits. * * 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. - * If error, use 'x' as a placeholder because we are probably - * dealing with fixed length strings where position matters. + * Empty string on error. * * Returns: Number of errors detected. * *----------------------------------------------------------------*/ -// TODO: need to test - -int tt_two_digits_to_letter (char *buttons, int quiet, char *text) +int tt_two_digits_to_letter (const char *buttons, int quiet, char *text, size_t textsiz) { char c1 = buttons[0]; char c2 = buttons[1]; int row, col; int errors = 0; + char stemp2[2]; - strcpy (text, "x"); + strlcpy (text, "", textsiz); if (c1 >= '2' && c1 <= '9') { @@ -874,12 +880,14 @@ int tt_two_digits_to_letter (char *buttons, int quiet, char *text) col = c2 - '1'; 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 { errors++; - strcpy (text, "x"); + strlcpy (text, "", textsiz); if (! quiet) { text_color_set (DW_COLOR_ERROR); 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 { errors++; - strcpy (text, "x"); + strlcpy (text, "", textsiz); if (! quiet) { text_color_set (DW_COLOR_ERROR); dw_printf ("Two digits to letter: Second character \"%c\" must be in range of 1 through 4.\n", c2); @@ -897,7 +905,7 @@ int tt_two_digits_to_letter (char *buttons, int quiet, char *text) } else { errors++; - strcpy (text, "x"); + strlcpy (text, "", textsiz); if (! quiet) { text_color_set (DW_COLOR_ERROR); dw_printf ("Two digits to letter: First character \"%c\" must be in range of 2 through 9.\n", c1); @@ -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 c; 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 * - * Purpose: Convert the 4, 6, 10, or 12 digit DTMF representation of Maidenhead - * Grid Square Locator to normal text representation. + * Purpose: Convert the DTMF representation of + * Maidenhead Grid Square Locator to normal text representation. * * 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. * * 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. + * Zero length if any error. * * 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; - char *t; + const char *b; int errors = 0; - strcpy (text, ""); + strlcpy (text, "", textsiz); /* 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) { text_color_set (DW_COLOR_ERROR); dw_printf ("DTMF to Maidenhead Gridsquare Locator: Input \"%s\" must be exactly 4, 6, 10, or 12 digits.\n", buttons); } errors++; + strlcpy (text, "", textsiz); 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); } errors++; + strlcpy (text, "", textsiz); return (errors); } } + +/* Convert DTMF to normal representation. */ + b = buttons; - t = text; - errors += tt_two_digits_to_letter (b, quiet, t); - b += 2; - t++; + int n; - errors += tt_two_digits_to_letter (b, quiet, t); - b += 2; - t++; + for (n = 0; n < 6 && b < buttons+strlen(buttons); n++) { + if ((n % 2) == 0) { - if (strlen(buttons) > 4) { + /* Convert pairs of digits to letter. */ - *t++ = *b++; - *t++ = *b++; - *t = '\0'; + char t2[2]; - if (strlen(buttons) > 6) { - - 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; - 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; - t++; + } + else { - if (strlen(buttons) > 10) { + /* Copy the digits. */ - *t++ = *b++; - *t++ = *b++; - *t = '\0'; - } + char d3[3]; + d3[0] = *b++; + d3[1] = *b++; + d3[2] = '\0'; + strlcat (text, d3, textsiz); } } + if (errors != 0) { + strlcpy (text, "", textsiz); + } return (errors); } /* 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 * - * Purpose: Convert the 2, 4, 6, or 8 character Maidenhead - * Grid Square Locator to DTMF representation. + * Purpose: Convert normal text Maidenhead Grid Square Locator to DTMF representation. * - * Outputs: text - Maidenhead Grid Square locator in usual format. - * Length should be 2, 4, 6, or 8 with alternating letter or digit pairs. - * - * Inputs: buttons - Result with 4, 6, 10, or 12 digits. - * Each letter is replaced by two digits. + * Inputs: text - Maidenhead Grid Square locator in usual format. + * Length should be 1 to 6 pairs with alternating letter or digit pairs. * * 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. * *----------------------------------------------------------------*/ - -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 buttonsize) { - char *b; - char *t; 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) { 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++; return (errors); } - t = text; - b = buttons; + if (np < 1 || np > MAXMHPAIRS) { - if (toupper(t[0]) < 'A' || toupper(t[0]) > 'R' || toupper(t[1]) < 'A' || toupper(t[1]) > 'R') { if (! quiet) { - text_color_set(DW_COLOR_ERROR); - dw_printf("The first pair of characters in Maidenhead locator \"%s\" must be in range of A thru R.\n", text); + text_color_set (DW_COLOR_ERROR); + dw_printf ("Maidenhead Gridsquare Locator to DTMF: Input \"%s\" must be 1 to %d pairs of characters.\n", text, np); } - errors++; - return(errors); - } + errors++; + return (errors); + } - errors += tt_letter_to_two_digits (*t, quiet, b); - t++; - b += 2; + for (i = 0; i < np; i++) { - errors += tt_letter_to_two_digits (*t, quiet, b); - t++; - b += 2; + char t0 = text[i*2]; + char t1 = text[i*2+1]; - if (strlen(text) > 2) { - - if ( ! isdigit(t[0]) || ! isdigit(t[1])) { + 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 (! quiet) { text_color_set(DW_COLOR_ERROR); - dw_printf("The second pair of characters in Maidenhead locator \"%s\" must digits 0 thru 9.\n", text); + 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++; return(errors); } - *b++ = *t++; - *b++ = *t++; - *b = '\0'; + if (mhpair[i].min_ch == 'A') { /* Should be letters */ - if (strlen(text) > 4) { + char b3[3]; - if (toupper(t[0]) < 'A' || toupper(t[0]) > 'X' || toupper(t[1]) < 'A' || toupper(t[1]) > 'X') { - if (! quiet) { - text_color_set(DW_COLOR_ERROR); - dw_printf("The third pair of characters in Maidenhead locator \"%s\" must be in range of A thru X.\n", text); - } - errors++; - return(errors); - } + errors += tt_letter_to_two_digits (t0, quiet, b3); + strlcat (buttons, b3, buttonsize); - errors += tt_letter_to_two_digits (*t, quiet, b); - t++; - b += 2; + errors += tt_letter_to_two_digits (t1, quiet, b3); + strlcat (buttons, b3, buttonsize); + } + else { /* Should be digits */ - errors += tt_letter_to_two_digits (*t, quiet, b); - t++; - b += 2; + char b3[3]; - 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'; - } + b3[0] = t0; + b3[1] = t1; + b3[2] = '\0'; + strlcat (buttons, b3, buttonsize); } } + if (errors != 0) strlcpy (buttons, "", buttonsize); + return (errors); } /* 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 errors = 0; @@ -1395,13 +1402,13 @@ int main (int argc, char *argv[]) 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) { dw_printf ("Push buttons for Maidenhead Grid Square Locator:\n"); 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) { dw_printf ("Push buttons for satellite gridsquare:\n"); dw_printf ("\"%s\"\n", buttons); @@ -1442,7 +1449,7 @@ int main (int argc, char *argv[]) strcpy (buttons, argv[1]); for (n = 2; n < argc; n++) { - strcat (buttons, argv[n]); + strlcat (buttons, argv[n], sizeof(buttons)); } switch (tt_guess_type(buttons)) { @@ -1471,7 +1478,7 @@ int main (int argc, char *argv[]) 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) { dw_printf ("Decoded Maidenhead Locator from DTMF digits:\n"); dw_printf ("\"%s\"\n", text); @@ -1490,6 +1497,108 @@ int main (int argc, char *argv[]) #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 */ diff --git a/tt_text.h b/tt_text.h index 440b827..98ee8b0 100644 --- a/tt_text.h +++ b/tt_text.h @@ -4,28 +4,28 @@ /* 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. */ -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 */ \ No newline at end of file diff --git a/tt_user.c b/tt_user.c index 4489945..98ba081 100644 --- a/tt_user.c +++ b/tt_user.c @@ -427,8 +427,10 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, char *lo int i; - //text_color_set(DW_COLOR_DEBUG); - //dw_printf ("tt_user_heard (%s, %d, %c, %c, %s, ...)\n", callsign, ssid, overlay, symbol, loc_text); +// TODO: remove debug + + 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. @@ -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, 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)); strlcat (stemp, object_info, sizeof(stemp)); diff --git a/ttcalc.c b/ttcalc.c index e3f32cf..b122be4 100644 --- a/ttcalc.c +++ b/ttcalc.c @@ -237,7 +237,7 @@ int main (int argc, char *argv[]) * Convert to AX.25 frame. * 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); /* @@ -508,7 +508,7 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S case AF_INET: sa4 = (struct sockaddr_in *)pAddr; #if __WIN32__ - sprintf (pStringBuf, "%d.%d.%d.%d", sa4->sin_addr.S_un.S_un_b.s_b1, + snprintf (pStringBuf, StringBufSize, "%d.%d.%d.%d", sa4->sin_addr.S_un.S_un_b.s_b1, sa4->sin_addr.S_un.S_un_b.s_b2, sa4->sin_addr.S_un.S_un_b.s_b3, sa4->sin_addr.S_un.S_un_b.s_b4); @@ -519,7 +519,7 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S case AF_INET6: sa6 = (struct sockaddr_in6 *)pAddr; #if __WIN32__ - sprintf (pStringBuf, "%x:%x:%x:%x:%x:%x:%x:%x", + snprintf (pStringBuf, StringBufSize, "%x:%x:%x:%x:%x:%x:%x:%x", ntohs(((unsigned short *)(&(sa6->sin6_addr)))[0]), ntohs(((unsigned short *)(&(sa6->sin6_addr)))[1]), ntohs(((unsigned short *)(&(sa6->sin6_addr)))[2]), @@ -533,9 +533,9 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S #endif break; default: - sprintf (pStringBuf, "Invalid address family!"); + snprintf (pStringBuf, StringBufSize, "Invalid address family!"); } - assert (strlen(pStringBuf) < StringBufSize); + return pStringBuf; } /* end ia_to_text */ diff --git a/walk96.c b/walk96.c index 619ed45..ef1c34c 100644 --- a/walk96.c +++ b/walk96.c @@ -32,47 +32,43 @@ #include #include - -#if __WIN32__ #include -#include -#else -#define __USE_XOPEN2KXSI 1 -#define __USE_XOPEN 1 -#include -#include -#include -#include -#include -#include -#include -#endif - #include #include +#include #include "direwolf.h" #include "config.h" #include "ax25_pad.h" #include "textcolor.h" #include "latlong.h" -#include "nmea.h" +#include "dwgps.h" #include "encode_aprs.h" #include "serial_port.h" +#include "kiss_frame.h" #define MYCALL "WB2OSZ" /************ Change this if you use it!!! ***************/ +#define HOWLONG 20 /* Run for 20 seconds then quit. */ + + + 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; 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. tnc = serial_port_open ("COM5", 9600); @@ -82,35 +78,60 @@ main (int argc, char *argv[]) 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)); - SLEEP_MS(500); + + + // USB GPS happens to be COM22 memset (&config, 0, sizeof(config)); - strcpy (config.nmea_port, "COM1"); - nmea_init (&config); + strlcpy (config.gpsnmea_port, "COM22", sizeof(config.nmea_port)); + + dwgps_init (&config, debug_gps); + + SLEEP_SEC(1); /* Wait for sample before reading. */ + + for (n=0; n 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. - serial_port_write (tnc, "\xc0\xff\c0", 3); + serial_port_write (tnc, "\xc0\xff\xc0", 3); SLEEP_MS(100); - + exit (EXIT_SUCCESS); } /* 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; char comment[50]; 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]; + +// TODO (high, bug): Why do we see !4237.13N/07120.84W=PHG0000... when all values set to unknown. + + info_len = encode_position (messaging, compressed, lat, lon, (int)(DW_METERS_TO_FEET(alt)), - '/', '?', // TODO: look up code for person. - G_UNKNOWN, G_UNKNOWN, G_UNKNOWN, "", // PHG - (int)course, (int)knots, + '/', '=', + G_UNKNOWN, G_UNKNOWN, G_UNKNOWN, "", // PHGd + (int)roundf(course), (int)roundf(knots), 445.925, 0, 0, comment, 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); dw_printf ("%s\n", position_report); diff --git a/xmit.c b/xmit.c index f468279..bfacfcb 100644 --- a/xmit.c +++ b/xmit.c @@ -59,13 +59,6 @@ #include #include -//#include -//#include - -//#if __WIN32__ -//#include -//#endif - #include "direwolf.h" #include "ax25_pad.h" #include "textcolor.h" @@ -449,7 +442,7 @@ static void * xmit_thread (void *arg) ssid = ax25_get_ssid(pp, AX25_DESTINATION); } else { - strcpy (dest, ""); + strlcpy (dest, "", sizeof(dest)); } 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. */ - strcpy (msg, orig_msg); + strlcpy (msg, orig_msg, sizeof(msg)); for (p=msg; *p!='\0'; p++) { if (*p == '"') *p = ' '; } #if __WIN32__ - sprintf (cmd, "%s %d \"%s\" >nul", script, c, msg); + snprintf (cmd, sizeof(cmd), "%s %d \"%s\" >nul", script, c, msg); #else - sprintf (cmd, "%s %d \"%s\"", script, c, msg); + snprintf (cmd, sizeof(cmd), "%s %d \"%s\"", script, c, msg); #endif //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); ignore = getcwd (cwd, sizeof(cwd)); - strcpy (path, getenv("PATH")); + strlcpy (path, getenv("PATH"), sizeof(path)); dw_printf ("CWD = %s\n", cwd); dw_printf ("PATH = %s\n", path);