mirror of https://github.com/wb2osz/direwolf.git
				
				
				
			
						commit
						188cb0a6a3
					
				| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -6,6 +6,10 @@ z*
 | 
			
		|||
*~
 | 
			
		||||
*.xlsx
 | 
			
		||||
*.stackdump
 | 
			
		||||
direwolf.conf
 | 
			
		||||
*.wav
 | 
			
		||||
fsk_fast_filter.h
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Object files
 | 
			
		||||
*.o
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										25
									
								
								CHANGES.md
								
								
								
								
							
							
						
						
									
										25
									
								
								CHANGES.md
								
								
								
								
							| 
						 | 
				
			
			@ -3,6 +3,31 @@
 | 
			
		|||
 | 
			
		||||
----------
 | 
			
		||||
 | 
			
		||||
## Version 1.3  -- Development snapshot H  --  November 2015 ##
 | 
			
		||||
 | 
			
		||||
### New Feature: ###
 | 
			
		||||
 | 
			
		||||
- New experimental demodulator.  More details later.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
----------
 | 
			
		||||
 | 
			
		||||
## 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: ###
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										508
									
								
								Makefile.linux
								
								
								
								
							
							
						
						
									
										508
									
								
								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,18 +218,28 @@ 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 \
 | 
			
		||||
		fcs_calc.o ax25_pad.o \
 | 
			
		||||
		decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
 | 
			
		||||
		gen_tone.o audio.o 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 \
 | 
			
		||||
		ptt.o beacon.o encode_aprs.o latlong.o encode_aprs.o latlong.o textcolor.o \
 | 
			
		||||
		dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o 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 dwgpsd.o serial_port.o symbols.o ax25_pad.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o misc.a
 | 
			
		||||
	$(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 dwgpsd.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,93 @@ 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 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
 | 
			
		||||
#
 | 
			
		||||
# ./README.md is an overview for the project main page.
 | 
			
		||||
# doc/README.md contains an overview of the PDF file contents and is more useful here.
 | 
			
		||||
#
 | 
			
		||||
	$(INSTALL) -D --mode=644 doc/README.md $(INSTALLDIR)/share/doc/direwolf/README.md
 | 
			
		||||
	$(INSTALL) -D --mode=644 doc/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
 | 
			
		||||
#
 | 
			
		||||
# Various sample config and other files go into examples under the doc directory.
 | 
			
		||||
# When building from source, these can be put in home directory with "make install-conf".
 | 
			
		||||
# When installed from .DEB or .RPM package, the user will need to copy these to
 | 
			
		||||
# the home directory or other desired location.
 | 
			
		||||
#
 | 
			
		||||
	$(INSTALL) -D --mode=644 direwolf.conf $(INSTALLDIR)/share/doc/direwolf/examples/direwolf.conf
 | 
			
		||||
	$(INSTALL) -D --mode=644 dw-start.sh $(INSTALLDIR)/share/doc/direwolf/examples/dw-start.sh
 | 
			
		||||
	$(INSTALL) -D --mode=644 sdr.conf $(INSTALLDIR)/share/doc/direwolf/examples/sdr.conf
 | 
			
		||||
	$(INSTALL) -D --mode=644 telemetry-toolkit/telem-m0xer-3.txt $(INSTALLDIR)/share/doc/direwolf/examples/telem-m0xer-3.txt
 | 
			
		||||
	$(INSTALL) -D --mode=644 telemetry-toolkit/telem-balloon.conf $(INSTALLDIR)/share/doc/direwolf/examples/telem-balloon.conf
 | 
			
		||||
	$(INSTALL) -D --mode=644 telemetry-toolkit/telem-volts.conf $(INSTALLDIR)/share/doc/direwolf/examples/telem-volts.conf
 | 
			
		||||
#
 | 
			
		||||
# "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 +543,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.
 | 
			
		||||
| 
						 | 
				
			
			@ -391,6 +553,7 @@ install : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx ge
 | 
			
		|||
.PHONY: install-conf
 | 
			
		||||
install-conf : direwolf.conf
 | 
			
		||||
	cp direwolf.conf ~
 | 
			
		||||
	cp sdr.conf ~
 | 
			
		||||
	cp telemetry-toolkit/telem-m0xer-3.txt ~
 | 
			
		||||
	cp telemetry-toolkit/telem-*.conf ~
 | 
			
		||||
ifneq ($(wildcard $(HOME)/Desktop),)
 | 
			
		||||
| 
						 | 
				
			
			@ -409,41 +572,124 @@ 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 dedupe.c \
 | 
			
		||||
		pfilter.o ax25_pad.o fcs_calc.o tq.o textcolor.o \
 | 
			
		||||
		decode_aprs.o dwgpsnmea.o dwgps.o dwgpsd.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 dwgpsd.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 +697,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 +741,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) -- $^
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										184
									
								
								Makefile.macosx
								
								
								
								
							
							
						
						
									
										184
									
								
								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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										301
									
								
								Makefile.win
								
								
								
								
							
							
						
						
									
										301
									
								
								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,131 @@ 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?
 | 
			
		||||
# Verify that single bit fixup increases the count.
 | 
			
		||||
 | 
			
		||||
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 dedupe.c \
 | 
			
		||||
		pfilter.o ax25_pad.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.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -201,14 +362,13 @@ demod_9600.o : tune.h
 | 
			
		|||
demod_afsk.o : tune.h
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c fsk_demod_agc.h \
 | 
			
		||||
testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.o fsk_demod_agc.h \
 | 
			
		||||
		hdlc_rec.o hdlc_rec2.o multi_modem.o \
 | 
			
		||||
		rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o latlong.o symbols.o textcolor.o telemetry.o \
 | 
			
		||||
		regex.a misc.a \
 | 
			
		||||
		
 | 
			
		||||
		dwgpsnmea.o dwgps.o serial_port.o tt_text.o regex.a misc.a
 | 
			
		||||
	rm -f atest.exe
 | 
			
		||||
	$(CC) $(CFLAGS) -o atest $^
 | 
			
		||||
	./atest -P E ../02_Track_2.wav | grep "packets decoded in" >atest.out
 | 
			
		||||
	./atest -P GGG- -F 0 ../02_Track_2.wav | grep "packets decoded in" >atest.out
 | 
			
		||||
	echo " " > tune.h
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -236,54 +396,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 +419,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 +444,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 +547,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 +579,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"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										19
									
								
								aclients.c
								
								
								
								
							
							
						
						
									
										19
									
								
								aclients.c
								
								
								
								
							| 
						 | 
				
			
			@ -71,6 +71,7 @@
 | 
			
		|||
#include <assert.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "direwolf.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -115,7 +116,7 @@ static char * ia_to_text (int  Family, void * pAddr, char * pStringBuf, size_t S
 | 
			
		|||
	  case AF_INET:
 | 
			
		||||
	    sa4 = (struct sockaddr_in *)pAddr;
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
	    sprintf (pStringBuf, "%d.%d.%d.%d", sa4->sin_addr.S_un.S_un_b.s_b1,
 | 
			
		||||
	    snprintf (pStringBuf, StringBufSize, "%d.%d.%d.%d", sa4->sin_addr.S_un.S_un_b.s_b1,
 | 
			
		||||
						sa4->sin_addr.S_un.S_un_b.s_b2,
 | 
			
		||||
						sa4->sin_addr.S_un.S_un_b.s_b3,
 | 
			
		||||
						sa4->sin_addr.S_un.S_un_b.s_b4);
 | 
			
		||||
| 
						 | 
				
			
			@ -126,7 +127,7 @@ static char * ia_to_text (int  Family, void * pAddr, char * pStringBuf, size_t S
 | 
			
		|||
	  case AF_INET6:
 | 
			
		||||
	    sa6 = (struct sockaddr_in6 *)pAddr;
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
	    sprintf (pStringBuf, "%x:%x:%x:%x:%x:%x:%x:%x",  
 | 
			
		||||
	    snprintf (pStringBuf, StringBufSize, "%x:%x:%x:%x:%x:%x:%x:%x",
 | 
			
		||||
					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[0]),
 | 
			
		||||
					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[1]),
 | 
			
		||||
					ntohs(((unsigned short *)(&(sa6->sin6_addr)))[2]),
 | 
			
		||||
| 
						 | 
				
			
			@ -140,7 +141,7 @@ static char * ia_to_text (int  Family, void * pAddr, char * pStringBuf, size_t S
 | 
			
		|||
#endif
 | 
			
		||||
	    break;
 | 
			
		||||
	  default:
 | 
			
		||||
	    sprintf (pStringBuf, "Invalid address family!");
 | 
			
		||||
	    snprintf (pStringBuf, StringBufSize, "Invalid address family!");
 | 
			
		||||
	}
 | 
			
		||||
	assert (strlen(pStringBuf) < StringBufSize);
 | 
			
		||||
	return pStringBuf;
 | 
			
		||||
| 
						 | 
				
			
			@ -222,20 +223,20 @@ int main (int argc, char *argv[])
 | 
			
		|||
	  char stemp[100];
 | 
			
		||||
	  char *p;
 | 
			
		||||
	
 | 
			
		||||
	  strcpy (stemp, argv[j+1]);
 | 
			
		||||
	  strlcpy (stemp, argv[j+1], sizeof(stemp));
 | 
			
		||||
	  p = strtok (stemp, "=");
 | 
			
		||||
	  if (p == NULL) {
 | 
			
		||||
	    printf ("Internal error 1\n");
 | 
			
		||||
	    exit (1);
 | 
			
		||||
	  }
 | 
			
		||||
	  strcpy (hostname[j], "localhost");
 | 
			
		||||
	  strcpy (port[j], p);
 | 
			
		||||
	  strlcpy (hostname[j], "localhost", sizeof(hostname[j]));
 | 
			
		||||
	  strlcpy (port[j], p, sizeof(port[j]));
 | 
			
		||||
	  p = strtok (NULL, "=");
 | 
			
		||||
	  if (p == NULL) {
 | 
			
		||||
	    printf ("Missing description after %s\n", port[j]);
 | 
			
		||||
	    exit (1);
 | 
			
		||||
	  }
 | 
			
		||||
	  strcpy (description[j], p);
 | 
			
		||||
	  strlcpy (description[j], p, sizeof(description[j]));
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	//printf ("_WIN32_WINNT = %04x\n", _WIN32_WINNT);
 | 
			
		||||
| 
						 | 
				
			
			@ -579,14 +580,14 @@ static void * client_thread_net (void *arg)
 | 
			
		|||
 | 
			
		||||
	    //printf ("server %d, portx = %d\n", my_index, mon_cmd.portx);
 | 
			
		||||
 | 
			
		||||
	    use_chan == mon_cmd.portx;
 | 
			
		||||
	    use_chan = mon_cmd.portx;
 | 
			
		||||
	    memset (&alevel, 0xff, sizeof(alevel));
 | 
			
		||||
	    pp = ax25_from_frame ((unsigned char *)(data+1), mon_cmd.data_len-1, alevel);
 | 
			
		||||
	    assert (pp != NULL);
 | 
			
		||||
	    ax25_format_addrs (pp, result);
 | 
			
		||||
	    info_len = ax25_get_info (pp, (unsigned char **)(&pinfo));
 | 
			
		||||
	    pinfo[info_len] = '\0';
 | 
			
		||||
	    strcat (result, pinfo);
 | 
			
		||||
	    strlcat (result, pinfo, sizeof(result));
 | 
			
		||||
	    for (p=result; *p!='\0'; p++) {
 | 
			
		||||
	      if (! isprint(*p)) *p = ' ';
 | 
			
		||||
	    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										370
									
								
								aprs_tt.c
								
								
								
								
							
							
						
						
									
										370
									
								
								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<len; k++) {
 | 
			
		||||
	      mc = tt_config.ttloc_ptr[ipat].pattern[k];
 | 
			
		||||
| 
						 | 
				
			
			@ -1342,7 +1351,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
 | 
			
		|||
	             char stemp[2];
 | 
			
		||||
		     stemp[0] = e[k];
 | 
			
		||||
		     stemp[1] = '\0';
 | 
			
		||||
		     strlcat (xstr, stemp, sizeof(xstr));
 | 
			
		||||
		     strlcat (xstr, stemp, valstrsize);
 | 
			
		||||
		   }
 | 
			
		||||
		   else {
 | 
			
		||||
		     match = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -1354,7 +1363,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
 | 
			
		|||
	             char stemp[2];
 | 
			
		||||
		     stemp[0] = e[k];
 | 
			
		||||
		     stemp[1] = '\0';
 | 
			
		||||
		     strlcat (ystr, stemp, sizeof(ystr));
 | 
			
		||||
		     strlcat (ystr, stemp, valstrsize);
 | 
			
		||||
		   }
 | 
			
		||||
		   else {
 | 
			
		||||
		     match = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -1366,7 +1375,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
 | 
			
		|||
	             char stemp[2];
 | 
			
		||||
		     stemp[0] = e[k];
 | 
			
		||||
		     stemp[1] = '\0';
 | 
			
		||||
		     strlcat (zstr, stemp, sizeof(zstr));
 | 
			
		||||
		     strlcat (zstr, stemp, valstrsize);
 | 
			
		||||
		   }
 | 
			
		||||
		   else {
 | 
			
		||||
		     match = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -1378,7 +1387,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
 | 
			
		|||
	             char stemp[2];
 | 
			
		||||
		     stemp[0] = e[k];
 | 
			
		||||
		     stemp[1] = '\0';
 | 
			
		||||
		     strlcat (bstr, stemp, sizeof(bstr));
 | 
			
		||||
		     strlcat (bstr, stemp, valstrsize);
 | 
			
		||||
		   }
 | 
			
		||||
		   else {
 | 
			
		||||
		     match = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -1390,7 +1399,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
 | 
			
		|||
	             char stemp[2];
 | 
			
		||||
		     stemp[0] = e[k];
 | 
			
		||||
		     stemp[1] = '\0';
 | 
			
		||||
		     strlcat (dstr, stemp, sizeof(dstr));
 | 
			
		||||
		     strlcat (dstr, stemp, valstrsize);
 | 
			
		||||
		   }
 | 
			
		||||
		   else {
 | 
			
		||||
		     match = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -1447,7 +1456,6 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
 | 
			
		|||
static int parse_comment (char *e)
 | 
			
		||||
{
 | 
			
		||||
	int len;
 | 
			
		||||
	int n;
 | 
			
		||||
 | 
			
		||||
	assert (*e == 'C');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1553,7 +1561,7 @@ static void raw_tt_data_to_app (int chan, char *msg)
 | 
			
		|||
	  alevel.mark = -2;
 | 
			
		||||
	  alevel.space = -2;
 | 
			
		||||
 | 
			
		||||
	  dlq_append (DLQ_REC_FRAME, chan, -1, pp, alevel, RETRY_NONE, "tt");
 | 
			
		||||
	  dlq_append (DLQ_REC_FRAME, chan, -1, 0, pp, alevel, RETRY_NONE, "tt");
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
| 
						 | 
				
			
			@ -1579,7 +1587,7 @@ static void raw_tt_data_to_app (int chan, char *msg)
 | 
			
		|||
 *					is one line of text.
 | 
			
		||||
 *				  2 = Also remove any trailing whitespace.
 | 
			
		||||
 *
 | 
			
		||||
 *		maxresult	- Amount of space available for result.
 | 
			
		||||
 *		resultsiz	- Amount of space available for result.
 | 
			
		||||
 *
 | 
			
		||||
 * Outputs:	result		- Output captured from running command.
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -1594,21 +1602,21 @@ static void raw_tt_data_to_app (int chan, char *msg)
 | 
			
		|||
 *
 | 
			
		||||
 *----------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
int dw_run_cmd (char *cmd, int oneline, char *result, int maxresult) 
 | 
			
		||||
int dw_run_cmd (char *cmd, int oneline, char *result, size_t resultsiz) 
 | 
			
		||||
{
 | 
			
		||||
	FILE *fp;
 | 
			
		||||
 | 
			
		||||
	strlcpy (result, "", sizeof(result));
 | 
			
		||||
	strlcpy (result, "", resultsiz);
 | 
			
		||||
 | 
			
		||||
	fp = popen (cmd, "r");
 | 
			
		||||
	if (fp != NULL) {
 | 
			
		||||
	  int remaining = maxresult;
 | 
			
		||||
	  int remaining = (int)resultsiz;
 | 
			
		||||
	  char *pr = result;
 | 
			
		||||
	  int err;
 | 
			
		||||
 | 
			
		||||
	  while (remaining > 2 && fgets(pr, remaining, fp) != NULL) {
 | 
			
		||||
	    pr = result + strlen(result);
 | 
			
		||||
	    remaining = maxresult - strlen(result);
 | 
			
		||||
	    remaining = (int)resultsiz - strlen(result);
 | 
			
		||||
	  }
 | 
			
		||||
 | 
			
		||||
	  if ((err = pclose(fp)) != 0) {	 
 | 
			
		||||
| 
						 | 
				
			
			@ -1670,106 +1678,182 @@ int dw_run_cmd (char *cmd, int oneline, char *result, int maxresult)
 | 
			
		|||
 *
 | 
			
		||||
 * Description:	Run unit test like this:
 | 
			
		||||
 *
 | 
			
		||||
 *		rm a.exe ; gcc tt_text.c -DTT_MAIN -DDEBUG aprs_tt.c latlong.c strtok_r.o utm/LatLong-UTMconversion.c ; ./a.exe 
 | 
			
		||||
 *
 | 
			
		||||
 * Bugs:	No automatic checking.
 | 
			
		||||
 *		Just eyeball it to see if things look right.
 | 
			
		||||
 *			rm a.exe ; gcc tt_text.c -DTT_MAIN -Igeotranz aprs_tt.c latlong.o textcolor.o geotranz.a misc.a  ; ./a.exe
 | 
			
		||||
 *		or
 | 
			
		||||
 *			make ttest
 | 
			
		||||
 *
 | 
			
		||||
 *----------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// TODO:  add this to "make check"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if TT_MAIN
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Regression test for the parsing.
 | 
			
		||||
 * It does not maintain any history so abbreviation will not invoke previous full call.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
void text_color_set (dw_color_t c) { return; }
 | 
			
		||||
 | 
			
		||||
int dw_printf (const char *fmt, ...) 
 | 
			
		||||
{
 | 
			
		||||
	va_list args;
 | 
			
		||||
	int len;
 | 
			
		||||
static const struct {
 | 
			
		||||
	char *toneseq;		/* Tone sequence in. */
 | 
			
		||||
	
 | 
			
		||||
	va_start (args, fmt);
 | 
			
		||||
	len = vprintf (fmt, args);
 | 
			
		||||
	va_end (args);
 | 
			
		||||
	return (len);
 | 
			
		||||
}
 | 
			
		||||
	char *callsign;		/* Expected results... */
 | 
			
		||||
	char *ssid;
 | 
			
		||||
	char *symbol;
 | 
			
		||||
	char *freq;
 | 
			
		||||
	char *comment;
 | 
			
		||||
	char *lat;
 | 
			
		||||
	char *lon;
 | 
			
		||||
	char *dao;
 | 
			
		||||
} testcases[] = {
 | 
			
		||||
 | 
			
		||||
  /* Callsigns & abbreviations. */
 | 
			
		||||
 | 
			
		||||
	{ "A9A2B42A7A7C71#",	"WB4APR", "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T  !" }, 	/* WB4APR/7 */
 | 
			
		||||
	{ "A27773#",		"277",    "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T  !" }, 	/* abbreviated form */
 | 
			
		||||
	/* Example in http://www.aprs.org/aprstt/aprstt-coding24.txt has a bad checksum! */
 | 
			
		||||
	/* Bad checksum for "2777".  Expected 3 but received 6. */
 | 
			
		||||
	{ "A27776#",		"",       "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T  !" },	/* Expect error message. */
 | 
			
		||||
	
 | 
			
		||||
	/* Bad checksum for "2A7A7C7".  E xpected 5 but received 1. */
 | 
			
		||||
	{ "A2A7A7C71#",		"",       "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T  !" },	/* Spelled suffix, overlay, checksum */
 | 
			
		||||
	{ "A27773#",		"277",    "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T  !" },	/* Suffix digits, overlay, checksum */
 | 
			
		||||
 | 
			
		||||
	{ "A9A2B26C7D9D71#",	"WB2OSZ", "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T  !" },	/* WB2OSZ/7 numeric overlay */
 | 
			
		||||
	{ "A67979#",		"679",    "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T  !" },	/* abbreviated form */
 | 
			
		||||
 | 
			
		||||
	{ "A9A2B26C7D9D5A9#",	"WB2OSZ", "12", "JA", "", "", "-999999.0000", "-999999.0000", "!T  !" },	/* WB2OSZ/J letter overlay */
 | 
			
		||||
	{ "A6795A7#",		"679",    "12", "JA", "", "", "-999999.0000", "-999999.0000", "!T  !" },	/* abbreviated form */
 | 
			
		||||
 | 
			
		||||
	{ "A277#",		"277",    "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T  !" },	/* Tactical call "277" no overlay and no checksum */
 | 
			
		||||
 | 
			
		||||
  /* Locations */
 | 
			
		||||
 | 
			
		||||
	{ "B01*A67979#",	"679",    "12", "7A", "", "", "12.2500", "56.2500", "!T1 !" },
 | 
			
		||||
	{ "B988*A67979#",	"679",    "12", "7A", "", "", "12.5000", "56.5000", "!T88!" },
 | 
			
		||||
 | 
			
		||||
	{ "B51000125*A67979#",	"679",    "12", "7A", "", "", "52.7907", "0.8309", "!TB5!" },		/* expect about 52.79  +0.83 */
 | 
			
		||||
 | 
			
		||||
	{ "B5206070*A67979#",	"679",    "12", "7A", "", "", "37.9137", "-81.1366", "!TB5!" },		/* Try to get from Hilltop Tower to Archery & Target Range. */
 | 
			
		||||
													/* Latitude comes out ok, 37.9137 -> 55.82 min. */
 | 
			
		||||
													/* Longitude -81.1254 -> 8.20 min */
 | 
			
		||||
	{ "B21234*A67979#",	"679",    "12", "7A", "", "", "12.3400", "56.1200", "!TB2!" },
 | 
			
		||||
	{ "B533686*A67979#",	"679",    "12", "7A", "", "", "37.9222", "81.1143", "!TB5!" },
 | 
			
		||||
 | 
			
		||||
  /* Comments */
 | 
			
		||||
 | 
			
		||||
	{ "C1",			"",       "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T  !" },
 | 
			
		||||
	{ "C2",			"",       "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T  !" },
 | 
			
		||||
	{ "C146520",		"",       "12", "\\A", "146.520MHz", "", "-999999.0000", "-999999.0000", "!T  !" },
 | 
			
		||||
	{ "C7788444222550227776669660333666990122223333",
 | 
			
		||||
	                        "",       "12", "\\A", "", "QUICK BROWN FOX 123", "-999999.0000", "-999999.0000", "!T  !" },
 | 
			
		||||
  /* Macros */
 | 
			
		||||
 | 
			
		||||
	{ "88345",		"BIKE 345", "0", "/b", "", "", "12.5000", "56.5000", "!T88!" },
 | 
			
		||||
 | 
			
		||||
  /* 10 digit representation for callsign & satellite grid. WB4APR near 39.5, -77   */
 | 
			
		||||
 | 
			
		||||
	{ "AC9242771558*BA1819", "WB4APR", "12", "\\A", "", "", "39.5000", "-77.0000", "!TBA!" },
 | 
			
		||||
	{ "18199242771558",	 "WB4APR", "12", "\\A", "", "", "39.5000", "-77.0000", "!TBA!" },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int test_num;
 | 
			
		||||
static int error_count;
 | 
			
		||||
 | 
			
		||||
static void check_result (void)
 | 
			
		||||
{
 | 
			
		||||
	char stemp[32];
 | 
			
		||||
 | 
			
		||||
	text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	dw_printf ("callsign=\"%s\", ssid=%d, symbol=\"%c%c\", freq=\"%s\", comment=\"%s\", lat=%.4f, lon=%.4f, dao=\"%s\"\n", 
 | 
			
		||||
		m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code, m_freq, m_comment, m_latitude, m_longitude, m_dao);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (strcmp(m_callsign, testcases[test_num].callsign) != 0) {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("ERROR: Expected \"%s\" for callsign.\n", testcases[test_num].callsign);
 | 
			
		||||
	  error_count++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	snprintf (stemp, sizeof(stemp), "%d", m_ssid);
 | 
			
		||||
	if (strcmp(stemp, testcases[test_num].ssid) != 0) {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("ERROR: Expected \"%s\" for SSID.\n", testcases[test_num].ssid);
 | 
			
		||||
	  error_count++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	stemp[0] = m_symtab_or_overlay;
 | 
			
		||||
	stemp[1] = m_symbol_code;
 | 
			
		||||
	stemp[2] = '\0';
 | 
			
		||||
	if (strcmp(stemp, testcases[test_num].symbol) != 0) {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("ERROR: Expected \"%s\" for Symbol.\n", testcases[test_num].symbol);
 | 
			
		||||
	  error_count++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strcmp(m_freq, testcases[test_num].freq) != 0) {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("ERROR: Expected \"%s\" for Freq.\n", testcases[test_num].freq);
 | 
			
		||||
	  error_count++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strcmp(m_comment, testcases[test_num].comment) != 0) {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("ERROR: Expected \"%s\" for Comment.\n", testcases[test_num].comment);
 | 
			
		||||
	  error_count++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	snprintf (stemp, sizeof(stemp), "%.4f", m_latitude);
 | 
			
		||||
	if (strcmp(stemp, testcases[test_num].lat) != 0) {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("ERROR: Expected \"%s\" for Latitude.\n", testcases[test_num].lat);
 | 
			
		||||
	  error_count++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	snprintf (stemp, sizeof(stemp), "%.4f", m_longitude);
 | 
			
		||||
	if (strcmp(stemp, testcases[test_num].lon) != 0) {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("ERROR: Expected \"%s\" for Longitude.\n", testcases[test_num].lon);
 | 
			
		||||
	  error_count++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strcmp(m_dao, testcases[test_num].dao) != 0) {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("ERROR: Expected \"%s\" for DAO.\n", testcases[test_num].dao);
 | 
			
		||||
	  error_count++;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int main (int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	char text[256], buttons[256];
 | 
			
		||||
	int n;
 | 
			
		||||
 | 
			
		||||
	dw_printf ("Hello, world!\n");
 | 
			
		||||
 | 
			
		||||
	aprs_tt_init (NULL);
 | 
			
		||||
 | 
			
		||||
	//if (argc < 2) {
 | 
			
		||||
	  //dw_printf ("Supply text string on command line.\n");
 | 
			
		||||
	  //exit (1);
 | 
			
		||||
	//}
 | 
			
		||||
	error_count = 0;
 | 
			
		||||
 | 
			
		||||
/* Callsigns & abbreviations. */
 | 
			
		||||
	for (test_num = 0; test_num < sizeof(testcases) / sizeof(testcases[0]); test_num++) {
 | 
			
		||||
 | 
			
		||||
	aprs_tt_sequence (0, "A9A2B42A7A7C71#");		/* WB4APR/7 */
 | 
			
		||||
	aprs_tt_sequence (0, "A27773#");			/* abbreviated form */
 | 
			
		||||
	/* Example in http://www.aprs.org/aprstt/aprstt-coding24.txt has a bad checksum! */
 | 
			
		||||
	aprs_tt_sequence (0, "A27776#");			/* Expect error message. */
 | 
			
		||||
	  text_color_set(DW_COLOR_INFO);
 | 
			
		||||
	  dw_printf ("\nTest case %d: %s\n", test_num, testcases[test_num].toneseq);
 | 
			
		||||
 | 
			
		||||
	aprs_tt_sequence (0, "A2A7A7C71#");		/* Spelled suffix, overlay, checksum */
 | 
			
		||||
	aprs_tt_sequence (0, "A27773#");			/* Suffix digits, overlay, checksum */
 | 
			
		||||
	  aprs_tt_sequence (0, testcases[test_num].toneseq);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	aprs_tt_sequence (0, "A9A2B26C7D9D71#");		/* WB2OSZ/7 numeric overlay */
 | 
			
		||||
	aprs_tt_sequence (0, "A67979#");			/* abbreviated form */
 | 
			
		||||
	if (error_count != 0) {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("\n\nTEST FAILED, Total of %d errors.\n", error_count);
 | 
			
		||||
	  return (EXIT_FAILURE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	aprs_tt_sequence (0, "A9A2B26C7D9D5A9#");	/* WB2OSZ/J letter overlay */
 | 
			
		||||
	aprs_tt_sequence (0, "A6795A7#");		/* abbreviated form */
 | 
			
		||||
 | 
			
		||||
	aprs_tt_sequence (0, "A277#");			/* Tactical call "277" no overlay and no checksum */
 | 
			
		||||
 | 
			
		||||
/* Locations */
 | 
			
		||||
 | 
			
		||||
	aprs_tt_sequence (0, "B01*A67979#"); 
 | 
			
		||||
	aprs_tt_sequence (0, "B988*A67979#"); 
 | 
			
		||||
 | 
			
		||||
	/* expect about 52.79  +0.83 */
 | 
			
		||||
	aprs_tt_sequence (0, "B51000125*A67979#"); 
 | 
			
		||||
 | 
			
		||||
	/* Try to get from Hilltop Tower to Archery & Target Range. */
 | 
			
		||||
	/* Latitude comes out ok, 37.9137 -> 55.82 min. */
 | 
			
		||||
	/* Longitude -81.1254 -> 8.20 min */
 | 
			
		||||
 | 
			
		||||
	aprs_tt_sequence (0, "B5206070*A67979#"); 
 | 
			
		||||
 | 
			
		||||
	aprs_tt_sequence (0, "B21234*A67979#"); 
 | 
			
		||||
	aprs_tt_sequence (0, "B533686*A67979#"); 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Comments */
 | 
			
		||||
 | 
			
		||||
	aprs_tt_sequence (0, "C1");
 | 
			
		||||
	aprs_tt_sequence (0, "C2");
 | 
			
		||||
	aprs_tt_sequence (0, "C146520");
 | 
			
		||||
	aprs_tt_sequence (0, "C7788444222550227776669660333666990122223333");
 | 
			
		||||
 | 
			
		||||
/* Macros */
 | 
			
		||||
 | 
			
		||||
	aprs_tt_sequence (0, "88345");
 | 
			
		||||
 | 
			
		||||
/* 10 digit representation for callsign & satellite grid. WB4APR near 39.5, -77   */
 | 
			
		||||
 | 
			
		||||
	aprs_tt_sequence (0, "AC9242771558*BA1819");
 | 
			
		||||
	aprs_tt_sequence (0, "18199242771558");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	return(0);
 | 
			
		||||
	text_color_set(DW_COLOR_REC);
 | 
			
		||||
	dw_printf ("\n\nAll tests passed.\n");
 | 
			
		||||
	return (EXIT_SUCCESS);
 | 
			
		||||
 | 
			
		||||
}  /* end main */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif		
 | 
			
		||||
 | 
			
		||||
/* end aprs_tt.c */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										132
									
								
								atest.c
								
								
								
								
							
							
						
						
									
										132
									
								
								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,12 @@ 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.  */
 | 
			
		||||
 | 
			
		||||
static int sample_number = -1;		/* Sample number from the file. */
 | 
			
		||||
					/* Incremented only for channel 0. */
 | 
			
		||||
					/* Use to print timestamp, relative to beginning */
 | 
			
		||||
					/* of file, when frame was decoded. */
 | 
			
		||||
 | 
			
		||||
int main (int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -171,7 +181,6 @@ int main (int argc, char *argv[])
 | 
			
		|||
	time_t start_time;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if defined(EXPERIMENT_G) || defined(EXPERIMENT_H)
 | 
			
		||||
	int j;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -245,7 +254,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 +270,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 +281,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 +306,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 +325,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 +353,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 +393,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 +419,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 +455,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 +497,12 @@ 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 (c == 0) sample_number++;
 | 
			
		||||
 | 
			
		||||
#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 +529,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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -539,16 +578,16 @@ int audio_get (int a)
 | 
			
		|||
 | 
			
		||||
void rdq_append (rrbb_t rrbb)
 | 
			
		||||
{
 | 
			
		||||
	int chan;
 | 
			
		||||
	int chan, subchan, slice;
 | 
			
		||||
	alevel_t alevel;
 | 
			
		||||
	int subchan;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	chan = rrbb_get_chan(rrbb);
 | 
			
		||||
	subchan = rrbb_get_subchan(rrbb);
 | 
			
		||||
	slice = rrbb_get_slice(rrbb);
 | 
			
		||||
	alevel = rrbb_get_audio_level(rrbb);
 | 
			
		||||
 | 
			
		||||
	hdlc_rec2_try_to_fix_later (rrbb, chan, subchan, alevel);
 | 
			
		||||
	hdlc_rec2_try_to_fix_later (rrbb, chan, subchan, slice, alevel);
 | 
			
		||||
	rrbb_delete (rrbb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -557,19 +596,18 @@ void rdq_append (rrbb_t rrbb)
 | 
			
		|||
 * This is called when we have a good frame.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)  
 | 
			
		||||
void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
 | 
			
		||||
{	
 | 
			
		||||
	
 | 
			
		||||
	char stemp[500];
 | 
			
		||||
	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 +623,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);
 | 
			
		||||
| 
						 | 
				
			
			@ -595,6 +633,15 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
 | 
			
		|||
	text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	dw_printf ("\n");
 | 
			
		||||
	dw_printf("DECODED[%d] ", packets_decoded );
 | 
			
		||||
 | 
			
		||||
	/* Insert time stamp relative to start of file. */
 | 
			
		||||
 | 
			
		||||
	double sec = (double)sample_number / my_audio_config.adev[0].samples_per_sec;
 | 
			
		||||
	int min = (int)(sec / 60.);
 | 
			
		||||
	sec -= min * 60;
 | 
			
		||||
 | 
			
		||||
	dw_printf ("%d:%07.4f ", min, sec);
 | 
			
		||||
 | 
			
		||||
	if (h != AX25_SOURCE) {
 | 
			
		||||
	  dw_printf ("Digipeater ");
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -609,27 +656,38 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
 | 
			
		|||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(EXPERIMENT_G) || defined(EXPERIMENT_H)
 | 
			
		||||
	int j;
 | 
			
		||||
 | 
			
		||||
	for (j=0; j<MAX_SUBCHANS; j++) {
 | 
			
		||||
	  if (spectrum[j] == '|') {
 | 
			
		||||
	    count[j]++;
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
//#if defined(EXPERIMENT_G) || defined(EXPERIMENT_H)
 | 
			
		||||
//	int j;
 | 
			
		||||
//
 | 
			
		||||
//	for (j=0; j<MAX_SUBCHANS; j++) {
 | 
			
		||||
//	  if (spectrum[j] == '|') {
 | 
			
		||||
//	    count[j]++;
 | 
			
		||||
//	  }
 | 
			
		||||
//	}
 | 
			
		||||
//#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Display non-APRS packets in a different color.
 | 
			
		||||
 | 
			
		||||
// TODO: display subchannel if appropriate.
 | 
			
		||||
// Display channel with subchannel/slice if applicable.
 | 
			
		||||
 | 
			
		||||
	if (ax25_is_aprs(pp)) {
 | 
			
		||||
	  text_color_set(DW_COLOR_REC);
 | 
			
		||||
	  dw_printf ("[%d] ", chan);
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (my_audio_config.achan[chan].num_subchan > 1 && my_audio_config.achan[chan].num_slicers == 1) {
 | 
			
		||||
	  dw_printf ("[%d.%d] ", chan, subchan);
 | 
			
		||||
	}
 | 
			
		||||
	else if (my_audio_config.achan[chan].num_subchan == 1 && my_audio_config.achan[chan].num_slicers > 1) {
 | 
			
		||||
	  dw_printf ("[%d.%d] ", chan, slice);
 | 
			
		||||
	}
 | 
			
		||||
	else if (my_audio_config.achan[chan].num_subchan > 1 && my_audio_config.achan[chan].num_slicers > 1) {
 | 
			
		||||
	  dw_printf ("[%d.%d.%d] ", chan, subchan, slice);
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  dw_printf ("[%d] ", chan);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -639,7 +697,7 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
 | 
			
		|||
 | 
			
		||||
	ax25_delete (pp);
 | 
			
		||||
 | 
			
		||||
} /* end app_process_rec_packet */
 | 
			
		||||
} /* end fake dlq_append */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void ptt_set (int ot, int chan, int ptt_signal)
 | 
			
		||||
| 
						 | 
				
			
			@ -676,6 +734,10 @@ static void usage (void) {
 | 
			
		|||
	dw_printf ("        -P m   Select  the  demodulator  type such as A, B, C, D (default for 300 baud),\n");
 | 
			
		||||
	dw_printf ("               E (default for 1200 baud), F, A+, B+, C+, D+, E+, F+.\n");
 | 
			
		||||
	dw_printf ("\n");
 | 
			
		||||
	dw_printf ("        -0     Use channel 0 (left) of stereo audio (default).\n");
 | 
			
		||||
	dw_printf ("        -1     use channel 1 (right) of stereo audio.\n");
 | 
			
		||||
	dw_printf ("        -1     decode both channels of stereo audio.\n");
 | 
			
		||||
	dw_printf ("\n");
 | 
			
		||||
	dw_printf ("        wav-file-in is a WAV format audio file.\n");
 | 
			
		||||
	dw_printf ("\n");
 | 
			
		||||
	dw_printf ("Examples:\n");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										25
									
								
								audio.c
								
								
								
								
							
							
						
						
									
										25
									
								
								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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										38
									
								
								audio.h
								
								
								
								
							
							
						
						
									
										38
									
								
								audio.h
								
								
								
								
							| 
						 | 
				
			
			@ -41,19 +41,12 @@ enum audio_in_type_e {
 | 
			
		|||
 | 
			
		||||
typedef enum retry_e {
 | 
			
		||||
		RETRY_NONE=0,
 | 
			
		||||
		RETRY_SWAP_SINGLE=1,
 | 
			
		||||
		RETRY_SWAP_DOUBLE=2,
 | 
			
		||||
		RETRY_SWAP_TRIPLE=3,
 | 
			
		||||
		RETRY_REMOVE_SINGLE=4,
 | 
			
		||||
		RETRY_REMOVE_DOUBLE=5,
 | 
			
		||||
		RETRY_REMOVE_TRIPLE=6,
 | 
			
		||||
		RETRY_INSERT_SINGLE=7,
 | 
			
		||||
		RETRY_INSERT_DOUBLE=8,
 | 
			
		||||
		RETRY_SWAP_TWO_SEP=9,
 | 
			
		||||
		RETRY_SWAP_MANY=10,
 | 
			
		||||
		RETRY_REMOVE_MANY=11,
 | 
			
		||||
		RETRY_REMOVE_TWO_SEP=12,
 | 
			
		||||
		RETRY_MAX = 13}  retry_t;
 | 
			
		||||
		RETRY_INVERT_SINGLE=1,
 | 
			
		||||
		RETRY_INVERT_DOUBLE=2,
 | 
			
		||||
		RETRY_INVERT_TRIPLE=3,
 | 
			
		||||
		RETRY_INVERT_TWO_SEP=4,
 | 
			
		||||
		RETRY_MAX = 5}  retry_t;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef enum sanity_e { SANITY_APRS, SANITY_AX25, SANITY_NONE } sanity_t;
 | 
			
		||||
			 
 | 
			
		||||
| 
						 | 
				
			
			@ -126,6 +119,9 @@ struct audio_s {
 | 
			
		|||
	    int decimate;		/* Reduce AFSK sample rate by this factor to */
 | 
			
		||||
					/* decrease computational requirements. */
 | 
			
		||||
 | 
			
		||||
	    int interleave;		/* If > 1, interleave samples among multiple decoders. */
 | 
			
		||||
					/* Quick hack for experiment. */
 | 
			
		||||
 | 
			
		||||
            int mark_freq;		/* Two tones for AFSK modulation, in Hz. */
 | 
			
		||||
	    int space_freq;		/* Standard tones are 1200 and 2200 for 1200 baud. */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -140,19 +136,13 @@ struct audio_s {
 | 
			
		|||
 | 
			
		||||
	    int offset;			/* Spacing between filter frequencies. */
 | 
			
		||||
 | 
			
		||||
	/* Next two are derived from 3 above by demod_init. */
 | 
			
		||||
	    int num_slicers;		/* Number of different threshold points to decide */
 | 
			
		||||
					/* between mark or space. */
 | 
			
		||||
 | 
			
		||||
	    int num_demod;		/* Number of different demodulators (filters). */
 | 
			
		||||
					/* Previously this was same as num_subchan but we add */
 | 
			
		||||
					/* a new variation in version 1.2 where a single modem */
 | 
			
		||||
					/* can feed multiple slicers and HDLC decoders. */
 | 
			
		||||
	/* This is derived from above by demod_init. */
 | 
			
		||||
 | 
			
		||||
					/* num_slicers could be added to be more general but */
 | 
			
		||||
					/* for the intial experiment, we can just examine profiles. */
 | 
			
		||||
	    int num_subchan;		/* Total number of modems for each channel. */
 | 
			
		||||
 | 
			
		||||
	    int num_subchan;		/* Total number of modems / hdlc decoders for each channel. */
 | 
			
		||||
					/* Potentially it could be product of strlen(profiles) * num_freq. */
 | 
			
		||||
					/* Currently can't use both at once. */
 | 
			
		||||
 | 
			
		||||
	/* These are for dealing with imperfect frames. */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -267,7 +257,7 @@ struct audio_s {
 | 
			
		|||
 | 
			
		||||
#define DEFAULT_BITS_PER_SAMPLE	16
 | 
			
		||||
 | 
			
		||||
#define DEFAULT_FIX_BITS RETRY_SWAP_SINGLE
 | 
			
		||||
#define DEFAULT_FIX_BITS RETRY_INVERT_SINGLE
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Standard for AFSK on VHF FM. 
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										11
									
								
								audio_win.c
								
								
								
								
							
							
						
						
									
										11
									
								
								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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										250
									
								
								ax25_pad.c
								
								
								
								
							
							
						
						
									
										250
									
								
								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
 | 
			
		||||
| 
						 | 
				
			
			@ -463,7 +476,7 @@ packet_t ax25_from_text (char *monitor, int strict)
 | 
			
		|||
	  return (NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( ! ax25_parse_addr (pa, strict, atemp, &ssid_temp, &heard_temp)) {
 | 
			
		||||
	if ( ! ax25_parse_addr (AX25_SOURCE, pa, strict, atemp, &ssid_temp, &heard_temp)) {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("Failed to create packet from text.  Bad source address\n");
 | 
			
		||||
	  ax25_delete (this_p);
 | 
			
		||||
| 
						 | 
				
			
			@ -487,7 +500,7 @@ packet_t ax25_from_text (char *monitor, int strict)
 | 
			
		|||
	  return (NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( ! ax25_parse_addr (pa, strict, atemp, &ssid_temp, &heard_temp)) {
 | 
			
		||||
	if ( ! ax25_parse_addr (AX25_DESTINATION, pa, strict, atemp, &ssid_temp, &heard_temp)) {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("Failed to create packet from text.  Bad destination address\n");
 | 
			
		||||
	  ax25_delete (this_p);
 | 
			
		||||
| 
						 | 
				
			
			@ -511,7 +524,7 @@ packet_t ax25_from_text (char *monitor, int strict)
 | 
			
		|||
 | 
			
		||||
	  // JWL 10:38 this_p->num_addr++;
 | 
			
		||||
 | 
			
		||||
	  if ( ! ax25_parse_addr (pa, strict, atemp, &ssid_temp, &heard_temp)) {
 | 
			
		||||
	  if ( ! ax25_parse_addr (k, pa, strict, atemp, &ssid_temp, &heard_temp)) {
 | 
			
		||||
	    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	    dw_printf ("Failed to create packet from text.  Bad digipeater address\n");
 | 
			
		||||
	    ax25_delete (this_p);
 | 
			
		||||
| 
						 | 
				
			
			@ -569,12 +582,9 @@ 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;
 | 
			
		||||
	packet_t this_p;
 | 
			
		||||
 | 
			
		||||
	int a;
 | 
			
		||||
	int addr_bytes;
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * First make sure we have an acceptable length:
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -674,7 +684,10 @@ packet_t ax25_dup (packet_t copy_from)
 | 
			
		|||
 * 
 | 
			
		||||
 * Purpose:	Parse address with optional ssid.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	in_addr		- Input such as "WB2OSZ-15*"
 | 
			
		||||
 * Inputs:	position	- AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER_1...
 | 
			
		||||
 *				  Used for more specific error message.  -1 if not used.
 | 
			
		||||
 *
 | 
			
		||||
 *		in_addr		- Input such as "WB2OSZ-15*"
 | 
			
		||||
 *
 | 
			
		||||
 * 		strict		- TRUE for strict checking (6 characters, no lower case,
 | 
			
		||||
 *				  SSID must be in range of 0 to 15).
 | 
			
		||||
| 
						 | 
				
			
			@ -697,8 +710,12 @@ packet_t ax25_dup (packet_t copy_from)
 | 
			
		|||
 *
 | 
			
		||||
 *------------------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
static const char *position_name[1 + AX25_MAX_ADDRS] = {
 | 
			
		||||
	"", "Destination ", "Source ",
 | 
			
		||||
	"Digi1 ", "Digi2 ", "Digi3 ", "Digi4 ",
 | 
			
		||||
	"Digi5 ", "Digi6 ", "Digi7 ", "Digi8 " };
 | 
			
		||||
 | 
			
		||||
int ax25_parse_addr (char *in_addr, int strict, char *out_addr, int *out_ssid, int *out_heard)
 | 
			
		||||
int ax25_parse_addr (int position, char *in_addr, int strict, char *out_addr, int *out_ssid, int *out_heard)
 | 
			
		||||
{
 | 
			
		||||
	char *p;
 | 
			
		||||
	char sstr[8];		/* Should be 1 or 2 digits for SSID. */
 | 
			
		||||
| 
						 | 
				
			
			@ -709,22 +726,33 @@ int ax25_parse_addr (char *in_addr, int strict, char *out_addr, int *out_ssid, i
 | 
			
		|||
	*out_ssid = 0;
 | 
			
		||||
	*out_heard = 0;
 | 
			
		||||
 | 
			
		||||
	if (strict && strlen(in_addr) >= 2 && strncmp(in_addr, "qA", 2) == 0) {
 | 
			
		||||
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("%sAddress \"%s\" is a \"q-construct\" used for communicating\n", position_name[position], in_addr);
 | 
			
		||||
	  dw_printf ("with APRS Internet Servers.  It was not expected here.\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//dw_printf ("ax25_parse_addr in: %s\n", in_addr);
 | 
			
		||||
 | 
			
		||||
	if (position < -1) position = -1;
 | 
			
		||||
	if (position > AX25_REPEATER_8) position = AX25_REPEATER_8;
 | 
			
		||||
	position++;	/* Adjust for position_name above. */
 | 
			
		||||
 | 
			
		||||
	maxlen = strict ? 6 : (AX25_MAX_ADDR_LEN-1);
 | 
			
		||||
	p = in_addr;
 | 
			
		||||
	i = 0;
 | 
			
		||||
	for (p = in_addr; isalnum(*p); p++) {
 | 
			
		||||
	  if (i >= maxlen) {
 | 
			
		||||
	    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	    dw_printf ("Address is too long. \"%s\" has more than %d characters.\n", in_addr, maxlen);
 | 
			
		||||
	    dw_printf ("%sAddress is too long. \"%s\" has more than %d characters.\n", position_name[position], in_addr, maxlen);
 | 
			
		||||
	    return 0;
 | 
			
		||||
	  }
 | 
			
		||||
	  out_addr[i++] = *p;
 | 
			
		||||
	  out_addr[i] = '\0';
 | 
			
		||||
	  if (strict && islower(*p)) {
 | 
			
		||||
	    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	    dw_printf ("Address has lower case letters. \"%s\" must be all upper case.\n", in_addr);
 | 
			
		||||
	    dw_printf ("%sAddress has lower case letters. \"%s\" must be all upper case.\n", position_name[position], in_addr);
 | 
			
		||||
	    return 0;
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -735,21 +763,21 @@ int ax25_parse_addr (char *in_addr, int strict, char *out_addr, int *out_ssid, i
 | 
			
		|||
	  for (p++; isalnum(*p); p++) {
 | 
			
		||||
	    if (j >= 2) {
 | 
			
		||||
	      text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	      dw_printf ("SSID is too long. SSID part of \"%s\" has more than 2 characters.\n", in_addr);
 | 
			
		||||
	      dw_printf ("%sSSID is too long. SSID part of \"%s\" has more than 2 characters.\n", position_name[position], in_addr);
 | 
			
		||||
	      return 0;
 | 
			
		||||
	    }
 | 
			
		||||
	    sstr[j++] = *p;
 | 
			
		||||
	    sstr[j] = '\0';
 | 
			
		||||
	    if (strict && ! isdigit(*p)) {
 | 
			
		||||
	      text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	      dw_printf ("SSID must be digits. \"%s\" has letters in SSID.\n", in_addr);
 | 
			
		||||
	      dw_printf ("%sSSID must be digits. \"%s\" has letters in SSID.\n", position_name[position], in_addr);
 | 
			
		||||
	      return 0;
 | 
			
		||||
	    }
 | 
			
		||||
	  }
 | 
			
		||||
	  k = atoi(sstr);
 | 
			
		||||
	  if (k < 0 || k > 15) {
 | 
			
		||||
	    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	    dw_printf ("SSID out of range. SSID of \"%s\" not in range of 0 to 15.\n", in_addr);
 | 
			
		||||
	    dw_printf ("%sSSID out of range. SSID of \"%s\" not in range of 0 to 15.\n", position_name[position], in_addr);
 | 
			
		||||
	    return 0;
 | 
			
		||||
	  }
 | 
			
		||||
	  *out_ssid = k;
 | 
			
		||||
| 
						 | 
				
			
			@ -762,7 +790,7 @@ int ax25_parse_addr (char *in_addr, int strict, char *out_addr, int *out_ssid, i
 | 
			
		|||
 | 
			
		||||
	if (*p != '\0') {
 | 
			
		||||
	    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	    dw_printf ("Invalid character \"%c\" found in address \"%s\".\n", *p, in_addr);
 | 
			
		||||
	    dw_printf ("Invalid character \"%c\" found in %saddress \"%s\".\n", *p, position_name[position], in_addr);
 | 
			
		||||
	  return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -773,6 +801,73 @@ int ax25_parse_addr (char *in_addr, int strict, char *out_addr, int *out_ssid, i
 | 
			
		|||
} /* end ax25_parse_addr */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:        ax25_check_addresses
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:     Check addresses of given packet and print message if any issues.
 | 
			
		||||
 *		We call this when receiving and transmitting.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	pp	- packet object pointer.
 | 
			
		||||
 *
 | 
			
		||||
 * Errors:	Print error message.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns:	1 for all valid.  0 if not.
 | 
			
		||||
 *
 | 
			
		||||
 * Examples:	I was surprised to get this from an APRS-IS server with
 | 
			
		||||
 *		a lower case source address.
 | 
			
		||||
 *
 | 
			
		||||
 *			n1otx>APRS,TCPIP*,qAC,THIRD:@141335z4227.48N/07111.73W_348/005g014t044r000p000h60b10075.wview_5_20_2
 | 
			
		||||
 *
 | 
			
		||||
 *		I haven't gotten to the bottom of this yet but it sounds
 | 
			
		||||
 *		like "q constructs" are somehow getting on to the air when
 | 
			
		||||
 *		they should only appear in conversations with IGate servers.
 | 
			
		||||
 *
 | 
			
		||||
 *			https://groups.yahoo.com/neo/groups/direwolf_packet/conversations/topics/678
 | 
			
		||||
 *
 | 
			
		||||
 *			WB0VGI-7>APDW12,W0YC-5*,qAR,AE0RF-10:}N0DZQ-10>APWW10,TCPIP,WB0VGI-7*:;145.230MN*080306z4607.62N/09230.58WrKE0ACL/R 145.230- T146.2 (Pine County ARES)	
 | 
			
		||||
 *
 | 
			
		||||
 * Typical result:
 | 
			
		||||
 *
 | 
			
		||||
 *			Digipeater WIDE2 (probably N3LEE-4) audio level = 28(10/6)   [NONE]   __|||||||
 | 
			
		||||
 *			[0.5] VE2DJE-9>P_0_P?,VE2PCQ-3,K1DF-7,N3LEE-4,WIDE2*:'{S+l <0x1c>>/
 | 
			
		||||
 *			Invalid character "_" in MIC-E destination/latitude.
 | 
			
		||||
 *			Invalid character "_" in MIC-E destination/latitude.
 | 
			
		||||
 *			Invalid character "?" in MIC-E destination/latitude.
 | 
			
		||||
 *			Invalid MIC-E N/S encoding in 4th character of destination.
 | 
			
		||||
 *			Invalid MIC-E E/W encoding in 6th character of destination.
 | 
			
		||||
 *			MIC-E, normal car (side view), Unknown manufacturer, Returning
 | 
			
		||||
 *			N 00 00.0000, E 005 55.1500, 0 MPH
 | 
			
		||||
 *			Invalid character "_" found in Destination address "P_0_P?".
 | 
			
		||||
 *
 | 
			
		||||
 *			*** The origin and journey of this packet should receive some scrutiny. ***
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
int ax25_check_addresses (packet_t pp)
 | 
			
		||||
{
 | 
			
		||||
	int n;
 | 
			
		||||
	char addr[AX25_MAX_ADDR_LEN];
 | 
			
		||||
	char ignore1[AX25_MAX_ADDR_LEN];
 | 
			
		||||
	int ignore2, ignore3;
 | 
			
		||||
	int all_ok = 1;
 | 
			
		||||
 | 
			
		||||
	for (n = 0; n < ax25_get_num_addr(pp); n++) {
 | 
			
		||||
	  ax25_get_addr_with_ssid (pp, n, addr);
 | 
			
		||||
	  all_ok &= ax25_parse_addr (n, addr, 1, ignore1, &ignore2, &ignore3);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (! all_ok) {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("\n");
 | 
			
		||||
	  dw_printf ("*** The origin and journey of this packet should receive some scrutiny. ***\n");
 | 
			
		||||
	  dw_printf ("\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (all_ok);
 | 
			
		||||
} /* end ax25_check_addresses */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*------------------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:	ax25_unwrap_third_party
 | 
			
		||||
| 
						 | 
				
			
			@ -849,7 +944,7 @@ void ax25_set_addr (packet_t this_p, int n, char *ad)
 | 
			
		|||
/* 
 | 
			
		||||
 * Set existing address position. 
 | 
			
		||||
 */
 | 
			
		||||
	  ax25_parse_addr (ad, 0, atemp, &ssid_temp, &heard_temp);
 | 
			
		||||
	  ax25_parse_addr (n, ad, 0, atemp, &ssid_temp, &heard_temp);
 | 
			
		||||
 | 
			
		||||
	  memset (this_p->frame_data + n*7, ' ' << 1, 6);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -908,7 +1003,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;
 | 
			
		||||
| 
						 | 
				
			
			@ -939,7 +1033,11 @@ void ax25_insert_addr (packet_t this_p, int n, char *ad)
 | 
			
		|||
 | 
			
		||||
	SET_LAST_ADDR_FLAG;
 | 
			
		||||
 | 
			
		||||
	ax25_parse_addr (ad, 0, atemp, &ssid_temp, &heard_temp);
 | 
			
		||||
	// Why aren't we setting 'strict' here?
 | 
			
		||||
	// Messages from IGate have q-constructs.
 | 
			
		||||
	// We use this to parse it and later remove unwanted parts.
 | 
			
		||||
 | 
			
		||||
	ax25_parse_addr (n, ad, 0, atemp, &ssid_temp, &heard_temp);
 | 
			
		||||
	memset (this_p->frame_data + n*7, ' ' << 1, 6);
 | 
			
		||||
	for (i=0; i<6 && atemp[i] != '\0'; i++) {
 | 
			
		||||
	  this_p->frame_data[n*7+i] = atemp[i] << 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -979,7 +1077,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 +1122,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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1149,8 +1246,9 @@ void ax25_get_addr_with_ssid (packet_t this_p, int n, char *station)
 | 
			
		|||
	if (ssid != 0) {
 | 
			
		||||
	  snprintf (sstr, sizeof(sstr), "-%d", ssid);
 | 
			
		||||
	  strlcat (station, sstr, 10);
 | 
			
		||||
	}   
 | 
			
		||||
}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
} /* end ax25_get_addr_with_ssid */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*------------------------------------------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			@ -1178,7 +1276,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 +1898,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 +1961,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 */
 | 
			
		||||
| 
						 | 
				
			
			@ -2054,6 +2151,27 @@ int ax25_get_pid (packet_t this_p)
 | 
			
		|||
 *		There is a very very small probability that two unrelated 
 | 
			
		||||
 *		packets will result in the same checksum, and the
 | 
			
		||||
 *		undesired dropping of the packet.
 | 
			
		||||
 *
 | 
			
		||||
 *		There is a 1 / 65536 chance of getting a false positive match
 | 
			
		||||
 *		which is good enough for this application.
 | 
			
		||||
 *		We could reduce that with a 32 bit CRC instead of reusing
 | 
			
		||||
 *		code from the AX.25 frame CRC calculation.
 | 
			
		||||
 *
 | 
			
		||||
 * Version 1.3:	We exclude any trailing CR/LF at the end of the info part
 | 
			
		||||
 *		so we can detect duplicates that are received only over the
 | 
			
		||||
 *		air and those which have gone thru an IGate where the process
 | 
			
		||||
 *		removes any trailing CR/LF.   Example:
 | 
			
		||||
 *
 | 
			
		||||
 *		Original via RF only:
 | 
			
		||||
 *		W1TG-1>APU25N,N3LEE-10*,WIDE2-1:<IGATE,MSG_CNT=30,LOC_CNT=61<0x0d>
 | 
			
		||||
 *
 | 
			
		||||
 *		When we get the same thing via APRS-IS:
 | 
			
		||||
 *		W1TG-1>APU25N,K1FFK,WIDE2*,qAR,WB2ZII-15:<IGATE,MSG_CNT=30,LOC_CNT=61
 | 
			
		||||
 *
 | 
			
		||||
 *		(Actually there is a trailing space.  Maybe some systems
 | 
			
		||||
 *		change control characters to space???)
 | 
			
		||||
 *		Hmmmm.  I guess we should ignore trailing space as well for 
 | 
			
		||||
 *		duplicate detection and suppression.
 | 
			
		||||
 *		
 | 
			
		||||
 *------------------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2069,6 +2187,20 @@ unsigned short ax25_dedupe_crc (packet_t pp)
 | 
			
		|||
	ax25_get_addr_with_ssid(pp, AX25_DESTINATION, dest);
 | 
			
		||||
	info_len = ax25_get_info (pp, &pinfo);
 | 
			
		||||
 | 
			
		||||
	while (info_len >= 1 && (pinfo[info_len-1] == '\r' ||
 | 
			
		||||
	                         pinfo[info_len-1] == '\n' ||
 | 
			
		||||
	                         pinfo[info_len-1] == ' ')) {
 | 
			
		||||
 | 
			
		||||
	// Temporary for debugging!
 | 
			
		||||
 | 
			
		||||
	//  if (pinfo[info_len-1] == ' ') {
 | 
			
		||||
	//    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	//    dw_printf ("DEBUG:  ax25_dedupe_crc ignoring trailing space.\n");
 | 
			
		||||
	//  }
 | 
			
		||||
 | 
			
		||||
	  info_len--;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	crc = 0xffff;
 | 
			
		||||
	crc = crc16((unsigned char *)src, strlen(src), crc);
 | 
			
		||||
	crc = crc16((unsigned char *)dest, strlen(dest), crc);
 | 
			
		||||
| 
						 | 
				
			
			@ -2149,6 +2281,11 @@ unsigned short ax25_m_m_crc (packet_t pp)
 | 
			
		|||
 *		as hexadecimal for troubleshooting? Maybe an option so the
 | 
			
		||||
 *		packet raw data is in hexadecimal but an extracted 
 | 
			
		||||
 *		comment displays UTF-8?  Or a command line option for only ASCII?
 | 
			
		||||
 *
 | 
			
		||||
 * Trailing space:
 | 
			
		||||
 *		I recently noticed a case where a packet has space character
 | 
			
		||||
 *		at the end.  If the last character of the line is a space,
 | 
			
		||||
 *		this will be displayed in hexadecimal to make it obvious.
 | 
			
		||||
 *			
 | 
			
		||||
 *------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2174,7 +2311,12 @@ void ax25_safe_print (char *pstr, int len, int ascii_only)
 | 
			
		|||
	{
 | 
			
		||||
	  ch = *((unsigned char *)pstr);
 | 
			
		||||
 | 
			
		||||
	  if (ch < ' ' || ch == 0x7f || ch == 0xfe || ch == 0xff ||
 | 
			
		||||
	  if (ch == ' ' && (len == 1 || pstr[1] == '\0')) {
 | 
			
		||||
 | 
			
		||||
	      snprintf (safe_str + safe_len, sizeof(safe_str)-safe_len, "<0x%02x>", ch);
 | 
			
		||||
	      safe_len += 6;
 | 
			
		||||
	  }
 | 
			
		||||
	  else if (ch < ' ' || ch == 0x7f || ch == 0xfe || ch == 0xff ||
 | 
			
		||||
			(ascii_only && ch >= 0x80) ) {
 | 
			
		||||
 | 
			
		||||
	      /* Control codes and delete. */
 | 
			
		||||
| 
						 | 
				
			
			@ -2182,7 +2324,7 @@ void ax25_safe_print (char *pstr, int len, int ascii_only)
 | 
			
		|||
	      /* "Byte Order Mark" (BOM) at the beginning. */
 | 
			
		||||
 | 
			
		||||
	      snprintf (safe_str + safe_len, sizeof(safe_str)-safe_len, "<0x%02x>", ch);
 | 
			
		||||
	      safe_len += 6;	      
 | 
			
		||||
	      safe_len += 6;
 | 
			
		||||
	    }
 | 
			
		||||
	  else {
 | 
			
		||||
	    /* Let everything else thru so we can handle UTF-8 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -307,7 +307,8 @@ extern void ax25_delete (packet_t pp);
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
extern int ax25_parse_addr (char *in_addr, int strict, char *out_addr, int *out_ssid, int *out_heard);
 | 
			
		||||
extern int ax25_parse_addr (int position, char *in_addr, int strict, char *out_addr, int *out_ssid, int *out_heard);
 | 
			
		||||
extern int ax25_check_addresses (packet_t pp);
 | 
			
		||||
 | 
			
		||||
extern packet_t ax25_unwrap_third_party (packet_t from_pp);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										568
									
								
								beacon.c
								
								
								
								
							
							
						
						
									
										568
									
								
								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 <stdio.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "direwolf.h"
 | 
			
		||||
#include "ax25_pad.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +47,6 @@
 | 
			
		|||
#include "tq.h"
 | 
			
		||||
#include "xmit.h"
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#include "digipeater.h"
 | 
			
		||||
#include "version.h"
 | 
			
		||||
#include "encode_aprs.h"
 | 
			
		||||
#include "beacon.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -62,14 +57,25 @@
 | 
			
		|||
#include "aprs_tt.h"		// for dw_run_cmd - should relocate someday.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Are we using GPS data?
 | 
			
		||||
 * Incremented if tracker beacons configured.  
 | 
			
		||||
 * Cleared if dwgps_init fails.
 | 
			
		||||
 * Windows doesn't have localtime_r.
 | 
			
		||||
 * It should have the equivalent localtime_s, with opposite parameter
 | 
			
		||||
 * order,  but I get undefined reference when trying to use it.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static int g_using_gps = 0;	
 | 
			
		||||
struct tm *localtime_r(time_t *clock, struct tm *res)
 | 
			
		||||
{
 | 
			
		||||
	struct tm *tm;
 | 
			
		||||
 | 
			
		||||
	tm = localtime (clock);
 | 
			
		||||
	memcpy (res, tm, sizeof(struct tm));
 | 
			
		||||
	return (res);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Save pointers to configuration settings.
 | 
			
		||||
| 
						 | 
				
			
			@ -77,8 +83,6 @@ static int g_using_gps = 0;
 | 
			
		|||
 | 
			
		||||
static struct audio_s        *g_modem_config_p;
 | 
			
		||||
static struct misc_config_s  *g_misc_config_p;
 | 
			
		||||
static struct digi_config_s  *g_digi_config_p;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
| 
						 | 
				
			
			@ -97,6 +101,11 @@ void beacon_tracker_set_debug (int level)
 | 
			
		|||
	g_tracker_debug_level = level;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static time_t sb_calculate_next_time (time_t now,
 | 
			
		||||
			float current_speed_mph, float current_course,
 | 
			
		||||
			time_t last_xmit_time, float last_xmit_course);
 | 
			
		||||
 | 
			
		||||
static void beacon_send (int j, dwgps_info_t *gpsinfo);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			@ -105,26 +114,24 @@ void beacon_tracker_set_debug (int level)
 | 
			
		|||
 *
 | 
			
		||||
 * Purpose:     Initialize the beacon process.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	pmodem		- Aduio device and modem configuration.
 | 
			
		||||
 *				  Used only to find valide channels.
 | 
			
		||||
 * Inputs:	pmodem		- Audio device and modem configuration.
 | 
			
		||||
 *				  Used only to find valid channels.
 | 
			
		||||
 *
 | 
			
		||||
 *		pconfig		- misc. configuration from config file.
 | 
			
		||||
 *		pdigi		- digipeater configuration from config file.
 | 
			
		||||
 *				TODO: Is this needed?
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * Outputs:	Remember required information for future use.
 | 
			
		||||
 *
 | 
			
		||||
 * Description:	Initialize the queue to be empty and set up other
 | 
			
		||||
 *		mechanisms for sharing it between different threads.
 | 
			
		||||
 * Description:	Do some validity checking on the beacon configuration.
 | 
			
		||||
 *
 | 
			
		||||
 *		Start up xmit_thread to actually send the packets
 | 
			
		||||
 *		Start up beacon_thread to actually send the packets
 | 
			
		||||
 *		at the appropriate time.
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct digi_config_s *pdigi)
 | 
			
		||||
void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig)
 | 
			
		||||
{
 | 
			
		||||
	time_t now;
 | 
			
		||||
	int j;
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +156,6 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct
 | 
			
		|||
 */
 | 
			
		||||
	g_modem_config_p = pmodem;
 | 
			
		||||
	g_misc_config_p = pconfig;
 | 
			
		||||
	g_digi_config_p = pdigi;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Precompute the packet contents so any errors are 
 | 
			
		||||
| 
						 | 
				
			
			@ -164,7 +170,9 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct
 | 
			
		|||
 | 
			
		||||
	  if (g_modem_config_p->achan[chan].valid) {
 | 
			
		||||
 | 
			
		||||
	    if (strlen(g_modem_config_p->achan[chan].mycall) > 0 && strcasecmp(g_modem_config_p->achan[chan].mycall, "NOCALL") != 0) {
 | 
			
		||||
	    if (strlen(g_modem_config_p->achan[chan].mycall) > 0 &&
 | 
			
		||||
			 strcasecmp(g_modem_config_p->achan[chan].mycall, "N0CALL") != 0 &&
 | 
			
		||||
			 strcasecmp(g_modem_config_p->achan[chan].mycall, "NOCALL") != 0) {
 | 
			
		||||
 | 
			
		||||
              switch (g_misc_config_p->beacon[j].btype) {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -194,14 +202,18 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct
 | 
			
		|||
 | 
			
		||||
	        case BEACON_TRACKER:
 | 
			
		||||
 | 
			
		||||
#if defined(ENABLE_GPS) || defined(DEBUG_SIM)
 | 
			
		||||
		  g_using_gps++;
 | 
			
		||||
#else
 | 
			
		||||
	          text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	          dw_printf ("Config file, line %d: GPS tracker feature is not enabled.\n", g_misc_config_p->beacon[j].lineno);
 | 
			
		||||
	   	  g_misc_config_p->beacon[j].btype = BEACON_IGNORE;
 | 
			
		||||
	   	  continue;
 | 
			
		||||
#endif
 | 
			
		||||
	          {
 | 
			
		||||
	            dwgps_info_t gpsinfo;
 | 
			
		||||
	            dwfix_t fix;
 | 
			
		||||
 | 
			
		||||
	            fix = dwgps_read (&gpsinfo);
 | 
			
		||||
		    if (fix == DWFIX_NOT_INIT) {
 | 
			
		||||
 | 
			
		||||
	              text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	              dw_printf ("Config file, line %d: GPS must be configured to use TBEACON.\n", g_misc_config_p->beacon[j].lineno);
 | 
			
		||||
	              g_misc_config_p->beacon[j].btype = BEACON_IGNORE;
 | 
			
		||||
	            }
 | 
			
		||||
	          }
 | 
			
		||||
		  break;
 | 
			
		||||
 | 
			
		||||
	        case BEACON_CUSTOM:
 | 
			
		||||
| 
						 | 
				
			
			@ -234,7 +246,7 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Calculate next time for each beacon.
 | 
			
		||||
 * Calculate first time for each beacon from the 'delay' value.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
	now = time(NULL);
 | 
			
		||||
| 
						 | 
				
			
			@ -253,37 +265,6 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Connect to GPS receiver if any tracker beacons are configured.
 | 
			
		||||
 * If open fails, disable all tracker beacons.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#if DEBUG_SIM
 | 
			
		||||
 | 
			
		||||
	g_using_gps = 1;
 | 
			
		||||
	
 | 
			
		||||
#elif ENABLE_GPS
 | 
			
		||||
 | 
			
		||||
	if (g_using_gps > 0) {
 | 
			
		||||
	  int err;
 | 
			
		||||
 | 
			
		||||
	  err = dwgps_init();
 | 
			
		||||
	  if (err != 0) {
 | 
			
		||||
	    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	    dw_printf ("All tracker beacons disabled.\n");
 | 
			
		||||
	    g_using_gps = 0;
 | 
			
		||||
 | 
			
		||||
	    for (j=0; j<g_misc_config_p->num_beacons; j++) {
 | 
			
		||||
              if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) {
 | 
			
		||||
		g_misc_config_p->beacon[j].btype = BEACON_IGNORE;
 | 
			
		||||
	      }
 | 
			
		||||
	    }
 | 
			
		||||
	  }
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Start up thread for processing only if at least one is valid.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -342,18 +323,6 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct
 | 
			
		|||
#define MIN(x,y) ((x) < (y) ? (x) : (y))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Difference between two angles. */
 | 
			
		||||
 | 
			
		||||
static inline float heading_change (float a, float b)
 | 
			
		||||
{
 | 
			
		||||
	float diff;
 | 
			
		||||
 | 
			
		||||
	diff = fabs(a - b);
 | 
			
		||||
	if (diff <= 180.)
 | 
			
		||||
	  return (diff);
 | 
			
		||||
	else
 | 
			
		||||
	  return (360. - diff);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
| 
						 | 
				
			
			@ -362,29 +331,17 @@ static unsigned __stdcall beacon_thread (void *arg)
 | 
			
		|||
static void * beacon_thread (void *arg)
 | 
			
		||||
#endif
 | 
			
		||||
{
 | 
			
		||||
	int j;
 | 
			
		||||
	int j;				/* Index into array of beacons. */
 | 
			
		||||
	time_t earliest;
 | 
			
		||||
	time_t now;
 | 
			
		||||
	time_t now;			/* Current time. */
 | 
			
		||||
	int number_of_tbeacons;		/* Number of tracker beacons. */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Information from GPS.
 | 
			
		||||
 */
 | 
			
		||||
	int fix = 0;			/* 0 = none, 2 = 2D, 3 = 3D */
 | 
			
		||||
	double my_lat = 0;		/* degrees */
 | 
			
		||||
	double my_lon = 0;
 | 
			
		||||
	float  my_course = 0;		/* degrees */
 | 
			
		||||
	float  my_speed_knots = 0;
 | 
			
		||||
	float  my_speed_mph = 0;
 | 
			
		||||
	float  my_alt_m = G_UNKNOWN;		/* meters */
 | 
			
		||||
	int    my_alt_ft = G_UNKNOWN;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * SmartBeaconing state.
 | 
			
		||||
 */
 | 
			
		||||
	time_t sb_prev_time = 0;	/* Time of most recent transmission. */
 | 
			
		||||
	float sb_prev_course = 0;	/* Most recent course reported. */
 | 
			
		||||
	//float sb_prev_speed_mph;	/* Most recent speed reported. */
 | 
			
		||||
	int sb_every;			/* Calculated time between transmissions. */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
| 
						 | 
				
			
			@ -392,30 +349,45 @@ static void * beacon_thread (void *arg)
 | 
			
		|||
	char hms[20];
 | 
			
		||||
 | 
			
		||||
	now = time(NULL);
 | 
			
		||||
 | 
			
		||||
	localtime_r (&now, &tm);
 | 
			
		||||
 | 
			
		||||
	strftime (hms, sizeof(hms), "%H:%M:%S", &tm);
 | 
			
		||||
	text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	dw_printf ("beacon_thread: started %s\n", hms);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * See if any tracker beacons are configured.
 | 
			
		||||
 * No need to obtain GPS data if none.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
	number_of_tbeacons = 0;
 | 
			
		||||
	for (j=0; j<g_misc_config_p->num_beacons; j++) {
 | 
			
		||||
	  if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) {
 | 
			
		||||
	    number_of_tbeacons++;
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	now = time(NULL);
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
 | 
			
		||||
	  assert (g_misc_config_p->num_beacons >= 1);
 | 
			
		||||
	  dwgps_info_t gpsinfo;
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Sleep until time for the earliest scheduled or
 | 
			
		||||
 * the soonest we could transmit due to corner pegging.
 | 
			
		||||
 */
 | 
			
		||||
	  
 | 
			
		||||
	  earliest = g_misc_config_p->beacon[0].next;
 | 
			
		||||
	  for (j=1; j<g_misc_config_p->num_beacons; j++) {
 | 
			
		||||
	    if (g_misc_config_p->beacon[j].btype == BEACON_IGNORE)
 | 
			
		||||
	      continue;
 | 
			
		||||
	    earliest = MIN(g_misc_config_p->beacon[j].next, earliest);
 | 
			
		||||
	  earliest = now + 60 * 60;
 | 
			
		||||
	  for (j=0; j<g_misc_config_p->num_beacons; j++) {
 | 
			
		||||
	    if (g_misc_config_p->beacon[j].btype != BEACON_IGNORE) {
 | 
			
		||||
	      earliest = MIN(g_misc_config_p->beacon[j].next, earliest);
 | 
			
		||||
	    }
 | 
			
		||||
	  }
 | 
			
		||||
 | 
			
		||||
	  if (g_misc_config_p->sb_configured && g_using_gps) {
 | 
			
		||||
	  if (g_misc_config_p->sb_configured && number_of_tbeacons > 0) {
 | 
			
		||||
	    earliest = MIN(now + g_misc_config_p->sb_turn_time, earliest);
 | 
			
		||||
            earliest = MIN(now + g_misc_config_p->sb_fast_rate, earliest);
 | 
			
		||||
	  }
 | 
			
		||||
| 
						 | 
				
			
			@ -442,128 +414,57 @@ static void * beacon_thread (void *arg)
 | 
			
		|||
 * beacon because corner pegging make it sooner. 
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#if DEBUG_SIM
 | 
			
		||||
	  FILE *fp;
 | 
			
		||||
	  char cs[40];
 | 
			
		||||
	  if (number_of_tbeacons > 0) {
 | 
			
		||||
 | 
			
		||||
	  fp = fopen ("c:\\cygwin\\tmp\\cs", "r");
 | 
			
		||||
	  if (fp != NULL) {
 | 
			
		||||
	    fscanf (fp, "%f %f", &my_course, &my_speed_knots);
 | 
			
		||||
	    fclose (fp);
 | 
			
		||||
	  }
 | 
			
		||||
	  else {
 | 
			
		||||
	    fprintf (stderr, "Can't read /tmp/cs.\n");
 | 
			
		||||
	  }
 | 
			
		||||
	  fix = 3;
 | 
			
		||||
	  my_speed_mph = DW_KNOTS_TO_MPH(my_speed_knots);
 | 
			
		||||
	  my_lat = 42.99;
 | 
			
		||||
	  my_lon = 71.99;
 | 
			
		||||
	  my_alt_m = 100;
 | 
			
		||||
#else
 | 
			
		||||
	  if (g_using_gps) {
 | 
			
		||||
 | 
			
		||||
	    fix = dwgps_read (&my_lat, &my_lon, &my_speed_knots, &my_course, &my_alt_m);
 | 
			
		||||
	    my_speed_mph = DW_KNOTS_TO_MPH(my_speed_knots);
 | 
			
		||||
	    dwfix_t fix = dwgps_read (&gpsinfo);
 | 
			
		||||
	    float my_speed_mph = DW_KNOTS_TO_MPH(gpsinfo.speed_knots);
 | 
			
		||||
 | 
			
		||||
	    if (g_tracker_debug_level >= 1) {
 | 
			
		||||
	      struct tm tm;
 | 
			
		||||
	      char hms[20];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	      localtime_r (&now, &tm);
 | 
			
		||||
	      strftime (hms, sizeof(hms), "%H:%M:%S", &tm);
 | 
			
		||||
	      text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	      if (fix == 3) {
 | 
			
		||||
	        dw_printf ("%s  3D, %.6f, %.6f, %.1f mph, %.0f\xc2\xb0, %.1f m\n", hms, my_lat, my_lon, my_speed_mph, my_course, my_alt_m);
 | 
			
		||||
	        dw_printf ("%s  3D, %.6f, %.6f, %.1f mph, %.0f\xc2\xb0, %.1f m\n", hms, gpsinfo.dlat, gpsinfo.dlon, my_speed_mph, gpsinfo.track, gpsinfo.altitude);
 | 
			
		||||
	      }
 | 
			
		||||
	      else if (fix == 2) {
 | 
			
		||||
	        dw_printf ("%s  2D, %.6f, %.6f, %.1f mph, %.0f\xc2\xb0\n", hms, my_lat, my_lon, my_speed_mph, my_course);
 | 
			
		||||
	        dw_printf ("%s  2D, %.6f, %.6f, %.1f mph, %.0f\xc2\xb0\n", hms, gpsinfo.dlat, gpsinfo.dlon, my_speed_mph, gpsinfo.track);
 | 
			
		||||
	      }
 | 
			
		||||
	      else {
 | 
			
		||||
	        dw_printf ("%s  No GPS fix\n", hms);
 | 
			
		||||
	      }
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    /* Transmit altitude only if 3D fix and user asked for it. */
 | 
			
		||||
 | 
			
		||||
	    my_alt_ft = G_UNKNOWN;
 | 
			
		||||
	    if (fix >= 3 && my_alt_m != G_UNKNOWN && g_misc_config_p->beacon[j].alt_m != G_UNKNOWN) {
 | 
			
		||||
	      my_alt_ft = DW_METERS_TO_FEET(my_alt_m);
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    /* Don't complain here for no fix. */
 | 
			
		||||
	    /* Possibly at the point where about to transmit. */
 | 
			
		||||
	  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Run SmartBeaconing calculation if configured and GPS data available.
 | 
			
		||||
 */
 | 
			
		||||
	  if (g_misc_config_p->sb_configured && g_using_gps && fix >= 2) {
 | 
			
		||||
	    if (g_misc_config_p->sb_configured && fix >= DWFIX_2D) {
 | 
			
		||||
 | 
			
		||||
	    if (my_speed_mph > g_misc_config_p->sb_fast_speed) {
 | 
			
		||||
	      sb_every = g_misc_config_p->sb_fast_rate;
 | 
			
		||||
	      if (g_tracker_debug_level >= 2) {
 | 
			
		||||
	      	 text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
		 dw_printf ("my speed %.1f > fast %d mph, interval = %d sec\n", my_speed_mph, g_misc_config_p->sb_fast_speed, sb_every);
 | 
			
		||||
	      } 
 | 
			
		||||
	    }
 | 
			
		||||
	    else if (my_speed_mph < g_misc_config_p->sb_slow_speed) {
 | 
			
		||||
	      sb_every = g_misc_config_p->sb_slow_rate;
 | 
			
		||||
	      if (g_tracker_debug_level >= 2) {
 | 
			
		||||
	      	 text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
		 dw_printf ("my speed %.1f < slow %d mph, interval = %d sec\n", my_speed_mph, g_misc_config_p->sb_slow_speed, sb_every);
 | 
			
		||||
	      } 
 | 
			
		||||
	    }
 | 
			
		||||
	    else {
 | 
			
		||||
	      /* Can't divide by 0 assuming sb_slow_speed > 0. */
 | 
			
		||||
	      sb_every = ( g_misc_config_p->sb_fast_rate * g_misc_config_p->sb_fast_speed ) / my_speed_mph;
 | 
			
		||||
	      if (g_tracker_debug_level >= 2) {
 | 
			
		||||
	      	 text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
		 dw_printf ("my speed %.1f mph, interval = %d sec\n", my_speed_mph, sb_every);
 | 
			
		||||
	      } 
 | 
			
		||||
	    }
 | 
			
		||||
	      time_t tnext = sb_calculate_next_time (now, 
 | 
			
		||||
			DW_KNOTS_TO_MPH(gpsinfo.speed_knots), gpsinfo.track,
 | 
			
		||||
			sb_prev_time, sb_prev_course);
 | 
			
		||||
 | 
			
		||||
#if DEBUG_SIM
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("SB: fast %d %d slow %d %d speed=%.1f every=%d\n",
 | 
			
		||||
			g_misc_config_p->sb_fast_speed, g_misc_config_p->sb_fast_rate,
 | 
			
		||||
			g_misc_config_p->sb_slow_speed, g_misc_config_p->sb_slow_rate,
 | 
			
		||||
			my_speed_mph, sb_every);
 | 
			
		||||
#endif 
 | 
			
		||||
	
 | 
			
		||||
/*
 | 
			
		||||
 * Test for "Corner Pegging" if moving.
 | 
			
		||||
 */
 | 
			
		||||
	    if (my_speed_mph >= 1.0) {
 | 
			
		||||
	      int turn_threshold = g_misc_config_p->sb_turn_angle + 
 | 
			
		||||
			g_misc_config_p->sb_turn_slope / my_speed_mph;
 | 
			
		||||
 | 
			
		||||
#if DEBUG_SIM
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("SB-moving: course %.0f  prev %.0f  thresh %d\n",
 | 
			
		||||
		my_course, sb_prev_course, turn_threshold);
 | 
			
		||||
#endif 
 | 
			
		||||
	      if (heading_change(my_course, sb_prev_course) > turn_threshold &&
 | 
			
		||||
		  now >= sb_prev_time + g_misc_config_p->sb_turn_time) {
 | 
			
		||||
 | 
			
		||||
	        if (g_tracker_debug_level >= 2) {
 | 
			
		||||
	      	   text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
		   dw_printf ("heading change (%.0f, %.0f) > threshold %d and %d since last >= turn time %d\n",
 | 
			
		||||
				my_course, sb_prev_course, turn_threshold, 
 | 
			
		||||
				(int)(now - sb_prev_time), g_misc_config_p->sb_turn_time);
 | 
			
		||||
	        } 
 | 
			
		||||
 | 
			
		||||
		/* Send it now. */
 | 
			
		||||
	        for (j=0; j<g_misc_config_p->num_beacons; j++) {
 | 
			
		||||
                  if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) {
 | 
			
		||||
		    g_misc_config_p->beacon[j].next = now;
 | 
			
		||||
	      for (j=0; j<g_misc_config_p->num_beacons; j++) {
 | 
			
		||||
	        if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) {
 | 
			
		||||
	          /* Haven't thought about the consequences of SmartBeaconing */
 | 
			
		||||
	          /* and having more than one tbeacon configured. */
 | 
			
		||||
	          if (tnext < g_misc_config_p->beacon[j].next) {
 | 
			
		||||
	             g_misc_config_p->beacon[j].next = tnext;
 | 
			
		||||
	          }
 | 
			
		||||
	        }
 | 
			
		||||
	      }  /* significant change in direction */
 | 
			
		||||
	    }  /* is moving */
 | 
			
		||||
	  }  /* apply SmartBeaconing */
 | 
			
		||||
	    
 | 
			
		||||
      
 | 
			
		||||
	      }  /* Update next time if sooner. */
 | 
			
		||||
	    }  /* apply SmartBeaconing */
 | 
			
		||||
	  }  /* tbeacon(s) configured. */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Send if the time has arrived.
 | 
			
		||||
 */
 | 
			
		||||
	  for (j=0; j<g_misc_config_p->num_beacons; j++) {
 | 
			
		||||
 | 
			
		||||
	    if (g_misc_config_p->beacon[j].btype == BEACON_IGNORE)
 | 
			
		||||
| 
						 | 
				
			
			@ -571,13 +472,199 @@ static void * beacon_thread (void *arg)
 | 
			
		|||
 | 
			
		||||
	    if (g_misc_config_p->beacon[j].next <= now) {
 | 
			
		||||
 | 
			
		||||
	      /* Send the beacon. */
 | 
			
		||||
 | 
			
		||||
	      beacon_send (j, &gpsinfo);
 | 
			
		||||
 | 
			
		||||
	      /* Calculate when the next one should be sent. */
 | 
			
		||||
	      /* Easy for fixed interval.  SmartBeaconing takes more effort. */
 | 
			
		||||
 | 
			
		||||
	      if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) {
 | 
			
		||||
 | 
			
		||||
	        if (gpsinfo.fix < DWFIX_2D) {
 | 
			
		||||
	          /* Fix not available so beacon was not sent. */
 | 
			
		||||
	          /* Try again in a couple seconds. */
 | 
			
		||||
 | 
			
		||||
	          g_misc_config_p->beacon[j].next = now + 2;
 | 
			
		||||
	        }
 | 
			
		||||
	        else if (g_misc_config_p->sb_configured) {
 | 
			
		||||
 | 
			
		||||
		  /* Remember most recent tracker beacon. */
 | 
			
		||||
	          /* Compute next time if not turning. */
 | 
			
		||||
 | 
			
		||||
		  sb_prev_time = now;
 | 
			
		||||
		  sb_prev_course = gpsinfo.track;
 | 
			
		||||
 | 
			
		||||
	          g_misc_config_p->beacon[j].next = sb_calculate_next_time (now, 
 | 
			
		||||
			DW_KNOTS_TO_MPH(gpsinfo.speed_knots), gpsinfo.track,
 | 
			
		||||
			sb_prev_time, sb_prev_course);
 | 
			
		||||
	        }
 | 
			
		||||
	        else {
 | 
			
		||||
	          g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
 | 
			
		||||
	        }
 | 
			
		||||
	      }
 | 
			
		||||
	      else {
 | 
			
		||||
	        /* non-tracker beacons are at fixed spacing. */
 | 
			
		||||
 | 
			
		||||
	        g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
 | 
			
		||||
	      }
 | 
			
		||||
 | 
			
		||||
	    }  /* if time to send it */
 | 
			
		||||
 | 
			
		||||
	  }  /* for each configured beacon */
 | 
			
		||||
 | 
			
		||||
	}  /* do forever */
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
	return(0);	/* unreachable but warning if not here. */
 | 
			
		||||
#else 
 | 
			
		||||
	return(NULL);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
} /* end beacon_thread */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:        sb_calculate_next_time
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:     Calculate next transmission time using the SmartBeaconing algorithm.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	now			- Current time.
 | 
			
		||||
 *
 | 
			
		||||
 *		current_speed_mph	- Current speed from GPS.
 | 
			
		||||
 *				  	  Not expecting G_UNKNOWN but should check for it.
 | 
			
		||||
 *
 | 
			
		||||
 *		current_course		- Current direction of travel.
 | 
			
		||||
 *				  	  Could be G_UNKNOWN if stationary.
 | 
			
		||||
 *
 | 
			
		||||
 *		last_xmit_time		- Time of most recent transmission.
 | 
			
		||||
 *
 | 
			
		||||
 *		last_xmit_course	- Direction included in most recent transmission.
 | 
			
		||||
 *
 | 
			
		||||
 * Global In:	g_misc_config_p->
 | 
			
		||||
 *			sb_configured	TRUE if SmartBeaconing is configured.
 | 
			
		||||
 *			sb_fast_speed	MPH
 | 
			
		||||
 *			sb_fast_rate	seconds
 | 
			
		||||
 *			sb_slow_speed	MPH
 | 
			
		||||
 *			sb_slow_rate	seconds
 | 
			
		||||
 *			sb_turn_time	seconds
 | 
			
		||||
 *			sb_turn_angle	degrees
 | 
			
		||||
 *			sb_turn_slope	degrees * MPH
 | 
			
		||||
 *
 | 
			
		||||
 * Returns:	Time of next transmission.
 | 
			
		||||
 *		Could vary from now to sb_slow_rate in the future.
 | 
			
		||||
 *
 | 
			
		||||
 * Caution:	The algorithm is defined in MPH units.    GPS uses knots.
 | 
			
		||||
 *		The caller must be careful about using the proper conversions.
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
/* Difference between two angles. */
 | 
			
		||||
 | 
			
		||||
static float heading_change (float a, float b)
 | 
			
		||||
{
 | 
			
		||||
	float diff;
 | 
			
		||||
 | 
			
		||||
	diff = fabs(a - b);
 | 
			
		||||
	if (diff <= 180.)
 | 
			
		||||
	  return (diff);
 | 
			
		||||
	else
 | 
			
		||||
	  return (360. - diff);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static time_t sb_calculate_next_time (time_t now,
 | 
			
		||||
			float current_speed_mph, float current_course,
 | 
			
		||||
			time_t last_xmit_time, float last_xmit_course)
 | 
			
		||||
{
 | 
			
		||||
	int beacon_rate;
 | 
			
		||||
	time_t next_time;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Compute time between beacons for travelling in a straight line.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
	if (current_speed_mph == G_UNKNOWN) {
 | 
			
		||||
	  beacon_rate = (int)roundf((g_misc_config_p->sb_fast_rate + g_misc_config_p->sb_slow_rate) / 2.);
 | 
			
		||||
	}
 | 
			
		||||
	else if (current_speed_mph > g_misc_config_p->sb_fast_speed) {
 | 
			
		||||
	  beacon_rate = g_misc_config_p->sb_fast_rate;
 | 
			
		||||
	}
 | 
			
		||||
	else if (current_speed_mph < g_misc_config_p->sb_slow_speed) {
 | 
			
		||||
	  beacon_rate = g_misc_config_p->sb_slow_rate;
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  /* Can't divide by 0 assuming sb_slow_speed > 0. */
 | 
			
		||||
	  beacon_rate = (int)roundf(( g_misc_config_p->sb_fast_rate * g_misc_config_p->sb_fast_speed ) / current_speed_mph);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (g_tracker_debug_level >= 2) {
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("SmartBeaconing: Beacon Rate = %d seconds for %.1f MPH\n", beacon_rate, current_speed_mph);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	next_time = last_xmit_time + beacon_rate;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Test for "Corner Pegging" if moving.
 | 
			
		||||
 */
 | 
			
		||||
	if (current_speed_mph != G_UNKNOWN && current_speed_mph >= 1.0 &&
 | 
			
		||||
		current_course != G_UNKNOWN && last_xmit_course != G_UNKNOWN) {
 | 
			
		||||
 | 
			
		||||
	  float change = heading_change(current_course, last_xmit_course);
 | 
			
		||||
	  float turn_threshold = g_misc_config_p->sb_turn_angle +
 | 
			
		||||
			g_misc_config_p->sb_turn_slope / current_speed_mph;
 | 
			
		||||
 | 
			
		||||
	  if (change > turn_threshold &&
 | 
			
		||||
		  now >= last_xmit_time + g_misc_config_p->sb_turn_time) {
 | 
			
		||||
 | 
			
		||||
	    if (g_tracker_debug_level >= 2) {
 | 
			
		||||
	      text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	      dw_printf ("SmartBeaconing: Send now for heading change of %.0f\n", change);
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    next_time = now;
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (next_time);
 | 
			
		||||
 | 
			
		||||
} /* end sb_calculate_next_time */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:        beacon_send
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:     Transmit one beacon after it was determined to be time.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	j			Index into beacon configuration array below.
 | 
			
		||||
 *
 | 
			
		||||
 *		gpsinfo			Information from GPS.  Used only for TBEACON.
 | 
			
		||||
 *
 | 
			
		||||
 * Global In:	g_misc_config_p->beacon		Array of beacon configurations.
 | 
			
		||||
 *
 | 
			
		||||
 * Outputs:	Destination(s) specified:
 | 
			
		||||
 *		 - Transmit queue.
 | 
			
		||||
 *		 - IGate.
 | 
			
		||||
 *		 - Simulated reception.
 | 
			
		||||
 *
 | 
			
		||||
 * Description:	Prepare text in monitor format.
 | 
			
		||||
 *		Convert to packet object.
 | 
			
		||||
 *		Send to desired destination(s).
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
static void beacon_send (int j, dwgps_info_t *gpsinfo)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	      int strict = 1;	/* Strict packet checking because they will go over air. */
 | 
			
		||||
	      char stemp[20];
 | 
			
		||||
	      char info[AX25_MAX_INFO_LEN];
 | 
			
		||||
	      char beacon_text[AX25_MAX_PACKET_LEN];
 | 
			
		||||
	      packet_t pp = NULL;
 | 
			
		||||
	      char mycall[AX25_MAX_ADDR_LEN];
 | 
			
		||||
	      int alt_ft;
 | 
			
		||||
 | 
			
		||||
	      char super_comment[AX25_MAX_INFO_LEN];	// Fixed part + any dynamic part.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -600,7 +687,7 @@ static void * beacon_thread (void *arg)
 | 
			
		|||
	      if (strlen(mycall) == 0 || strcmp(mycall, "NOCALL") == 0) {
 | 
			
		||||
	        text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	        dw_printf ("MYCALL not set for beacon in config file line %d.\n", g_misc_config_p->beacon[j].lineno);
 | 
			
		||||
		continue;
 | 
			
		||||
		return;
 | 
			
		||||
	      }
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
| 
						 | 
				
			
			@ -645,7 +732,7 @@ static void * beacon_thread (void *arg)
 | 
			
		|||
 | 
			
		||||
	        /* Run given command to get variable part of comment. */
 | 
			
		||||
 | 
			
		||||
	        k = dw_run_cmd (g_misc_config_p->beacon[j].commentcmd, 2, var_comment, (int)sizeof(var_comment));
 | 
			
		||||
	        k = dw_run_cmd (g_misc_config_p->beacon[j].commentcmd, 2, var_comment, sizeof(var_comment));
 | 
			
		||||
	        if (k > 0) {
 | 
			
		||||
	          strlcat (super_comment, var_comment, sizeof(super_comment));
 | 
			
		||||
	        }
 | 
			
		||||
| 
						 | 
				
			
			@ -663,18 +750,16 @@ static void * beacon_thread (void *arg)
 | 
			
		|||
 | 
			
		||||
		case BEACON_POSITION:
 | 
			
		||||
 | 
			
		||||
		  alt_ft = DW_METERS_TO_FEET(g_misc_config_p->beacon[j].alt_m);
 | 
			
		||||
 | 
			
		||||
		  encode_position (g_misc_config_p->beacon[j].messaging, 
 | 
			
		||||
			g_misc_config_p->beacon[j].compress, g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon, alt_ft,
 | 
			
		||||
		  encode_position (g_misc_config_p->beacon[j].messaging, g_misc_config_p->beacon[j].compress,
 | 
			
		||||
			g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon,
 | 
			
		||||
			(int)roundf(DW_METERS_TO_FEET(g_misc_config_p->beacon[j].alt_m)),
 | 
			
		||||
			g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, 
 | 
			
		||||
			g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
 | 
			
		||||
			0, 0, /* course, speed */	
 | 
			
		||||
			G_UNKNOWN, G_UNKNOWN, /* course, speed */
 | 
			
		||||
			g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
 | 
			
		||||
			super_comment,
 | 
			
		||||
			info, sizeof(info));
 | 
			
		||||
		  strlcat (beacon_text, info, sizeof(beacon_text));
 | 
			
		||||
	          g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
 | 
			
		||||
		  break;
 | 
			
		||||
 | 
			
		||||
		case BEACON_OBJECT:
 | 
			
		||||
| 
						 | 
				
			
			@ -682,48 +767,43 @@ static void * beacon_thread (void *arg)
 | 
			
		|||
		  encode_object (g_misc_config_p->beacon[j].objname, g_misc_config_p->beacon[j].compress, 0, g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon, 
 | 
			
		||||
			g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, 
 | 
			
		||||
			g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
 | 
			
		||||
			0, 0, /* course, speed */
 | 
			
		||||
			G_UNKNOWN, G_UNKNOWN, /* course, speed */
 | 
			
		||||
			g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset, super_comment,
 | 
			
		||||
			info, sizeof(info));
 | 
			
		||||
		  strlcat (beacon_text, info, sizeof(beacon_text));
 | 
			
		||||
	          g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
 | 
			
		||||
		  break;
 | 
			
		||||
 | 
			
		||||
		case BEACON_TRACKER:
 | 
			
		||||
 | 
			
		||||
		  if (fix >= 2) {
 | 
			
		||||
		    int coarse;		/* APRS encoder wants 1 - 360.  */
 | 
			
		||||
					/* 0 means none or unknown. */
 | 
			
		||||
		  if (gpsinfo->fix >= DWFIX_2D) {
 | 
			
		||||
 | 
			
		||||
		    coarse = (int)roundf(my_course);
 | 
			
		||||
		    if (coarse == 0) {
 | 
			
		||||
		      coarse = 360;
 | 
			
		||||
		    }
 | 
			
		||||
		    encode_position (g_misc_config_p->beacon[j].messaging, 
 | 
			
		||||
			g_misc_config_p->beacon[j].compress, 
 | 
			
		||||
			my_lat, my_lon, my_alt_ft,
 | 
			
		||||
		    int coarse;		/* Round to nearest integer. retaining unknown state. */
 | 
			
		||||
	            int my_alt_ft;
 | 
			
		||||
 | 
			
		||||
	            /* Transmit altitude only if user asked for it. */
 | 
			
		||||
		    /* A positive altitude in the config file enables */
 | 
			
		||||
	            /* transmission of altitude from GPS. */
 | 
			
		||||
 | 
			
		||||
	            my_alt_ft = G_UNKNOWN;
 | 
			
		||||
	            if (gpsinfo->fix >= 3 && gpsinfo->altitude != G_UNKNOWN && g_misc_config_p->beacon[j].alt_m > 0) {
 | 
			
		||||
	              my_alt_ft = (int)roundf(DW_METERS_TO_FEET(gpsinfo->altitude));
 | 
			
		||||
	            }
 | 
			
		||||
 | 
			
		||||
		    coarse = G_UNKNOWN;
 | 
			
		||||
		    if (gpsinfo->track != G_UNKNOWN) {
 | 
			
		||||
	              coarse = (int)roundf(gpsinfo->track);
 | 
			
		||||
	            }
 | 
			
		||||
 | 
			
		||||
		    encode_position (g_misc_config_p->beacon[j].messaging, g_misc_config_p->beacon[j].compress, 
 | 
			
		||||
			gpsinfo->dlat, gpsinfo->dlon, my_alt_ft,
 | 
			
		||||
			g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, 
 | 
			
		||||
			g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
 | 
			
		||||
			coarse, (int)roundf(my_speed_knots),	
 | 
			
		||||
			coarse, (int)roundf(gpsinfo->speed_knots),
 | 
			
		||||
			g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
 | 
			
		||||
			super_comment,
 | 
			
		||||
			info, sizeof(info));
 | 
			
		||||
		    strlcat (beacon_text, info, sizeof(beacon_text));
 | 
			
		||||
 | 
			
		||||
		    /* Remember most recent tracker beacon. */
 | 
			
		||||
 | 
			
		||||
		    sb_prev_time = now;
 | 
			
		||||
		    sb_prev_course = my_course;
 | 
			
		||||
		    //sb_prev_speed_mph = my_speed_mph;
 | 
			
		||||
 | 
			
		||||
		    /* Calculate time for next transmission. */
 | 
			
		||||
	            if (g_misc_config_p->sb_configured) {
 | 
			
		||||
	              g_misc_config_p->beacon[j].next = now + sb_every;
 | 
			
		||||
	            }
 | 
			
		||||
	            else {
 | 
			
		||||
	              g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
 | 
			
		||||
	            }
 | 
			
		||||
 | 
			
		||||
		    /* Write to log file for testing. */
 | 
			
		||||
		    /* The idea is to run log2gpx and map the result rather than */
 | 
			
		||||
		    /* actually transmitting and relying on someone else to receive */
 | 
			
		||||
| 
						 | 
				
			
			@ -743,11 +823,11 @@ static void * beacon_thread (void *arg)
 | 
			
		|||
		      strlcpy (A.g_src, mycall, sizeof(A.g_src));
 | 
			
		||||
		      A.g_symbol_table = g_misc_config_p->beacon[j].symtab;
 | 
			
		||||
		      A.g_symbol_code = g_misc_config_p->beacon[j].symbol;
 | 
			
		||||
		      A.g_lat = my_lat;
 | 
			
		||||
		      A.g_lon = my_lon;
 | 
			
		||||
		      A.g_speed = DW_KNOTS_TO_MPH(my_speed_knots);
 | 
			
		||||
		      A.g_lat = gpsinfo->dlat;
 | 
			
		||||
		      A.g_lon = gpsinfo->dlon;
 | 
			
		||||
		      A.g_speed_mph = DW_KNOTS_TO_MPH(gpsinfo->speed_knots);
 | 
			
		||||
		      A.g_course = coarse;
 | 
			
		||||
		      A.g_altitude = my_alt_ft;
 | 
			
		||||
		      A.g_altitude_ft = DW_METERS_TO_FEET(gpsinfo->altitude);
 | 
			
		||||
 | 
			
		||||
		      /* Fake channel of 999 to distinguish from real data. */
 | 
			
		||||
		      memset (&alevel, 0, sizeof(alevel));
 | 
			
		||||
| 
						 | 
				
			
			@ -755,8 +835,7 @@ static void * beacon_thread (void *arg)
 | 
			
		|||
		    }
 | 
			
		||||
	 	  }
 | 
			
		||||
	          else {
 | 
			
		||||
		    g_misc_config_p->beacon[j].next = now + 2;
 | 
			
		||||
	            continue;   /* No fix.  Try again in a couple seconds. */
 | 
			
		||||
	            return;   /* No fix.  Skip this time. */
 | 
			
		||||
		  }
 | 
			
		||||
		  break;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -770,12 +849,11 @@ static void * beacon_thread (void *arg)
 | 
			
		|||
		  }
 | 
			
		||||
		  else if (g_misc_config_p->beacon[j].custom_infocmd != NULL) {
 | 
			
		||||
		    char info_part[AX25_MAX_INFO_LEN];
 | 
			
		||||
		    char *p;
 | 
			
		||||
		    int k;
 | 
			
		||||
 | 
			
		||||
	            /* Run given command to obtain the info part for packet. */
 | 
			
		||||
 | 
			
		||||
		    k = dw_run_cmd (g_misc_config_p->beacon[j].custom_infocmd, 2, info_part, (int)sizeof(info_part));
 | 
			
		||||
		    k = dw_run_cmd (g_misc_config_p->beacon[j].custom_infocmd, 2, info_part, sizeof(info_part));
 | 
			
		||||
		    if (k > 0) {
 | 
			
		||||
	              strlcat (beacon_text, info_part, sizeof(beacon_text));
 | 
			
		||||
	            }
 | 
			
		||||
| 
						 | 
				
			
			@ -790,7 +868,6 @@ static void * beacon_thread (void *arg)
 | 
			
		|||
	    	    dw_printf ("Internal error. custom_info is null. %s %d\n", __FILE__, __LINE__);
 | 
			
		||||
		    strlcpy (beacon_text, "", sizeof(beacon_text));  // abort!
 | 
			
		||||
	          }
 | 
			
		||||
	          g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
 | 
			
		||||
		  break;
 | 
			
		||||
 | 
			
		||||
		case BEACON_IGNORE:		
 | 
			
		||||
| 
						 | 
				
			
			@ -803,7 +880,7 @@ static void * beacon_thread (void *arg)
 | 
			
		|||
 * Parse monitor format into form for transmission.
 | 
			
		||||
 */	
 | 
			
		||||
	      if (strlen(beacon_text) == 0) {
 | 
			
		||||
		continue;
 | 
			
		||||
		return;
 | 
			
		||||
	      }
 | 
			
		||||
	      
 | 
			
		||||
	      pp = ax25_from_text (beacon_text, strict);
 | 
			
		||||
| 
						 | 
				
			
			@ -819,11 +896,9 @@ static void * beacon_thread (void *arg)
 | 
			
		|||
 | 
			
		||||
	          case SENDTO_IGATE:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if 1
 | 
			
		||||
	  	    text_color_set(DW_COLOR_XMIT);
 | 
			
		||||
	  	    dw_printf ("[ig] %s\n", beacon_text);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		    igate_send_rec_packet (0, pp);
 | 
			
		||||
		    ax25_delete (pp);
 | 
			
		||||
	            break;
 | 
			
		||||
| 
						 | 
				
			
			@ -839,7 +914,7 @@ static void * beacon_thread (void *arg)
 | 
			
		|||
	            /* Simulated reception. */
 | 
			
		||||
 | 
			
		||||
		    memset (&alevel, 0xff, sizeof(alevel));
 | 
			
		||||
	            dlq_append (DLQ_REC_FRAME, g_misc_config_p->beacon[j].sendto_chan, 0, pp, alevel, 0, "");
 | 
			
		||||
	            dlq_append (DLQ_REC_FRAME, g_misc_config_p->beacon[j].sendto_chan, 0, 0, pp, alevel, 0, "");
 | 
			
		||||
	            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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								beacon.h
								
								
								
								
							
							
						
						
									
										2
									
								
								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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										223
									
								
								config.c
								
								
								
								
							
							
						
						
									
										223
									
								
								config.c
								
								
								
								
							| 
						 | 
				
			
			@ -42,10 +42,8 @@
 | 
			
		|||
#include <ctype.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
//#include "pthreads/pthread.h"
 | 
			
		||||
#else
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#if ENABLE_GPSD
 | 
			
		||||
#include <gps.h>		/* for DEFAULT_GPSD_PORT  (2947) */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "ax25_pad.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -184,13 +182,12 @@ static int alllettersorpm(char *p)
 | 
			
		|||
/* Acceptable symbols to separate degrees & minutes. */
 | 
			
		||||
/* Degree symbol is not in ASCII so documentation says to use "^" instead. */
 | 
			
		||||
/* Some wise guy will try to use degree symbol. */
 | 
			
		||||
/* UTF-8 is more difficult because it is a two byte sequence, c2 b0. */
 | 
			
		||||
 | 
			
		||||
#define DEG1 '^'
 | 
			
		||||
#define DEG2 0xb0	/* ISO Latin1 */
 | 
			
		||||
#define DEG3 0xf8	/* Microsoft code page 437 */
 | 
			
		||||
 | 
			
		||||
// TODO: recognize UTF-8 degree symbol.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
enum parse_ll_which_e { LAT, LON };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -484,7 +481,6 @@ static char *split (char *string, int rest_of_line)
 | 
			
		|||
{
 | 
			
		||||
	static char cmd[MAXCMDLEN];
 | 
			
		||||
	static char token[MAXCMDLEN];
 | 
			
		||||
	static char *nextp = NULL;
 | 
			
		||||
	static char *c;		// current position in cmd.
 | 
			
		||||
	char *s, *t;
 | 
			
		||||
	int in_quotes;
 | 
			
		||||
| 
						 | 
				
			
			@ -746,8 +742,8 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
			
		|||
	memset (p_igate_config, 0, sizeof(struct igate_config_s));
 | 
			
		||||
	p_igate_config->t2_server_port = DEFAULT_IGATE_PORT;
 | 
			
		||||
	p_igate_config->tx_chan = -1;			/* IS->RF not enabled */
 | 
			
		||||
	p_igate_config->tx_limit_1 = 6;
 | 
			
		||||
	p_igate_config->tx_limit_5 = 20;
 | 
			
		||||
	p_igate_config->tx_limit_1 = IGATE_TX_LIMIT_1_DEFAULT;
 | 
			
		||||
	p_igate_config->tx_limit_5 = IGATE_TX_LIMIT_5_DEFAULT;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/* People find this confusing. */
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
	    }
 | 
			
		||||
	  }
 | 
			
		||||
| 
						 | 
				
			
			@ -1392,15 +1390,27 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
			
		|||
	      continue;
 | 
			
		||||
	    }
 | 
			
		||||
	    n = atoi(t);
 | 
			
		||||
            if (n >= RETRY_NONE && n <= RETRY_REMOVE_TWO_SEP) {
 | 
			
		||||
            if (n >= RETRY_NONE && n < RETRY_MAX) {		// MAX is actually last valid +1
 | 
			
		||||
	      p_audio_config->achan[channel].fix_bits = (retry_t)n;
 | 
			
		||||
	    }
 | 
			
		||||
	    else {
 | 
			
		||||
	      p_audio_config->achan[channel].fix_bits = DEFAULT_FIX_BITS;
 | 
			
		||||
	      text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
              dw_printf ("Line %d: Invalid value for FIX_BITS. Using %d.\n", 
 | 
			
		||||
			line, p_audio_config->achan[channel].fix_bits);
 | 
			
		||||
   	    }
 | 
			
		||||
              dw_printf ("Line %d: Invalid value %d for FIX_BITS. Using default of %d.\n",
 | 
			
		||||
			line, n, p_audio_config->achan[channel].fix_bits);
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    if (p_audio_config->achan[channel].fix_bits > RETRY_INVERT_SINGLE) {
 | 
			
		||||
	      text_color_set(DW_COLOR_INFO);
 | 
			
		||||
              dw_printf ("Line %d: Using a FIX_BITS value greater than %d is not recommended for normal operation.\n",
 | 
			
		||||
			line, RETRY_INVERT_SINGLE);
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    if (p_audio_config->achan[channel].fix_bits >= RETRY_INVERT_TWO_SEP) {
 | 
			
		||||
	      text_color_set(DW_COLOR_INFO);
 | 
			
		||||
              dw_printf ("Line %d: Using a FIX_BITS value of %d will waste a lot of CPU power and produce wrong results.\n",
 | 
			
		||||
			line, RETRY_INVERT_TWO_SEP);
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    t = split(NULL,0);
 | 
			
		||||
	    while (t != NULL) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1440,7 +1450,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 +1738,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 +1904,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 +1964,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 +2041,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 +2376,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 +2488,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 +2680,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 +2803,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 +2959,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 +2973,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 +3127,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));
 | 
			
		||||
	    }
 | 
			
		||||
	  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3179,7 +3177,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
			
		|||
	      if (islower(*p)) *p = toupper(*p);
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    if ( ! ax25_parse_addr(t, 1, method, &ssid, &heard)) {
 | 
			
		||||
	    if ( ! ax25_parse_addr(-1, t, 1, method, &ssid, &heard)) {
 | 
			
		||||
	       continue;  // function above prints any error message
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3246,9 +3244,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 +3256,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 +3264,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 +3287,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 +3322,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 +3340,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 +3348,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 +3380,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 +3396,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. */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3429,18 +3421,22 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
			
		|||
	      continue;
 | 
			
		||||
	    }
 | 
			
		||||
	    
 | 
			
		||||
	    /* limits of 20 and 100 are unfriendly but not insane. */
 | 
			
		||||
 | 
			
		||||
	    n = atoi(t);
 | 
			
		||||
            if (n >= 1 && n <= 20) {
 | 
			
		||||
            if (n < 1) {
 | 
			
		||||
	      p_igate_config->tx_limit_1 = 1;
 | 
			
		||||
	    }
 | 
			
		||||
            else if (n <= IGATE_TX_LIMIT_1_MAX) {
 | 
			
		||||
	      p_igate_config->tx_limit_1 = n;
 | 
			
		||||
	    }
 | 
			
		||||
	    else {
 | 
			
		||||
	      p_igate_config->tx_limit_1 = IGATE_TX_LIMIT_1_MAX;
 | 
			
		||||
	      text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
              dw_printf ("Line %d: Invalid one minute transmit limit. Using %d.\n", 
 | 
			
		||||
              dw_printf ("Line %d: One minute transmit limit has been reduced to %d.\n",
 | 
			
		||||
				line, p_igate_config->tx_limit_1);
 | 
			
		||||
	      dw_printf ("You won't make friends by setting a limit this high.\n");
 | 
			
		||||
   	    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	    t = split(NULL,0);
 | 
			
		||||
	    if (t == NULL) {
 | 
			
		||||
	      text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
| 
						 | 
				
			
			@ -3449,13 +3445,18 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 | 
			
		|||
	    }
 | 
			
		||||
	    
 | 
			
		||||
	    n = atoi(t);
 | 
			
		||||
            if (n >= 1 && n <= 100) {
 | 
			
		||||
            if (n < 1) {
 | 
			
		||||
	      p_igate_config->tx_limit_5 = 1;
 | 
			
		||||
	    }
 | 
			
		||||
            else if (n <= IGATE_TX_LIMIT_5_MAX) {
 | 
			
		||||
	      p_igate_config->tx_limit_5 = n;
 | 
			
		||||
	    }
 | 
			
		||||
	    else {
 | 
			
		||||
	      p_igate_config->tx_limit_5 = IGATE_TX_LIMIT_5_MAX;
 | 
			
		||||
	      text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
              dw_printf ("Line %d: Invalid one minute transmit limit. Using %d.\n", 
 | 
			
		||||
              dw_printf ("Line %d: Five minute transmit limit has been reduced to %d.\n",
 | 
			
		||||
				line, p_igate_config->tx_limit_5);
 | 
			
		||||
	      dw_printf ("You won't make friends by setting a limit this high.\n");
 | 
			
		||||
   	    }
 | 
			
		||||
	  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3524,12 +3525,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 +3602,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 +3617,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 +3654,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 +3687,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 +3698,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 +3719,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 +3842,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 +3952,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 +3964,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 +3995,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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										16
									
								
								config.h
								
								
								
								
							
							
						
						
									
										16
									
								
								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. */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										398
									
								
								decode_aprs.c
								
								
								
								
							
							
						
						
									
										398
									
								
								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;
 | 
			
		||||
| 
						 | 
				
			
			@ -193,6 +193,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet)
 | 
			
		|||
	A->g_footprint_radius = G_UNKNOWN;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Extract source and destination including the SSID.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -416,6 +417,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 +513,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 +529,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 +668,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 +764,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 +856,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 +869,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 +882,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 +1293,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 +1385,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 +1534,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 +1636,9 @@ static void aprs_object (decode_aprs_t *A, unsigned char *info, int ilen)
 | 
			
		|||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
	(void)(ts);
 | 
			
		||||
 | 
			
		||||
} /* end aprs_object */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*------------------------------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			@ -1890,7 +1650,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 +1688,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 +1969,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 +2169,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 +2191,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 +2208,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 +2306,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 +2333,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 +2342,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 +2425,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 +2437,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 +2448,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 +2459,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 +2805,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 +2858,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 +2932,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 +2942,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 +3483,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 +3534,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 +3641,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 +3699,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 +3831,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 +4147,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 +4156,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 +4167,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 +4179,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 +4215,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 +4320,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 +4348,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 +4442,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 +4471,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen)
 | 
			
		|||
 *
 | 
			
		||||
 *------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
#if TEST
 | 
			
		||||
#if DECAMAIN
 | 
			
		||||
 | 
			
		||||
/* Stub for stand-alone decoder. */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -4791,6 +4553,12 @@ int main (int argc, char *argv[])
 | 
			
		|||
 | 
			
		||||
	      decode_aprs_print (&A);
 | 
			
		||||
 | 
			
		||||
	      /*
 | 
			
		||||
	       * Perform validity check on each address.
 | 
			
		||||
	       * This should print an error message if any issues.
 | 
			
		||||
	       */
 | 
			
		||||
	      (void)ax25_check_addresses(pp);
 | 
			
		||||
 | 
			
		||||
	      // Send to log file?
 | 
			
		||||
 | 
			
		||||
	      // if (logdir != NULL && *logdir != '\0') {
 | 
			
		||||
| 
						 | 
				
			
			@ -4809,6 +4577,6 @@ int main (int argc, char *argv[])
 | 
			
		|||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* TEST */
 | 
			
		||||
#endif /* DECAMAIN */
 | 
			
		||||
 | 
			
		||||
/* end decode_aprs.c */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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. */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										11
									
								
								dedupe.c
								
								
								
								
							
							
						
						
									
										11
									
								
								dedupe.c
								
								
								
								
							| 
						 | 
				
			
			@ -108,6 +108,9 @@
 | 
			
		|||
#include "dedupe.h"
 | 
			
		||||
#include "fcs_calc.h"
 | 
			
		||||
#include "textcolor.h"
 | 
			
		||||
#ifndef DIGITEST
 | 
			
		||||
#include "igate.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*------------------------------------------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			@ -205,6 +208,14 @@ void dedupe_remember (packet_t pp, int chan)
 | 
			
		|||
	if (insert_next >= HISTORY_MAX) {
 | 
			
		||||
	  insert_next = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* If we send something by digipeater, we don't */
 | 
			
		||||
	/* want to do it again if it comes from APRS-IS. */
 | 
			
		||||
	/* Not sure about the other way around. */
 | 
			
		||||
 | 
			
		||||
#ifndef DIGITEST
 | 
			
		||||
	ig_to_tx_remember (pp, chan, 1);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										161
									
								
								demod.c
								
								
								
								
							
							
						
						
									
										161
									
								
								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;
 | 
			
		||||
	
 | 
			
		||||
| 
						 | 
				
			
			@ -119,6 +119,21 @@ int demod_init (struct audio_s *pa)
 | 
			
		|||
	  int num_letters;
 | 
			
		||||
	  int have_plus;
 | 
			
		||||
 | 
			
		||||
	  /*
 | 
			
		||||
	   * These are derived from config file parameters.
 | 
			
		||||
	   *
 | 
			
		||||
	   * num_subchan is number of demodulators.
 | 
			
		||||
	   * This can be increased by:
 | 
			
		||||
	   *	Multiple frequencies.
 | 
			
		||||
	   *	Multiple letters (not sure if I will continue this).
 | 
			
		||||
	   *	New interleaved decoders.
 | 
			
		||||
	   *
 | 
			
		||||
	   * num_slicers is set to max by the "+" option.
 | 
			
		||||
	   */
 | 
			
		||||
 | 
			
		||||
	  save_audio_config_p->achan[chan].num_subchan = 1;
 | 
			
		||||
	  save_audio_config_p->achan[chan].num_slicers = 1;
 | 
			
		||||
 | 
			
		||||
	  switch (save_audio_config_p->achan[chan].modem_type) {
 | 
			
		||||
 | 
			
		||||
	    case MODEM_OFF:
 | 
			
		||||
| 
						 | 
				
			
			@ -184,7 +199,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 +208,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 +217,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,19 +238,18 @@ 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. */
 | 
			
		||||
 | 
			
		||||
	      save_audio_config_p->achan[chan].num_subchan = num_letters;
 | 
			
		||||
	      save_audio_config_p->achan[chan].num_demod = num_letters;		
 | 
			
		||||
	   
 | 
			
		||||
	      save_audio_config_p->achan[chan].num_slicers = 1;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Some error checking - Can use only one of these:
 | 
			
		||||
| 
						 | 
				
			
			@ -245,24 +259,10 @@ int demod_init (struct audio_s *pa)
 | 
			
		|||
 *	- Multiple frequencies.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
	      if (have_plus && num_letters > 1) {
 | 
			
		||||
 | 
			
		||||
		  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.
 | 
			
		||||
		  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.
 | 
			
		||||
	          save_audio_config_p->achan[chan].num_freq = 1;
 | 
			
		||||
	      }
 | 
			
		||||
 | 
			
		||||
	      if (have_plus && save_audio_config_p->achan[chan].num_freq > 1) {
 | 
			
		||||
 | 
			
		||||
		  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
		  dw_printf ("Channel %d: Demodulator + option can't be combined with multiple frequencies.\n", chan);
 | 
			
		||||
 | 
			
		||||
	          save_audio_config_p->achan[chan].num_demod = 1;
 | 
			
		||||
	          save_audio_config_p->achan[chan].num_subchan = 1;	// Will be set higher later.
 | 
			
		||||
	          save_audio_config_p->achan[chan].num_freq = 1;
 | 
			
		||||
	      }
 | 
			
		||||
| 
						 | 
				
			
			@ -302,6 +302,7 @@ int demod_init (struct audio_s *pa)
 | 
			
		|||
 * We have 3 cases to consider.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// TODO1.3: revisit this logic now that it is less restrictive.
 | 
			
		||||
 | 
			
		||||
	      if (num_letters > 1) {
 | 
			
		||||
	        int d;
 | 
			
		||||
| 
						 | 
				
			
			@ -311,17 +312,58 @@ int demod_init (struct audio_s *pa)
 | 
			
		|||
 * Each one corresponds to a demodulator and subchannel.
 | 
			
		||||
 *
 | 
			
		||||
 * An interesting experiment but probably not too useful.
 | 
			
		||||
 * Can't have multiple frequency pairs or the + option.
 | 
			
		||||
 * Can't have multiple frequency pairs.
 | 
			
		||||
 * In version 1.3 this can be combined with the + option.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
	        save_audio_config_p->achan[chan].num_subchan = num_letters;
 | 
			
		||||
	        save_audio_config_p->achan[chan].num_demod = num_letters;
 | 
			
		||||
		
 | 
			
		||||
/*
 | 
			
		||||
 * Quick hack with special case for another experiment.
 | 
			
		||||
 * Do this in a more general way if it turns out to be useful.
 | 
			
		||||
 */
 | 
			
		||||
	        save_audio_config_p->achan[chan].interleave = 1;
 | 
			
		||||
	        if (strcasecmp(save_audio_config_p->achan[chan].profiles, "EE") == 0) {
 | 
			
		||||
	          save_audio_config_p->achan[chan].interleave = 2;
 | 
			
		||||
	          save_audio_config_p->achan[chan].decimate = 1;
 | 
			
		||||
	        }
 | 
			
		||||
	        else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "EEE") == 0) {
 | 
			
		||||
	          save_audio_config_p->achan[chan].interleave = 3;
 | 
			
		||||
	          save_audio_config_p->achan[chan].decimate = 1;
 | 
			
		||||
	        }
 | 
			
		||||
	        else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "EEEE") == 0) {
 | 
			
		||||
	          save_audio_config_p->achan[chan].interleave = 4;
 | 
			
		||||
	          save_audio_config_p->achan[chan].decimate = 1;
 | 
			
		||||
	        }
 | 
			
		||||
	        else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "EEEEE") == 0) {
 | 
			
		||||
	          save_audio_config_p->achan[chan].interleave = 5;
 | 
			
		||||
	          save_audio_config_p->achan[chan].decimate = 1;
 | 
			
		||||
	        }
 | 
			
		||||
	        else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "GG") == 0) {
 | 
			
		||||
	          save_audio_config_p->achan[chan].interleave = 2;
 | 
			
		||||
	          save_audio_config_p->achan[chan].decimate = 1;
 | 
			
		||||
	        }
 | 
			
		||||
	        else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "GGG") == 0) {
 | 
			
		||||
	          save_audio_config_p->achan[chan].interleave = 3;
 | 
			
		||||
	          save_audio_config_p->achan[chan].decimate = 1;
 | 
			
		||||
	        }
 | 
			
		||||
	        else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "GGG+") == 0) {
 | 
			
		||||
	          save_audio_config_p->achan[chan].interleave = 3;
 | 
			
		||||
	          save_audio_config_p->achan[chan].decimate = 1;
 | 
			
		||||
	        }
 | 
			
		||||
	        else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "GGGG") == 0) {
 | 
			
		||||
	          save_audio_config_p->achan[chan].interleave = 4;
 | 
			
		||||
	          save_audio_config_p->achan[chan].decimate = 1;
 | 
			
		||||
	        }
 | 
			
		||||
	        else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "GGGGG") == 0) {
 | 
			
		||||
	          save_audio_config_p->achan[chan].interleave = 5;
 | 
			
		||||
	          save_audio_config_p->achan[chan].decimate = 1;
 | 
			
		||||
	        }
 | 
			
		||||
 | 
			
		||||
		if (save_audio_config_p->achan[chan].num_demod != num_letters) {
 | 
			
		||||
		if (save_audio_config_p->achan[chan].num_subchan != num_letters) {
 | 
			
		||||
		  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
		  dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_demod(%d) != strlen(\"%s\")\n",
 | 
			
		||||
				__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_demod, save_audio_config_p->achan[chan].profiles);
 | 
			
		||||
		  dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_subchan(%d) != strlen(\"%s\")\n",
 | 
			
		||||
				__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_subchan, save_audio_config_p->achan[chan].profiles);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	        if (save_audio_config_p->achan[chan].num_freq != 1) {
 | 
			
		||||
| 
						 | 
				
			
			@ -329,9 +371,8 @@ int demod_init (struct audio_s *pa)
 | 
			
		|||
		  dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_freq(%d) != 1\n",
 | 
			
		||||
				__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq);
 | 
			
		||||
		}
 | 
			
		||||
	  
 | 
			
		||||
	        for (d = 0; d < save_audio_config_p->achan[chan].num_demod; d++) {
 | 
			
		||||
 | 
			
		||||
	        for (d = 0; d < save_audio_config_p->achan[chan].num_subchan; d++) {
 | 
			
		||||
	          int mark, space;
 | 
			
		||||
	          assert (d >= 0 && d < MAX_SUBCHANS);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -342,18 +383,26 @@ int demod_init (struct audio_s *pa)
 | 
			
		|||
	          mark = save_audio_config_p->achan[chan].mark_freq;
 | 
			
		||||
	          space = save_audio_config_p->achan[chan].space_freq;
 | 
			
		||||
 | 
			
		||||
	          if (save_audio_config_p->achan[chan].num_demod != 1) {
 | 
			
		||||
	          if (save_audio_config_p->achan[chan].num_subchan != 1) {
 | 
			
		||||
	            text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	            dw_printf ("        %d.%d: %c %d & %d\n", chan, d, profile, mark, space);
 | 
			
		||||
	          }
 | 
			
		||||
      
 | 
			
		||||
	          demod_afsk_init (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate, 
 | 
			
		||||
 | 
			
		||||
	          demod_afsk_init (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / (save_audio_config_p->achan[chan].decimate * save_audio_config_p->achan[chan].interleave), 
 | 
			
		||||
			    save_audio_config_p->achan[chan].baud,
 | 
			
		||||
		            mark, 
 | 
			
		||||
	                    space,
 | 
			
		||||
			    profile,
 | 
			
		||||
			    D);
 | 
			
		||||
 | 
			
		||||
	          if (have_plus) {
 | 
			
		||||
		    /* I'm not happy about putting this hack here. */
 | 
			
		||||
		    /* should pass in as a parameter rather than adding on later. */
 | 
			
		||||
 | 
			
		||||
	            save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
 | 
			
		||||
		    D->num_slicers = MAX_SLICERS;
 | 
			
		||||
	          }
 | 
			
		||||
 | 
			
		||||
	          /* For siginal level reporting, we want a longer term view. */
 | 
			
		||||
		  // TODO: Should probably move this into the init functions.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -364,7 +413,7 @@ int demod_init (struct audio_s *pa)
 | 
			
		|||
	      else if (have_plus) {
 | 
			
		||||
	       
 | 
			
		||||
/*
 | 
			
		||||
 * PLUS - which implies we have only one letter and one frequency pair.
 | 
			
		||||
 * PLUS - which (formerly) implies we have only one letter and one frequency pair.
 | 
			
		||||
 *
 | 
			
		||||
 * One demodulator feeds multiple slicers, each a subchannel.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -381,12 +430,11 @@ int demod_init (struct audio_s *pa)
 | 
			
		|||
				__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	        if (save_audio_config_p->achan[chan].num_demod != save_audio_config_p->achan[chan].num_demod) {
 | 
			
		||||
	        if (save_audio_config_p->achan[chan].num_freq != save_audio_config_p->achan[chan].num_subchan) {
 | 
			
		||||
		  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
		  dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_freq(%d) != num_demod(%d)\n",
 | 
			
		||||
				__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq, save_audio_config_p->achan[chan].num_demod);
 | 
			
		||||
		  dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_freq(%d) != num_subchan(%d)\n",
 | 
			
		||||
				__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq, save_audio_config_p->achan[chan].num_subchan);
 | 
			
		||||
		}
 | 
			
		||||
	  
 | 
			
		||||
 | 
			
		||||
	        struct demodulator_state_s *D;
 | 
			
		||||
	        D = &demodulator_state[chan][0];
 | 
			
		||||
| 
						 | 
				
			
			@ -394,8 +442,7 @@ int demod_init (struct audio_s *pa)
 | 
			
		|||
		/* I'm not happy about putting this hack here. */
 | 
			
		||||
		/* This belongs in demod_afsk_init but it doesn't have access to the audio config. */
 | 
			
		||||
 | 
			
		||||
	        save_audio_config_p->achan[chan].num_demod = 1;
 | 
			
		||||
	        save_audio_config_p->achan[chan].num_subchan = MAX_SUBCHANS;
 | 
			
		||||
	        save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
 | 
			
		||||
     
 | 
			
		||||
	        demod_afsk_init (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate, 
 | 
			
		||||
			save_audio_config_p->achan[chan].baud,
 | 
			
		||||
| 
						 | 
				
			
			@ -404,10 +451,13 @@ int demod_init (struct audio_s *pa)
 | 
			
		|||
			save_audio_config_p->achan[chan].profiles[0],
 | 
			
		||||
			D);
 | 
			
		||||
 | 
			
		||||
		/* I'm not happy about putting this hack here. */
 | 
			
		||||
		/* should pass in as a parameter rather than adding on later. */
 | 
			
		||||
	        if (have_plus) {
 | 
			
		||||
		  /* I'm not happy about putting this hack here. */
 | 
			
		||||
		  /* should pass in as a parameter rather than adding on later. */
 | 
			
		||||
 | 
			
		||||
		D->num_slicers = MAX_SUBCHANS;
 | 
			
		||||
	          save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
 | 
			
		||||
		  D->num_slicers = MAX_SLICERS;
 | 
			
		||||
	        }
 | 
			
		||||
 | 
			
		||||
	        /* For siginal level reporting, we want a longer term view. */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -427,7 +477,6 @@ int demod_init (struct audio_s *pa)
 | 
			
		|||
				__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].profiles);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	        save_audio_config_p->achan[chan].num_demod = save_audio_config_p->achan[chan].num_freq;
 | 
			
		||||
	        save_audio_config_p->achan[chan].num_subchan = save_audio_config_p->achan[chan].num_freq;
 | 
			
		||||
 | 
			
		||||
	        for (d = 0; d < save_audio_config_p->achan[chan].num_freq; d++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -455,6 +504,14 @@ int demod_init (struct audio_s *pa)
 | 
			
		|||
			profile,
 | 
			
		||||
			D);
 | 
			
		||||
 | 
			
		||||
	          if (have_plus) {
 | 
			
		||||
		    /* I'm not happy about putting this hack here. */
 | 
			
		||||
		    /* should pass in as a parameter rather than adding on later. */
 | 
			
		||||
 | 
			
		||||
	            save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
 | 
			
		||||
		    D->num_slicers = MAX_SLICERS;
 | 
			
		||||
	          }
 | 
			
		||||
 | 
			
		||||
	          /* For siginal level reporting, we want a longer term view. */
 | 
			
		||||
 | 
			
		||||
	          D->quick_attack = D->agc_fast_attack * 0.2;
 | 
			
		||||
| 
						 | 
				
			
			@ -478,7 +535,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
 | 
			
		||||
	      }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -495,15 +552,14 @@ int demod_init (struct audio_s *pa)
 | 
			
		|||
	      D = &demodulator_state[chan][0];	// first subchannel
 | 
			
		||||
 | 
			
		||||
	      save_audio_config_p->achan[chan].num_subchan = 1;
 | 
			
		||||
	      save_audio_config_p->achan[chan].num_demod = 1;		
 | 
			
		||||
              save_audio_config_p->achan[chan].num_slicers = 1;
 | 
			
		||||
 | 
			
		||||
	      if (strchr(save_audio_config_p->achan[chan].profiles, '+') != NULL) {
 | 
			
		||||
 | 
			
		||||
		/* I'm not happy about putting this hack here. */
 | 
			
		||||
		/* This belongs in demod_9600_init but it doesn't have access to the audio config. */
 | 
			
		||||
 | 
			
		||||
	        save_audio_config_p->achan[chan].num_demod = 1;
 | 
			
		||||
	        save_audio_config_p->achan[chan].num_subchan = MAX_SUBCHANS;
 | 
			
		||||
	        save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
 | 
			
		||||
     	      }
 | 
			
		||||
	        
 | 
			
		||||
	      demod_9600_init (UPSAMPLE * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec, save_audio_config_p->achan[chan].baud, D);
 | 
			
		||||
| 
						 | 
				
			
			@ -513,7 +569,8 @@ int demod_init (struct audio_s *pa)
 | 
			
		|||
		/* I'm not happy about putting this hack here. */
 | 
			
		||||
		/* should pass in as a parameter rather than adding on later. */
 | 
			
		||||
 | 
			
		||||
		D->num_slicers = MAX_SUBCHANS;
 | 
			
		||||
	        save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
 | 
			
		||||
		D->num_slicers = MAX_SLICERS;
 | 
			
		||||
	      }
 | 
			
		||||
 | 
			
		||||
	      /* For siginal level reporting, we want a longer term view. */
 | 
			
		||||
| 
						 | 
				
			
			@ -641,7 +698,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 +708,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 +844,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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										72
									
								
								demod_9600.c
								
								
								
								
							
							
						
						
									
										72
									
								
								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<filter_size; j++) {
 | 
			
		||||
	    sum += f[j] * d[j];
 | 
			
		||||
	}
 | 
			
		||||
#else
 | 
			
		||||
#pragma GCC ivdep				// ignored until gcc 4.9
 | 
			
		||||
	for (j=0; j<filter_size; j++) {
 | 
			
		||||
	    sum += filter[j] * data[j];
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -95,7 +97,7 @@ static inline float convolve (const float *__restrict__ data, const float *__res
 | 
			
		|||
/* Automatic gain control. */
 | 
			
		||||
/* Result should settle down to 1 unit peak to peak.  i.e. -0.5 to +0.5 */
 | 
			
		||||
 | 
			
		||||
__attribute__((hot))
 | 
			
		||||
__attribute__((hot)) __attribute__((always_inline))
 | 
			
		||||
static inline float agc (float in, float fast_attack, float slow_decay, float *ppeak, float *pvalley)
 | 
			
		||||
{
 | 
			
		||||
	if (in >= *ppeak) {
 | 
			
		||||
| 
						 | 
				
			
			@ -143,6 +145,7 @@ void demod_9600_init (int samples_per_sec, int baud, struct demodulator_state_s
 | 
			
		|||
	int j;
 | 
			
		||||
 | 
			
		||||
	memset (D, 0, sizeof(struct demodulator_state_s));
 | 
			
		||||
	D->num_slicers = 1;
 | 
			
		||||
 | 
			
		||||
	//dw_printf ("demod_9600_init(rate=%d, baud=%d, D ptr)\n", samples_per_sec, baud);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -257,14 +260,14 @@ void demod_9600_init (int samples_per_sec, int baud, struct demodulator_state_s
 | 
			
		|||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator_state_s *D);
 | 
			
		||||
static void inline nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D);
 | 
			
		||||
 | 
			
		||||
__attribute__((hot))
 | 
			
		||||
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 +276,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. */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -375,19 +378,16 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D
 | 
			
		|||
	  /* AGC should generally keep this around -1 to +1 range. */
 | 
			
		||||
 | 
			
		||||
	  demod_data = demod_out > 0;
 | 
			
		||||
 | 
			
		||||
	  nudge_pll (chan, subchan, demod_data, D);
 | 
			
		||||
	  nudge_pll (chan, subchan, 0, demod_data, D);
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  int s;
 | 
			
		||||
 | 
			
		||||
	  assert (subchan == 0);
 | 
			
		||||
	  int slice;
 | 
			
		||||
 | 
			
		||||
	  /* Multiple slicers each feeding its own HDLC decoder. */
 | 
			
		||||
 | 
			
		||||
	  for (s=0; s<D->num_slicers; s++) {
 | 
			
		||||
	    demod_data = demod_out > slice_point[s];
 | 
			
		||||
	    nudge_pll (chan, s, demod_data, D);		
 | 
			
		||||
	  for (slice=0; slice<D->num_slicers; slice++) {
 | 
			
		||||
	    demod_data = demod_out > slice_point[slice];
 | 
			
		||||
	    nudge_pll (chan, subchan, slice, demod_data, D);
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -395,9 +395,8 @@ 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)
 | 
			
		||||
static void inline nudge_pll (int chan, int subchan, int slice, 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.
 | 
			
		||||
| 
						 | 
				
			
			@ -424,10 +423,11 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
 | 
			
		|||
 * This was optimized for 1200 baud AFSK.  There might be some opportunity
 | 
			
		||||
 * for improvement here.
 | 
			
		||||
 */
 | 
			
		||||
	D->slicer[subchan].prev_d_c_pll = D->slicer[subchan].data_clock_pll;
 | 
			
		||||
	D->slicer[subchan].data_clock_pll += D->pll_step_per_sample;
 | 
			
		||||
 | 
			
		||||
	if (D->slicer[subchan].data_clock_pll < 0 && D->slicer[subchan].prev_d_c_pll > 0) {
 | 
			
		||||
	D->slicer[slice].prev_d_c_pll = D->slicer[slice].data_clock_pll;
 | 
			
		||||
	D->slicer[slice].data_clock_pll += D->pll_step_per_sample;
 | 
			
		||||
 | 
			
		||||
	if (D->slicer[slice].data_clock_pll < 0 && D->slicer[slice].prev_d_c_pll > 0) {
 | 
			
		||||
 | 
			
		||||
	  /* Overflow. */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -439,33 +439,23 @@ 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[slice].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, slice, demod_data, 1, D->slicer[slice].lfsr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
        if (demod_data != D->slicer[subchan].prev_demod_data) {
 | 
			
		||||
        if (demod_data != D->slicer[slice].prev_demod_data) {
 | 
			
		||||
 | 
			
		||||
	  // Note:  Test for this demodulator, not overall for channel.
 | 
			
		||||
 | 
			
		||||
	  if (hdlc_rec_gathering (chan, subchan)) {
 | 
			
		||||
	    D->slicer[subchan].data_clock_pll = (int)(D->slicer[subchan].data_clock_pll * D->pll_locked_inertia);
 | 
			
		||||
	  if (hdlc_rec_gathering (chan, subchan, slice)) {
 | 
			
		||||
	    D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_locked_inertia);
 | 
			
		||||
	  }
 | 
			
		||||
	  else {
 | 
			
		||||
	    D->slicer[subchan].data_clock_pll = (int)(D->slicer[subchan].data_clock_pll * D->pll_searching_inertia);
 | 
			
		||||
	    D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_searching_inertia);
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -473,14 +463,14 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
 | 
			
		|||
#if DEBUG5
 | 
			
		||||
 | 
			
		||||
	//if (chan == 0) {
 | 
			
		||||
	if (hdlc_rec_gathering (chan,subchan)) {
 | 
			
		||||
	if (hdlc_rec_gathering (chan,subchan,slice)) {
 | 
			
		||||
	
 | 
			
		||||
	  char fname[30];
 | 
			
		||||
 | 
			
		||||
	  
 | 
			
		||||
	  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
 | 
			
		||||
| 
						 | 
				
			
			@ -516,7 +506,7 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
 | 
			
		|||
 * Remember demodulator output (pre-descrambling) so we can compare next time
 | 
			
		||||
 * for the DPLL sync.
 | 
			
		||||
 */
 | 
			
		||||
	D->slicer[subchan].prev_demod_data = demod_data;
 | 
			
		||||
	D->slicer[slice].prev_demod_data = demod_data;
 | 
			
		||||
 | 
			
		||||
} /* end nudge_pll */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										96
									
								
								demod_afsk.c
								
								
								
								
							
							
						
						
									
										96
									
								
								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<filter_size; j++) {
 | 
			
		||||
	    sum += f[j] * d[j];
 | 
			
		||||
	}
 | 
			
		||||
#else
 | 
			
		||||
#pragma GCC ivdep				// ignored until gcc 4.9
 | 
			
		||||
	for (j=0; j<filter_size; j++) {
 | 
			
		||||
	    sum += filter[j] * data[j];
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -127,7 +129,7 @@ static inline float convolve (const float *__restrict__ data, const float *__res
 | 
			
		|||
/* Automatic gain control. */
 | 
			
		||||
/* Result should settle down to 1 unit peak to peak.  i.e. -0.5 to +0.5 */
 | 
			
		||||
 | 
			
		||||
__attribute__((hot))
 | 
			
		||||
__attribute__((hot)) __attribute__((always_inline))
 | 
			
		||||
static inline float agc (float in, float fast_attack, float slow_decay, float *ppeak, float *pvalley)
 | 
			
		||||
{
 | 
			
		||||
	if (in >= *ppeak) {
 | 
			
		||||
| 
						 | 
				
			
			@ -196,6 +198,7 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
 | 
			
		|||
	int j;
 | 
			
		||||
	
 | 
			
		||||
	memset (D, 0, sizeof(struct demodulator_state_s));
 | 
			
		||||
	D->num_slicers = 1;
 | 
			
		||||
 | 
			
		||||
#if DEBUG1
 | 
			
		||||
	dw_printf ("demod_afsk_init (rate=%d, baud=%d, mark=%d, space=%d, profile=%c\n",
 | 
			
		||||
| 
						 | 
				
			
			@ -330,7 +333,7 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
 | 
			
		|||
	  case 'E':
 | 
			
		||||
 | 
			
		||||
		/* 1200 baud - Started out similar to C but add prefilter. */
 | 
			
		||||
		/* Version 1.2 - EXPERIMENTAL - Needs more fine tuning. */
 | 
			
		||||
		/* Version 1.2 */
 | 
			
		||||
		/* Enhancements: 					*/
 | 
			
		||||
		/*  + Add prefilter.  Previously used for 300 baud D, but not 1200. */
 | 
			
		||||
		/*  + Prefilter length now independent of M/S filters.	*/
 | 
			
		||||
| 
						 | 
				
			
			@ -364,6 +367,34 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
 | 
			
		|||
	    D->pll_searching_inertia = 0.50;
 | 
			
		||||
	    break;
 | 
			
		||||
 | 
			
		||||
	  case 'G':
 | 
			
		||||
 | 
			
		||||
		/* 1200 baud - Started out same as E but add 3 way interleave. */
 | 
			
		||||
		/* Version 1.3 - EXPERIMENTAL - Needs more fine tuning. */
 | 
			
		||||
 | 
			
		||||
	    //D->bp_window = BP_WINDOW_COSINE;	/* The name says BP but it is used for all of them. */
 | 
			
		||||
 | 
			
		||||
	    D->use_prefilter = 1;		/* first, a bandpass filter. */
 | 
			
		||||
	    D->prefilter_baud = 0.15;
 | 
			
		||||
	    D->pre_filter_len_bits = 128 * 1200. / (44100. / 3.);
 | 
			
		||||
	    D->pre_window = BP_WINDOW_TRUNCATED;
 | 
			
		||||
 | 
			
		||||
	    D->ms_filter_len_bits = 25 * 1200. / (44100. / 3.);
 | 
			
		||||
	    D->ms_window = BP_WINDOW_COSINE;
 | 
			
		||||
 | 
			
		||||
	    D->lpf_use_fir = 1;
 | 
			
		||||
	    D->lpf_baud = 1.16;
 | 
			
		||||
	    D->lp_filter_len_bits = 21 * 1200. / (44100. / 3.);
 | 
			
		||||
	    D->lp_window = BP_WINDOW_TRUNCATED;
 | 
			
		||||
 | 
			
		||||
	    D->agc_fast_attack = 0.130;
 | 
			
		||||
	    D->agc_slow_decay = 0.00013;
 | 
			
		||||
	    D->hysteresis = 0.01;
 | 
			
		||||
 | 
			
		||||
	    D->pll_locked_inertia = 0.73;
 | 
			
		||||
	    D->pll_searching_inertia = 0.64;
 | 
			
		||||
	    break;
 | 
			
		||||
 | 
			
		||||
	  default:
 | 
			
		||||
 | 
			
		||||
	    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
| 
						 | 
				
			
			@ -717,8 +748,8 @@ int main ()
 | 
			
		|||
	modem.achan[0].mark_freq = DEFAULT_MARK_FREQ;
 | 
			
		||||
	modem.achan[0].space_freq = DEFAULT_SPACE_FREQ;
 | 
			
		||||
	modem.achan[0].baud = DEFAULT_BAUD;
 | 
			
		||||
 	modem.achan[0].num_demod = 1;
 | 
			
		||||
 	modem.achan[0].num_subchan = 1;
 | 
			
		||||
 	modem.achan[0].num_slicers = 1;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	demod_afsk_init (modem.adev[0].samples_per_sec, modem.achan[0].baud,
 | 
			
		||||
| 
						 | 
				
			
			@ -790,12 +821,13 @@ int main ()
 | 
			
		|||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator_state_s *D);
 | 
			
		||||
static void inline nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D);
 | 
			
		||||
 | 
			
		||||
__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 +838,7 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	int j;
 | 
			
		||||
	//int j;
 | 
			
		||||
	int demod_data;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -827,7 +859,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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -1020,26 +1052,18 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat
 | 
			
		|||
	  else {
 | 
			
		||||
	    demod_data = D->slicer[subchan].prev_demod_data;
 | 
			
		||||
	  }
 | 
			
		||||
 | 
			
		||||
	  nudge_pll (chan, subchan, demod_data, D);
 | 
			
		||||
	  nudge_pll (chan, subchan, 0, demod_data, D);
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  int s;
 | 
			
		||||
	  int slice;
 | 
			
		||||
 | 
			
		||||
	  assert (subchan == 0);
 | 
			
		||||
 | 
			
		||||
	  /* "G" profile with one demodulator and multiple slicers */
 | 
			
		||||
	  /* each feeding its own HDLC decoder. */
 | 
			
		||||
 | 
			
		||||
	  for (s=0; s<D->num_slicers; s++) {
 | 
			
		||||
	    demod_data = m_amp > s_amp * space_gain[s];
 | 
			
		||||
	    nudge_pll (chan, s, demod_data, D);		
 | 
			
		||||
	  for (slice=0; slice<D->num_slicers; slice++) {
 | 
			
		||||
	    demod_data = m_amp > s_amp * space_gain[slice];
 | 
			
		||||
	    nudge_pll (chan, subchan, slice, demod_data, D);
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if DEBUG4
 | 
			
		||||
 | 
			
		||||
	if (chan == 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1049,7 +1073,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");
 | 
			
		||||
| 
						 | 
				
			
			@ -1077,12 +1101,15 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
__attribute__((hot))
 | 
			
		||||
static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator_state_s *D)
 | 
			
		||||
static void inline nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Finally, a PLL is used to sample near the centers of the data bits.
 | 
			
		||||
 *
 | 
			
		||||
 * D points to a demodulator for a channel/subchannel pair so we don't
 | 
			
		||||
 * have to keep recalculating it.
 | 
			
		||||
 *
 | 
			
		||||
 * D->data_clock_pll is a SIGNED 32 bit variable.
 | 
			
		||||
 * When it overflows from a large positive value to a negative value, we 
 | 
			
		||||
 * sample a data bit from the demodulated signal.
 | 
			
		||||
| 
						 | 
				
			
			@ -1106,33 +1133,34 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
 | 
			
		|||
 * I don't think the optimal value will depend on the audio sample rate
 | 
			
		||||
 * because this happens for each transition from the demodulator.
 | 
			
		||||
 */
 | 
			
		||||
	D->slicer[subchan].prev_d_c_pll = D->slicer[subchan].data_clock_pll;
 | 
			
		||||
	D->slicer[subchan].data_clock_pll += D->pll_step_per_sample;
 | 
			
		||||
 | 
			
		||||
	D->slicer[slice].prev_d_c_pll = D->slicer[slice].data_clock_pll;
 | 
			
		||||
	D->slicer[slice].data_clock_pll += D->pll_step_per_sample;
 | 
			
		||||
 | 
			
		||||
	  //text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  // dw_printf ("prev = %lx, new data clock pll = %lx\n" D->prev_d_c_pll, D->data_clock_pll);
 | 
			
		||||
 | 
			
		||||
	if (D->slicer[subchan].data_clock_pll < 0 && D->slicer[subchan].prev_d_c_pll > 0) {
 | 
			
		||||
	if (D->slicer[slice].data_clock_pll < 0 && D->slicer[slice].prev_d_c_pll > 0) {
 | 
			
		||||
 | 
			
		||||
	  /* Overflow. */
 | 
			
		||||
 | 
			
		||||
	  hdlc_rec_bit (chan, subchan, demod_data, 0, -1);
 | 
			
		||||
	  hdlc_rec_bit (chan, subchan, slice, demod_data, 0, -1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
        if (demod_data != D->slicer[subchan].prev_demod_data) {
 | 
			
		||||
        if (demod_data != D->slicer[slice].prev_demod_data) {
 | 
			
		||||
 | 
			
		||||
	  if (hdlc_rec_gathering (chan, subchan)) {
 | 
			
		||||
	    D->slicer[subchan].data_clock_pll = (int)(D->slicer[subchan].data_clock_pll * D->pll_locked_inertia);
 | 
			
		||||
	  if (hdlc_rec_gathering (chan, subchan, slice)) {
 | 
			
		||||
	    D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_locked_inertia);
 | 
			
		||||
	  }
 | 
			
		||||
	  else {
 | 
			
		||||
	    D->slicer[subchan].data_clock_pll = (int)(D->slicer[subchan].data_clock_pll * D->pll_searching_inertia);
 | 
			
		||||
	    D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_searching_inertia);
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Remember demodulator output so we can compare next time.
 | 
			
		||||
 */
 | 
			
		||||
	D->slicer[subchan].prev_demod_data = demod_data;
 | 
			
		||||
	D->slicer[slice].prev_demod_data = demod_data;
 | 
			
		||||
 | 
			
		||||
} /* end nudge_pll */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										30
									
								
								digipeater.c
								
								
								
								
							
							
						
						
									
										30
									
								
								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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										84
									
								
								direwolf.c
								
								
								
								
							
							
						
						
									
										84
									
								
								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;
 | 
			
		||||
| 
						 | 
				
			
			@ -177,12 +177,14 @@ int main (int argc, char *argv[])
 | 
			
		|||
	char input_file[80];
 | 
			
		||||
	
 | 
			
		||||
	int t_opt = 1;		/* Text color option. */				
 | 
			
		||||
	int a_opt = 0;		/* "-a n" interval, in seconds, for audio statistics report.  0 for none. */
 | 
			
		||||
 | 
			
		||||
	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. */
 | 
			
		||||
			
 | 
			
		||||
	int d_i_opt = 0;	/* "-d i" option for IGate.  Repeat for more detail */
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	strlcpy(l_opt, "", sizeof(l_opt));
 | 
			
		||||
| 
						 | 
				
			
			@ -199,12 +201,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 +226,25 @@ 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, "H", __DATE__);
 | 
			
		||||
	//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
 | 
			
		||||
 | 
			
		||||
#if defined(ENABLE_GPSD) 	// later or hamlib ...
 | 
			
		||||
	dw_printf ("Includes optional support for: ");
 | 
			
		||||
#if defined(ENABLE_GPSD)
 | 
			
		||||
	dw_printf (" gpsd");
 | 
			
		||||
#endif
 | 
			
		||||
	dw_printf ("\n");
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
	SetConsoleCtrlHandler (cleanup_win, TRUE);
 | 
			
		||||
	SetConsoleCtrlHandler ((PHANDLER_ROUTINE)cleanup_win, TRUE);
 | 
			
		||||
#else
 | 
			
		||||
	setlinebuf (stdout);
 | 
			
		||||
	signal (SIGINT, cleanup_linux);
 | 
			
		||||
| 
						 | 
				
			
			@ -278,19 +283,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 +300,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,11 +441,13 @@ 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.
 | 
			
		||||
	      case 'p':  d_p_opt = 1; break;			// TODO: packet dump for xmit side.
 | 
			
		||||
	      case 'o':  d_o_opt++; ptt_set_debug(d_o_opt); break;	
 | 
			
		||||
	      case 'i':  d_i_opt++; break;
 | 
			
		||||
#if AX25MEMDEBUG
 | 
			
		||||
	      case 'm':  ax25memdebug_set(); break;		// Track down memory leak.  Not documented.		
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -668,7 +663,7 @@ int main (int argc, char *argv[])
 | 
			
		|||
 * Initialize the digipeater and IGate functions.
 | 
			
		||||
 */
 | 
			
		||||
	digipeater_init (&audio_config, &digi_config);
 | 
			
		||||
	igate_init (&audio_config, &igate_config, &digi_config);
 | 
			
		||||
	igate_init (&audio_config, &igate_config, &digi_config, d_i_opt);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Provide the AGW & KISS socket interfaces for use by a client application.
 | 
			
		||||
| 
						 | 
				
			
			@ -685,7 +680,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.
 | 
			
		||||
| 
						 | 
				
			
			@ -694,18 +691,19 @@ int main (int argc, char *argv[])
 | 
			
		|||
 | 
			
		||||
/*
 | 
			
		||||
 * Enable beaconing.
 | 
			
		||||
 * Open log file first because "-dttt" (along with -l...) will
 | 
			
		||||
 * log the tracker beacon transmissions with fake channel 999.
 | 
			
		||||
 */
 | 
			
		||||
	beacon_init (&audio_config, &misc_config, &digi_config);
 | 
			
		||||
 | 
			
		||||
	log_init(misc_config.logdir);
 | 
			
		||||
	beacon_init (&audio_config, &misc_config);
 | 
			
		||||
 | 
			
		||||
	log_init(misc_config.logdir);	
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Get sound samples and decode them.
 | 
			
		||||
 * Use hot attribute for all functions called for every audio sample.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	recv_init (&audio_config);
 | 
			
		||||
	recv_process ();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -723,6 +721,7 @@ int main (int argc, char *argv[])
 | 
			
		|||
 * Inputs:	chan	- Audio channel number, 0 or 1.
 | 
			
		||||
 *		subchan	- Which modem caught it.  
 | 
			
		||||
 *			  Special case -1 for DTMF decoder.
 | 
			
		||||
 *		slice	- Slicer which caught it.
 | 
			
		||||
 *		pp	- Packet handle.
 | 
			
		||||
 *		alevel	- Audio level, range of 0 - 100.
 | 
			
		||||
 *				(Special case, use negative to skip
 | 
			
		||||
| 
						 | 
				
			
			@ -739,8 +738,7 @@ int main (int argc, char *argv[])
 | 
			
		|||
 | 
			
		||||
// TODO:  Use only one printf per line so output doesn't get jumbled up with stuff from other threads.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)  
 | 
			
		||||
void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
 | 
			
		||||
{	
 | 
			
		||||
	
 | 
			
		||||
	char stemp[500];
 | 
			
		||||
| 
						 | 
				
			
			@ -753,6 +751,7 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
 | 
			
		|||
 | 
			
		||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
			
		||||
	assert (subchan >= -1 && subchan < MAX_SUBCHANS);
 | 
			
		||||
	assert (slice >= 0 && slice < MAX_SLICERS);
 | 
			
		||||
	assert (pp != NULL);	// 1.1J+
 | 
			
		||||
     
 | 
			
		||||
	strlcpy (display_retries, "", sizeof(display_retries));
 | 
			
		||||
| 
						 | 
				
			
			@ -850,9 +849,16 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
 | 
			
		|||
	  else {
 | 
			
		||||
	    text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  }
 | 
			
		||||
	  if (audio_config.achan[chan].num_subchan > 1) {
 | 
			
		||||
 | 
			
		||||
	  if (audio_config.achan[chan].num_subchan > 1 && audio_config.achan[chan].num_slicers == 1) {
 | 
			
		||||
	    dw_printf ("[%d.%d] ", chan, subchan);
 | 
			
		||||
	  }
 | 
			
		||||
	  else if (audio_config.achan[chan].num_subchan == 1 && audio_config.achan[chan].num_slicers > 1) {
 | 
			
		||||
	    dw_printf ("[%d.%d] ", chan, slice);
 | 
			
		||||
	  }
 | 
			
		||||
	  else if (audio_config.achan[chan].num_subchan > 1 && audio_config.achan[chan].num_slicers > 1) {
 | 
			
		||||
	    dw_printf ("[%d.%d.%d] ", chan, subchan, slice);
 | 
			
		||||
	  }
 | 
			
		||||
	  else {
 | 
			
		||||
	    dw_printf ("[%d] ", chan);
 | 
			
		||||
	  }
 | 
			
		||||
| 
						 | 
				
			
			@ -911,6 +917,12 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
 | 
			
		|||
 | 
			
		||||
	  decode_aprs_print (&A);
 | 
			
		||||
 | 
			
		||||
	  /*
 | 
			
		||||
	   * Perform validity check on each address.
 | 
			
		||||
	   * This should print an error message if any issues.
 | 
			
		||||
	   */
 | 
			
		||||
	  (void)ax25_check_addresses(pp);
 | 
			
		||||
 | 
			
		||||
	  // Send to log file.
 | 
			
		||||
 | 
			
		||||
	  log_write (chan, &A, pp, alevel, retries);
 | 
			
		||||
| 
						 | 
				
			
			@ -920,7 +932,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 +959,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,8 +1060,10 @@ 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 ("       i             i = IGate.\n");
 | 
			
		||||
	dw_printf ("    -q             Quiet (suppress output) options:\n");
 | 
			
		||||
	dw_printf ("       h             h = Heard line with the audio level.\n");
 | 
			
		||||
	dw_printf ("       d             d = Decoding of APRS packets.\n");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										458
									
								
								direwolf.conf
								
								
								
								
							
							
						
						
									
										458
									
								
								direwolf.conf
								
								
								
								
							| 
						 | 
				
			
			@ -1,458 +0,0 @@
 | 
			
		|||
#############################################################
 | 
			
		||||
#                                                           #
 | 
			
		||||
#               Configuration file for Dire Wolf            #
 | 
			
		||||
#                                                           #
 | 
			
		||||
#                   Linux version                           #
 | 
			
		||||
#                                                           #
 | 
			
		||||
#############################################################
 | 
			
		||||
#
 | 
			
		||||
# Consult the User Guide for more details on configuration options.
 | 
			
		||||
#
 | 
			
		||||
#
 | 
			
		||||
# These are the most likely settings you might change:
 | 
			
		||||
#
 | 
			
		||||
#	(1)   	MYCALL 	-  call sign and SSID for your station.
 | 
			
		||||
#
 | 
			
		||||
#			Look for lines starting with MYCALL and 
 | 
			
		||||
#			change NOCALL to your own.
 | 
			
		||||
#
 | 
			
		||||
#	(2)	PBEACON	-  enable position beaconing.
 | 
			
		||||
#
 | 
			
		||||
#			Look for lines starting with PBEACON and 
 | 
			
		||||
#			modify for your call, location, etc.
 | 
			
		||||
#
 | 
			
		||||
#	(3)	DIGIPEATER  -  configure digipeating rules.
 | 
			
		||||
#
 | 
			
		||||
#			Look for lines starting with DIGIPEATER.
 | 
			
		||||
#			Most people will probably use the given example.
 | 
			
		||||
#			Just remove the "#" from the start of the line
 | 
			
		||||
#			to enable it.
 | 
			
		||||
#
 | 
			
		||||
#	(4)	IGSERVER, IGLOGIN  - IGate server and login
 | 
			
		||||
#
 | 
			
		||||
#			Configure an IGate client to relay messages between 
 | 
			
		||||
#			radio and internet servers.
 | 
			
		||||
#
 | 
			
		||||
#
 | 
			
		||||
# The default location is "direwolf.conf" in the current working directory.
 | 
			
		||||
# On Linux, the user's home directory will also be searched.
 | 
			
		||||
# An alternate configuration file location can be specified with the "-c" command line option.  
 | 
			
		||||
#
 | 
			
		||||
# As you probably guessed by now, # indicates a comment line.
 | 
			
		||||
#
 | 
			
		||||
# Remove the # at the beginning of a line if you want to use a sample
 | 
			
		||||
# configuration that is currently commented out.
 | 
			
		||||
#
 | 
			
		||||
# Commands are a keyword followed by parameters.
 | 
			
		||||
#
 | 
			
		||||
# Command key words are case insensitive.  i.e. upper and lower case are equivalent.
 | 
			
		||||
#
 | 
			
		||||
# Command parameters are generally case sensitive.  i.e. upper and lower case are different.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#############################################################
 | 
			
		||||
#                                                           #
 | 
			
		||||
#               FIRST AUDIO DEVICE PROPERTIES               #
 | 
			
		||||
#               (Channel 0 + 1 if in stereo)                #
 | 
			
		||||
#                                                           #
 | 
			
		||||
#############################################################
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Many people will simply use the default sound device.
 | 
			
		||||
# Some might want to use an alternative device by chosing it here.
 | 
			
		||||
#
 | 
			
		||||
# Linux ALSA is complicated.  See User Guide for discussion.
 | 
			
		||||
# To use something other than the default, generally use plughw
 | 
			
		||||
# and a card number reported by "arecord -l" command.  Example:
 | 
			
		||||
 | 
			
		||||
# ADEVICE  plughw:1,0
 | 
			
		||||
 | 
			
		||||
# Starting with version 1.0, you can also use "-" or "stdin" to 
 | 
			
		||||
# pipe stdout from some other application such as a software defined
 | 
			
		||||
# radio.  You can also specify "UDP:" and an optional port for input.
 | 
			
		||||
# Something different must be specified for output.
 | 
			
		||||
 | 
			
		||||
# ADEVICE - plughw:1,0
 | 
			
		||||
# ADEVICE UDP:7355 default
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Number of audio channels for this souncard:  1 or 2.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
ACHANNELS 1
 | 
			
		||||
#ACHANNELS 2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#############################################################
 | 
			
		||||
#                                                           #
 | 
			
		||||
#               SECOND AUDIO DEVICE PROPERTIES              #
 | 
			
		||||
#               (Channel 2 + 3 if in stereo)                #
 | 
			
		||||
#                                                           #
 | 
			
		||||
#############################################################
 | 
			
		||||
 | 
			
		||||
#ADEVICE1  ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#############################################################
 | 
			
		||||
#                                                           #
 | 
			
		||||
#               THIRD AUDIO DEVICE PROPERTIES               #
 | 
			
		||||
#               (Channel 4 + 5 if in stereo)                #
 | 
			
		||||
#                                                           #
 | 
			
		||||
#############################################################
 | 
			
		||||
 | 
			
		||||
#ADEVICE2  ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#############################################################
 | 
			
		||||
#                                                           #
 | 
			
		||||
#               CHANNEL 0 PROPERTIES                        #
 | 
			
		||||
#                                                           #
 | 
			
		||||
#############################################################
 | 
			
		||||
 | 
			
		||||
CHANNEL 0
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# The following MYCALL, MODEM, PTT, etc. configuration items
 | 
			
		||||
# apply to the most recent CHANNEL.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Station identifier for this channel.
 | 
			
		||||
# Multiple channels can have the same or different names.
 | 
			
		||||
#
 | 
			
		||||
# It can be up to 6 letters and digits with an optional ssid.
 | 
			
		||||
# The APRS specification requires that it be upper case.
 | 
			
		||||
#
 | 
			
		||||
# Example (don't use this unless you are me):  MYCALL	WB2OSZ-5
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
MYCALL N0CALL
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Pick a suitable modem speed based on your situation.
 | 
			
		||||
#	1200 	Most common for VHF/UHF.  Default if not specified.
 | 
			
		||||
#	300	Low speed for HF SSB.
 | 
			
		||||
#	9600	High speed - Can't use Microphone and Speaker connections.
 | 
			
		||||
#
 | 
			
		||||
# In the simplest form, just specify the speed. 
 | 
			
		||||
# 
 | 
			
		||||
 | 
			
		||||
MODEM 1200
 | 
			
		||||
#MODEM 300
 | 
			
		||||
#MODEM 9600
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# These are the defaults should be fine for most cases.  In special situations, 
 | 
			
		||||
# you might want to specify different AFSK tones or the baseband mode which does
 | 
			
		||||
# not use AFSK.
 | 
			
		||||
#
 | 
			
		||||
#MODEM 1200 1200:2200
 | 
			
		||||
#MODEM 300  1600:1800
 | 
			
		||||
#MODEM 9600 0:0
 | 
			
		||||
#
 | 
			
		||||
#
 | 
			
		||||
# On HF SSB, you might want to use multiple demodulators on slightly different
 | 
			
		||||
# frequencies to compensate for stations off frequency.  Here we have 7 different
 | 
			
		||||
# demodulators at 30 Hz intervals.  This takes a lot of CPU power so you will 
 | 
			
		||||
# probably need to reduce the audio sampling rate with the /n option.
 | 
			
		||||
 | 
			
		||||
#MODEM 300 1600:1800 7@30 /4
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Uncomment line below to enable the DTMF decoder for this channel.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#DTMF
 | 
			
		||||
 | 
			
		||||
# 
 | 
			
		||||
# If not using a VOX circuit, the transmitter Push to Talk (PTT) 
 | 
			
		||||
# control is usually wired to a serial port with a suitable interface circuit.  
 | 
			
		||||
# DON'T connect it directly!
 | 
			
		||||
#
 | 
			
		||||
# For the PTT command, specify the device and either RTS or DTR.
 | 
			
		||||
# RTS or DTR may be preceded by "-" to invert the signal.
 | 
			
		||||
# Both can be used for interfaces that want them driven with opposite polarity.
 | 
			
		||||
#
 | 
			
		||||
# COM1 can be used instead of /dev/ttyS0, COM2 for /dev/ttyS1, and so on.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#PTT COM1 RTS
 | 
			
		||||
#PTT COM1 RTS -DTR
 | 
			
		||||
#PTT /dev/ttyUSB0 RTS
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# On Linux, you can also use general purpose I/O pins if
 | 
			
		||||
# your system is configured for user access to them. 
 | 
			
		||||
# This would apply mostly to microprocessor boards, not a regular PC.
 | 
			
		||||
# See separate Raspberry Pi document for more details.
 | 
			
		||||
# The number may be preceded by "-" to invert the signal.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#PTT GPIO 25
 | 
			
		||||
 | 
			
		||||
# The Data Carrier Detect (DCD) signal can be sent to the same places
 | 
			
		||||
# as the PTT signal.  This could be used to light up an LED like a normal TNC.
 | 
			
		||||
 | 
			
		||||
#DCD COM1 -DTR
 | 
			
		||||
#DCD GPIO 24
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#############################################################
 | 
			
		||||
#                                                           #
 | 
			
		||||
#               CHANNEL 1 PROPERTIES                        #
 | 
			
		||||
#                                                           #
 | 
			
		||||
#############################################################
 | 
			
		||||
 | 
			
		||||
#CHANNEL 1
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Specify MYCALL, MODEM, PTT, etc. configuration items for 
 | 
			
		||||
# CHANNEL 1.   Repeat for any other channels.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#############################################################
 | 
			
		||||
#                                                           #
 | 
			
		||||
#               TEXT TO SPEECH COMMAND FILE                 #
 | 
			
		||||
#                                                           #
 | 
			
		||||
#############################################################
 | 
			
		||||
 | 
			
		||||
#SPEECH dwespeak.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#############################################################
 | 
			
		||||
#                                                           #
 | 
			
		||||
#               VIRTUAL TNC SERVER PROPERTIES               #
 | 
			
		||||
#                                                           #
 | 
			
		||||
#############################################################
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Dire Wolf acts as a virtual TNC and can communicate with
 | 
			
		||||
# client applications by different protocols:
 | 
			
		||||
#
 | 
			
		||||
#	- the "AGW TCPIP Socket Interface" - default port 8000
 | 
			
		||||
#	- KISS protocol over TCP socket - default port 8001
 | 
			
		||||
#	- KISS TNC via pseudo terminal   (-p command line option)
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
AGWPORT 8000
 | 
			
		||||
KISSPORT 8001
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# It is sometimes possible to recover frames with a bad FCS.
 | 
			
		||||
# This applies to all channels.  
 | 
			
		||||
#
 | 
			
		||||
#	0  [NONE] - Don't try to repair.
 | 
			
		||||
#	1  [SINGLE] - Attempt to fix single bit error.  (default)
 | 
			
		||||
#	2  [DOUBLE] - Also attempt to fix two adjacent bits.
 | 
			
		||||
#	... see User Guide for more values and in-depth discussion.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#FIX_BITS 0
 | 
			
		||||
 | 
			
		||||
#	
 | 
			
		||||
#############################################################
 | 
			
		||||
#                                                           #
 | 
			
		||||
#               BEACONING PROPERTIES                        #
 | 
			
		||||
#                                                           #
 | 
			
		||||
#############################################################
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Beaconing is configured with these two commands:
 | 
			
		||||
#
 | 
			
		||||
#	PBEACON		- for a position report (usually yourself)
 | 
			
		||||
#	OBEACON		- for an object report (usually some other entity)
 | 
			
		||||
#
 | 
			
		||||
# Each has a series of keywords and values for options.  
 | 
			
		||||
# See User Guide for details.
 | 
			
		||||
#
 | 
			
		||||
# Example:
 | 
			
		||||
#
 | 
			
		||||
# This results in a broadcast once every 10 minutes.
 | 
			
		||||
# Every half hour, it can travel via two digipeater hops.
 | 
			
		||||
# The others are kept local.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#PBEACON delay=1  every=30 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W power=50 height=20 gain=4 comment="Chelmsford MA" via=WIDE1-1,WIDE2-1 
 | 
			
		||||
#PBEACON delay=11 every=30 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W power=50 height=20 gain=4 comment="Chelmsford MA"  
 | 
			
		||||
#PBEACON delay=21 every=30 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W power=50 height=20 gain=4 comment="Chelmsford MA"  
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# With UTM coordinates instead of latitude and longitude.
 | 
			
		||||
 | 
			
		||||
#PBEACON delay=1 every=10 overlay=S symbol="digi" zone=19T easting=307477 northing=4720178 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# When the destination field is set to "SPEECH" the information part is
 | 
			
		||||
# converted to speech rather than transmitted as a data frame.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#CBEACON dest="SPEECH" info="Club meeting tonight at 7 pm."
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Modify for your particular situation before removing 
 | 
			
		||||
# the # comment character from the beginning of appropriate lines above.
 | 
			
		||||
# 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#############################################################
 | 
			
		||||
#                                                           #
 | 
			
		||||
#               DIGIPEATER PROPERTIES                       #
 | 
			
		||||
#                                                           #
 | 
			
		||||
#############################################################
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# For most common situations, use something like this by removing
 | 
			
		||||
# the "#" from the beginning of the line below.  
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#DIGIPEAT 0 0 ^WIDE[3-7]-[1-7]$|^TEST$ ^WIDE[12]-[12]$ TRACE 
 | 
			
		||||
 | 
			
		||||
# See User Guide for more explanation of what this means and how
 | 
			
		||||
# it can be customized for your particular needs.
 | 
			
		||||
 
 | 
			
		||||
# Filtering can be used to limit was is digipeated.
 | 
			
		||||
# For example, only weather weather reports, received on channel 0,
 | 
			
		||||
# will be retransmitted on channel 1.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#FILTER 0 1 t/wn 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#############################################################
 | 
			
		||||
#                                                           #
 | 
			
		||||
#               INTERNET GATEWAY                            #
 | 
			
		||||
#                                                           #
 | 
			
		||||
#############################################################
 | 
			
		||||
 | 
			
		||||
# First you need to specify the name of a Tier 2 server.  
 | 
			
		||||
# The current preferred way is to use one of these regional rotate addresses:
 | 
			
		||||
 | 
			
		||||
#	noam.aprs2.net 		- for North America
 | 
			
		||||
#	soam.aprs2.net		- for South America
 | 
			
		||||
#	euro.aprs2.net		- for Europe and Africa
 | 
			
		||||
#	asia.aprs2.net 		- for Asia
 | 
			
		||||
#	aunz.aprs2.net		- for Oceania 
 | 
			
		||||
 | 
			
		||||
#IGSERVER noam.aprs2.net
 | 
			
		||||
 | 
			
		||||
# You also need to specify your login name and passcode. 
 | 
			
		||||
# Contact the author if you can't figure out how to generate the passcode.
 | 
			
		||||
 
 | 
			
		||||
#IGLOGIN WB2OSZ-5 123456
 | 
			
		||||
 | 
			
		||||
# That's all you need for a receive only IGate which relays
 | 
			
		||||
# messages from the local radio channel to the global servers.
 | 
			
		||||
 | 
			
		||||
# Some might want to send an IGate client position directly to a server
 | 
			
		||||
# without sending it over the air and relying on someone else to 
 | 
			
		||||
# forward it to an IGate server.  This is done by using sendto=IG rather
 | 
			
		||||
# than a radio channel number. Overlay R for receive only, T for two way.
 | 
			
		||||
 | 
			
		||||
#PBEACON sendto=IG delay=0:30 every=60:00 symbol="igate" overlay=R lat=42^37.14N long=071^20.83W 
 | 
			
		||||
#PBEACON sendto=IG delay=0:30 every=60:00 symbol="igate" overlay=T lat=42^37.14N long=071^20.83W 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# To relay messages from the Internet to radio, you need to add
 | 
			
		||||
# one more option with the transmit channel number and a VIA path.
 | 
			
		||||
 | 
			
		||||
#IGTXVIA 0 WIDE1-1
 | 
			
		||||
 | 
			
		||||
# You might want to apply a filter for what packets will be obtained from the server.
 | 
			
		||||
# Read about filters here:  http://www.aprs-is.net/javaprsfilter.aspx
 | 
			
		||||
# Example, positions and objects within 50 km of my location:
 | 
			
		||||
 | 
			
		||||
#IGFILTER m/50 
 | 
			
		||||
 | 
			
		||||
# That is known as a server-side filter.  It is processed by the IGate server.
 | 
			
		||||
# You can also apply local filtering to limit what will be transmitted on the 
 | 
			
		||||
# RF side.  For example, transmit only "messages" on channel 0 and weather 
 | 
			
		||||
# reports on channel 1. 
 | 
			
		||||
 | 
			
		||||
#FILTER IG 0 t/m
 | 
			
		||||
#FILTER IG 1 t/wn
 | 
			
		||||
 | 
			
		||||
# Finally, we don't want to flood the radio channel.  
 | 
			
		||||
# The IGate function will limit the number of packets transmitted 
 | 
			
		||||
# during 1 minute and 5 minute intervals.   If a limit would 
 | 
			
		||||
# be exceeded, the packet is dropped and message is displayed in red.
 | 
			
		||||
 | 
			
		||||
IGTXLIMIT 6 10
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#############################################################
 | 
			
		||||
#                                                           #
 | 
			
		||||
#               APRStt GATEWAY                              #
 | 
			
		||||
#                                                           #
 | 
			
		||||
#############################################################
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Dire Wolf can receive DTMF (commonly known as Touch Tone)
 | 
			
		||||
# messages and convert them to packet objects.
 | 
			
		||||
#
 | 
			
		||||
# See separate "APRStt-Implementation-Notes" document for details.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Sample gateway configuration based on:
 | 
			
		||||
#
 | 
			
		||||
#	http://www.aprs.org/aprstt/aprstt-coding24.txt
 | 
			
		||||
#	http://www.aprs.org/aprs-jamboree-2013.html
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# Define specific points.
 | 
			
		||||
 | 
			
		||||
TTPOINT  B01  37^55.37N  81^7.86W  			
 | 
			
		||||
TTPOINT  B7495088  42.605237  -71.34456		
 | 
			
		||||
TTPOINT  B934  42.605237  -71.34456			
 | 
			
		||||
 | 
			
		||||
TTPOINT B901  42.661279  -71.364452 
 | 
			
		||||
TTPOINT B902  42.660411  -71.364419 
 | 
			
		||||
TTPOINT B903  42.659046  -71.364452 
 | 
			
		||||
TTPOINT B904  42.657578  -71.364602 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# For location at given bearing and distance from starting point.
 | 
			
		||||
 | 
			
		||||
TTVECTOR  B5bbbddd  37^55.37N  81^7.86W  0.01  mi
 | 
			
		||||
 | 
			
		||||
# For location specified by x, y coordinates.
 | 
			
		||||
 | 
			
		||||
TTGRID   Byyyxxx    37^50.00N  81^00.00W  37^59.99N  81^09.99W   
 | 
			
		||||
 | 
			
		||||
# UTM location for Lowell-Dracut-Tyngsborough State Forest.
 | 
			
		||||
 | 
			
		||||
TTUTM  B6xxxyyy  19T  10  300000  4720000
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Location for the corral.
 | 
			
		||||
 | 
			
		||||
TTCORRAL   37^55.50N  81^7.00W  0^0.02N
 | 
			
		||||
 | 
			
		||||
# Compact messages - Fixed locations xx and object yyy where 
 | 
			
		||||
#   	Object numbers 100 - 199	= bicycle	
 | 
			
		||||
#	Object numbers 200 - 299	= fire truck
 | 
			
		||||
#	Others				= dog
 | 
			
		||||
 | 
			
		||||
TTMACRO  xx1yy  B9xx*AB166*AA2B4C5B3B0A1yy
 | 
			
		||||
TTMACRO  xx2yy  B9xx*AB170*AA3C4C7C3B0A2yy
 | 
			
		||||
TTMACRO  xxyyy  B9xx*AB180*AA3A6C4A0Ayyy
 | 
			
		||||
 | 
			
		||||
TTMACRO  z  Cz
 | 
			
		||||
 | 
			
		||||
# Receive on channel 0, Transmit object reports on channel 1 with optional via path.
 | 
			
		||||
 | 
			
		||||
#TTOBJ 0 1 WIDE1-1
 | 
			
		||||
 | 
			
		||||
# Advertise gateway position with beacon.
 | 
			
		||||
 | 
			
		||||
# OBEACON DELAY=0:15 EVERY=10:00 VIA=WIDE1-1 OBJNAME=WB2OSZ-tt SYMBOL=APRStt LAT=42^37.14N LONG=71^20.83W COMMENT="APRStt Gateway"  
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								direwolf.h
								
								
								
								
							
							
						
						
									
										18
									
								
								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.
 | 
			
		||||
| 
						 | 
				
			
			@ -49,6 +48,17 @@
 | 
			
		|||
 | 
			
		||||
#define MAX_SUBCHANS 9
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Each one of these can have multiple slicers, at
 | 
			
		||||
 * different levels, to compensate for different
 | 
			
		||||
 * amplitudes of the AFSK tones.
 | 
			
		||||
 * Intially used same number as subchannels but
 | 
			
		||||
 * we could probably trim this down a little
 | 
			
		||||
 * without impacting performance.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define MAX_SLICERS 9
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -75,6 +85,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 +177,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__
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										28
									
								
								dlq.c
								
								
								
								
							
							
						
						
									
										28
									
								
								dlq.c
								
								
								
								
							| 
						 | 
				
			
			@ -64,14 +64,15 @@ struct dlq_item_s {
 | 
			
		|||
					/* Special case, -1 means DTMF decoder. */
 | 
			
		||||
					/* Maybe we should have a different type in this case? */
 | 
			
		||||
 | 
			
		||||
	int slice;			/* Winning slicer. */
 | 
			
		||||
 | 
			
		||||
	packet_t pp;			/* Pointer to frame structure. */
 | 
			
		||||
 | 
			
		||||
	alevel_t alevel;			/* Audio level. */
 | 
			
		||||
	alevel_t alevel;		/* Audio level. */
 | 
			
		||||
 | 
			
		||||
	retry_t retries;		/* Effort expended to get a valid CRC. */
 | 
			
		||||
 | 
			
		||||
	char spectrum[MAX_SUBCHANS+1];	/* "Spectrum" display for multi-decoders. */
 | 
			
		||||
 | 
			
		||||
	char spectrum[MAX_SUBCHANS*MAX_SLICERS+1];	/* "Spectrum" display for multi-decoders. */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -206,6 +207,8 @@ void dlq_init (void)
 | 
			
		|||
 *		subchan	- Which modem caught it.  
 | 
			
		||||
 *			  Special case -1 for APRStt gateway.
 | 
			
		||||
 *
 | 
			
		||||
 *		slice	- Which slice we picked.
 | 
			
		||||
 *
 | 
			
		||||
 *		pp	- Address of packet object.
 | 
			
		||||
 *				Caller should NOT make any references to
 | 
			
		||||
 *				it after this point because it could
 | 
			
		||||
| 
						 | 
				
			
			@ -231,7 +234,7 @@ void dlq_init (void)
 | 
			
		|||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
 | 
			
		||||
void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	struct dlq_item_s *pnew;
 | 
			
		||||
| 
						 | 
				
			
			@ -271,14 +274,15 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
 | 
			
		|||
	pnew->nextp = NULL;
 | 
			
		||||
	pnew->type = type;
 | 
			
		||||
	pnew->chan = chan;
 | 
			
		||||
	pnew->slice = slice;
 | 
			
		||||
	pnew->subchan = subchan;
 | 
			
		||||
	pnew->pp = pp;
 | 
			
		||||
	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);
 | 
			
		||||
| 
						 | 
				
			
			@ -514,7 +518,8 @@ void dlq_wait_while_empty (void)
 | 
			
		|||
 * Outputs:	type		- type of queue entry.
 | 
			
		||||
 *
 | 
			
		||||
 *		chan		- channel of received frame.
 | 
			
		||||
 *		subchan		- which modem caught it.
 | 
			
		||||
 *		subchan		- which demodulator caught it.
 | 
			
		||||
 *		slice		- which slicer caught it.
 | 
			
		||||
 *
 | 
			
		||||
 *		pp		- pointer to packet object when type is DLQ_REC_FRAME.
 | 
			
		||||
 *				   Caller should destroy it with ax25_delete when finished with it.
 | 
			
		||||
| 
						 | 
				
			
			@ -524,7 +529,8 @@ 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, int *slice, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum, size_t spectrumsize)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	struct dlq_item_s *phead;
 | 
			
		||||
| 
						 | 
				
			
			@ -557,12 +563,13 @@ int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_
 | 
			
		|||
	  *type = -1;
 | 
			
		||||
	  *chan = -1;
 | 
			
		||||
	  *subchan = -1;
 | 
			
		||||
	  *slice = -1;
 | 
			
		||||
	  *pp = NULL;
 | 
			
		||||
 | 
			
		||||
	  memset (alevel, 0xff, sizeof(*alevel));
 | 
			
		||||
 | 
			
		||||
	  *retries = -1;
 | 
			
		||||
	  strcpy(spectrum,"");
 | 
			
		||||
	  strlcpy(spectrum, "", spectrumsize);
 | 
			
		||||
	  result = 0;
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
| 
						 | 
				
			
			@ -573,10 +580,11 @@ int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_
 | 
			
		|||
	  *type = phead->type;
 | 
			
		||||
	  *chan = phead->chan;
 | 
			
		||||
	  *subchan = phead->subchan;
 | 
			
		||||
	  *slice = phead->slice;
 | 
			
		||||
	  *pp = phead->pp;
 | 
			
		||||
	  *alevel = phead->alevel;
 | 
			
		||||
	  *retries = phead->retries;
 | 
			
		||||
	  strcpy (spectrum, phead->spectrum);
 | 
			
		||||
	  strlcpy (spectrum, phead->spectrum, spectrumsize);
 | 
			
		||||
	  result = 1;
 | 
			
		||||
	}
 | 
			
		||||
	 
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										5
									
								
								dlq.h
								
								
								
								
							
							
						
						
									
										5
									
								
								dlq.h
								
								
								
								
							| 
						 | 
				
			
			@ -18,12 +18,11 @@ void dlq_init (void);
 | 
			
		|||
 | 
			
		||||
typedef enum dlq_type_e {DLQ_REC_FRAME} dlq_type_t; 
 | 
			
		||||
 | 
			
		||||
void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum);
 | 
			
		||||
void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum);
 | 
			
		||||
 | 
			
		||||
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, int *slice, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum, size_t spectrumsize); 
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
#include "direwolf.h"
 | 
			
		||||
#include "textcolor.h"
 | 
			
		||||
#include "dtime_now.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -17,9 +17,7 @@
 | 
			
		|||
#include <sys/time.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								dtmf.c
								
								
								
								
							
							
						
						
									
										2
									
								
								dtmf.c
								
								
								
								
							| 
						 | 
				
			
			@ -273,7 +273,7 @@ char dtmf_sample (int c, float input)
 | 
			
		|||
 | 
			
		||||
	    // Update Data Carrier Detect Indicator.
 | 
			
		||||
 | 
			
		||||
	    dcd_change (c, MAX_SUBCHANS, decoded != ' ');
 | 
			
		||||
	    dcd_change (c, MAX_SUBCHANS, 0, decoded != ' ');
 | 
			
		||||
 | 
			
		||||
	    /* Reset timeout timer. */
 | 
			
		||||
	    if (decoded != ' ') {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										37
									
								
								dw-start.sh
								
								
								
								
							
							
						
						
									
										37
									
								
								dw-start.sh
								
								
								
								
							| 
						 | 
				
			
			@ -14,11 +14,20 @@
 | 
			
		|||
# This script has some specifics the Raspberry Pi.
 | 
			
		||||
# Some adjustments might be needed for other Linux variations.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# When running from cron, we have a very minimal environment
 | 
			
		||||
# including PATH=/usr/bin:/bin.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
export PATH=/usr/local/bin:$PATH
 | 
			
		||||
 | 
			
		||||
# First wait a little while in case we just rebooted
 | 
			
		||||
# and the desktop hasn't started up yet.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
sleep 30
 | 
			
		||||
LOGFILE=/tmp/dw-start.log
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Nothing to do if it is already running.
 | 
			
		||||
| 
						 | 
				
			
			@ -28,7 +37,7 @@ a=`pgrep direwolf`
 | 
			
		|||
if [ "$a" != "" ] 
 | 
			
		||||
then
 | 
			
		||||
  #date >> /tmp/dw-start.log
 | 
			
		||||
  #echo "Already running." >> /tmp/dw-start.log
 | 
			
		||||
  #echo "Already running." >> $LOGFILE
 | 
			
		||||
  exit
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -52,26 +61,40 @@ then
 | 
			
		|||
  export DISPLAY="$d"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo "DISPLAY=$DISPLAY" >> /tmp/dw-start.log
 | 
			
		||||
echo "DISPLAY=$DISPLAY" >> $LOGFILE
 | 
			
		||||
 | 
			
		||||
echo "Start up application." >> /tmp/dw-start.log
 | 
			
		||||
echo "Start up application." >> $LOGFILE
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# For normal operation as TNC, digipeater, IGate, etc.
 | 
			
		||||
# Print audio statistics each 100 seconds for troubleshooting.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
DWCMD="direwolf -a 100"
 | 
			
		||||
 | 
			
		||||
# Alternative for running with SDR receiver.
 | 
			
		||||
# Piping one application into another makes it a little more complicated.
 | 
			
		||||
# We need to use bash for the | to be recognized. 
 | 
			
		||||
 | 
			
		||||
#DWCMD="bash -c 'rtl_fm -f 144.39M - | direwolf -c sdr.conf -r 24000 -D 1 -'"
 | 
			
		||||
 | 
			
		||||
# 
 | 
			
		||||
# Adjust for your particular situation:  gnome-terminal, xterm, etc.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if [ -x /usr/bin/lxterminal ]
 | 
			
		||||
then
 | 
			
		||||
  /usr/bin/lxterminal -t "Dire Wolf" -e "/usr/local/bin/direwolf -a 100" &
 | 
			
		||||
  /usr/bin/lxterminal -t "Dire Wolf" -e "$DWCMD" &
 | 
			
		||||
elif [ -x /usr/bin/xterm ] 
 | 
			
		||||
then
 | 
			
		||||
  /usr/bin/xterm -bg white -fg black -e "/usr/local/bin/direwolf -a 100" &
 | 
			
		||||
  /usr/bin/xterm -bg white -fg black -e "$DWCMD" &
 | 
			
		||||
elif [ -x /usr/bin/x-terminal-emulator ]
 | 
			
		||||
then
 | 
			
		||||
  /usr/bin/x-terminal-emulator -e  "/usr/local/bin/direwolf -a 100" &
 | 
			
		||||
  /usr/bin/x-terminal-emulator -e "$DWCMD" &
 | 
			
		||||
else
 | 
			
		||||
  echo "Did not find an X terminal emulator."
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo "-----------------------" >> /tmp/dw-start.log
 | 
			
		||||
echo "-----------------------" >> $LOGFILE
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										423
									
								
								dwgps.c
								
								
								
								
							
							
						
						
									
										423
									
								
								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 <stdio.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <sys/time.h>
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#else
 | 
			
		||||
#if ENABLE_GPS
 | 
			
		||||
#include <gps.h>
 | 
			
		||||
 | 
			
		||||
#if GPSD_API_MAJOR_VERSION != 5
 | 
			
		||||
#error libgps API version might be incompatible.
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
#include "direwolf.h"
 | 
			
		||||
#include "textcolor.h"
 | 
			
		||||
#include "dwgps.h"
 | 
			
		||||
#include "dwgpsnmea.h"
 | 
			
		||||
#include "dwgpsd.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Was init successful? */
 | 
			
		||||
static int s_dwgps_debug = 0;		/* Enable debug output. */
 | 
			
		||||
					/* >= 2 show updates from GPS. */
 | 
			
		||||
					/* >= 1 show results from dwgps_read. */
 | 
			
		||||
 | 
			
		||||
static enum { INIT_NOT_YET, INIT_SUCCESS, INIT_FAILED } init_status = INIT_NOT_YET;
 | 
			
		||||
/*
 | 
			
		||||
 * The GPS reader threads deposit current data here when it becomes available.
 | 
			
		||||
 * dwgps_read returns it to the requesting application.
 | 
			
		||||
 *
 | 
			
		||||
 * A critical region to avoid inconsistency between fields.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#else
 | 
			
		||||
#if ENABLE_GPS
 | 
			
		||||
static dwgps_info_t s_dwgps_info = {
 | 
			
		||||
	.timestamp = 0,
 | 
			
		||||
	.fix = DWFIX_NOT_INIT,			/* to detect read without init. */
 | 
			
		||||
	.dlat = G_UNKNOWN,
 | 
			
		||||
	.dlon = G_UNKNOWN,
 | 
			
		||||
	.speed_knots = G_UNKNOWN,
 | 
			
		||||
	.track = G_UNKNOWN,
 | 
			
		||||
	.altitude = G_UNKNOWN
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct gps_data_t gpsdata;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
static dw_mutex_t s_gps_mutex;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			@ -93,223 +91,125 @@ static struct gps_data_t gpsdata;
 | 
			
		|||
 *
 | 
			
		||||
 * Purpose:    	Intialize the GPS interface.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	none.
 | 
			
		||||
 *		
 | 
			
		||||
 * Returns:	0 = success
 | 
			
		||||
 *		-1 = failure
 | 
			
		||||
 * Inputs:	pconfig		Configuration settings.  This might include
 | 
			
		||||
 *				serial port name for direct connect and host
 | 
			
		||||
 *				name or address for network connection.
 | 
			
		||||
 *
 | 
			
		||||
 * Description:	For Linux, this maps into gps_open.
 | 
			
		||||
 *		Not yet implemented for Windows.
 | 
			
		||||
 *		debug	- If >= 1, print results when dwgps_read is called.
 | 
			
		||||
 *				(In this file.)
 | 
			
		||||
 *
 | 
			
		||||
 *			  If >= 2, location updates are also printed.
 | 
			
		||||
 *				(In other two related files.)
 | 
			
		||||
 *
 | 
			
		||||
 * Returns:	none
 | 
			
		||||
 *
 | 
			
		||||
 * Description:	Call corresponding functions for implementations.
 | 
			
		||||
 * 		Normally we would expect someone to use either GPSNMEA or
 | 
			
		||||
 *		GPSD but there is nothing to prevent use of both at the
 | 
			
		||||
 *		same time.
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
int dwgps_init (void)
 | 
			
		||||
 | 
			
		||||
void dwgps_init (struct misc_config_s *pconfig, int debug)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
	s_dwgps_debug = debug;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Windows version.  Not implemented yet.
 | 
			
		||||
 */
 | 
			
		||||
	dw_mutex_init (&s_gps_mutex);
 | 
			
		||||
 | 
			
		||||
	text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	dw_printf ("GPS interface not yet available in Windows version.\n");
 | 
			
		||||
	init_status = INIT_FAILED;
 | 
			
		||||
	return (-1);
 | 
			
		||||
	dwgpsnmea_init (pconfig, debug);
 | 
			
		||||
 | 
			
		||||
#elif ENABLE_GPS
 | 
			
		||||
#if ENABLE_GPSD
 | 
			
		||||
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
#if USE_GPS_SHM
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Linux - Shared memory interface to gpsd.
 | 
			
		||||
 *
 | 
			
		||||
 * I wanted to use this method because it is simpler and more efficient.
 | 
			
		||||
 *
 | 
			
		||||
 * The current version of gpsd, supplied with Raspian, is 3.6 from back in 
 | 
			
		||||
 * May 2012, is missing support for the shared memory interface.  
 | 
			
		||||
 * https://github.com/raspberrypi/linux/issues/523
 | 
			
		||||
 *
 | 
			
		||||
 * I tried to download a newer source and build with shared memory support
 | 
			
		||||
 * but ran into a couple other issues.
 | 
			
		||||
 * 
 | 
			
		||||
 * 	sudo apt-get install libncurses5-dev
 | 
			
		||||
 * 	sudo apt-get install scons
 | 
			
		||||
 * 	cd ~
 | 
			
		||||
 * 	wget http://download-mirror.savannah.gnu.org/releases/gpsd/gpsd-3.11.tar.gz
 | 
			
		||||
 * 	tar xfz gpsd-3.11.tar.gz
 | 
			
		||||
 * 	cd gpsd-3.11
 | 
			
		||||
 * 	scons prefix=/usr libdir=lib/arm-linux-gnueabihf shm_export=True python=False
 | 
			
		||||
 * 	sudo scons udev-install
 | 
			
		||||
 * 
 | 
			
		||||
 * For now, we will use the socket interface.
 | 
			
		||||
 * Maybe get back to this again someday.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
	err = gps_open (GPSD_SHARED_MEMORY, NULL, &gpsdata);
 | 
			
		||||
	if (err != 0) {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("Unable to connect to GPSD shared memory interface, status=%d.\n", err);
 | 
			
		||||
	  if (err == NL_NOHOST) {
 | 
			
		||||
	    // I don't think this is right but we are not using it anyhow.
 | 
			
		||||
	    dw_printf ("Shared memory interface is not enabled in libgps.\n");
 | 
			
		||||
	    dw_printf ("Download the gpsd source and build with 'shm_export=True' option.\n");
 | 
			
		||||
	  }
 | 
			
		||||
	  else {
 | 
			
		||||
	    dw_printf ("%s\n", gps_errstr(errno));
 | 
			
		||||
	  }
 | 
			
		||||
	  init_status = INIT_FAILED;
 | 
			
		||||
	  return (-1);
 | 
			
		||||
	}
 | 
			
		||||
	init_status = INIT_SUCCESS;
 | 
			
		||||
	return (0);
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Linux - Socket interface to gpsd.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
	err = gps_open ("localhost", DEFAULT_GPSD_PORT, &gpsdata);
 | 
			
		||||
	if (err != 0) {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("Unable to connect to GPSD stream, status%d.\n", err);
 | 
			
		||||
	  dw_printf ("%s\n", gps_errstr(errno));
 | 
			
		||||
	  init_status = INIT_FAILED;
 | 
			
		||||
	  return (-1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gps_stream(&gpsdata, WATCH_ENABLE | WATCH_JSON, NULL);
 | 
			
		||||
 | 
			
		||||
	init_status = INIT_SUCCESS;
 | 
			
		||||
	return (0);
 | 
			
		||||
 | 
			
		||||
#endif 
 | 
			
		||||
 | 
			
		||||
#else	/* end ENABLE_GPS */
 | 
			
		||||
 | 
			
		||||
	text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	dw_printf ("GPS interface not enabled in this version.\n");
 | 
			
		||||
	dw_printf ("See documentation on how to rebuild with ENABLE_GPS.\n");
 | 
			
		||||
	init_status = INIT_FAILED;
 | 
			
		||||
	return (-1);
 | 
			
		||||
	dwgpsd_init (pconfig, debug);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  /* end dwgps_init */
 | 
			
		||||
	SLEEP_MS(500);		/* So receive thread(s) can clear the */
 | 
			
		||||
				/* not init status before it gets checked. */
 | 
			
		||||
 | 
			
		||||
} /* end dwgps_init */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:        dwgps_clear
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:    	Clear the gps info structure.
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
void dwgps_clear (dwgps_info_t *gpsinfo)
 | 
			
		||||
{
 | 
			
		||||
	gpsinfo->timestamp = 0;
 | 
			
		||||
	gpsinfo->fix = DWFIX_NOT_SEEN;
 | 
			
		||||
	gpsinfo->dlat = G_UNKNOWN;
 | 
			
		||||
	gpsinfo->dlon = G_UNKNOWN;
 | 
			
		||||
	gpsinfo->speed_knots = G_UNKNOWN;
 | 
			
		||||
	gpsinfo->track = G_UNKNOWN;
 | 
			
		||||
	gpsinfo->altitude = G_UNKNOWN;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:        dwgps_read
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:    	Obtain current location from GPS receiver.
 | 
			
		||||
 * Purpose:     Return most recent location data available.
 | 
			
		||||
 *
 | 
			
		||||
 * Outputs:	*plat		- Latitude.
 | 
			
		||||
 *		*plon		- Longitude.
 | 
			
		||||
 *		*pspeed		- Speed, knots.
 | 
			
		||||
 *		*pcourse	- Course over ground, degrees.
 | 
			
		||||
 *		*palt		- Altitude, meters.
 | 
			
		||||
 * Outputs:	gpsinfo		- Structure with latitude, longitude, etc.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns:	Position fix quality.  Same as in structure.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns:	-1 = error
 | 
			
		||||
 *		0 = location currently not available (no fix)
 | 
			
		||||
 *		2 = 2D fix, lat/lon, speed, and course are set.
 | 
			
		||||
 *		3 - 3D fix, altitude is also set.
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
int dwgps_read (double *plat, double *plon, float *pspeed, float *pcourse, float *palt)
 | 
			
		||||
dwfix_t dwgps_read (dwgps_info_t *gpsinfo)
 | 
			
		||||
{
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
 | 
			
		||||
	text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	dw_printf ("Internal error, dwgps_read, shouldn't be here.\n");
 | 
			
		||||
	return (-1);
 | 
			
		||||
	dw_mutex_lock (&s_gps_mutex);
 | 
			
		||||
 | 
			
		||||
#elif ENABLE_GPS
 | 
			
		||||
	memcpy (gpsinfo, &s_dwgps_info, sizeof(*gpsinfo));
 | 
			
		||||
 | 
			
		||||
	int err;
 | 
			
		||||
	dw_mutex_unlock (&s_gps_mutex);
 | 
			
		||||
 | 
			
		||||
	if (init_status != INIT_SUCCESS) {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("Internal error, dwgps_read without successful init.\n");
 | 
			
		||||
	  return (-1);
 | 
			
		||||
	if (s_dwgps_debug >= 1) {
 | 
			
		||||
	  text_color_set (DW_COLOR_DEBUG);
 | 
			
		||||
	  dwgps_print ("gps_read: ", gpsinfo);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#if USE_GPS_SHM
 | 
			
		||||
	// TODO: Should we check timestamp and complain if very stale?
 | 
			
		||||
	// or should we leave that up to the caller?
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Shared memory version.
 | 
			
		||||
 */
 | 
			
		||||
	return (s_dwgps_info.fix);
 | 
			
		||||
} 
 | 
			
		||||
 | 
			
		||||
	err = gps_read (&gpsdata);
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	dw_printf ("gps_read returns %d bytes\n", err);
 | 
			
		||||
#endif
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:        dwgps_print
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:     Print gps information for debugging.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	msg		- Message for prefix on line.
 | 
			
		||||
 *		gpsinfo		- Structure with latitude, longitude, etc.
 | 
			
		||||
 *
 | 
			
		||||
 * Description:	Caller is responsible for setting text color.
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
void dwgps_print (char *msg, dwgps_info_t *gpsinfo)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Socket version.
 | 
			
		||||
 */
 | 
			
		||||
	dw_printf ("%stime=%d fix=%d lat=%.6f lon=%.6f trk=%.0f spd=%.1f alt=%.0f\n",
 | 
			
		||||
			msg,
 | 
			
		||||
			(int)gpsinfo->timestamp, (int)gpsinfo->fix,
 | 
			
		||||
			gpsinfo->dlat, gpsinfo->dlon,
 | 
			
		||||
			gpsinfo->track, gpsinfo->speed_knots,
 | 
			
		||||
			gpsinfo->altitude);
 | 
			
		||||
 | 
			
		||||
	// Wait for up to 1000 milliseconds.
 | 
			
		||||
	// This should only happen in the beaconing thread so 
 | 
			
		||||
	// I'm not worried about other functions hanging.
 | 
			
		||||
 | 
			
		||||
        if (gps_waiting(&gpsdata, 1000)) {
 | 
			
		||||
 | 
			
		||||
	  err = gps_read (&gpsdata);
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  gps_stream(&gpsdata, WATCH_ENABLE | WATCH_JSON, NULL);
 | 
			
		||||
	  sleep (1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (err > 0) {
 | 
			
		||||
	  /* Data is available. */
 | 
			
		||||
 | 
			
		||||
	  if (gpsdata.status >= STATUS_FIX && gpsdata.fix.mode >= MODE_2D) {
 | 
			
		||||
 | 
			
		||||
	     *plat = gpsdata.fix.latitude;
 | 
			
		||||
	     *plon = gpsdata.fix.longitude;
 | 
			
		||||
	     *pcourse = gpsdata.fix.track;		
 | 
			
		||||
	     *pspeed = MPS_TO_KNOTS * gpsdata.fix.speed; /* libgps uses meters/sec */
 | 
			
		||||
 | 
			
		||||
	     if (gpsdata.fix.mode >= MODE_3D) {
 | 
			
		||||
	       *palt = gpsdata.fix.altitude;
 | 
			
		||||
	       return (3);
 | 
			
		||||
	     }
 | 
			
		||||
	     return (2);
 | 
			
		||||
	   }
 | 
			
		||||
 | 
			
		||||
	   /* No fix.  Probably temporary condition. */
 | 
			
		||||
	   return (0);
 | 
			
		||||
	}
 | 
			
		||||
	else if (err == 0) {
 | 
			
		||||
 | 
			
		||||
	   /* No data available at the present time. */
 | 
			
		||||
	   return (0);
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
 | 
			
		||||
	  /* More serious error. */
 | 
			
		||||
	  return (-1);
 | 
			
		||||
	}
 | 
			
		||||
#else 
 | 
			
		||||
 | 
			
		||||
	text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	dw_printf ("Internal error, dwgps_read, shouldn't be here.\n");
 | 
			
		||||
	return (-1);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
} /* end dwgps_read */
 | 
			
		||||
}  /* end dwgps_set_data */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			@ -326,19 +226,10 @@ int dwgps_read (double *plat, double *plon, float *pspeed, float *pcourse, float
 | 
			
		|||
 | 
			
		||||
void dwgps_term (void) {
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
	
 | 
			
		||||
#elif ENABLE_GPS
 | 
			
		||||
 | 
			
		||||
	if (init_status == INIT_SUCCESS) {
 | 
			
		||||
 | 
			
		||||
#ifndef USE_GPS_SHM
 | 
			
		||||
	  gps_stream(&gpsdata, WATCH_DISABLE, NULL);
 | 
			
		||||
#endif
 | 
			
		||||
	  gps_close (&gpsdata);
 | 
			
		||||
	}
 | 
			
		||||
#else 
 | 
			
		||||
	dwgpsnmea_term ();
 | 
			
		||||
 | 
			
		||||
#if ENABLE_GPSD
 | 
			
		||||
	dwgpsd_term ();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
} /* end dwgps_term */
 | 
			
		||||
| 
						 | 
				
			
			@ -348,69 +239,27 @@ void dwgps_term (void) {
 | 
			
		|||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:        main
 | 
			
		||||
 * Name:        dwgps_set_data
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:    	Simple unit test for other functions in this file.
 | 
			
		||||
 * Purpose:     Called by the GPS interfaces when new data is available.
 | 
			
		||||
 *
 | 
			
		||||
 * Description: Compile with -DTEST option.
 | 
			
		||||
 *
 | 
			
		||||
 *			gcc -DTEST dwgps.c textcolor.c -lgps
 | 
			
		||||
 *			./a.out
 | 
			
		||||
 * Inputs:	gpsinfo		- Structure with latitude, longitude, etc.
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
#if TEST
 | 
			
		||||
 | 
			
		||||
int main (int argc, char *argv[])
 | 
			
		||||
void dwgps_set_data (dwgps_info_t *gpsinfo)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
	/* Debug print is handled by the two callers so */
 | 
			
		||||
	/* we can distinguish the source. */
 | 
			
		||||
 | 
			
		||||
	printf ("Not in win32 version yet.\n");
 | 
			
		||||
	dw_mutex_lock (&s_gps_mutex);
 | 
			
		||||
 | 
			
		||||
#elif ENABLE_GPS
 | 
			
		||||
	int err;
 | 
			
		||||
	int fix;
 | 
			
		||||
	double lat;
 | 
			
		||||
	double lon;
 | 
			
		||||
	float speed;
 | 
			
		||||
	float course;
 | 
			
		||||
	float alt;
 | 
			
		||||
	memcpy (&s_dwgps_info, gpsinfo, sizeof(s_dwgps_info));
 | 
			
		||||
 | 
			
		||||
	err = dwgps_init ();
 | 
			
		||||
 | 
			
		||||
	if (err != 0) exit(1);
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
	  fix = dwgps_read (&lat, &lon, &speed, &course, &alt)
;
 | 
			
		||||
	  switch (fix) {
 | 
			
		||||
	    case 3:
 | 
			
		||||
	    case 2:
 | 
			
		||||
	      dw_printf ("%.6f  %.6f", lat, lon);
 | 
			
		||||
	      dw_printf ("  %.1f knots  %.0f degrees", speed, course);
 | 
			
		||||
	      if (fix==3) dw_printf ("  altitude = %.1f meters", alt);
 | 
			
		||||
	      dw_printf ("\n");
 | 
			
		||||
	      break;
 | 
			
		||||
	    case 0:
 | 
			
		||||
	      dw_printf ("location currently not available.\n");
 | 
			
		||||
	      break;
 | 
			
		||||
	    default:
 | 
			
		||||
	      dw_printf ("ERROR getting GPS information.\n");
 | 
			
		||||
	  }
 | 
			
		||||
	  sleep (3);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#else 
 | 
			
		||||
 | 
			
		||||
	printf ("Test: Shouldn't be here.\n");
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
} /* end main */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
	dw_mutex_unlock (&s_gps_mutex);
 | 
			
		||||
 | 
			
		||||
}  /* end dwgps_set_data */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* end dwgps.c */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										50
									
								
								dwgps.h
								
								
								
								
							
							
						
						
									
										50
									
								
								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 <time.h>
 | 
			
		||||
#include "config.h"	/* for struct misc_config_s */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Values for fix, equivalent to values from libgps.
 | 
			
		||||
 *	-2 = not initialized.
 | 
			
		||||
 *	-1 = error communicating with GPS receiver.
 | 
			
		||||
 *	0 = nothing heard yet.
 | 
			
		||||
 *	1 = had signal but lost it.
 | 
			
		||||
 *	2 = 2D.
 | 
			
		||||
 *	3 = 3D.
 | 
			
		||||
 *
 | 
			
		||||
 * Undefined float & double values are set to G_UNKNOWN.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
enum dwfix_e { DWFIX_NOT_INIT= -2, DWFIX_ERROR= -1, DWFIX_NOT_SEEN=0, DWFIX_NO_FIX=1, DWFIX_2D=2, DWFIX_3D=3 };
 | 
			
		||||
 | 
			
		||||
typedef enum dwfix_e dwfix_t;
 | 
			
		||||
 | 
			
		||||
typedef struct dwgps_info_s {
 | 
			
		||||
	time_t timestamp;	/* When last updated.  System time. */
 | 
			
		||||
	dwfix_t fix;		/* Quality of position fix. */
 | 
			
		||||
	double dlat;		/* Latitude.  Valid if fix >= 2. */
 | 
			
		||||
	double dlon;		/* Longitude. Valid if fix >= 2. */
 | 
			
		||||
	float speed_knots;	/* libgps uses meters/sec but we use GPS usual knots. */
 | 
			
		||||
	float track;		/* What is difference between track and course? */
 | 
			
		||||
	float altitude;		/* meters above mean sea level. Valid if fix == 3. */
 | 
			
		||||
} dwgps_info_t;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void dwgps_init (struct misc_config_s *pconfig, int debug);
 | 
			
		||||
 | 
			
		||||
void dwgps_clear (dwgps_info_t *gpsinfo);
 | 
			
		||||
 | 
			
		||||
dwfix_t dwgps_read (dwgps_info_t *gpsinfo);
 | 
			
		||||
 | 
			
		||||
void dwgps_print (char *msg, dwgps_info_t *gpsinfo);
 | 
			
		||||
 | 
			
		||||
void dwgps_term (void);
 | 
			
		||||
 | 
			
		||||
void dwgps_set_data (dwgps_info_t *gpsinfo);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* DWGPS_H 1 */
 | 
			
		||||
 | 
			
		||||
/* end dwgps.h */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,434 @@
 | 
			
		|||
//
 | 
			
		||||
//    This file is part of Dire Wolf, an amateur radio packet TNC.
 | 
			
		||||
//
 | 
			
		||||
//    Copyright (C) 2013, 2014, 2015  John Langner, WB2OSZ
 | 
			
		||||
//
 | 
			
		||||
//    This program is free software: you can redistribute it and/or modify
 | 
			
		||||
//    it under the terms of the GNU General Public License as published by
 | 
			
		||||
//    the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
//    (at your option) any later version.
 | 
			
		||||
//
 | 
			
		||||
//    This program is distributed in the hope that it will be useful,
 | 
			
		||||
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
//    GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
//    You should have received a copy of the GNU General Public License
 | 
			
		||||
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Module:      dwgps.c
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:   	Interface to location data, i.e. GPS receiver.
 | 
			
		||||
 *		
 | 
			
		||||
 * Description:	For Linux, we would normally want to use gpsd and libgps.
 | 
			
		||||
 *		This allows multiple applications to access the GPS data,
 | 
			
		||||
 *		without fighting over the same serial port, and has the 
 | 
			
		||||
 *		extra benefit that the system clock can be set from the GPS signal.
 | 
			
		||||
 *
 | 
			
		||||
 * Reference:	http://www.catb.org/gpsd/
 | 
			
		||||
 *
 | 
			
		||||
 *---------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
#error Not for Windows
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if ENABLE_GPSD
 | 
			
		||||
#include <gps.h>
 | 
			
		||||
 | 
			
		||||
// Debian bug report:  direwolf (1.2-1) FTBFS with libgps22 as part of the gpsd transition (#803605):
 | 
			
		||||
// dwgps.c claims to only support GPSD_API_MAJOR_VERSION 5, but also builds successfully with
 | 
			
		||||
// GPSD_API_MAJOR_VERSION 6 provided by libgps22 when the attached patch is applied.
 | 
			
		||||
#if GPSD_API_MAJOR_VERSION < 5 || GPSD_API_MAJOR_VERSION > 6
 | 
			
		||||
#error libgps API version might be incompatible.
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "direwolf.h"
 | 
			
		||||
#include "textcolor.h"
 | 
			
		||||
#include "dwgps.h"
 | 
			
		||||
#include "dwgpsd.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int s_debug = 0;		/* Enable debug output. */
 | 
			
		||||
				/* >= 1 show results from dwgps_read. */
 | 
			
		||||
				/* >= 2 show updates from GPS. */
 | 
			
		||||
 | 
			
		||||
static void * read_gpsd_thread (void *arg);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Information for interface to gpsd daemon. 
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static struct gps_data_t gpsdata;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:        dwgpsd_init
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:    	Intialize the GPS interface.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	pconfig		Configuration settings.  This includes
 | 
			
		||||
 *				host name or address for network connection.
 | 
			
		||||
 *
 | 
			
		||||
 *		debug	- If >= 1, print results when dwgps_read is called.
 | 
			
		||||
 *				(In different file.)
 | 
			
		||||
 *
 | 
			
		||||
 *			  If >= 2, location updates are also printed.
 | 
			
		||||
 *				(In this file.)
 | 
			
		||||
 *		
 | 
			
		||||
 * Returns:	1 = success
 | 
			
		||||
 *		0 = nothing to do  (no host specified in config)
 | 
			
		||||
 *		-1 = failure
 | 
			
		||||
 *
 | 
			
		||||
 * Description:	- Establish socket connection with gpsd.
 | 
			
		||||
 *		- Start up thread to process incoming data.
 | 
			
		||||
 *		  It reads from the daemon and deposits into
 | 
			
		||||
 *		  shared region via dwgps_put_data.
 | 
			
		||||
 *
 | 
			
		||||
 * 		The application calls dwgps_read to get the most 
 | 
			
		||||
 8		recent information.			
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Historical notes:
 | 
			
		||||
 *
 | 
			
		||||
 * Originally, I wanted to use the shared memory interface to gpsd
 | 
			
		||||
 * because it is simpler and more efficient.  Just access it when we
 | 
			
		||||
 * actually need the data and we don't have a lot of extra unnecessary
 | 
			
		||||
 * busy work going on.
 | 
			
		||||
 *
 | 
			
		||||
 * The current version of gpsd, supplied with Raspian, is 3.6 from back in 
 | 
			
		||||
 * May 2012, is missing support for the shared memory interface.  
 | 
			
		||||
 * 
 | 
			
		||||
 * I tried to download a newer source and build with shared memory support
 | 
			
		||||
 * but ran into a couple other issues.
 | 
			
		||||
 * 
 | 
			
		||||
 * 	sudo apt-get install libncurses5-dev
 | 
			
		||||
 * 	sudo apt-get install scons
 | 
			
		||||
 * 	cd ~
 | 
			
		||||
 * 	wget http://download-mirror.savannah.gnu.org/releases/gpsd/gpsd-3.11.tar.gz
 | 
			
		||||
 * 	tar xfz gpsd-3.11.tar.gz
 | 
			
		||||
 * 	cd gpsd-3.11
 | 
			
		||||
 * 	scons prefix=/usr libdir=lib/arm-linux-gnueabihf shm_export=True python=False
 | 
			
		||||
 * 	sudo scons udev-install
 | 
			
		||||
 * 
 | 
			
		||||
 * For now, we will use the socket interface.  Maybe get back to this again someday.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int dwgpsd_init (struct misc_config_s *pconfig, int debug)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
#if ENABLE_GPSD
 | 
			
		||||
 | 
			
		||||
	pthread_t read_gps_tid;
 | 
			
		||||
	int e;
 | 
			
		||||
	int err;
 | 
			
		||||
	int arg = 0;
 | 
			
		||||
	char sport[12];
 | 
			
		||||
	dwgps_info_t info;
 | 
			
		||||
 | 
			
		||||
	s_debug = debug;
 | 
			
		||||
 | 
			
		||||
	if (s_debug >= 2) {
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("dwgpsd_init()\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Socket interface to gpsd.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
	if (strlen(pconfig->gpsd_host) == 0) {
 | 
			
		||||
 | 
			
		||||
	  /* Nothing to do.  Leave initial fix value of errror. */
 | 
			
		||||
	  return (0);
 | 
			
		||||
	}	  
 | 
			
		||||
 | 
			
		||||
	snprintf (sport, sizeof(sport), "%d", pconfig->gpsd_port);
 | 
			
		||||
	err = gps_open (pconfig->gpsd_host, sport, &gpsdata);
 | 
			
		||||
	if (err != 0) {
 | 
			
		||||
	  dwgps_info_t info;
 | 
			
		||||
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("Unable to connect to GPSD stream at %s:%s.\n", pconfig->gpsd_host, sport);
 | 
			
		||||
	  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 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,800 @@
 | 
			
		|||
//
 | 
			
		||||
//    This file is part of Dire Wolf, an amateur radio packet TNC.
 | 
			
		||||
//
 | 
			
		||||
//    Copyright (C) 2013, 2014, 2015  John Langner, WB2OSZ
 | 
			
		||||
//
 | 
			
		||||
//    This program is free software: you can redistribute it and/or modify
 | 
			
		||||
//    it under the terms of the GNU General Public License as published by
 | 
			
		||||
//    the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
//    (at your option) any later version.
 | 
			
		||||
//
 | 
			
		||||
//    This program is distributed in the hope that it will be useful,
 | 
			
		||||
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
//    GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
//    You should have received a copy of the GNU General Public License
 | 
			
		||||
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Module:      dwgpsnmea.c
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:   	process NMEA sentences from a GPS receiver.
 | 
			
		||||
 *		
 | 
			
		||||
 * Description:	This version is available for all operating systems.
 | 
			
		||||
 *
 | 
			
		||||
 *---------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "direwolf.h"
 | 
			
		||||
#include "textcolor.h"
 | 
			
		||||
#include "dwgps.h"
 | 
			
		||||
#include "dwgpsnmea.h"
 | 
			
		||||
#include "serial_port.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int s_debug = 0;		/* Enable debug output. */
 | 
			
		||||
				/* See dwgpsnmea_init description for values. */
 | 
			
		||||
					
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
static unsigned __stdcall read_gpsnmea_thread (void *arg);
 | 
			
		||||
#else
 | 
			
		||||
static void * read_gpsnmea_thread (void *arg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:        dwgpsnmea_init
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:    	Open serial port for the GPS receiver.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	pconfig		Configuration settings.  This includes
 | 
			
		||||
 *				serial port name for direct connect.
 | 
			
		||||
 *
 | 
			
		||||
 *		debug	- If >= 1, print results when dwgps_read is called.
 | 
			
		||||
 *				(In different file.)
 | 
			
		||||
 *
 | 
			
		||||
 *			  If >= 2, location updates are also printed.
 | 
			
		||||
 *				(In this file.)
 | 
			
		||||
 *				Why not do it in dwgps_set_data() ?
 | 
			
		||||
 *				Here, we can prefix it with GPSNMEA to
 | 
			
		||||
 *				distinguish it from GPSD.
 | 
			
		||||
 *
 | 
			
		||||
 *			  If >= 3, Also the NMEA sentences.
 | 
			
		||||
 *				(In this file.)
 | 
			
		||||
 *		
 | 
			
		||||
 * Returns:	1 = success
 | 
			
		||||
 *		0 = nothing to do  (no serial port specified in config)
 | 
			
		||||
 *		-1 = failure
 | 
			
		||||
 *
 | 
			
		||||
 * Description:	When talking directly to GPS receiver  (any operating system):
 | 
			
		||||
 *
 | 
			
		||||
 *			- Open the appropriate serial port.
 | 
			
		||||
 *			- Start up thread to process incoming data.
 | 
			
		||||
 *			  It reads from the serial port and deposits into
 | 
			
		||||
 *			  dwgps_info, above.
 | 
			
		||||
 *
 | 
			
		||||
 * 		The application calls dwgps_read to get the most recent information.			
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
/* Make this static and available to all functions so term function can access it. */
 | 
			
		||||
 | 
			
		||||
static MYFDTYPE s_gpsnmea_port_fd = MYFDERROR;   /* Handle for serial port. */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int dwgpsnmea_init (struct misc_config_s *pconfig, int debug)
 | 
			
		||||
{
 | 
			
		||||
	//dwgps_info_t info;
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
	HANDLE read_gps_th;
 | 
			
		||||
#else
 | 
			
		||||
	pthread_t read_gps_tid;
 | 
			
		||||
	int e;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	s_debug = debug;
 | 
			
		||||
 | 
			
		||||
	if (s_debug >= 2) {
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("dwgpsnmea_init()\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strlen(pconfig->gpsnmea_port) == 0) {
 | 
			
		||||
 | 
			
		||||
	  /* Nothing to do.  Leave initial fix value for not init. */
 | 
			
		||||
	  return (0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Open serial port connection.
 | 
			
		||||
 * 4800 baud is standard for GPS.
 | 
			
		||||
 * Should add an option to allow changing someday.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
	s_gpsnmea_port_fd = serial_port_open (pconfig->gpsnmea_port, 4800);
 | 
			
		||||
 | 
			
		||||
	if (s_gpsnmea_port_fd != MYFDERROR) {
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
	  read_gps_th = (HANDLE)_beginthreadex (NULL, 0, read_gpsnmea_thread, (void*)(long)s_gpsnmea_port_fd, 0, NULL);
 | 
			
		||||
	  if (read_gps_th == NULL) {
 | 
			
		||||
	    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	    dw_printf ("Could not create GPS NMEA listening thread.\n");
 | 
			
		||||
	    return (-1);
 | 
			
		||||
	  }
 | 
			
		||||
#else
 | 
			
		||||
	  int e;
 | 
			
		||||
	  e = pthread_create (&read_gps_tid, NULL, read_gpsnmea_thread, (void*)(long)s_gpsnmea_port_fd);
 | 
			
		||||
	  if (e != 0) {
 | 
			
		||||
	    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	    perror("Could not create GPS NMEA listening thread.");
 | 
			
		||||
	    return (-1);
 | 
			
		||||
	  }
 | 
			
		||||
#endif
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("Could not open serial port %s for GPS receiver.\n", pconfig->gpsnmea_port);
 | 
			
		||||
	  return (-1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
/* success */
 | 
			
		||||
 | 
			
		||||
	return (1);
 | 
			
		||||
 | 
			
		||||
}  /* end dwgpsnmea_init */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:        read_gpsnmea_thread
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:     Read information from GPS, as it becomes available, and
 | 
			
		||||
 *		store it for later retrieval by dwgps_read.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	fd	- File descriptor for serial port.
 | 
			
		||||
 *
 | 
			
		||||
 * Description:	This version reads from serial port and parses the 
 | 
			
		||||
 *		NMEA sentences.
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
#define TIMEOUT 5
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
static unsigned __stdcall read_gpsnmea_thread (void *arg)
 | 
			
		||||
#else
 | 
			
		||||
static void * read_gpsnmea_thread (void *arg)
 | 
			
		||||
#endif
 | 
			
		||||
{
 | 
			
		||||
	MYFDTYPE fd = (MYFDTYPE)(long)arg;
 | 
			
		||||
 | 
			
		||||
// Maximum length of message from GPS receiver is 82 according to some people.  
 | 
			
		||||
// Make buffer considerably larger to be safe.
 | 
			
		||||
 | 
			
		||||
#define NMEA_MAX_LEN 160	
 | 
			
		||||
 | 
			
		||||
	char gps_msg[NMEA_MAX_LEN];
 | 
			
		||||
	int gps_msg_len = 0;
 | 
			
		||||
	dwgps_info_t info;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (s_debug >= 2) {
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("read_gpsnmea_thread (%d)\n", (int)(long)arg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dwgps_clear (&info);
 | 
			
		||||
	info.fix = DWFIX_NOT_SEEN;	/* clear not init state. */
 | 
			
		||||
	if (s_debug >= 2) {
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dwgps_print ("GPSNMEA: ", &info);
 | 
			
		||||
	}
 | 
			
		||||
	dwgps_set_data (&info);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
	  int ch;
 | 
			
		||||
 | 
			
		||||
	  ch = serial_port_get1(fd);
 | 
			
		||||
 | 
			
		||||
	  if (ch < 0) {
 | 
			
		||||
 | 
			
		||||
	    /* This might happen if a USB  device is unplugged. */
 | 
			
		||||
	    /* I can't imagine anything that would cause it with */
 | 
			
		||||
	    /* a normal serial port. */
 | 
			
		||||
 | 
			
		||||
	    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	    dw_printf ("----------------------------------------------\n");
 | 
			
		||||
	    dw_printf ("GPSNMEA: Lost communication with GPS receiver.\n");
 | 
			
		||||
	    dw_printf ("----------------------------------------------\n");
 | 
			
		||||
 | 
			
		||||
	    info.fix = DWFIX_ERROR;
 | 
			
		||||
	    if (s_debug >= 2) {
 | 
			
		||||
	      text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	      dwgps_print ("GPSNMEA: ", &info);
 | 
			
		||||
	    }
 | 
			
		||||
	    dwgps_set_data (&info);
 | 
			
		||||
 | 
			
		||||
	    // TODO: doesn't exist yet - serial_port_close(fd);
 | 
			
		||||
	    s_gpsnmea_port_fd = MYFDERROR;
 | 
			
		||||
 | 
			
		||||
	    break;	/* terminate thread. */
 | 
			
		||||
	  }
 | 
			
		||||
 | 
			
		||||
	  if (ch == '$') {
 | 
			
		||||
	    // Start of new sentence.
 | 
			
		||||
	    gps_msg_len = 0;
 | 
			
		||||
	    gps_msg[gps_msg_len++] = ch;
 | 
			
		||||
	    gps_msg[gps_msg_len] = '\0';
 | 
			
		||||
	  }
 | 
			
		||||
	  else if (ch == '\r' || ch == '\n') {
 | 
			
		||||
	    if (gps_msg_len >= 6 && gps_msg[0] == '$') {
 | 
			
		||||
 | 
			
		||||
	      dwfix_t f;
 | 
			
		||||
 | 
			
		||||
	      if (s_debug >= 3) {
 | 
			
		||||
	        text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	        dw_printf ("%s\n", gps_msg);
 | 
			
		||||
	      }
 | 
			
		||||
 | 
			
		||||
/* Process sentence. */
 | 
			
		||||
 | 
			
		||||
	      if (strncmp(gps_msg, "$GPRMC", 6) == 0) {
 | 
			
		||||
 | 
			
		||||
		f = dwgpsnmea_gprmc (gps_msg, 0, &info.dlat, &info.dlon, &info.speed_knots, &info.track);
 | 
			
		||||
 | 
			
		||||
	        if (f == DWFIX_ERROR) {
 | 
			
		||||
 | 
			
		||||
		  /* Parse error.  Shouldn't happen.  Better luck next time. */
 | 
			
		||||
	            text_color_set(DW_COLOR_INFO);
 | 
			
		||||
	            dw_printf ("GPSNMEA: Error parsing $GPRMC sentence.\n");
 | 
			
		||||
	            dw_printf ("%s\n", gps_msg);
 | 
			
		||||
	        }
 | 
			
		||||
	        else if (f == DWFIX_2D) {
 | 
			
		||||
 | 
			
		||||
	          if (info.fix != DWFIX_2D && info.fix != DWFIX_3D) {
 | 
			
		||||
 | 
			
		||||
		    text_color_set(DW_COLOR_INFO);
 | 
			
		||||
	            dw_printf ("GPSNMEA: Location fix is now available.\n");
 | 
			
		||||
 | 
			
		||||
		    info.fix = DWFIX_2D;   // Don't know if 2D or 3D.  Take minimum.
 | 
			
		||||
	          }
 | 
			
		||||
	  	  info.timestamp = time(NULL);
 | 
			
		||||
	          if (s_debug >= 2) {
 | 
			
		||||
	            text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	            dwgps_print ("GPSNMEA: ", &info);
 | 
			
		||||
	          }
 | 
			
		||||
	          dwgps_set_data (&info);
 | 
			
		||||
	        }
 | 
			
		||||
	        else {
 | 
			
		||||
 | 
			
		||||
	          if (info.fix == DWFIX_2D || info.fix == DWFIX_3D) {
 | 
			
		||||
 | 
			
		||||
		    text_color_set(DW_COLOR_INFO);
 | 
			
		||||
	            dw_printf ("GPSNMEA: Lost location fix.\n");
 | 
			
		||||
	          }
 | 
			
		||||
	          info.fix = f;		/* lost it. */
 | 
			
		||||
	  	  info.timestamp = time(NULL);
 | 
			
		||||
	          if (s_debug >= 2) {
 | 
			
		||||
	            text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	            dwgps_print ("GPSNMEA: ", &info);
 | 
			
		||||
	          }
 | 
			
		||||
	          dwgps_set_data (&info);
 | 
			
		||||
	        }
 | 
			
		||||
 | 
			
		||||
	      }
 | 
			
		||||
	      else if (strncmp(gps_msg, "$GPGGA", 6) == 0) {
 | 
			
		||||
		int nsat;
 | 
			
		||||
 | 
			
		||||
		f = dwgpsnmea_gpgga (gps_msg, 0, &info.dlat, &info.dlon, &info.altitude, &nsat);
 | 
			
		||||
 | 
			
		||||
	        /* Only switch between 2D & 3D.  */
 | 
			
		||||
	        /* Let GPRMC handle other changes in fix state and data transfer. */
 | 
			
		||||
 | 
			
		||||
	        if (f == DWFIX_ERROR) {
 | 
			
		||||
 | 
			
		||||
		    /* Parse error.  Shouldn't happen.  Better luck next time. */
 | 
			
		||||
	            text_color_set(DW_COLOR_INFO);
 | 
			
		||||
	            dw_printf ("GPSNMEA: Error parsing $GPGGA sentence.\n");
 | 
			
		||||
	            dw_printf ("%s\n", gps_msg);
 | 
			
		||||
	        }
 | 
			
		||||
	        else if ((f == DWFIX_3D && info.fix == DWFIX_2D) ||
 | 
			
		||||
	                 (f == DWFIX_2D && info.fix == DWFIX_3D)) {
 | 
			
		||||
		  text_color_set(DW_COLOR_INFO);
 | 
			
		||||
	          dw_printf ("GPSNMEA: Location fix is now %dD.\n", (int)f);
 | 
			
		||||
	          info.fix = f;
 | 
			
		||||
	        }	
 | 
			
		||||
	      }
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    gps_msg_len = 0;
 | 
			
		||||
	    gps_msg[gps_msg_len] = '\0';
 | 
			
		||||
	  }
 | 
			
		||||
	  else {	
 | 
			
		||||
	    if (gps_msg_len < NMEA_MAX_LEN-1) {
 | 
			
		||||
	      gps_msg[gps_msg_len++] = ch;
 | 
			
		||||
	      gps_msg[gps_msg_len] = '\0';
 | 
			
		||||
	    }
 | 
			
		||||
	  }
 | 
			
		||||
	}	/* while (1) */
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
	return (0);
 | 
			
		||||
#else
 | 
			
		||||
	return (NULL);	
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
} /* end read_gpsnmea_thread */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:	remove_checksum
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:	Validate checksum and remove before further processing.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	sentence
 | 
			
		||||
 *		quiet		suppress printing of error messages.
 | 
			
		||||
 *
 | 
			
		||||
 * Outputs:	sentence	modified in place.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns:	0 = good checksum.
 | 
			
		||||
 *		-1 = error.  missing or wrong.
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int remove_checksum (char *sent, int quiet)
 | 
			
		||||
{
 | 
			
		||||
        char *p;
 | 
			
		||||
        unsigned char cs;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Do we have valid checksum?
 | 
			
		||||
 | 
			
		||||
        cs = 0;
 | 
			
		||||
        for (p = sent+1; *p != '*' && *p != '\0'; p++) {
 | 
			
		||||
          cs ^= *p;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        p = strchr (sent, '*');
 | 
			
		||||
        if (p == NULL) {
 | 
			
		||||
	  if ( ! quiet) {
 | 
			
		||||
	    text_color_set (DW_COLOR_INFO);
 | 
			
		||||
            dw_printf("Missing GPS checksum.\n");
 | 
			
		||||
	  }
 | 
			
		||||
          return (-1);
 | 
			
		||||
        }
 | 
			
		||||
        if (cs != strtoul(p+1, NULL, 16)) {
 | 
			
		||||
	  if ( ! quiet) {
 | 
			
		||||
	    text_color_set (DW_COLOR_ERROR);
 | 
			
		||||
            dw_printf("GPS checksum error. Expected %02x but found %s.\n", cs, p+1);
 | 
			
		||||
	  }
 | 
			
		||||
          return (-1);
 | 
			
		||||
        }
 | 
			
		||||
        *p = '\0';      // Remove the checksum.
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:        dwgpsnmea_gprmc
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:    	Parse $GPRMC sentence and extract interesing parts.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	sentence	NMEA sentence.
 | 
			
		||||
 *
 | 
			
		||||
 *		quiet		suppress printing of error messages.
 | 
			
		||||
 *
 | 
			
		||||
 * Outputs:	odlat		latitude
 | 
			
		||||
 *		odlon		longitude
 | 
			
		||||
 *		oknots		speed
 | 
			
		||||
 *		ocourse		direction of travel.
 | 
			
		||||
 *		
 | 
			
		||||
 *					Left undefined if not valid.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns:	DWFIX_ERROR	Parse error.
 | 
			
		||||
 *		DWFIX_NO_FIX	GPS is there but Position unknown.  Could be temporary.
 | 
			
		||||
 *		DWFIX_2D	Valid position.   We don't know if it is really 2D or 3D.
 | 
			
		||||
 *
 | 
			
		||||
 * Examples:	$GPRMC,001431.00,V,,,,,,,121015,,,N*7C
 | 
			
		||||
 *		$GPRMC,212404.000,V,4237.1505,N,07120.8602,W,,,150614,,*0B
 | 
			
		||||
 *		$GPRMC,000029.020,V,,,,,,,080810,,,N*45
 | 
			
		||||
 *		$GPRMC,003413.710,A,4237.1240,N,07120.8333,W,5.07,291.42,160614,,,A*7F
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
dwfix_t dwgpsnmea_gprmc (char *sentence, int quiet, double *odlat, double *odlon, float *oknots, float *ocourse)
 | 
			
		||||
{
 | 
			
		||||
	char stemp[NMEA_MAX_LEN];	/* Make copy because parsing is destructive. */
 | 
			
		||||
 | 
			
		||||
	char *next;
 | 
			
		||||
 | 
			
		||||
	char *ptype;			/* Should be $GPRMC */
 | 
			
		||||
	char *ptime;			/* Time, hhmmss[.sss] */
 | 
			
		||||
	char *pstatus;			/* Status, A=Active (valid position), V=Void */
 | 
			
		||||
	char *plat;			/* Latitude */
 | 
			
		||||
	char *pns;			/* North/South */
 | 
			
		||||
	char *plon;			/* Longitude */
 | 
			
		||||
	char *pew;			/* East/West */
 | 
			
		||||
	char *pknots;			/* Speed over ground, knots. */
 | 
			
		||||
	char *pcourse;			/* True course, degrees. */
 | 
			
		||||
	char *pdate;			/* Date, ddmmyy */
 | 
			
		||||
					/* Magnetic variation */
 | 
			
		||||
					/* In version 3.00, mode is added: A D E N (see below) */
 | 
			
		||||
					/* Checksum */
 | 
			
		||||
 | 
			
		||||
	strlcpy (stemp, sentence, sizeof(stemp));
 | 
			
		||||
 | 
			
		||||
	if (remove_checksum (stemp, quiet) < 0) {
 | 
			
		||||
	  return (DWFIX_ERROR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	next = stemp;
 | 
			
		||||
	ptype = strsep(&next, ",");
 | 
			
		||||
	ptime = strsep(&next, ",");
 | 
			
		||||
	pstatus = strsep(&next, ",");	
 | 
			
		||||
	plat = strsep(&next, ",");
 | 
			
		||||
	pns = strsep(&next, ",");
 | 
			
		||||
	plon = strsep(&next, ",");
 | 
			
		||||
	pew = strsep(&next, ",");
 | 
			
		||||
	pknots = strsep(&next, ",");
 | 
			
		||||
	pcourse = strsep(&next, ",");
 | 
			
		||||
	pdate = strsep(&next, ",");	
 | 
			
		||||
 | 
			
		||||
	/* Suppress the 'set but not used' warnings. */
 | 
			
		||||
	/* Alternatively, we might use __attribute__((unused)) */
 | 
			
		||||
 | 
			
		||||
	(void)(ptype);
 | 
			
		||||
	(void)(ptime);
 | 
			
		||||
	(void)(pdate);
 | 
			
		||||
 | 
			
		||||
	if (pstatus != NULL && strlen(pstatus) == 1) {
 | 
			
		||||
	  if (*pstatus != 'A') {
 | 
			
		||||
	    return (DWFIX_NO_FIX);		/* Not "Active." Don't parse. */
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  if ( ! quiet) {
 | 
			
		||||
	    text_color_set (DW_COLOR_ERROR);
 | 
			
		||||
            dw_printf("No status in GPRMC sentence.\n");
 | 
			
		||||
	  }
 | 
			
		||||
	  return (DWFIX_ERROR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (plat != NULL && strlen(plat) > 0 && pns != NULL && strlen(pns) > 0) {
 | 
			
		||||
	  *odlat = latitude_from_nmea(plat, pns);
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  if ( ! quiet) {
 | 
			
		||||
	    text_color_set (DW_COLOR_ERROR);
 | 
			
		||||
            dw_printf("Can't get latitude from GPRMC sentence.\n");
 | 
			
		||||
	  }
 | 
			
		||||
	  return (DWFIX_ERROR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (plon != NULL && strlen(plon) > 0 && pew != NULL && strlen(pew) > 0) {
 | 
			
		||||
	  *odlon = longitude_from_nmea(plon, pew);
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  if ( ! quiet) {
 | 
			
		||||
	    text_color_set (DW_COLOR_ERROR);
 | 
			
		||||
            dw_printf("Can't get longitude from GPRMC sentence.\n");
 | 
			
		||||
	  }
 | 
			
		||||
	  return (DWFIX_ERROR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (pknots != NULL && strlen(pknots) > 0) {
 | 
			
		||||
	  *oknots = atof(pknots);
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  if ( ! quiet) {
 | 
			
		||||
	    text_color_set (DW_COLOR_ERROR);
 | 
			
		||||
            dw_printf("Can't get speed from GPRMC sentence.\n");
 | 
			
		||||
	  }
 | 
			
		||||
	  return (DWFIX_ERROR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (pcourse != NULL) {
 | 
			
		||||
	  if (strlen(pcourse) > 0) {
 | 
			
		||||
	    *ocourse = atof(pcourse);
 | 
			
		||||
	  }
 | 
			
		||||
	  else {
 | 
			
		||||
	    /* When stationary, this field might be empty. */
 | 
			
		||||
	    *ocourse = G_UNKNOWN;
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  if ( ! quiet) {
 | 
			
		||||
 | 
			
		||||
	    text_color_set (DW_COLOR_ERROR);
 | 
			
		||||
            dw_printf("Can't get course from GPRMC sentence.\n");
 | 
			
		||||
	  }
 | 
			
		||||
	  return (DWFIX_ERROR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//text_color_set (DW_COLOR_INFO);
 | 
			
		||||
        //dw_printf("%.6f %.6f %.1f %.0f\n", *odlat, *odlon, *oknots, *ocourse);
 | 
			
		||||
 | 
			
		||||
	return (DWFIX_2D);
 | 
			
		||||
 | 
			
		||||
} /* end dwgpsnmea_gprmc */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:        dwgpsnmea_gpgga
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:    	Parse $GPGGA sentence and extract interesing parts.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	sentence	NMEA sentence.
 | 
			
		||||
 *
 | 
			
		||||
 *		quiet		suppress printing of error messages.
 | 
			
		||||
 *
 | 
			
		||||
 * Outputs:	odlat		latitude
 | 
			
		||||
 *		odlon		longitude
 | 
			
		||||
 *		oalt		altitude in meters
 | 
			
		||||
 *		onsat		number of satellites.
 | 
			
		||||
 *		
 | 
			
		||||
 *					Left undefined if not valid.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns:	DWFIX_ERROR	Parse error.
 | 
			
		||||
 *		DWFIX_NO_FIX	GPS is there but Position unknown.  Could be temporary.
 | 
			
		||||
 *		DWFIX_2D	Valid position.   We don't know if it is really 2D or 3D.
 | 
			
		||||
 *				Take more cautious value so we don't try using altitude.
 | 
			
		||||
 *
 | 
			
		||||
 * Examples:	$GPGGA,001429.00,,,,,0,00,99.99,,,,,,*68
 | 
			
		||||
 *		$GPGGA,212407.000,4237.1505,N,07120.8602,W,0,00,,,M,,M,,*58
 | 
			
		||||
 *		$GPGGA,000409.392,,,,,0,00,,,M,0.0,M,,0000*53
 | 
			
		||||
 *		$GPGGA,003518.710,4237.1250,N,07120.8327,W,1,03,5.9,33.5,M,-33.5,M,,0000*5B
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// TODO: in progress...
 | 
			
		||||
 | 
			
		||||
dwfix_t dwgpsnmea_gpgga (char *sentence, int quiet, double *odlat, double *odlon, float *oalt, int *onsat)
 | 
			
		||||
{
 | 
			
		||||
	char stemp[NMEA_MAX_LEN];	/* Make copy because parsing is destructive. */
 | 
			
		||||
 | 
			
		||||
	char *next;
 | 
			
		||||
 | 
			
		||||
	char *ptype;			/* Should be $GPGGA */
 | 
			
		||||
	char *ptime;			/* Time, hhmmss[.sss] */
 | 
			
		||||
	char *plat;			/* Latitude */
 | 
			
		||||
	char *pns;			/* North/South */
 | 
			
		||||
	char *plon;			/* Longitude */
 | 
			
		||||
	char *pew;			/* East/West */
 | 
			
		||||
	char *pfix;			/* 0=invalid, 1=GPS fix, 2=DGPS fix */
 | 
			
		||||
	char *pnum_sat;			/* Number of satellites */
 | 
			
		||||
	char *phdop;			/* Horiz. Dilution fo Precision */
 | 
			
		||||
	char *paltitude;		/* Altitude, above mean sea level */
 | 
			
		||||
	char *palt_u;			/* Units for Altitude, typically M for meters. */
 | 
			
		||||
	char *pheight;			/* Height above ellipsoid */
 | 
			
		||||
	char *pheight_u;		/* Units for height, typically M for meters. */
 | 
			
		||||
	char *psince;			/* Time since last DGPS update. */
 | 
			
		||||
	char *pdsta;			/* DGPS reference station id. */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	strlcpy (stemp, sentence, sizeof(stemp));
 | 
			
		||||
 | 
			
		||||
	if (remove_checksum (stemp, quiet) < 0) {
 | 
			
		||||
	  return (DWFIX_ERROR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	next = stemp;
 | 
			
		||||
	ptype = strsep(&next, ",");
 | 
			
		||||
	ptime = strsep(&next, ",");
 | 
			
		||||
	plat = strsep(&next, ",");
 | 
			
		||||
	pns = strsep(&next, ",");
 | 
			
		||||
	plon = strsep(&next, ",");
 | 
			
		||||
	pew = strsep(&next, ",");
 | 
			
		||||
	pfix = strsep(&next, ",");	
 | 
			
		||||
	pnum_sat = strsep(&next, ",");
 | 
			
		||||
	phdop = strsep(&next, ",");
 | 
			
		||||
	paltitude = strsep(&next, ",");
 | 
			
		||||
	palt_u = strsep(&next, ",");
 | 
			
		||||
	pheight = strsep(&next, ",");
 | 
			
		||||
	pheight_u = strsep(&next, ",");
 | 
			
		||||
	psince = strsep(&next, ",");
 | 
			
		||||
	pdsta = strsep(&next, ",");
 | 
			
		||||
 | 
			
		||||
	/* Suppress the 'set but not used' warnings. */
 | 
			
		||||
	/* Alternatively, we might use __attribute__((unused)) */
 | 
			
		||||
 | 
			
		||||
	(void)(ptype);
 | 
			
		||||
	(void)(ptime);
 | 
			
		||||
	(void)(pnum_sat);
 | 
			
		||||
	(void)(phdop);
 | 
			
		||||
	(void)(palt_u);
 | 
			
		||||
	(void)(pheight);
 | 
			
		||||
	(void)(pheight_u);
 | 
			
		||||
	(void)(psince);
 | 
			
		||||
	(void)(pdsta);
 | 
			
		||||
 | 
			
		||||
	if (pfix != NULL && strlen(pfix) == 1) {
 | 
			
		||||
	  if (*pfix == '0') {
 | 
			
		||||
	    return (DWFIX_NO_FIX);		/* No Fix. Don't parse the rest. */
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  if ( ! quiet) {
 | 
			
		||||
	    text_color_set (DW_COLOR_ERROR);
 | 
			
		||||
            dw_printf("No fix in GPGGA sentence.\n");
 | 
			
		||||
	  }
 | 
			
		||||
	  return (DWFIX_ERROR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (plat != NULL && strlen(plat) > 0 && pns != NULL && strlen(pns) > 0) {
 | 
			
		||||
	  *odlat = latitude_from_nmea(plat, pns);
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  if ( ! quiet) {
 | 
			
		||||
	    text_color_set (DW_COLOR_ERROR);
 | 
			
		||||
            dw_printf("Can't get latitude from GPGGA sentence.\n");
 | 
			
		||||
	  }
 | 
			
		||||
	  return (DWFIX_ERROR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (plon != NULL && strlen(plon) > 0 && pew != NULL && strlen(pew) > 0) {
 | 
			
		||||
	  *odlon = longitude_from_nmea(plon, pew);
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  if ( ! quiet) {
 | 
			
		||||
	    text_color_set (DW_COLOR_ERROR);
 | 
			
		||||
            dw_printf("Can't get longitude from GPGGA sentence.\n");
 | 
			
		||||
	  }
 | 
			
		||||
	  return (DWFIX_ERROR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO: num sat...
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * We can distinguish between 2D & 3D fix by presence 
 | 
			
		||||
 * of altitude or an empty field.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
	if (paltitude != NULL) {
 | 
			
		||||
 | 
			
		||||
	  if (strlen(paltitude) > 0) {
 | 
			
		||||
	    *oalt = atof(paltitude);
 | 
			
		||||
	    return (DWFIX_3D);
 | 
			
		||||
	  }
 | 
			
		||||
	  else {
 | 
			
		||||
	    return (DWFIX_2D);
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  if ( ! quiet) {
 | 
			
		||||
	    text_color_set (DW_COLOR_ERROR);
 | 
			
		||||
            dw_printf("Can't get altitude from GPGGA sentence.\n");
 | 
			
		||||
	  }
 | 
			
		||||
	  return (DWFIX_ERROR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
} /* end dwgpsnmea_gpgga */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:        dwgpsnmea_term
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:    	Shut down GPS interface before exiting from application.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	none.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns:	none.
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void dwgpsnmea_term (void) {
 | 
			
		||||
 | 
			
		||||
	// Should probably kill reader thread before closing device to avoid
 | 
			
		||||
	// message about read error.
 | 
			
		||||
 | 
			
		||||
	// serial_port_close (s_gpsnmea_port_fd); 
 | 
			
		||||
 | 
			
		||||
} /* end dwgps_term */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:        main
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:    	Simple unit test for other functions in this file.
 | 
			
		||||
 *
 | 
			
		||||
 * Description: Compile with -DGPSTEST option.
 | 
			
		||||
 *
 | 
			
		||||
 *		Windows:
 | 
			
		||||
 *			gcc  -DGPSTEST -Iregex dwgpsnmea.c dwgps.c textcolor.o serial_port.o latlong.o misc.a
 | 
			
		||||
 *			a.exe
 | 
			
		||||
 *
 | 
			
		||||
 *		Linux:
 | 
			
		||||
 *			 gcc -DGPSTEST dwgpsnmea.c dwgps.c textcolor.o serial_port.o latlong.o misc.a -lm -lpthread
 | 
			
		||||
 *			./a.out
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
#if GPSTEST
 | 
			
		||||
 | 
			
		||||
int main (int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	struct misc_config_s config;
 | 
			
		||||
	dwgps_info_t info;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	memset (&config, 0, sizeof(config));
 | 
			
		||||
	strlcpy (config.gpsnmea_port, "COM22", sizeof(config.gpsnmea_port));
 | 
			
		||||
 | 
			
		||||
	dwgps_init (&config, 3);
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
	  dwfix_t fix;
 | 
			
		||||
 | 
			
		||||
	  fix = dwgps_read (&info)
;
 | 
			
		||||
	  text_color_set (DW_COLOR_INFO);
 | 
			
		||||
	  switch (fix) {
 | 
			
		||||
	    case DWFIX_2D:
 | 
			
		||||
	    case DWFIX_3D:
 | 
			
		||||
	      dw_printf ("%.6f  %.6f", info.dlat, info.dlon);
 | 
			
		||||
	      dw_printf ("  %.1f knots  %.0f degrees", info.speed_knots, info.track);
 | 
			
		||||
	      if (fix==3) dw_printf ("  altitude = %.1f meters", info.altitude);
 | 
			
		||||
	      dw_printf ("\n");
 | 
			
		||||
	      break;
 | 
			
		||||
	    case DWFIX_NOT_SEEN:
 | 
			
		||||
	    case DWFIX_NO_FIX:
 | 
			
		||||
	      dw_printf ("Location currently not available.\n");
 | 
			
		||||
	      break;
 | 
			
		||||
	    case DWFIX_NOT_INIT:
 | 
			
		||||
	      dw_printf ("GPS Init failed.\n");
 | 
			
		||||
	      exit (1);
 | 
			
		||||
	    case DWFIX_ERROR:
 | 
			
		||||
	    default:
 | 
			
		||||
	      dw_printf ("ERROR getting GPS information.\n");
 | 
			
		||||
	      break;
 | 
			
		||||
	  }
 | 
			
		||||
	  SLEEP_SEC (3);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
} /* end main */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* end dwgpsnmea.c */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										149
									
								
								encode_aprs.c
								
								
								
								
							
							
						
						
									
										149
									
								
								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, "!D8yKC<Hn[&  !") != 0) dw_printf ("ERROR!  line %d\n", __LINE__);
 | 
			
		||||
	if (strcmp(result, "!D8yKC<Hn[&  !") != 0) { dw_printf ("ERROR!  line %d\n", __LINE__); errors++; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* with PHG. In this case it is converted to precomputed radio range.  TODO: check on this.  Is 27.4 correct? */
 | 
			
		||||
 | 
			
		||||
	encode_position (1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&', 
 | 
			
		||||
		50, 100, 6, "N", 0, 0, 0, 0, 0, NULL, result, sizeof(result));
 | 
			
		||||
	encode_position (0, 1, 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, "!D8yKC<Hn[&{CG") != 0) dw_printf ("ERROR!  line %d\n", __LINE__);
 | 
			
		||||
	if (strcmp(result, "!D8yKC<Hn[&{CG") != 0) { dw_printf ("ERROR!  line %d\n", __LINE__); errors++; }
 | 
			
		||||
 | 
			
		||||
/* with course/speed, freq, and comment!  TODO:  check on this 55 knots should be 63 MPH.  we get 62. */
 | 
			
		||||
/* with course/speed, freq, and comment!  Roundoff. 55 knots should be 63 MPH.  we get 62. */
 | 
			
		||||
 | 
			
		||||
	encode_position (1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&', 
 | 
			
		||||
	encode_position (0, 1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
 | 
			
		||||
		0, 0, 0, NULL, 180, 55, 146.955, 74.4, -0.6, "River flooding", result, sizeof(result));
 | 
			
		||||
	dw_printf ("%s\n", result);
 | 
			
		||||
	if (strcmp(result, "!D8yKC<Hn[&  !146.955MHz T074 -060 River flooding") != 0) dw_printf ("ERROR!  line %d\n", __LINE__);
 | 
			
		||||
 | 
			
		||||
//$ echo 'A>B:!D8yKC<Hn[&NUG146.955MHz T074 -060 River flooding' | decode_aprs
 | 
			
		||||
 | 
			
		||||
//A>B:!D8yKC<Hn[&NUG146.955MHz T074 -060 River flooding
 | 
			
		||||
//Position, I=Igte IGate R=RX T=1hopTX 2=2hopTX w/overlay D
 | 
			
		||||
//N 42 34.6100, W 071 26.4700, 62 MPH, course 180, 146.955 MHz, -600k, PL 74.4
 | 
			
		||||
// River flooding
 | 
			
		||||
	if (strcmp(result, "!D8yKC<Hn[&NUG146.955MHz T074 -060 River flooding") != 0) { dw_printf ("ERROR!  line %d\n", __LINE__); errors++; }
 | 
			
		||||
 | 
			
		||||
// TODO:  test alt; cs+alt
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*********** Object. ***********/
 | 
			
		||||
 | 
			
		||||
	encode_object ("WB1GOF-C", 0, 0, 42+34.61/60, -(71+26.47/60), 'D', '&', 
 | 
			
		||||
		0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result, sizeof(result));
 | 
			
		||||
	encode_object ("WB1GOF-C", 0, 0, 42+34.61/60, -(71+26.47/60), 'D', '&',
 | 
			
		||||
		0, 0, 0, NULL, G_UNKNOWN, 0, 0, 0, 0, NULL, result, sizeof(result));
 | 
			
		||||
	dw_printf ("%s\n", result);
 | 
			
		||||
	if (strcmp(result, ";WB1GOF-C *111111z4234.61ND07126.47W&") != 0) dw_printf ("ERROR!  line %d\n", __LINE__);
 | 
			
		||||
	if (strcmp(result, ";WB1GOF-C *111111z4234.61ND07126.47W&") != 0) { dw_printf ("ERROR!  line %d\n", __LINE__); errors++; }
 | 
			
		||||
 | 
			
		||||
// TODO: need more tests.
 | 
			
		||||
 | 
			
		||||
	return(0);
 | 
			
		||||
	if (errors > 0) {
 | 
			
		||||
	  text_color_set (DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("Encode APRS test FAILED with %d errors.\n", errors);
 | 
			
		||||
	  exit (EXIT_FAILURE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	text_color_set (DW_COLOR_REC);
 | 
			
		||||
	dw_printf ("Encode APRS test PASSED with no errors.\n");
 | 
			
		||||
	exit (EXIT_SUCCESS);
 | 
			
		||||
 | 
			
		||||
}  /* end main */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -180,7 +180,28 @@ struct demodulator_state_s
 | 
			
		|||
 * Each slicer has its own PLL and HDLC decoder.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#if 1
 | 
			
		||||
/*
 | 
			
		||||
 * Version 1.3: Clean up subchan vs. slicer.
 | 
			
		||||
 *
 | 
			
		||||
 * Originally some number of CHANNELS (originally 2, later 6)
 | 
			
		||||
 * which can have multiple parallel demodulators called SUB-CHANNELS.
 | 
			
		||||
 * This was originally for staggered frequencies for HF SSB.
 | 
			
		||||
 * It can also be used for multiple demodulators with the same
 | 
			
		||||
 * frequency but other differing parameters.
 | 
			
		||||
 * Each subchannel has its own demodulator and HDLC decoder.
 | 
			
		||||
 *
 | 
			
		||||
 * In version 1.2 we added multiple SLICERS.
 | 
			
		||||
 * The data structure, here, has multiple slicers per
 | 
			
		||||
 * demodulator (subchannel).  Due to fuzzy thinking or
 | 
			
		||||
 * expediency, the multiple slicers got mapped into subchannels.
 | 
			
		||||
 * This means we can't use both multiple decoders and
 | 
			
		||||
 * multiple slicers at the same time.
 | 
			
		||||
 *
 | 
			
		||||
 * Clean this up in 1.3 and keep the concepts separate.
 | 
			
		||||
 * This means adding a third variable many places
 | 
			
		||||
 * we are passing around the origin.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
	struct {
 | 
			
		||||
 | 
			
		||||
		signed int data_clock_pll;		// PLL for data clock recovery.
 | 
			
		||||
| 
						 | 
				
			
			@ -197,25 +218,13 @@ struct demodulator_state_s
 | 
			
		|||
 | 
			
		||||
		int lfsr;				// Descrambler shift register.
 | 
			
		||||
 | 
			
		||||
	} slicer [MAX_SUBCHANS];
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
	signed int data_clock_pll;		// PLL for data clock recovery.
 | 
			
		||||
						// It is incremented by pll_step_per_sample
 | 
			
		||||
						// for each audio sample.
 | 
			
		||||
 | 
			
		||||
	signed int prev_d_c_pll;		// Previous value of above, before
 | 
			
		||||
						// incrementing, to detect overflows.
 | 
			
		||||
 | 
			
		||||
	int prev_demod_data;			// Previous data bit detected.
 | 
			
		||||
						// Used to look for transitions.
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	} slicer [MAX_SLICERS];				// Actual number in use is num_slicers.
 | 
			
		||||
							// Should be in range 1 .. MAX_SLICERS,
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Special for Rino decoder only.
 | 
			
		||||
 * One for each possible signal polarity.
 | 
			
		||||
 * The project showed promise but fell by the wayside.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										124
									
								
								hdlc_rec.c
								
								
								
								
							
							
						
						
									
										124
									
								
								hdlc_rec.c
								
								
								
								
							| 
						 | 
				
			
			@ -29,6 +29,7 @@
 | 
			
		|||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "direwolf.h"
 | 
			
		||||
#include "demod.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -105,13 +106,11 @@ struct hdlc_state_s {
 | 
			
		|||
					
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct hdlc_state_s hdlc_state[MAX_CHANS][MAX_SUBCHANS];
 | 
			
		||||
static struct hdlc_state_s hdlc_state[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS];
 | 
			
		||||
 | 
			
		||||
static int num_subchan[MAX_CHANS];		//TODO1.2 use ptr rather than copy.
 | 
			
		||||
 | 
			
		||||
static int composite_dcd[MAX_CHANS];
 | 
			
		||||
 | 
			
		||||
static int composite_dcd[MAX_CHANS][MAX_SUBCHANS+1];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/***********************************************************************************
 | 
			
		||||
| 
						 | 
				
			
			@ -128,7 +127,7 @@ static int was_init = 0;
 | 
			
		|||
 | 
			
		||||
void hdlc_rec_init (struct audio_s *pa)
 | 
			
		||||
{
 | 
			
		||||
	int j, k;
 | 
			
		||||
	int ch, sub, slice;
 | 
			
		||||
	struct hdlc_state_s *H;
 | 
			
		||||
 | 
			
		||||
	//text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
| 
						 | 
				
			
			@ -136,34 +135,33 @@ void hdlc_rec_init (struct audio_s *pa)
 | 
			
		|||
 | 
			
		||||
	assert (pa != NULL);
 | 
			
		||||
	
 | 
			
		||||
	for (j=0; j<MAX_CHANS; j++)
 | 
			
		||||
	memset (composite_dcd, 0, sizeof(composite_dcd));
 | 
			
		||||
 | 
			
		||||
	for (ch = 0; ch < MAX_CHANS; ch++)
 | 
			
		||||
	{
 | 
			
		||||
	  composite_dcd[j] = 0;
 | 
			
		||||
 | 
			
		||||
	  if (pa->achan[j].valid) {
 | 
			
		||||
	  if (pa->achan[ch].valid) {
 | 
			
		||||
 | 
			
		||||
	    num_subchan[j] = pa->achan[j].num_subchan;
 | 
			
		||||
	    num_subchan[ch] = pa->achan[ch].num_subchan;
 | 
			
		||||
 | 
			
		||||
	    assert (num_subchan[j] >= 1 && num_subchan[j] <= MAX_SUBCHANS);
 | 
			
		||||
	    assert (num_subchan[ch] >= 1 && num_subchan[ch] <= MAX_SUBCHANS);
 | 
			
		||||
 | 
			
		||||
	    for (k=0; k<MAX_SUBCHANS; k++) 
 | 
			
		||||
	    for (sub = 0; sub < num_subchan[ch]; sub++)
 | 
			
		||||
	    {
 | 
			
		||||
	      H = &hdlc_state[j][k];
 | 
			
		||||
	      for (slice = 0; slice < MAX_SLICERS; slice++) {
 | 
			
		||||
 | 
			
		||||
	      H->prev_raw = 0;
 | 
			
		||||
	      H->lfsr = 0;
 | 
			
		||||
	      H->prev_descram = 0;
 | 
			
		||||
	      H->pat_det = 0;
 | 
			
		||||
	      H->flag4_det = 0;
 | 
			
		||||
	      H->olen = -1;
 | 
			
		||||
	      H->frame_len = 0;
 | 
			
		||||
	      H->data_detect = 0;
 | 
			
		||||
		// TODO: wasteful if not needed.
 | 
			
		||||
	      H->rrbb = rrbb_new(j, k, pa->achan[j].modem_type == MODEM_SCRAMBLE, H->lfsr, H->prev_descram);
 | 
			
		||||
	        H = &hdlc_state[ch][sub][slice];
 | 
			
		||||
 | 
			
		||||
	        H->olen = -1;
 | 
			
		||||
 | 
			
		||||
		// TODO: FIX13 wasteful if not needed.
 | 
			
		||||
		// Should loop on number of slicers, not max.
 | 
			
		||||
 | 
			
		||||
	        H->rrbb = rrbb_new(ch, sub, slice, pa->achan[ch].modem_type == MODEM_SCRAMBLE, H->lfsr, H->prev_descram);
 | 
			
		||||
	      }
 | 
			
		||||
	    }
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hdlc_rec2_init (pa);
 | 
			
		||||
	was_init = 1;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -178,14 +176,16 @@ void hdlc_rec_init (struct audio_s *pa)
 | 
			
		|||
 *
 | 
			
		||||
 * Inputs:	chan	- Channel number.  
 | 
			
		||||
 *
 | 
			
		||||
 *		subchan	- This allows multiple decoders per channel.
 | 
			
		||||
 *		subchan	- This allows multiple demodulators per channel.
 | 
			
		||||
 *
 | 
			
		||||
 *		slice	- Allows multiple slicers per demodulator (subchannel).
 | 
			
		||||
 *
 | 
			
		||||
 *		raw 	- One bit from the demodulator.
 | 
			
		||||
 *			  should be 0 or 1.
 | 
			
		||||
 *	
 | 
			
		||||
 *		is_scrambled - Is the data scrambled?
 | 
			
		||||
 *
 | 
			
		||||
 *		descram_state - Current descrambler state.
 | 
			
		||||
 *		descram_state - Current descrambler state.  (not used - remove)
 | 
			
		||||
 *					
 | 
			
		||||
 *
 | 
			
		||||
 * Description:	This is called once for each received bit.
 | 
			
		||||
| 
						 | 
				
			
			@ -196,9 +196,7 @@ void hdlc_rec_init (struct audio_s *pa)
 | 
			
		|||
 | 
			
		||||
// TODO: int not_used_remove
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_used_remove)
 | 
			
		||||
 | 
			
		||||
void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled, int not_used_remove)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	int dbit;			/* Data bit after undoing NRZI. */
 | 
			
		||||
| 
						 | 
				
			
			@ -210,10 +208,12 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
 | 
			
		|||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
			
		||||
	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
			
		||||
 | 
			
		||||
	assert (slice >= 0 && slice < MAX_SLICERS);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Different state information for each channel.
 | 
			
		||||
 * Different state information for each channel / subchannel / slice.
 | 
			
		||||
 */
 | 
			
		||||
	H = &hdlc_state[chan][subchan];
 | 
			
		||||
	H = &hdlc_state[chan][subchan][slice];
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Using NRZI encoding,
 | 
			
		||||
| 
						 | 
				
			
			@ -284,7 +284,7 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
 | 
			
		|||
 | 
			
		||||
	  if ( ! H->data_detect) {
 | 
			
		||||
	    H->data_detect = 1;
 | 
			
		||||
	    dcd_change (chan, subchan, 1);
 | 
			
		||||
	    dcd_change (chan, subchan, slice, 1);
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
	//else if (H->flag4_det == 0x7e000000) {	
 | 
			
		||||
| 
						 | 
				
			
			@ -293,7 +293,7 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
 | 
			
		|||
	  
 | 
			
		||||
	  if ( ! H->data_detect) {
 | 
			
		||||
	    H->data_detect = 1;
 | 
			
		||||
	    dcd_change (chan, subchan, 1);
 | 
			
		||||
	    dcd_change (chan, subchan, slice, 1);
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -308,7 +308,7 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
 | 
			
		|||
	  
 | 
			
		||||
	  if ( H->data_detect ) {
 | 
			
		||||
	    H->data_detect = 0;
 | 
			
		||||
	    dcd_change (chan, subchan, 0);
 | 
			
		||||
	    dcd_change (chan, subchan, slice, 0);
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -372,7 +372,7 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
 | 
			
		|||
	    if (actual_fcs == expected_fcs) {
 | 
			
		||||
	      alevel_t alevel = demod_get_audio_level (chan, subchan);
 | 
			
		||||
 | 
			
		||||
	      multi_modem_process_rec_frame (chan, subchan, H->frame_buf, H->frame_len - 2, alevel, RETRY_NONE);   /* len-2 to remove FCS. */
 | 
			
		||||
	      multi_modem_process_rec_frame (chan, subchan, slice, H->frame_buf, H->frame_len - 2, alevel, RETRY_NONE);   /* len-2 to remove FCS. */
 | 
			
		||||
	    }
 | 
			
		||||
	    else {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -401,7 +401,8 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
 | 
			
		|||
	    rrbb_set_audio_level (H->rrbb, alevel);
 | 
			
		||||
	    hdlc_rec2_block (H->rrbb);
 | 
			
		||||
	    	/* Now owned by someone else who will free it. */
 | 
			
		||||
	    H->rrbb = rrbb_new (chan, subchan, is_scrambled, H->lfsr, H->prev_descram); /* Allocate a new one. */
 | 
			
		||||
 | 
			
		||||
	    H->rrbb = rrbb_new (chan, subchan, slice, is_scrambled, H->lfsr, H->prev_descram); /* Allocate a new one. */
 | 
			
		||||
	  }
 | 
			
		||||
	  else {
 | 
			
		||||
	    rrbb_clear (H->rrbb, is_scrambled, H->lfsr, H->prev_descram); 
 | 
			
		||||
| 
						 | 
				
			
			@ -514,86 +515,81 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
 | 
			
		|||
 *
 | 
			
		||||
 * Inputs:	chan
 | 
			
		||||
 *		subchan
 | 
			
		||||
 *		slice
 | 
			
		||||
 *
 | 
			
		||||
 * Returns:	True if we are currently gathering bits.
 | 
			
		||||
 *		In this case we want the PLL to have more inertia.
 | 
			
		||||
 *
 | 
			
		||||
 * Discussion:	Originally I used the data carrier detect.
 | 
			
		||||
 *		Later, it seemed like the we should be using "olen>=0" instead.
 | 
			
		||||
 *
 | 
			
		||||
 *		Seems to make no difference for Track 1 and the original
 | 
			
		||||
 *		way was a hair better for Track 2.
 | 
			
		||||
 * Discussion:	This simply returns the data carrier detect state.
 | 
			
		||||
 *		A couple other variations were tried but turned out to
 | 
			
		||||
 *		be slightly worse.
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
int hdlc_rec_gathering (int chan, int subchan)
 | 
			
		||||
int hdlc_rec_gathering (int chan, int subchan, int slice)
 | 
			
		||||
{
 | 
			
		||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
			
		||||
	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
			
		||||
	assert (slice >= 0 && slice < MAX_SLICERS);
 | 
			
		||||
 | 
			
		||||
	// Counts from 	     Track 1 & Track 2
 | 
			
		||||
	// data_detect		992	988
 | 
			
		||||
	// olen>=0		992	985
 | 
			
		||||
	// OR-ed		992	985
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	return ( hdlc_state[chan][subchan].data_detect );
 | 
			
		||||
 | 
			
		||||
	//return ( hdlc_state[chan][subchan].olen >= 0);
 | 
			
		||||
 | 
			
		||||
	//return ( hdlc_state[chan][subchan].data_detect || hdlc_state[chan][subchan].olen >= 0 );
 | 
			
		||||
	return ( hdlc_state[chan][subchan][slice].data_detect );
 | 
			
		||||
 | 
			
		||||
} /* end hdlc_rec_gathering */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:        dcd_change
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:     Combine DCD states of all subchannels into an overall
 | 
			
		||||
 * Purpose:     Combine DCD states of all subchannels/ into an overall
 | 
			
		||||
 *		state for the channel.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	chan	
 | 
			
		||||
 *
 | 
			
		||||
 *		subchan		0 to MAX_SUBCHANS-1 for HDLC.
 | 
			
		||||
 *				MAX_SUBCHANS for DTMF decoder.
 | 
			
		||||
 *				SPECIAL CASE --> MAX_SUBCHANS for DTMF decoder.
 | 
			
		||||
 *
 | 
			
		||||
 *		slice		slicer number, 0 .. MAX_SLICERS - 1.
 | 
			
		||||
 *
 | 
			
		||||
 *		state		1 for active, 0 for not.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns:	None.  Use ??? to retrieve result.
 | 
			
		||||
 * Returns:	None.  Use hdlc_rec_data_detect_any to retrieve result.
 | 
			
		||||
 *
 | 
			
		||||
 * Description:	DCD for the channel is active if ANY of the subchannels
 | 
			
		||||
 *		is active.  Update the DCD indicator.
 | 
			
		||||
 * Description:	DCD for the channel is active if ANY of the subchannels/slices
 | 
			
		||||
 *		are active.  Update the DCD indicator.
 | 
			
		||||
 *
 | 
			
		||||
 * version 1.3:	Add DTMF detection into the final result.
 | 
			
		||||
 *		This is now called from dtmf.c too.
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void dcd_change (int chan, int subchan, int state)
 | 
			
		||||
void dcd_change (int chan, int subchan, int slice, int state)
 | 
			
		||||
{
 | 
			
		||||
	int old, new;
 | 
			
		||||
 | 
			
		||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
			
		||||
	assert (subchan >= 0 && subchan <= MAX_SUBCHANS);
 | 
			
		||||
	assert (slice >= 0 && slice < MAX_SLICERS);
 | 
			
		||||
	assert (state == 0 || state == 1);
 | 
			
		||||
 | 
			
		||||
#if DEBUG3
 | 
			
		||||
	text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	dw_printf ("DCD %d.%d = %d \n", chan, subchan, state);
 | 
			
		||||
	dw_printf ("DCD %d.%d.%d = %d \n", chan, subchan, slice, state);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	old = hdlc_rec_data_detect_any(chan);
 | 
			
		||||
 | 
			
		||||
	if (state) {
 | 
			
		||||
	  composite_dcd[chan] |= (1 << subchan);
 | 
			
		||||
	  composite_dcd[chan][subchan] |= (1 << slice);
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  composite_dcd[chan] &=  ~ (1 << subchan);
 | 
			
		||||
	  composite_dcd[chan][subchan] &=  ~ (1 << slice);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	new = hdlc_rec_data_detect_any(chan);
 | 
			
		||||
| 
						 | 
				
			
			@ -634,15 +630,17 @@ void dcd_change (int chan, int subchan, int state)
 | 
			
		|||
 | 
			
		||||
int hdlc_rec_data_detect_any (int chan)
 | 
			
		||||
{
 | 
			
		||||
	int subchan;
 | 
			
		||||
 | 
			
		||||
	int sc;
 | 
			
		||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
			
		||||
 | 
			
		||||
	return (composite_dcd[chan] != 0);
 | 
			
		||||
	for (sc = 0; sc < num_subchan[chan]; sc++) {
 | 
			
		||||
	  if (composite_dcd[chan][sc] != 0)
 | 
			
		||||
	    return (1);
 | 
			
		||||
	}
 | 
			
		||||
	return (0);
 | 
			
		||||
 | 
			
		||||
} /* end hdlc_rec_data_detect_any */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* end hdlc_rec.c */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,9 +5,7 @@
 | 
			
		|||
 | 
			
		||||
void hdlc_rec_init (struct audio_s *pa);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int descram_state);
 | 
			
		||||
 | 
			
		||||
void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled, int descram_state);
 | 
			
		||||
 | 
			
		||||
/* Provided elsewhere to process a complete frame. */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -18,11 +16,10 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int descram
 | 
			
		|||
/* Similar to, but not exactly the same as, data carrier detect. */
 | 
			
		||||
/* We use this to influence the PLL inertia. */
 | 
			
		||||
 | 
			
		||||
int hdlc_rec_gathering (int chan, int subchan);
 | 
			
		||||
 | 
			
		||||
int hdlc_rec_gathering (int chan, int subchan, int slice);
 | 
			
		||||
 | 
			
		||||
/* Transmit needs to know when someone else is transmitting. */
 | 
			
		||||
 | 
			
		||||
void dcd_change (int chan, int subchan, int state);
 | 
			
		||||
void dcd_change (int chan, int subchan, int slice, int state);
 | 
			
		||||
 | 
			
		||||
int hdlc_rec_data_detect_any (int chan);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										510
									
								
								hdlc_rec2.c
								
								
								
								
							
							
						
						
									
										510
									
								
								hdlc_rec2.c
								
								
								
								
							| 
						 | 
				
			
			@ -76,6 +76,12 @@
 | 
			
		|||
 *		It was necessary to retain more initial state information after
 | 
			
		||||
 *		the start flag octet.
 | 
			
		||||
 *
 | 
			
		||||
 * Version 1.3: Took out all of the "insert" and "remove" cases because they
 | 
			
		||||
 *		offer no benenfit.
 | 
			
		||||
 *
 | 
			
		||||
 *		Took out the delayed processing and just do it realtime.
 | 
			
		||||
 *		Changed SWAP to INVERT because it is more descriptive.
 | 
			
		||||
 *
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -152,11 +158,11 @@ struct hdlc_state_s {
 | 
			
		|||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define MAX_RETRY_SWAP_BITS 24			/* Maximum number of contiguous bits to swap */
 | 
			
		||||
#define MAX_RETRY_REMOVE_SEPARATED_BITS 24	/* Maximum number of contiguous bits to remove */
 | 
			
		||||
 | 
			
		||||
static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, retry_conf_t retry_conf, int passall);
 | 
			
		||||
static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t alevel);
 | 
			
		||||
static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel, retry_conf_t retry_conf, int passall);
 | 
			
		||||
 | 
			
		||||
static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel);
 | 
			
		||||
 | 
			
		||||
static int sanity_check (unsigned char *buf, int blen, retry_t bits_flipped, enum sanity_e sanity_test);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -173,7 +179,7 @@ static int sanity_check (unsigned char *buf, int blen, retry_t bits_flipped, enu
 | 
			
		|||
 *					Level of effort to recover from 
 | 
			
		||||
 *					a bad FCS on the frame. 
 | 
			
		||||
 *					0 = no effort 
 | 
			
		||||
 *					1 = try fixing a single bit 
 | 
			
		||||
 *					1 = try inverting a single bit
 | 
			
		||||
 *					2... = more techniques... 
 | 
			
		||||
 *
 | 
			
		||||
 *	    			enum sanity_e sanity_test;
 | 
			
		||||
| 
						 | 
				
			
			@ -222,11 +228,11 @@ void hdlc_rec2_block (rrbb_t block)
 | 
			
		|||
{
 | 
			
		||||
	int chan = rrbb_get_chan(block);
 | 
			
		||||
	int subchan = rrbb_get_subchan(block);
 | 
			
		||||
	int slice = rrbb_get_slice(block);
 | 
			
		||||
	alevel_t alevel = rrbb_get_audio_level(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);
 | 
			
		||||
| 
						 | 
				
			
			@ -246,11 +252,8 @@ void hdlc_rec2_block (rrbb_t block)
 | 
			
		|||
	retry_cfg.retry = RETRY_NONE;
 | 
			
		||||
	retry_cfg.u_bits.contig.nr_bits = 0;
 | 
			
		||||
	retry_cfg.u_bits.contig.bit_idx = 0;
 | 
			
		||||
	/* Prepare the decoded bits in an array for faster processing 
 | 
			
		||||
	 *(at cost of memory but 1 or 2 kbytes is nothing compared to processing time ) */
 | 
			
		||||
	rrbb_compute_bits(block);
 | 
			
		||||
	ok = try_decode (block, chan, subchan, alevel, retry_cfg, passall & (fix_bits == RETRY_NONE));
 | 
			
		||||
 | 
			
		||||
	ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, passall & (fix_bits == RETRY_NONE));
 | 
			
		||||
	if (ok) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	  text_color_set(DW_COLOR_INFO);
 | 
			
		||||
| 
						 | 
				
			
			@ -262,29 +265,19 @@ void hdlc_rec2_block (rrbb_t block)
 | 
			
		|||
 | 
			
		||||
/*
 | 
			
		||||
 * Not successful with frame in orginal form.
 | 
			
		||||
 * Try the quick techniques with time proportional to the frame length.
 | 
			
		||||
 */	
 | 
			
		||||
	if (try_to_fix_quick_now (block, chan, subchan, alevel)) {
 | 
			
		||||
 * See if we can "fix" it.
 | 
			
		||||
 */
 | 
			
		||||
	if (try_to_fix_quick_now (block, chan, subchan, slice, alevel)) {
 | 
			
		||||
	  rrbb_delete (block);
 | 
			
		||||
	  return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Not successful with the quick fix up attempts.
 | 
			
		||||
 * Do we want to try the more aggressive techniques where processing
 | 
			
		||||
 * time is proportional to the square of length?
 | 
			
		||||
 * Rather than doing it now, we throw it in a queue for processing
 | 
			
		||||
 * by a different thread.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
	if (fix_bits >= RETRY_SWAP_TWO_SEP) {
 | 
			
		||||
	  rdq_append (block);
 | 
			
		||||
	}
 | 
			
		||||
	else if (passall) {
 | 
			
		||||
	if (passall) {
 | 
			
		||||
	  /* Exhausted all desired fix up attempts. */
 | 
			
		||||
	  /* Let thru even with bad CRC.  Of course, it still */
 | 
			
		||||
	  /* needs to be a minimum number of whole octets. */
 | 
			
		||||
	  ok = try_decode (block, chan, subchan, alevel, retry_cfg, 1);
 | 
			
		||||
	  ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 1);
 | 
			
		||||
	  rrbb_delete (block);
 | 
			
		||||
	}
 | 
			
		||||
	else {  
 | 
			
		||||
| 
						 | 
				
			
			@ -308,7 +301,7 @@ void hdlc_rec2_block (rrbb_t block)
 | 
			
		|||
 * Global In:	configuration fix_bits - Maximum level of fix up to attempt.
 | 
			
		||||
 *
 | 
			
		||||
 *				RETRY_NONE (0)	- Don't try any.
 | 
			
		||||
 *				RETRY_SWAP_SINGLE (1)  - Try inverting single bits.
 | 
			
		||||
 *				RETRY_INVERT_SINGLE (1)  - Try inverting single bits.
 | 
			
		||||
 *				etc.
 | 
			
		||||
 *
 | 
			
		||||
 *		configuration passall - Let it thru with bad CRC after exhausting
 | 
			
		||||
| 
						 | 
				
			
			@ -319,20 +312,24 @@ void hdlc_rec2_block (rrbb_t block)
 | 
			
		|||
 *				processing step.
 | 
			
		||||
 *		0 for failure.  Caller might continue with more aggressive attempts.
 | 
			
		||||
 *
 | 
			
		||||
 * Description:	Some of the attempted fix up techniques are quick.
 | 
			
		||||
 * Original:	Some of the attempted fix up techniques are quick.
 | 
			
		||||
 *		We will attempt them immediately after receiving the frame.
 | 
			
		||||
 *		Others, that take time order N**2, will be done in a later section.
 | 
			
		||||
 *
 | 
			
		||||
 * Version 1.2:	Now works properly for G3RUH type scrambling.
 | 
			
		||||
 *
 | 
			
		||||
 * Version 1.3: Removed the extra cases that didn't help.
 | 
			
		||||
 *		The separated bit case is now handled immediately instead of
 | 
			
		||||
 *		being thrown in a queue for later processing.
 | 
			
		||||
 *
 | 
			
		||||
 ***********************************************************************************/
 | 
			
		||||
 | 
			
		||||
static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t alevel)
 | 
			
		||||
static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel)
 | 
			
		||||
{
 | 
			
		||||
	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);
 | 
			
		||||
| 
						 | 
				
			
			@ -341,9 +338,9 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t a
 | 
			
		|||
	/* Will modify only contiguous bits*/
 | 
			
		||||
	retry_cfg.mode = RETRY_MODE_CONTIGUOUS; 
 | 
			
		||||
/* 
 | 
			
		||||
 * Try fixing one bit.   
 | 
			
		||||
 * Try inverting one bit.
 | 
			
		||||
 */
 | 
			
		||||
	if (fix_bits < RETRY_SWAP_SINGLE) {
 | 
			
		||||
	if (fix_bits < RETRY_INVERT_SINGLE) {
 | 
			
		||||
 | 
			
		||||
	  /* Stop before single bit fix up. */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -351,13 +348,13 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t a
 | 
			
		|||
	}
 | 
			
		||||
	/* Try to swap one bit */
 | 
			
		||||
	retry_cfg.type = RETRY_TYPE_SWAP;
 | 
			
		||||
	retry_cfg.retry = RETRY_SWAP_SINGLE;
 | 
			
		||||
	retry_cfg.retry = RETRY_INVERT_SINGLE;
 | 
			
		||||
	retry_cfg.u_bits.contig.nr_bits = 1;
 | 
			
		||||
 | 
			
		||||
	for (i=0; i<len; i++) {
 | 
			
		||||
	  /* Set the index of the bit to swap */
 | 
			
		||||
	  retry_cfg.u_bits.contig.bit_idx = i;
 | 
			
		||||
	  ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
 | 
			
		||||
	  ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 0);
 | 
			
		||||
	  if (ok) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
| 
						 | 
				
			
			@ -368,19 +365,19 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t a
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Try fixing two adjacent bits.  
 | 
			
		||||
 * Try inverting two adjacent bits.
 | 
			
		||||
 */
 | 
			
		||||
	if (fix_bits < RETRY_SWAP_DOUBLE) {
 | 
			
		||||
	if (fix_bits < RETRY_INVERT_DOUBLE) {
 | 
			
		||||
	  return 0;
 | 
			
		||||
	}
 | 
			
		||||
	/* Try to swap two contiguous bits */
 | 
			
		||||
	retry_cfg.retry = RETRY_SWAP_DOUBLE;
 | 
			
		||||
	retry_cfg.retry = RETRY_INVERT_DOUBLE;
 | 
			
		||||
	retry_cfg.u_bits.contig.nr_bits = 2;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	for (i=0; i<len-1; i++) {
 | 
			
		||||
	  retry_cfg.u_bits.contig.bit_idx = i;
 | 
			
		||||
	  ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
 | 
			
		||||
	  ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 0);
 | 
			
		||||
	  if (ok) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
| 
						 | 
				
			
			@ -391,18 +388,18 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t a
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Try fixing adjacent three bits.
 | 
			
		||||
 * Try inverting adjacent three bits.
 | 
			
		||||
 */
 | 
			
		||||
	if (fix_bits < RETRY_SWAP_TRIPLE) {
 | 
			
		||||
	if (fix_bits < RETRY_INVERT_TRIPLE) {
 | 
			
		||||
	  return 0;
 | 
			
		||||
	}
 | 
			
		||||
	/* Try to swap three contiguous bits */
 | 
			
		||||
	retry_cfg.retry = RETRY_SWAP_TRIPLE;
 | 
			
		||||
	retry_cfg.retry = RETRY_INVERT_TRIPLE;
 | 
			
		||||
	retry_cfg.u_bits.contig.nr_bits = 3;
 | 
			
		||||
 | 
			
		||||
	for (i=0; i<len-2; i++) {
 | 
			
		||||
	  retry_cfg.u_bits.contig.bit_idx = i;
 | 
			
		||||
	  ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
 | 
			
		||||
	  ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 0);
 | 
			
		||||
	  if (ok) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
| 
						 | 
				
			
			@ -412,204 +409,20 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t a
 | 
			
		|||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (fix_bits < RETRY_REMOVE_SINGLE) {
 | 
			
		||||
	  return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Try removing one bit.   
 | 
			
		||||
 */
 | 
			
		||||
		retry_cfg.type = RETRY_TYPE_REMOVE;
 | 
			
		||||
		retry_cfg.retry = RETRY_REMOVE_SINGLE;
 | 
			
		||||
		retry_cfg.u_bits.contig.nr_bits = 1;
 | 
			
		||||
 | 
			
		||||
		for (i=0; i<len; i++) {
 | 
			
		||||
		  retry_cfg.u_bits.contig.bit_idx = i;
 | 
			
		||||
		  ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
 | 
			
		||||
		  if (ok) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
		    dw_printf ("*** Success by removing SINGLE bit %d of %d ***\n", i, len);
 | 
			
		||||
#endif
 | 
			
		||||
		    return 1;
 | 
			
		||||
		  }
 | 
			
		||||
		}
 | 
			
		||||
	if (fix_bits < RETRY_REMOVE_DOUBLE) {
 | 
			
		||||
	  return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Try removing two contiguous bits.   
 | 
			
		||||
 */
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
		dw_printf ("*** Try removing DOUBLE bits *** for %d bits\n", len);
 | 
			
		||||
#endif
 | 
			
		||||
		retry_cfg.retry = RETRY_REMOVE_DOUBLE;
 | 
			
		||||
		retry_cfg.u_bits.contig.nr_bits = 2;
 | 
			
		||||
 | 
			
		||||
		for (i=0; i<len-1; i++) {
 | 
			
		||||
	  	  retry_cfg.u_bits.contig.bit_idx = i;
 | 
			
		||||
		  ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
 | 
			
		||||
		  if (ok) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
		    dw_printf ("*** Success by removing DOUBLE bits %d of %d ***\n", i, len);
 | 
			
		||||
#endif
 | 
			
		||||
		    return 1;
 | 
			
		||||
		  }
 | 
			
		||||
		}
 | 
			
		||||
	if (fix_bits < RETRY_REMOVE_TRIPLE) {
 | 
			
		||||
	  return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Try removing three contiguous bits.
 | 
			
		||||
 */
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
		dw_printf ("*** Try removing TRIPLE bits *** for %d bits\n", len);
 | 
			
		||||
#endif
 | 
			
		||||
		retry_cfg.retry = RETRY_REMOVE_TRIPLE;
 | 
			
		||||
		retry_cfg.u_bits.contig.nr_bits = 3;
 | 
			
		||||
 | 
			
		||||
		for (i=0; i<len-2; i++) {
 | 
			
		||||
	  	  retry_cfg.u_bits.contig.bit_idx = i;
 | 
			
		||||
		  ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
 | 
			
		||||
		  if (ok) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
		    dw_printf ("*** Success by removing TRIPLE bits %d of %d ***\n", i, len);
 | 
			
		||||
#endif
 | 
			
		||||
		    return 1;
 | 
			
		||||
		  }
 | 
			
		||||
		}
 | 
			
		||||
	if (fix_bits < RETRY_INSERT_SINGLE) {
 | 
			
		||||
	  return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Try inserting one bit (two values possibles for this inserted bit).   
 | 
			
		||||
 */
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
		dw_printf ("*** Try inserting SINGLE bit *** for %d bits\n", len);
 | 
			
		||||
#endif
 | 
			
		||||
		retry_cfg.type = RETRY_TYPE_INSERT;
 | 
			
		||||
		retry_cfg.retry = RETRY_INSERT_SINGLE;
 | 
			
		||||
		retry_cfg.u_bits.contig.nr_bits = 1;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		for (i=0; i<len; i++) {
 | 
			
		||||
	  	  retry_cfg.u_bits.contig.bit_idx = i;
 | 
			
		||||
		  retry_cfg.insert_value=0;
 | 
			
		||||
		  ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
 | 
			
		||||
		  if (!ok) {
 | 
			
		||||
		    retry_cfg.insert_value=1;
 | 
			
		||||
		    ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
 | 
			
		||||
		  }
 | 
			
		||||
		  if (ok) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
		    dw_printf ("*** Success by inserting SINGLE bit %d of %d ***\n", i, len);
 | 
			
		||||
#endif
 | 
			
		||||
		    return 1;
 | 
			
		||||
		  }
 | 
			
		||||
		}
 | 
			
		||||
	if (fix_bits < RETRY_INSERT_DOUBLE) {
 | 
			
		||||
	  return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Try inserting two contiguous bits (4 possible values for two bits).   
 | 
			
		||||
 */
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
		dw_printf ("*** Try inserting DOUBLE bits *** for %d bits\n", len);
 | 
			
		||||
#endif
 | 
			
		||||
		retry_cfg.retry = RETRY_INSERT_DOUBLE;
 | 
			
		||||
		retry_cfg.u_bits.contig.nr_bits = 2;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		for (i=0; i<len-1; i++) {
 | 
			
		||||
	  	  retry_cfg.u_bits.contig.bit_idx = i;
 | 
			
		||||
		  for (j=0;j<4;j++) {
 | 
			
		||||
		    retry_cfg.insert_value=j;
 | 
			
		||||
		    ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
 | 
			
		||||
 | 
			
		||||
		    if (ok) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		      text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
		      dw_printf ("*** Success by inserting DOUBLE bits %d of %d ***\n", i, len);
 | 
			
		||||
#endif
 | 
			
		||||
		      return 1;
 | 
			
		||||
		    }
 | 
			
		||||
		  }
 | 
			
		||||
		}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/***********************************************************************************
 | 
			
		||||
 *
 | 
			
		||||
 * Name:	hdlc_rec2_try_to_fix_later
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:	Attempt some more time-consuming techniques.
 | 
			
		||||
 *		Rather than trying these immediately, the information is
 | 
			
		||||
 *		put into a queue and processed by another thread.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	block	- Stream of bits that might be a frame.
 | 
			
		||||
 *		chan	- Radio channel from which it was received.
 | 
			
		||||
 *		subchan	- Which demodulator when more than one per channel.
 | 
			
		||||
 *		alevel	- Audio level for later reporting.
 | 
			
		||||
 *
 | 
			
		||||
 * Global In:	configuration fix_bits - Maximum level of fix up to attempt.
 | 
			
		||||
 *
 | 
			
		||||
 *				RETRY_NONE (0)	- Don't try any.
 | 
			
		||||
 *				RETRY_SWAP_SINGLE (1)  - Try inverting single bits.
 | 
			
		||||
 *				etc.
 | 
			
		||||
 *
 | 
			
		||||
 *		configuration passall - Let it thru with bad CRC after exhausting
 | 
			
		||||
 *				all fixup attempts.
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * Returns:	1 for success.  "try_decode" has passed the result along to the 
 | 
			
		||||
 *				processing step.
 | 
			
		||||
 *		0 for failure.  Caller might try again if "passall" option specified.
 | 
			
		||||
 *
 | 
			
		||||
 ***********************************************************************************/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, alevel_t alevel)
 | 
			
		||||
{
 | 
			
		||||
	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;
 | 
			
		||||
#if DEBUG_LATER
 | 
			
		||||
	double tstart, tend;
 | 
			
		||||
#endif
 | 
			
		||||
	retry_conf_t retry_cfg;
 | 
			
		||||
	len = rrbb_get_len(block);
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	if (fix_bits < RETRY_SWAP_TWO_SEP) {
 | 
			
		||||
	  goto failure;  
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	retry_cfg.mode = RETRY_MODE_SEPARATED; 
 | 
			
		||||
/*
 | 
			
		||||
 * Two  non-adjacent ("separated") single bits.
 | 
			
		||||
 * It chews up a lot of CPU time.  Test takes 4 times longer to run.
 | 
			
		||||
 * It chews up a lot of CPU time.  Usual test takes 4 times longer to run.
 | 
			
		||||
 *
 | 
			
		||||
 * Ran up to xx seconds (TODO check again with optimized code) seconds for 1040 bits before giving up .
 | 
			
		||||
 * Processing time is order N squared so time goes up rapidly with larger frames.
 | 
			
		||||
 */
 | 
			
		||||
	if (fix_bits < RETRY_INVERT_TWO_SEP) {
 | 
			
		||||
	  return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	retry_cfg.mode = RETRY_MODE_SEPARATED;
 | 
			
		||||
	retry_cfg.type = RETRY_TYPE_SWAP;
 | 
			
		||||
	retry_cfg.retry = RETRY_SWAP_TWO_SEP;
 | 
			
		||||
	retry_cfg.retry = RETRY_INVERT_TWO_SEP;
 | 
			
		||||
	retry_cfg.u_bits.sep.bit_idx_c = -1;
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_LATER
 | 
			
		||||
| 
						 | 
				
			
			@ -624,7 +437,7 @@ int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, alevel_t al
 | 
			
		|||
	  ok = 0;
 | 
			
		||||
	  for (j=i+2; j<len; j++) {
 | 
			
		||||
	    retry_cfg.u_bits.sep.bit_idx_b = j;
 | 
			
		||||
	    ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
 | 
			
		||||
	    ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 0);
 | 
			
		||||
	    if (ok) {
 | 
			
		||||
	      break;
 | 
			
		||||
	    }
 | 
			
		||||
| 
						 | 
				
			
			@ -638,128 +451,28 @@ int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, alevel_t al
 | 
			
		|||
	    return (1);
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// TODO:  Remove this.  but first figure out what to do in atest.c
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel)
 | 
			
		||||
{
 | 
			
		||||
	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;
 | 
			
		||||
#if DEBUG_LATER
 | 
			
		||||
	tend = dtime_now();
 | 
			
		||||
	text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	dw_printf ("*** No luck flipping TWO SEPARATED bits of %d *** %.3f sec.\n", len, tend-tstart);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (fix_bits < RETRY_SWAP_MANY) {
 | 
			
		||||
	  goto failure;
 | 
			
		||||
	}
 | 
			
		||||
	/* Try to swap many contiguous bits */
 | 
			
		||||
	retry_cfg.mode = RETRY_MODE_CONTIGUOUS; 
 | 
			
		||||
	retry_cfg.type = RETRY_TYPE_SWAP;
 | 
			
		||||
	retry_cfg.retry = RETRY_SWAP_MANY;
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_LATER
 | 
			
		||||
	tstart = dtime_now();
 | 
			
		||||
	dw_printf ("*** Try swapping many BITS %d bits\n", len);
 | 
			
		||||
	double tstart, tend;
 | 
			
		||||
#endif
 | 
			
		||||
	retry_conf_t retry_cfg;
 | 
			
		||||
	len = rrbb_get_len(block);
 | 
			
		||||
	for (i=0; i<len; i++) {
 | 
			
		||||
	  for (j=1; j<len-i && j < MAX_RETRY_SWAP_BITS;j++) {
 | 
			
		||||
	    retry_cfg.u_bits.contig.bit_idx = i;
 | 
			
		||||
	    retry_cfg.u_bits.contig.nr_bits = j;
 | 
			
		||||
//	    dw_printf ("*** Trying swapping %d bits starting at %d of %d ***\n", j,i, len);
 | 
			
		||||
	    ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
 | 
			
		||||
	    if (ok) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	      text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	      dw_printf ("*** Success by swapping %d bits starting at %d of %d ***\n", j,i, len);
 | 
			
		||||
#endif
 | 
			
		||||
	      return (1);
 | 
			
		||||
	    }
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
#if DEBUG_LATER
 | 
			
		||||
	tend = dtime_now();
 | 
			
		||||
	text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	dw_printf ("*** No luck swapping many bits for len %d  in %.3f sec.\n",len, tend-tstart);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (fix_bits < RETRY_REMOVE_MANY) {
 | 
			
		||||
	  goto failure;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/* Try to remove many contiguous bits */
 | 
			
		||||
	retry_cfg.type = RETRY_TYPE_REMOVE;
 | 
			
		||||
	retry_cfg.retry = RETRY_REMOVE_MANY;
 | 
			
		||||
#ifdef DEBUG_LATER
 | 
			
		||||
	tstart = dtime_now();
 | 
			
		||||
	dw_printf ("*** Trying removing many bits for len\n", len);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	len = rrbb_get_len(block);
 | 
			
		||||
	for (i=0; i<2; i++) {
 | 
			
		||||
	  for (j=1; j<len-i && j<len/2;j++) {
 | 
			
		||||
	    retry_cfg.u_bits.contig.bit_idx = i;
 | 
			
		||||
	    retry_cfg.u_bits.contig.nr_bits = j;
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
	    dw_printf ("*** Trying removing %d bits starting at %d of %d ***\n", j,i, len);
 | 
			
		||||
#endif
 | 
			
		||||
	    ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
 | 
			
		||||
	    if (ok) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	      text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	      dw_printf ("*** Success by removing %d bits starting at %d of %d ***\n", j,i, len);
 | 
			
		||||
#endif
 | 
			
		||||
	      return (1);
 | 
			
		||||
	    }
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
#if DEBUG_LATER
 | 
			
		||||
	tend = dtime_now();
 | 
			
		||||
	text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	dw_printf ("*** No luck removing many bits for len %d *** in %.3f sec.\n", len, tend-tstart);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (fix_bits < RETRY_REMOVE_TWO_SEP) {
 | 
			
		||||
	  goto failure;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Try to remove Two  non-adjacent ("separated") single bits.
 | 
			
		||||
 */
 | 
			
		||||
	retry_cfg.mode = RETRY_MODE_SEPARATED; 
 | 
			
		||||
	retry_cfg.type = RETRY_TYPE_REMOVE;
 | 
			
		||||
	retry_cfg.retry = RETRY_REMOVE_TWO_SEP;
 | 
			
		||||
	retry_cfg.u_bits.sep.bit_idx_c = -1;
 | 
			
		||||
 | 
			
		||||
#if DEBUG_LATER 
 | 
			
		||||
	tstart = dtime_now();
 | 
			
		||||
	dw_printf ("*** Try removing TWO SEPARATED BITS %d bits\n", len);
 | 
			
		||||
#endif
 | 
			
		||||
	len = rrbb_get_len(block);
 | 
			
		||||
	for (i=0; i<len-2; i++) {
 | 
			
		||||
	  retry_cfg.u_bits.sep.bit_idx_a = i;
 | 
			
		||||
	  int j;
 | 
			
		||||
	  ok = 0;
 | 
			
		||||
	  for (j=i+2; j<len && j - i < MAX_RETRY_REMOVE_SEPARATED_BITS; j++) {
 | 
			
		||||
	    retry_cfg.u_bits.sep.bit_idx_b = j;
 | 
			
		||||
	    ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
 | 
			
		||||
	    if (ok) {
 | 
			
		||||
	      break;
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	  }	  
 | 
			
		||||
	  if (ok) {
 | 
			
		||||
#if DEBUG_LATER
 | 
			
		||||
	    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	    dw_printf ("*** Success by removing TWO SEPARATED bits %d and %d of %d \n", i, j, len);
 | 
			
		||||
#endif
 | 
			
		||||
	    return (1);
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
#if DEBUG_LATER
 | 
			
		||||
	tend = dtime_now();
 | 
			
		||||
	text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	dw_printf ("*** No luck removing TWO SEPARATED bits of %d *** %.3f sec.\n", len, tend-tstart);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
failure:
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * All fix up attempts have failed.  
 | 
			
		||||
| 
						 | 
				
			
			@ -770,11 +483,10 @@ failure:
 | 
			
		|||
 | 
			
		||||
	  retry_cfg.type = RETRY_TYPE_NONE;
 | 
			
		||||
	  retry_cfg.mode = RETRY_MODE_CONTIGUOUS;
 | 
			
		||||
	   retry_cfg.retry = RETRY_NONE;
 | 
			
		||||
	  retry_cfg.retry = RETRY_NONE;
 | 
			
		||||
	  retry_cfg.u_bits.contig.nr_bits = 0;
 | 
			
		||||
	  retry_cfg.u_bits.contig.bit_idx = 0;
 | 
			
		||||
	
 | 
			
		||||
	  ok = try_decode (block, chan, subchan, alevel, retry_cfg, passall);
 | 
			
		||||
	  ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, passall);
 | 
			
		||||
	  return (ok);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -783,6 +495,7 @@ failure:
 | 
			
		|||
}  /* end hdlc_rec2_try_to_fix_later */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Check if the specified index of bit has been modified with the current type of configuration
 | 
			
		||||
 * Provide a specific implementation for contiguous mode to optimize number of tests done in the loop 
 | 
			
		||||
| 
						 | 
				
			
			@ -812,14 +525,7 @@ inline static char is_sep_bit_modified(int bit_idx, retry_conf_t retry_conf) {
 | 
			
		|||
	    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Get the bit value from a precalculated array to optimize access time in the loop 
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
inline static unsigned int get_bit (const rrbb_t b,const unsigned int ind)
 | 
			
		||||
{
 | 
			
		||||
	return b->computed_data[ind];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************************
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -836,20 +542,12 @@ inline static unsigned int get_bit (const rrbb_t b,const unsigned int ind)
 | 
			
		|||
 *		retry_conf	- Controls changes that will be attempted to get a good CRC.
 | 
			
		||||
 *
 | 
			
		||||
 *	   			retry:	
 | 
			
		||||
 *					Level of effort to recover from A bad FCS on the frame. 
 | 
			
		||||
 *				                RETRY_NONE=0,
 | 
			
		||||
 *				                RETRY_SWAP_SINGLE=1,
 | 
			
		||||
 *				                RETRY_SWAP_DOUBLE=2,
 | 
			
		||||
 *		                                RETRY_SWAP_TRIPLE=3,
 | 
			
		||||
 *		                                RETRY_REMOVE_SINGLE=4,
 | 
			
		||||
 *		                                RETRY_REMOVE_DOUBLE=5,
 | 
			
		||||
 *		                                RETRY_REMOVE_TRIPLE=6,
 | 
			
		||||
 *		                                RETRY_INSERT_SINGLE=7,
 | 
			
		||||
 *		                                RETRY_INSERT_DOUBLE=8,
 | 
			
		||||
 *		                                RETRY_SWAP_TWO_SEP=9,
 | 
			
		||||
 *		                                RETRY_SWAP_MANY=10,
 | 
			
		||||
 *		                                RETRY_REMOVE_MANY=11,
 | 
			
		||||
 *		                                RETRY_REMOVE_TWO_SEP=12,
 | 
			
		||||
 *					Level of effort to recover from a bad FCS on the frame.
 | 
			
		||||
 *				                RETRY_NONE = 0
 | 
			
		||||
 *				                RETRY_INVERT_SINGLE = 1
 | 
			
		||||
 *				                RETRY_INVERT_DOUBLE = 2
 | 
			
		||||
 *		                                RETRY_INVERT_TRIPLE = 3
 | 
			
		||||
 *		                                RETRY_INVERT_TWO_SEP = 4
 | 
			
		||||
 *
 | 
			
		||||
 *	    			mode:	RETRY_MODE_CONTIGUOUS - change adjacent bits.
 | 
			
		||||
 *						contig.bit_idx - first bit position
 | 
			
		||||
| 
						 | 
				
			
			@ -862,8 +560,6 @@ inline static unsigned int get_bit (const rrbb_t b,const unsigned int ind)
 | 
			
		|||
 *
 | 
			
		||||
 *				type:	RETRY_TYPE_NONE	- Make no changes.
 | 
			
		||||
 *					RETRY_TYPE_SWAP - Try inverting.
 | 
			
		||||
 *					RETRY_TYPE_REMOVE - Try removing.
 | 
			
		||||
 *					RETRY_TYPE_INSERT - Try inserting.
 | 
			
		||||
 *					
 | 
			
		||||
 *		passall		- All it thru even with bad CRC.
 | 
			
		||||
 *				  Valid only when no changes make.  i.e.
 | 
			
		||||
| 
						 | 
				
			
			@ -874,14 +570,15 @@ inline static unsigned int get_bit (const rrbb_t b,const unsigned int ind)
 | 
			
		|||
 *
 | 
			
		||||
 ***********************************************************************************/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, retry_conf_t retry_conf, int passall)
 | 
			
		||||
static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel, retry_conf_t retry_conf, int passall)
 | 
			
		||||
{
 | 
			
		||||
	struct hdlc_state_s H;	
 | 
			
		||||
	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;
 | 
			
		||||
| 
						 | 
				
			
			@ -890,7 +587,7 @@ static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, ret
 | 
			
		|||
	H.is_scrambled = rrbb_get_is_scrambled (block);
 | 
			
		||||
	H.prev_descram = rrbb_get_prev_descram (block);
 | 
			
		||||
	H.lfsr = rrbb_get_descram_state (block);
 | 
			
		||||
	H.prev_raw = get_bit (block, 0);	  /* Actually last bit of the */
 | 
			
		||||
	H.prev_raw = rrbb_get_bit (block, 0);	  /* Actually last bit of the */
 | 
			
		||||
					/* opening flag so we can derive the */
 | 
			
		||||
					/* first data bit.  */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -910,9 +607,6 @@ static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, ret
 | 
			
		|||
	H.frame_len = 0;
 | 
			
		||||
 | 
			
		||||
	blen = rrbb_get_len(block);
 | 
			
		||||
	/* Prepare space for the inserted bits in contiguous mode (separated mode for insert is not supported yet) */
 | 
			
		||||
	if (retry_conf.type == RETRY_TYPE_INSERT && retry_conf.mode == RETRY_MODE_CONTIGUOUS)
 | 
			
		||||
		blen+=retry_conf.u_bits.contig.nr_bits;
 | 
			
		||||
 | 
			
		||||
#if DEBUGx
 | 
			
		||||
	text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
| 
						 | 
				
			
			@ -921,43 +615,16 @@ static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, ret
 | 
			
		|||
#endif
 | 
			
		||||
	for (i=1; i<blen; i++) {
 | 
			
		||||
	  /* Get the value for the current bit */
 | 
			
		||||
	  raw = get_bit (block, i);
 | 
			
		||||
	  raw = rrbb_get_bit (block, i);
 | 
			
		||||
	  /* If swap two sep mode , swap the bit if needed */
 | 
			
		||||
	  if (retry_conf_retry == RETRY_SWAP_TWO_SEP) {
 | 
			
		||||
	  if (retry_conf_retry == RETRY_INVERT_TWO_SEP) {
 | 
			
		||||
	      if (is_sep_bit_modified(i, retry_conf))
 | 
			
		||||
	        raw = ! raw;
 | 
			
		||||
	  /* Else if remove two sep bits mode , remove the bit if needed */
 | 
			
		||||
	  } else if (retry_conf_retry == RETRY_REMOVE_TWO_SEP) {
 | 
			
		||||
	      if (is_sep_bit_modified(i, retry_conf))
 | 
			
		||||
	         //Remove (ignore) this bit from the buffer!
 | 
			
		||||
                 continue;
 | 
			
		||||
	  }
 | 
			
		||||
	  } 
 | 
			
		||||
	  /* Else handle all the others contiguous modes */
 | 
			
		||||
	  else if (retry_conf_mode == RETRY_MODE_CONTIGUOUS) {
 | 
			
		||||
	    /* If contiguous remove, ignore this bit from the buffer */
 | 
			
		||||
   	    if (retry_conf_type == RETRY_TYPE_REMOVE)  {
 | 
			
		||||
	      if ( is_contig_bit_modified(i, retry_conf))
 | 
			
		||||
	         //Remove (ignore) this bit from the buffer!
 | 
			
		||||
                 continue;
 | 
			
		||||
	    }
 | 
			
		||||
	    /* If insert bits mode */
 | 
			
		||||
            else if (retry_conf_type == RETRY_TYPE_INSERT) {
 | 
			
		||||
	        int nr_bits = retry_conf.u_bits.contig.nr_bits;
 | 
			
		||||
	        int bit_idx = retry_conf.u_bits.contig.bit_idx;
 | 
			
		||||
		/* If bit is after the index to insert, use the existing bit value (but shifted from the array) */
 | 
			
		||||
	        if (i >= bit_idx + nr_bits)
 | 
			
		||||
	          raw = get_bit (block, i-nr_bits);
 | 
			
		||||
		/* Else if this is a bit to insert, calculate the value of the bit from insert_value */
 | 
			
		||||
	        else if (is_contig_bit_modified(i, retry_conf)) {
 | 
			
		||||
	          raw = (retry_conf.insert_value >> (i-bit_idx)) & 1;
 | 
			
		||||
/*        	  dw_printf ("raw is %d for i %d bit_idx %d insert_value %d\n", 
 | 
			
		||||
	            raw, i, bit_idx, retry_conf.insert_value);*/
 | 
			
		||||
	        /* Else use the original bit value from the buffer */
 | 
			
		||||
	        } else {
 | 
			
		||||
	          /* Already set before */
 | 
			
		||||
		}
 | 
			
		||||
	    /* If in swap mode */
 | 
			
		||||
            } else if (retry_conf_type == RETRY_TYPE_SWAP) {
 | 
			
		||||
 | 
			
		||||
            if (retry_conf_type == RETRY_TYPE_SWAP) {
 | 
			
		||||
	        /* If this is the bit to swap */
 | 
			
		||||
	        if (is_contig_bit_modified(i, retry_conf))
 | 
			
		||||
	          raw = ! raw;
 | 
			
		||||
| 
						 | 
				
			
			@ -1096,8 +763,7 @@ static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, ret
 | 
			
		|||
 | 
			
		||||
	      assert (rrbb_get_chan(block) == chan);
 | 
			
		||||
	      assert (rrbb_get_subchan(block) == subchan);
 | 
			
		||||
 | 
			
		||||
	      multi_modem_process_rec_frame (chan, subchan, H.frame_buf, H.frame_len - 2, alevel, retry_conf.retry);   /* len-2 to remove FCS. */
 | 
			
		||||
	      multi_modem_process_rec_frame (chan, subchan, slice, H.frame_buf, H.frame_len - 2, alevel, retry_conf.retry);   /* len-2 to remove FCS. */
 | 
			
		||||
	      return 1;		/* success */
 | 
			
		||||
 | 
			
		||||
	  } else if (passall) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1106,7 +772,7 @@ static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, ret
 | 
			
		|||
	      //text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	      //dw_printf ("ATTEMPTING PASSALL PROCESSING\n");
 | 
			
		||||
  
 | 
			
		||||
	      multi_modem_process_rec_frame (chan, subchan, H.frame_buf, H.frame_len - 2, alevel, RETRY_MAX);   /* len-2 to remove FCS. */
 | 
			
		||||
	      multi_modem_process_rec_frame (chan, subchan, slice, H.frame_buf, H.frame_len - 2, alevel, RETRY_MAX);   /* len-2 to remove FCS. */
 | 
			
		||||
	      return 1;		/* success */
 | 
			
		||||
	    }
 | 
			
		||||
	    else {
 | 
			
		||||
| 
						 | 
				
			
			@ -1119,7 +785,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 +819,8 @@ failure:
 | 
			
		|||
	  }
 | 
			
		||||
	  dw_printf ("\n");
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
end:
 | 
			
		||||
#endif
 | 
			
		||||
	return 0;	/* failure. */
 | 
			
		||||
 | 
			
		||||
} /* end try_decode */
 | 
			
		||||
| 
						 | 
				
			
			@ -1183,7 +851,9 @@ end:
 | 
			
		|||
 *
 | 
			
		||||
 * Returns:	1 if it passes the sanity test.
 | 
			
		||||
 *
 | 
			
		||||
 * Description:	
 | 
			
		||||
 * Description:	This is NOT a validity check.
 | 
			
		||||
 *		We don't know if modifying the frame fixed the problem or made it worse.
 | 
			
		||||
 *		We can only test if it looks reasonable.
 | 
			
		||||
 *
 | 
			
		||||
 ***********************************************************************************/
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										17
									
								
								hdlc_rec2.h
								
								
								
								
							
							
						
						
									
										17
									
								
								hdlc_rec2.h
								
								
								
								
							| 
						 | 
				
			
			@ -17,9 +17,7 @@ typedef enum retry_mode_e {
 | 
			
		|||
 | 
			
		||||
typedef enum retry_type_e {
 | 
			
		||||
		RETRY_TYPE_NONE=0,
 | 
			
		||||
		RETRY_TYPE_SWAP=1,
 | 
			
		||||
		RETRY_TYPE_REMOVE=2,
 | 
			
		||||
		RETRY_TYPE_INSERT=3}  retry_type_t;
 | 
			
		||||
		RETRY_TYPE_SWAP=1 }  retry_type_t;
 | 
			
		||||
 | 
			
		||||
typedef struct retry_conf_s {
 | 
			
		||||
	retry_t      retry;
 | 
			
		||||
| 
						 | 
				
			
			@ -52,15 +50,7 @@ static const char * retry_text[] = {
 | 
			
		|||
		"SINGLE",
 | 
			
		||||
		"DOUBLE",
 | 
			
		||||
		"TRIPLE",
 | 
			
		||||
		"REMOVE_SINGLE",
 | 
			
		||||
		"REMOVE_DOUBLE",
 | 
			
		||||
		"REMOVE_TRIPLE",
 | 
			
		||||
		"INSERT_SINGLE",
 | 
			
		||||
		"INSERT_DOUBLE",
 | 
			
		||||
		"TWO_SEP",
 | 
			
		||||
		"MANY",
 | 
			
		||||
		"REMOVE_MANY",
 | 
			
		||||
		"REMOVE_SEP",
 | 
			
		||||
		"PASSALL" };
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -68,11 +58,10 @@ void hdlc_rec2_init (struct audio_s *audio_config_p);
 | 
			
		|||
 | 
			
		||||
void hdlc_rec2_block (rrbb_t block);
 | 
			
		||||
 | 
			
		||||
int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, alevel_t alevel);
 | 
			
		||||
int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel);
 | 
			
		||||
 | 
			
		||||
/* Provided by the top level application to process a complete frame. */
 | 
			
		||||
 | 
			
		||||
void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t level, retry_t retries, char *spectrum);
 | 
			
		||||
 | 
			
		||||
void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t level, retry_t retries, char *spectrum);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										531
									
								
								igate.c
								
								
								
								
							
							
						
						
									
										531
									
								
								igate.c
								
								
								
								
							| 
						 | 
				
			
			@ -107,16 +107,15 @@ static void * connnect_thread (void *arg);
 | 
			
		|||
static void * igate_recv_thread (void *arg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static void send_msg_to_server (char *msg);
 | 
			
		||||
static void xmit_packet (char *message);
 | 
			
		||||
static void send_msg_to_server (const char *msg);
 | 
			
		||||
static void xmit_packet (char *message, int chan);
 | 
			
		||||
 | 
			
		||||
static void rx_to_ig_init (void);
 | 
			
		||||
static void rx_to_ig_remember (packet_t pp);
 | 
			
		||||
static int rx_to_ig_allow (packet_t pp);
 | 
			
		||||
 | 
			
		||||
static void ig_to_tx_init (void);
 | 
			
		||||
static void ig_to_tx_remember (packet_t pp);
 | 
			
		||||
static int ig_to_tx_allow (packet_t pp);
 | 
			
		||||
static int ig_to_tx_allow (packet_t pp, int chan);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
| 
						 | 
				
			
			@ -256,7 +255,7 @@ int main (int argc, char *argv[])
 | 
			
		|||
	  SLEEP_SEC (20);
 | 
			
		||||
	  text_color_set(DW_COLOR_INFO);
 | 
			
		||||
	  dw_printf ("Send received packet\n");
 | 
			
		||||
	  send_msg_to_server ("W1ABC>APRS:?\r\n");
 | 
			
		||||
	  send_msg_to_server ("W1ABC>APRS:?");
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
	return 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -277,6 +276,8 @@ int main (int argc, char *argv[])
 | 
			
		|||
static struct audio_s		*save_audio_config_p;
 | 
			
		||||
static struct igate_config_s	*save_igate_config_p;
 | 
			
		||||
static struct digi_config_s 	*save_digi_config_p;
 | 
			
		||||
static int 			s_debug;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Statistics.  
 | 
			
		||||
| 
						 | 
				
			
			@ -327,7 +328,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
 | 
			
		||||
	<IGATE,MSG_CNT=n,LOC_CNT=n
 | 
			
		||||
| 
						 | 
				
			
			@ -359,6 +360,13 @@ static int stats_rf_xmit_packets;	/* Number of packets passed along to radio */
 | 
			
		|||
 *		p_digi_config	- Digipeater configuration.  
 | 
			
		||||
 *				  All we care about here is the packet filtering options.
 | 
			
		||||
 *
 | 
			
		||||
 *		debug_level	- 0  print packets FROM APRS-IS,
 | 
			
		||||
 *				     establishing connection with sergver, and
 | 
			
		||||
 *				     and anything rejected by client side filtering.
 | 
			
		||||
 *				  1  plus packets sent TO server or why not.
 | 
			
		||||
 *				  2  plus duplicate detection overview.
 | 
			
		||||
 *				  3  plus duplicate detection details.
 | 
			
		||||
 *
 | 
			
		||||
 * Description:	This starts two threads:
 | 
			
		||||
 *
 | 
			
		||||
 *		  *  to establish and maintain a connection to the server.
 | 
			
		||||
| 
						 | 
				
			
			@ -367,7 +375,7 @@ static int stats_rf_xmit_packets;	/* Number of packets passed along to radio */
 | 
			
		|||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void igate_init (struct audio_s *p_audio_config, struct igate_config_s *p_igate_config, struct digi_config_s *p_digi_config)
 | 
			
		||||
void igate_init (struct audio_s *p_audio_config, struct igate_config_s *p_igate_config, struct digi_config_s *p_digi_config, int debug_level)
 | 
			
		||||
{
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
	HANDLE connnect_th;
 | 
			
		||||
| 
						 | 
				
			
			@ -377,7 +385,7 @@ void igate_init (struct audio_s *p_audio_config, struct igate_config_s *p_igate_
 | 
			
		|||
	pthread_t cmd_listen_tid;
 | 
			
		||||
	int e;
 | 
			
		||||
#endif
 | 
			
		||||
	int j;
 | 
			
		||||
	s_debug = debug_level;
 | 
			
		||||
 | 
			
		||||
#if DEBUGx
 | 
			
		||||
	text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
| 
						 | 
				
			
			@ -739,7 +747,6 @@ static void * connnect_thread (void *arg)
 | 
			
		|||
	        strlcat (stemp, " filter ", sizeof(stemp));
 | 
			
		||||
	        strlcat (stemp, save_igate_config_p->t2_filter, sizeof(stemp));
 | 
			
		||||
	      }
 | 
			
		||||
	      strlcat (stemp, "\r\n", sizeof(stemp));
 | 
			
		||||
	      send_msg_to_server (stemp);
 | 
			
		||||
 | 
			
		||||
/* Delay until it is ok to start sending packets. */
 | 
			
		||||
| 
						 | 
				
			
			@ -767,7 +774,7 @@ static void * connnect_thread (void *arg)
 | 
			
		|||
 | 
			
		||||
	    char heartbeat[10];
 | 
			
		||||
 | 
			
		||||
	    strlcpy (heartbeat, "#\r\n", sizeof(heartbeat));
 | 
			
		||||
	    strlcpy (heartbeat, "#", sizeof(heartbeat));
 | 
			
		||||
 | 
			
		||||
	    /* This will close the socket if any error. */
 | 
			
		||||
	    send_msg_to_server (heartbeat);
 | 
			
		||||
| 
						 | 
				
			
			@ -801,13 +808,15 @@ static void * connnect_thread (void *arg)
 | 
			
		|||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
#define IGATE_MAX_MSG 520	/* Message to IGate max 512 characters. */
 | 
			
		||||
 | 
			
		||||
void igate_send_rec_packet (int chan, packet_t recv_pp)
 | 
			
		||||
{
 | 
			
		||||
	packet_t pp;
 | 
			
		||||
	int n;
 | 
			
		||||
	unsigned char *pinfo;
 | 
			
		||||
	char *p;
 | 
			
		||||
	char msg[520];		/* Message to IGate max 512 characters. */
 | 
			
		||||
	char msg[IGATE_MAX_MSG];
 | 
			
		||||
	int info_len;
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -827,11 +836,9 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
 | 
			
		|||
 | 
			
		||||
	  if (pfilter(chan, MAX_CHANS, save_digi_config_p->filter_str[chan][MAX_CHANS], recv_pp) != 1) {
 | 
			
		||||
 | 
			
		||||
// TODO1.2: take out debug message.  
 | 
			
		||||
//#if DEBUG
 | 
			
		||||
	    text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	    text_color_set(DW_COLOR_INFO);
 | 
			
		||||
	    dw_printf ("Packet from channel %d to IGate was rejected by filter: %s\n", chan, save_digi_config_p->filter_str[chan][MAX_CHANS]);
 | 
			
		||||
//#endif
 | 
			
		||||
 | 
			
		||||
	    return;
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -863,19 +870,22 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
 | 
			
		|||
	        strcmp(via, "TCPXX") == 0 ||
 | 
			
		||||
	        strcmp(via, "RFONLY") == 0 ||
 | 
			
		||||
	        strcmp(via, "NOGATE") == 0) {
 | 
			
		||||
#if DEBUGx
 | 
			
		||||
	      text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	      dw_printf ("Rx IGate: Do not relay with TCPIP etc. in path.\n");
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	      if (s_debug >= 1) {
 | 
			
		||||
	        text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	        dw_printf ("Rx IGate: Do not relay with %s in path.\n", via);
 | 
			
		||||
	      }
 | 
			
		||||
 | 
			
		||||
	      ax25_delete (pp);
 | 
			
		||||
	      return;
 | 
			
		||||
	    }
 | 
			
		||||
	  }
 | 
			
		||||
 | 
			
		||||
#if DEBUGx
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("Rx IGate: Unwrap third party message.\n");
 | 
			
		||||
#endif
 | 
			
		||||
	  if (s_debug >= 1) {
 | 
			
		||||
	    text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	    dw_printf ("Rx IGate: Unwrap third party message.\n");
 | 
			
		||||
	  }
 | 
			
		||||
 | 
			
		||||
	  inner_pp = ax25_unwrap_third_party(pp);
 | 
			
		||||
	  if (inner_pp == NULL) {
 | 
			
		||||
	    ax25_delete (pp);
 | 
			
		||||
| 
						 | 
				
			
			@ -897,10 +907,12 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
 | 
			
		|||
	      strcmp(via, "TCPXX") == 0 ||
 | 
			
		||||
	      strcmp(via, "RFONLY") == 0 ||
 | 
			
		||||
	      strcmp(via, "NOGATE") == 0) {
 | 
			
		||||
#if DEBUGx
 | 
			
		||||
	    text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	    dw_printf ("Rx IGate: Do not relay with TCPIP etc. in path.\n");
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	    if (s_debug >= 1) {
 | 
			
		||||
	      text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	      dw_printf ("Rx IGate: Do not relay with %s in path.\n", via);
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    ax25_delete (pp);
 | 
			
		||||
	    return;
 | 
			
		||||
	  }
 | 
			
		||||
| 
						 | 
				
			
			@ -910,10 +922,10 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
 | 
			
		|||
 * Do not relay generic query.
 | 
			
		||||
 */
 | 
			
		||||
	if (ax25_get_dti(pp) == '?') {
 | 
			
		||||
#if DEBUGx
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("Rx IGate: Do not relay generic query.\n");
 | 
			
		||||
#endif
 | 
			
		||||
	  if (s_debug >= 1) {
 | 
			
		||||
	    text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	    dw_printf ("Rx IGate: Do not relay generic query.\n");
 | 
			
		||||
	  }
 | 
			
		||||
	  ax25_delete (pp);
 | 
			
		||||
	  return;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -924,20 +936,21 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
 | 
			
		|||
 */
 | 
			
		||||
 | 
			
		||||
	info_len = ax25_get_info (pp, &pinfo);
 | 
			
		||||
	(void)(info_len);
 | 
			
		||||
 | 
			
		||||
	if ((p = strchr ((char*)pinfo, '\r')) != NULL) {
 | 
			
		||||
#if DEBUGx
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("Rx IGate: Truncated information part at CR.\n");
 | 
			
		||||
#endif
 | 
			
		||||
	  if (s_debug >= 1) {
 | 
			
		||||
	    text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	    dw_printf ("Rx IGate: Truncated information part at CR.\n");
 | 
			
		||||
	  }
 | 
			
		||||
          *p = '\0';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ((p = strchr ((char*)pinfo, '\n')) != NULL) {
 | 
			
		||||
#if DEBUGx
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("Rx IGate: Truncated information part at LF.\n");
 | 
			
		||||
#endif
 | 
			
		||||
	  if (s_debug >= 1) {
 | 
			
		||||
	    text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	    dw_printf ("Rx IGate: Truncated information part at LF.\n");
 | 
			
		||||
	  }
 | 
			
		||||
          *p = '\0';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -947,10 +960,10 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
 | 
			
		|||
 */
 | 
			
		||||
	if (strlen((char*)pinfo) == 0) {
 | 
			
		||||
 | 
			
		||||
#if DEBUGx
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("Rx IGate: Information part length is zero.\n");
 | 
			
		||||
#endif
 | 
			
		||||
	  if (s_debug >= 1) {
 | 
			
		||||
	    text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	    dw_printf ("Rx IGate: Information part length is zero.\n");
 | 
			
		||||
	  }
 | 
			
		||||
	  ax25_delete (pp);
 | 
			
		||||
	  return;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -962,10 +975,10 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
 | 
			
		|||
 */
 | 
			
		||||
 | 
			
		||||
	if ( ! rx_to_ig_allow(pp)) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("Rx IGate: Drop duplicate of same packet seen recently.\n");
 | 
			
		||||
#endif
 | 
			
		||||
	  if (s_debug >= 1) {
 | 
			
		||||
	    text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	    dw_printf ("Rx IGate: Drop duplicate of same packet seen recently.\n");
 | 
			
		||||
	  }
 | 
			
		||||
	  ax25_delete (pp);
 | 
			
		||||
	  return;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -980,7 +993,6 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
 | 
			
		|||
	strlcat (msg, save_audio_config_p->achan[chan].mycall, sizeof(msg));
 | 
			
		||||
	strlcat (msg, ":", sizeof(msg));
 | 
			
		||||
	strlcat (msg, (char*)pinfo, sizeof(msg));
 | 
			
		||||
	strlcat (msg, "\r\n", sizeof(msg));
 | 
			
		||||
 | 
			
		||||
	send_msg_to_server (msg);
 | 
			
		||||
	stats_rx_igate_packets++;
 | 
			
		||||
| 
						 | 
				
			
			@ -1006,7 +1018,7 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
 | 
			
		|||
 *		This one function should be used for login, hearbeats,
 | 
			
		||||
 *		and packets.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	msg	- Message.  Should end with CR/LF.
 | 
			
		||||
 * Inputs:	imsg	- Message.  We will add CR/LF.
 | 
			
		||||
 *		
 | 
			
		||||
 *
 | 
			
		||||
 * Description:	Send message to IGate Server if connected.
 | 
			
		||||
| 
						 | 
				
			
			@ -1015,26 +1027,30 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
 | 
			
		|||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void send_msg_to_server (char *msg)
 | 
			
		||||
static void send_msg_to_server (const char *imsg)
 | 
			
		||||
{
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	char stemp[IGATE_MAX_MSG];
 | 
			
		||||
 | 
			
		||||
	if (igate_sock == -1) {
 | 
			
		||||
	  return;	/* Silently discard if not connected. */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	stats_uplink_bytes += strlen(msg);
 | 
			
		||||
	strlcpy(stemp, imsg, sizeof(stemp));
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	text_color_set(DW_COLOR_XMIT);
 | 
			
		||||
	dw_printf ("[ig] ");
 | 
			
		||||
	ax25_safe_print (msg, strlen(msg), 0);
 | 
			
		||||
	dw_printf ("\n");
 | 
			
		||||
#endif
 | 
			
		||||
	if (s_debug >= 1) {
 | 
			
		||||
	  text_color_set(DW_COLOR_XMIT);
 | 
			
		||||
	  dw_printf ("[rx>ig] ");
 | 
			
		||||
	  ax25_safe_print (stemp, strlen(stemp), 0);
 | 
			
		||||
	  dw_printf ("\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	strlcat (stemp, "\r\n", sizeof(stemp));
 | 
			
		||||
 | 
			
		||||
	stats_uplink_bytes += strlen(stemp);
 | 
			
		||||
 | 
			
		||||
#if __WIN32__	
 | 
			
		||||
        err = send (igate_sock, msg, strlen(msg), 0);
 | 
			
		||||
        err = send (igate_sock, stemp, strlen(stemp), 0);
 | 
			
		||||
	if (err == SOCKET_ERROR)
 | 
			
		||||
	{
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
| 
						 | 
				
			
			@ -1045,7 +1061,7 @@ static void send_msg_to_server (char *msg)
 | 
			
		|||
	  WSACleanup();
 | 
			
		||||
	}
 | 
			
		||||
#else
 | 
			
		||||
        err = write (igate_sock, msg, strlen(msg));
 | 
			
		||||
        err = write (igate_sock, stemp, strlen(stemp));
 | 
			
		||||
	if (err <= 0)
 | 
			
		||||
	{
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
| 
						 | 
				
			
			@ -1169,7 +1185,31 @@ static void * igate_recv_thread (void *arg)
 | 
			
		|||
 | 
			
		||||
/*
 | 
			
		||||
 * We have a complete message terminated by LF.
 | 
			
		||||
 *
 | 
			
		||||
 * Remove CR LF from end.
 | 
			
		||||
 * This is a record separator for the protocol, not part of the data.
 | 
			
		||||
 * Should probably have an error if we don't have this.
 | 
			
		||||
 */
 | 
			
		||||
	  if (len >=2 && message[len-1] == '\n') { message[len-1] = '\0'; len--; }
 | 
			
		||||
	  if (len >=1 && message[len-1] == '\r') { message[len-1] = '\0'; len--; }
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * I've seen a case where the original RF packet had a trailing CR but
 | 
			
		||||
 * after someone else sent it to the server and it came back to me, that
 | 
			
		||||
 * CR was now a trailing space.
 | 
			
		||||
 * At first I was tempted to trim a trailing space as well.
 | 
			
		||||
 * By fixing this one case it might corrupt the data in other cases.
 | 
			
		||||
 * We compensate for this by ignoring trailing spaces when performing
 | 
			
		||||
 * the duplicate detection and removal.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * I've also seen a multiple trailing spaces like this.
 | 
			
		||||
 * Notice how safe_print shows a trailing space in hexadecimal to make it obvious.
 | 
			
		||||
 *
 | 
			
		||||
 * W1CLA-1>APVR30,TCPIP*,qAC,T2TOKYO3:;IRLP-4942*141503z4218.46NI07108.24W0446325-146IDLE    <0x20>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
	  if (len == 0) 
 | 
			
		||||
	  {
 | 
			
		||||
/* 
 | 
			
		||||
| 
						 | 
				
			
			@ -1184,35 +1224,33 @@ static void * igate_recv_thread (void *arg)
 | 
			
		|||
 * That way we can see login confirmation but not 
 | 
			
		||||
 * be bothered by the heart beat messages.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef DEBUG
 | 
			
		||||
 | 
			
		||||
	    if ( ! ok_to_send) {
 | 
			
		||||
#endif
 | 
			
		||||
	      text_color_set(DW_COLOR_REC);
 | 
			
		||||
	      dw_printf ("[ig] ");
 | 
			
		||||
	      ax25_safe_print ((char *)message, len, 0);
 | 
			
		||||
	      dw_printf ("\n");
 | 
			
		||||
#ifndef DEBUG
 | 
			
		||||
	    }
 | 
			
		||||
#endif
 | 
			
		||||
	  }
 | 
			
		||||
	  else 
 | 
			
		||||
	  {
 | 
			
		||||
/*
 | 
			
		||||
 * Convert to third party packet and transmit.
 | 
			
		||||
 *
 | 
			
		||||
 * Future: might have ability to configure multiple transmit
 | 
			
		||||
 * channels, each with own client side filtering and via path.
 | 
			
		||||
 * Loop here over all configured channels.
 | 
			
		||||
 */
 | 
			
		||||
	    
 | 
			
		||||
	    text_color_set(DW_COLOR_REC);
 | 
			
		||||
	    dw_printf ("\n[ig] ");
 | 
			
		||||
	    dw_printf ("\n[ig>tx] ");		// formerly just [ig]
 | 
			
		||||
	    ax25_safe_print ((char *)message, len, 0);
 | 
			
		||||
	    dw_printf ("\n");
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Remove CR LF from end.
 | 
			
		||||
 */
 | 
			
		||||
	    if (len >=2 && message[len-1] == '\n') { message[len-1] = '\0'; len--; }
 | 
			
		||||
	    if (len >=1 && message[len-1] == '\r') { message[len-1] = '\0'; len--; }
 | 
			
		||||
	    int to_chan = save_igate_config_p->tx_chan;
 | 
			
		||||
 | 
			
		||||
	    xmit_packet ((char*)message);
 | 
			
		||||
	    if (to_chan >= 0) {
 | 
			
		||||
	      xmit_packet ((char*)message, to_chan);
 | 
			
		||||
	    }
 | 
			
		||||
	  }
 | 
			
		||||
 | 
			
		||||
	}  /* while (1) */
 | 
			
		||||
| 
						 | 
				
			
			@ -1229,31 +1267,43 @@ static void * igate_recv_thread (void *arg)
 | 
			
		|||
 *		packet and send to transmit queue.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	message		- As sent by the server.  
 | 
			
		||||
 *				  Any trailing CRLF should have been removed.
 | 
			
		||||
 *				  Typical examples:
 | 
			
		||||
 *
 | 
			
		||||
 *				KA1BTK-5>APDR13,TCPIP*,qAC,T2IRELAND:=4237.62N/07040.68W$/A=-00054 http://aprsdroid.org/
 | 
			
		||||
 *				N1HKO-10>APJI40,TCPIP*,qAC,N1HKO-JS:<IGATE,MSG_CNT=0,LOC_CNT=0
 | 
			
		||||
 *				K1RI-2>APWW10,WIDE1-1,WIDE2-1,qAS,K1RI:/221700h/9AmA<Ct3_ sT010/002g005t045r000p023P020h97b10148
 | 
			
		||||
 *				KC1BOS-2>T3PQ3S,WIDE1-1,WIDE2-1,qAR,W1TG-1:`c)@qh\>/"50}TinyTrak4 Mobile
 | 
			
		||||
 *
 | 
			
		||||
 *				  Notice how the final address in the header might not
 | 
			
		||||
 *				  be a valid AX.25 address.  We see a 9 character address
 | 
			
		||||
 *				  (with no ssid) and an ssid of two letters.
 | 
			
		||||
 *				  We don't care because we end up discarding them before
 | 
			
		||||
 *				  repackaging to go over the radio.
 | 
			
		||||
 *
 | 
			
		||||
 *				  The "q construct"  ( http://www.aprs-is.net/q.aspx ) provides
 | 
			
		||||
 *				  a clue about the journey taken but I don't think we care here.
 | 
			
		||||
 *
 | 
			
		||||
 *		to_chan		- Radio channel for transmitting.
 | 
			
		||||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
static void xmit_packet (char *message)
 | 
			
		||||
static void xmit_packet (char *message, int to_chan)
 | 
			
		||||
{
 | 
			
		||||
	packet_t pp3;
 | 
			
		||||
	char payload[AX25_MAX_PACKET_LEN];	/* what is max len? */
 | 
			
		||||
	char *pinfo = NULL;
 | 
			
		||||
	int info_len;
 | 
			
		||||
	int to_chan = save_igate_config_p->tx_chan;	/* Should be -1 if not configured for xmit!!! */
 | 
			
		||||
							/* Future:  Array of boolean to allow multiple xmit channels? */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Is IGate to Radio direction enabled?
 | 
			
		||||
 */
 | 
			
		||||
	if (to_chan == -1) {
 | 
			
		||||
	  return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	stats_tx_igate_packets++;
 | 
			
		||||
 | 
			
		||||
	assert (to_chan >= 0 && to_chan < MAX_CHANS);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Try to parse it into a packet object.
 | 
			
		||||
 * This will contain "q constructs" and we might see an address
 | 
			
		||||
 * with two alphnumeric characters in the SSID so we must use
 | 
			
		||||
 * the non-strict parsing.
 | 
			
		||||
 *
 | 
			
		||||
 * Bug:  Up to 8 digipeaters are allowed in radio format.
 | 
			
		||||
 * There is a potential of finding a larger number here.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -1268,6 +1318,9 @@ static void xmit_packet (char *message)
 | 
			
		|||
 | 
			
		||||
/*
 | 
			
		||||
 * Apply our own packet filtering if configured.
 | 
			
		||||
 * Do we want to do this before or after removing the VIA path?
 | 
			
		||||
 * I suppose by doing it first, we have the possibility of
 | 
			
		||||
 * filtering by stations along the way or the q construct.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
	assert (to_chan >= 0 && to_chan < MAX_CHANS);
 | 
			
		||||
| 
						 | 
				
			
			@ -1276,35 +1329,37 @@ static void xmit_packet (char *message)
 | 
			
		|||
 | 
			
		||||
	  if (pfilter(MAX_CHANS, to_chan, save_digi_config_p->filter_str[MAX_CHANS][to_chan], pp3) != 1) {
 | 
			
		||||
 | 
			
		||||
// TODO1.2: take out debug message.  One person liked it as a confirmation of what was going on.
 | 
			
		||||
// Maybe it should be part of a more comprehensive debug facility?
 | 
			
		||||
//#if DEBUG
 | 
			
		||||
	    text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	    text_color_set(DW_COLOR_INFO);
 | 
			
		||||
	    dw_printf ("Packet from IGate to channel %d was rejected by filter: %s\n", to_chan, save_digi_config_p->filter_str[MAX_CHANS][to_chan]);
 | 
			
		||||
//#endif
 | 
			
		||||
 | 
			
		||||
	    ax25_delete (pp3);
 | 
			
		||||
	    return;
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * TODO: Discard if qAX in path???  others?
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Remove the VIA path.
 | 
			
		||||
 *
 | 
			
		||||
 * For example, we might get something like this from the server.
 | 
			
		||||
 *	K1USN-1>APWW10,TCPIP*,qAC,N5JXS-F1:T#479,100,048,002,500,000,10000000<0x0d><0x0a>
 | 
			
		||||
 *
 | 
			
		||||
 * We want to reduce it to this before wrapping it as third party traffic.
 | 
			
		||||
 *	K1USN-1>APWW10:T#479,100,048,002,500,000,10000000<0x0d><0x0a>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
	while (ax25_get_num_repeaters(pp3) > 0) {
 | 
			
		||||
	  ax25_remove_addr (pp3, AX25_REPEATER_1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Replace the VIA path with TCPIP and my call.
 | 
			
		||||
 * Mark my call as having been used.
 | 
			
		||||
 */
 | 
			
		||||
	ax25_set_addr (pp3, AX25_REPEATER_1, "TCPIP");
 | 
			
		||||
	ax25_set_h (pp3, AX25_REPEATER_1);
 | 
			
		||||
	ax25_set_addr (pp3, AX25_REPEATER_2, save_audio_config_p->achan[save_igate_config_p->tx_chan].mycall); 
 | 
			
		||||
	ax25_set_addr (pp3, AX25_REPEATER_2, save_audio_config_p->achan[to_chan].mycall);
 | 
			
		||||
	ax25_set_h (pp3, AX25_REPEATER_2);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -1314,6 +1369,7 @@ static void xmit_packet (char *message)
 | 
			
		|||
 | 
			
		||||
	ax25_format_addrs (pp3, payload);
 | 
			
		||||
	info_len = ax25_get_info (pp3, (unsigned char **)(&pinfo));
 | 
			
		||||
	(void)(info_len);
 | 
			
		||||
	strlcat (payload, pinfo, sizeof(payload));
 | 
			
		||||
#if DEBUGx
 | 
			
		||||
	text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
| 
						 | 
				
			
			@ -1323,12 +1379,12 @@ static void xmit_packet (char *message)
 | 
			
		|||
/*
 | 
			
		||||
 * Encapsulate for sending over radio if no reason to drop it.
 | 
			
		||||
 */
 | 
			
		||||
	if (ig_to_tx_allow (pp3)) {
 | 
			
		||||
	if (ig_to_tx_allow (pp3, to_chan)) {
 | 
			
		||||
	  char radio [500];
 | 
			
		||||
	  packet_t pradio;
 | 
			
		||||
 | 
			
		||||
	  snprintf (radio, sizeof(radio), "%s>%s%d%d%s:}%s",
 | 
			
		||||
				save_audio_config_p->achan[save_igate_config_p->tx_chan].mycall,
 | 
			
		||||
				save_audio_config_p->achan[to_chan].mycall,
 | 
			
		||||
				APP_TOCALL, MAJOR_VERSION, MINOR_VERSION,
 | 
			
		||||
				save_igate_config_p->tx_via,
 | 
			
		||||
				payload);
 | 
			
		||||
| 
						 | 
				
			
			@ -1340,16 +1396,18 @@ static void xmit_packet (char *message)
 | 
			
		|||
 | 
			
		||||
	  if (pradio != NULL) {
 | 
			
		||||
 | 
			
		||||
	    stats_tx_igate_packets++;
 | 
			
		||||
 | 
			
		||||
#if ITEST
 | 
			
		||||
	    text_color_set(DW_COLOR_XMIT);
 | 
			
		||||
	    dw_printf ("Xmit: %s\n", radio);
 | 
			
		||||
	    ax25_delete (pradio);
 | 
			
		||||
#else
 | 
			
		||||
	    /* This consumes packet so don't reference it again! */
 | 
			
		||||
	    tq_append (save_igate_config_p->tx_chan, TQ_PRIO_1_LO, pradio);
 | 
			
		||||
	    tq_append (to_chan, TQ_PRIO_1_LO, pradio);
 | 
			
		||||
#endif
 | 
			
		||||
	    stats_rf_xmit_packets++;
 | 
			
		||||
	    ig_to_tx_remember (pp3);
 | 
			
		||||
	    ig_to_tx_remember (pp3, save_igate_config_p->tx_chan, 0);	// correct. version before encapsulating it.
 | 
			
		||||
	  }
 | 
			
		||||
	  else {
 | 
			
		||||
	    text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
| 
						 | 
				
			
			@ -1380,7 +1438,8 @@ static void xmit_packet (char *message)
 | 
			
		|||
 *
 | 
			
		||||
 * Name:	rx_to_ig_allow
 | 
			
		||||
 * 
 | 
			
		||||
 * Purpose:	Check whether this is a duplicate of another sent recently.
 | 
			
		||||
 * Purpose:	Check whether this is a duplicate of another
 | 
			
		||||
 *		recently received from RF and sent to the Server
 | 
			
		||||
 *
 | 
			
		||||
 * Input:	pp	- Pointer to packet object.
 | 
			
		||||
 *		
 | 
			
		||||
| 
						 | 
				
			
			@ -1426,9 +1485,28 @@ static void rx_to_ig_init (void)
 | 
			
		|||
 | 
			
		||||
static void rx_to_ig_remember (packet_t pp)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
       	rx2ig_time_stamp[rx2ig_insert_next] = time(NULL);
 | 
			
		||||
        rx2ig_checksum[rx2ig_insert_next] = ax25_dedupe_crc(pp);
 | 
			
		||||
 | 
			
		||||
	if (s_debug >= 3) {
 | 
			
		||||
	  char src[AX25_MAX_ADDR_LEN];
 | 
			
		||||
	  char dest[AX25_MAX_ADDR_LEN];
 | 
			
		||||
	  unsigned char *pinfo;
 | 
			
		||||
	  int info_len;
 | 
			
		||||
 | 
			
		||||
	  ax25_get_addr_with_ssid(pp, AX25_SOURCE, src);
 | 
			
		||||
	  ax25_get_addr_with_ssid(pp, AX25_DESTINATION, dest);
 | 
			
		||||
	  info_len = ax25_get_info (pp, &pinfo);
 | 
			
		||||
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("rx_to_ig_remember [%d] = %d %d \"%s>%s:%s\"\n",
 | 
			
		||||
			rx2ig_insert_next,
 | 
			
		||||
			(int)(rx2ig_time_stamp[rx2ig_insert_next]),
 | 
			
		||||
			rx2ig_checksum[rx2ig_insert_next],
 | 
			
		||||
			src, dest, pinfo);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
        rx2ig_insert_next++;
 | 
			
		||||
        if (rx2ig_insert_next >= RX2IG_HISTORY_MAX) {
 | 
			
		||||
          rx2ig_insert_next = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -1441,11 +1519,35 @@ static int rx_to_ig_allow (packet_t pp)
 | 
			
		|||
	time_t now = time(NULL);
 | 
			
		||||
	int j;
 | 
			
		||||
 | 
			
		||||
	if (s_debug >= 2) {
 | 
			
		||||
	  char src[AX25_MAX_ADDR_LEN];
 | 
			
		||||
	  char dest[AX25_MAX_ADDR_LEN];
 | 
			
		||||
	  unsigned char *pinfo;
 | 
			
		||||
	  int info_len;
 | 
			
		||||
 | 
			
		||||
	  ax25_get_addr_with_ssid(pp, AX25_SOURCE, src);
 | 
			
		||||
	  ax25_get_addr_with_ssid(pp, AX25_DESTINATION, dest);
 | 
			
		||||
	  info_len = ax25_get_info (pp, &pinfo);
 | 
			
		||||
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("rx_to_ig_allow? %d \"%s>%s:%s\"\n", crc, src, dest, pinfo);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (j=0; j<RX2IG_HISTORY_MAX; j++) {
 | 
			
		||||
	  if (rx2ig_time_stamp[j] >= now - RX2IG_DEDUPE_TIME && rx2ig_checksum[j] == crc) {
 | 
			
		||||
	  if (rx2ig_checksum[j] == crc && rx2ig_time_stamp[j] >= now - RX2IG_DEDUPE_TIME) {
 | 
			
		||||
	    if (s_debug >= 2) {
 | 
			
		||||
	      text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	      // could be multiple entries and this might not be the most recent.
 | 
			
		||||
	      dw_printf ("rx_to_ig_allow? NO. Seen %d seconds ago.\n", (int)(now - rx2ig_time_stamp[j]));
 | 
			
		||||
	    }
 | 
			
		||||
	    return 0;
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (s_debug >= 2) {
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("rx_to_ig_allow? YES\n");
 | 
			
		||||
	}
 | 
			
		||||
	return 1;
 | 
			
		||||
 | 
			
		||||
} /* end rx_to_ig_allow */
 | 
			
		||||
| 
						 | 
				
			
			@ -1461,6 +1563,12 @@ static int rx_to_ig_allow (packet_t pp)
 | 
			
		|||
 *
 | 
			
		||||
 * Inputs:	pp	- Pointer to a packet object.
 | 
			
		||||
 *
 | 
			
		||||
 *		chan	- Channel number where it is being transmitted.
 | 
			
		||||
 *			  Duplicate detection needs to be separate for each radio channel.
 | 
			
		||||
 *
 | 
			
		||||
 *		bydigi	- True if transmitted by digipeater function.  False for IGate.
 | 
			
		||||
 *			  Why do we care about digpeating here?  See discussion below.
 | 
			
		||||
 *
 | 
			
		||||
 *------------------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:	ig_to_tx_allow
 | 
			
		||||
| 
						 | 
				
			
			@ -1469,6 +1577,8 @@ static int rx_to_ig_allow (packet_t pp)
 | 
			
		|||
 *		or if we exceed the transmit rate limits.
 | 
			
		||||
 *
 | 
			
		||||
 * Input:	pp	- Pointer to packet object.
 | 
			
		||||
 *
 | 
			
		||||
 *		chan	- Radio channel number where we want to transmit.
 | 
			
		||||
 *		
 | 
			
		||||
 * Returns:	True if it is OK to send.
 | 
			
		||||
 *		
 | 
			
		||||
| 
						 | 
				
			
			@ -1487,8 +1597,8 @@ static int rx_to_ig_allow (packet_t pp)
 | 
			
		|||
 *		This is the essentially the same as the pair of functions
 | 
			
		||||
 *		above with one addition restriction.  
 | 
			
		||||
 *
 | 
			
		||||
 *		The typical residential Internet connection is about 10,000
 | 
			
		||||
 *		times faster than the radio links we are using.  It would
 | 
			
		||||
 *		The typical residential Internet connection is around 10,000
 | 
			
		||||
 *		to 50,000 times faster than the radio links we are using.  It would
 | 
			
		||||
 *		be easy to completely saturate the radio channel if we are
 | 
			
		||||
 *		not careful.
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -1496,26 +1606,155 @@ static int rx_to_ig_allow (packet_t pp)
 | 
			
		|||
 *		number of packets sent during the past minute and past 5
 | 
			
		||||
 *		minutes and stop sending if a limit is reached.
 | 
			
		||||
 *
 | 
			
		||||
 * Future?	We might also want to avoid transmitting if the same packet
 | 
			
		||||
 *		was heard on the radio recently.  If everything is kept in
 | 
			
		||||
 *		the same table, we'd need to distinguish between those from
 | 
			
		||||
 *		the IGate server and those heard on the radio.
 | 
			
		||||
 *		Those heard on the radio would not count toward the
 | 
			
		||||
 *		1 and 5 minute rate limiting.
 | 
			
		||||
 *		Maybe even provide informative information such as -
 | 
			
		||||
 *		Tx IGate: Same packet heard recently from W1ABC and W9XYZ.
 | 
			
		||||
 * More Discussion:
 | 
			
		||||
 *
 | 
			
		||||
 *		Of course, the radio encapsulation would need to be removed
 | 
			
		||||
 *		and only the 3rd party packet inside compared.
 | 
			
		||||
 *		Consider the following example.
 | 
			
		||||
 *		I hear a packet from W1TG-1 three times over the radio then get the
 | 
			
		||||
 *		(almost) same thing twice from APRS-IS.
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *		Digipeater N3LEE-10 audio level = 23(10/6)   [NONE]   __|||||||
 | 
			
		||||
 *		[0.5] W1TG-1>APU25N,N3LEE-10*,WIDE2-1:<IGATE,MSG_CNT=30,LOC_CNT=61<0x0d>
 | 
			
		||||
 *		Station Capabilities, Ambulance, UIview 32 bit apps
 | 
			
		||||
 *		IGATE,MSG_CNT=30,LOC_CNT=61
 | 
			
		||||
 *
 | 
			
		||||
 *		[0H] W1TG-1>APU25N,N3LEE-10,WB2OSZ-14*:<IGATE,MSG_CNT=30,LOC_CNT=61<0x0d>
 | 
			
		||||
 *
 | 
			
		||||
 *		Digipeater WIDE2 (probably N3LEE-4) audio level = 22(10/6)   [NONE]   __|||||||
 | 
			
		||||
 *		[0.5] W1TG-1>APU25N,N3LEE-10,N3LEE-4,WIDE2*:<IGATE,MSG_CNT=30,LOC_CNT=61<0x0d>
 | 
			
		||||
 *		Station Capabilities, Ambulance, UIview 32 bit apps
 | 
			
		||||
 *		IGATE,MSG_CNT=30,LOC_CNT=61
 | 
			
		||||
 *
 | 
			
		||||
 *		Digipeater WIDE2 (probably AB1OC-10) audio level = 31(14/11)   [SINGLE]   ____:____
 | 
			
		||||
 *		[0.4] W1TG-1>APU25N,N3LEE-10,AB1OC-10,WIDE2*:<IGATE,MSG_CNT=30,LOC_CNT=61<0x0d>
 | 
			
		||||
 *		Station Capabilities, Ambulance, UIview 32 bit apps
 | 
			
		||||
 *		IGATE,MSG_CNT=30,LOC_CNT=61
 | 
			
		||||
 *
 | 
			
		||||
 *		[ig] W1TG-1>APU25N,WIDE2-2,qAR,W1GLO-11:<IGATE,MSG_CNT=30,LOC_CNT=61
 | 
			
		||||
 *		[0L] WB2OSZ-14>APDW13,WIDE1-1:}W1TG-1>APU25N,TCPIP,WB2OSZ-14*:<IGATE,MSG_CNT=30,LOC_CNT=61
 | 
			
		||||
 *
 | 
			
		||||
 *		[ig] W1TG-1>APU25N,K1FFK,WIDE2*,qAR,WB2ZII-15:<IGATE,MSG_CNT=30,LOC_CNT=61<0x20>
 | 
			
		||||
 *		[0L] WB2OSZ-14>APDW13,WIDE1-1:}W1TG-1>APU25N,TCPIP,WB2OSZ-14*:<IGATE,MSG_CNT=30,LOC_CNT=61<0x20>
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *		The first one gets retransmitted by digipeating.
 | 
			
		||||
 *
 | 
			
		||||
 *		Why are we getting the same thing twice from APRS-IS?  Shouldn't remove duplicates?
 | 
			
		||||
 *		Look closely.  The original packet, on RF, had a CR character at the end.
 | 
			
		||||
 *		At first I thought duplicate removal was broken but it turns out they
 | 
			
		||||
 *		are not exactly the same.
 | 
			
		||||
 *
 | 
			
		||||
 *		The receive IGate spec says a packet should be cut at a CR.
 | 
			
		||||
 *		In one case it is removed as expected   In another case, it is replaced by a trailing
 | 
			
		||||
 *		space character.  Maybe someone thought non printable characters should be
 | 
			
		||||
 *		replaced by spaces???
 | 
			
		||||
 *
 | 
			
		||||
 *		At first I was tempted to remove any trailing spaces to make up for the other
 | 
			
		||||
 *		IGate adding it.  Two wrongs don't make a right.   Trailing spaces are not that
 | 
			
		||||
 *		rare and removing them would corrupt the data.  My new strategy is for
 | 
			
		||||
 *		the duplicate detection compare to ignore trailing space, CR, and LF.
 | 
			
		||||
 *
 | 
			
		||||
 *		We already transmitted the same thing by the digipeater function so this should
 | 
			
		||||
 *		also go into memory for avoiding duplicates out of the transmit IGate.
 | 
			
		||||
 *
 | 
			
		||||
 * Future:
 | 
			
		||||
 *		Should the digipeater function avoid transmitting something if it
 | 
			
		||||
 *		was recently transmitted by the IGate funtion?
 | 
			
		||||
 *		This code is pretty much the same as dedupe.c. Maybe it could all
 | 
			
		||||
 *		be combined into one.  Need to ponder this some more.
 | 
			
		||||
 * 
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Here is another complete example, with the "-diii" debugging option to show details.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
We receive the signal directly from the source: (zzz.log 1011)
 | 
			
		||||
 | 
			
		||||
	N1ZKO-7 audio level = 33(16/10)   [NONE]   ___||||||
 | 
			
		||||
	[0.5] N1ZKO-7>T2TS7X,WIDE1-1,WIDE2-1:`c6wl!i[/>"4]}[scanning]=<0x0d>
 | 
			
		||||
	MIC-E, Human, Kenwood TH-D72, In Service
 | 
			
		||||
	N 42 43.7800, W 071 26.9100, 0 MPH, course 177, alt 230 ft
 | 
			
		||||
	[scanning]
 | 
			
		||||
 | 
			
		||||
We did not send it to the IS server recently.
 | 
			
		||||
 | 
			
		||||
	Rx IGate: Truncated information part at CR.
 | 
			
		||||
	rx_to_ig_allow? 57185 "N1ZKO-7>T2TS7X:`c6wl!i[/>"4]}[scanning]="
 | 
			
		||||
	rx_to_ig_allow? YES
 | 
			
		||||
 | 
			
		||||
Send it now and remember that fact.
 | 
			
		||||
 | 
			
		||||
	[rx>ig] N1ZKO-7>T2TS7X,WIDE1-1,WIDE2-1,qAR,WB2OSZ-14:`c6wl!i[/>"4]}[scanning]=
 | 
			
		||||
	rx_to_ig_remember [21] = 1447683040 57185 "N1ZKO-7>T2TS7X:`c6wl!i[/>"4]}[scanning]="
 | 
			
		||||
 | 
			
		||||
Digipeat it.  Notice how it has a trailing CR.
 | 
			
		||||
TODO:  Why is the CRC different?  Content looks the same.
 | 
			
		||||
 | 
			
		||||
	ig_to_tx_remember [38] = ch0 d1 1447683040 27598 "N1ZKO-7>T2TS7X:`c6wl!i[/>"4]}[scanning]=
"
 | 
			
		||||
	[0H] N1ZKO-7>T2TS7X,WB2OSZ-14*,WIDE2-1:`c6wl!i[/>"4]}[scanning]=<0x0d>
 | 
			
		||||
 | 
			
		||||
Now we hear it again, thru a digipeater.
 | 
			
		||||
Not sure who.   Was it UNCAN or was it someone else who doesn't use tracing?
 | 
			
		||||
See my rant in the User Guide about this.
 | 
			
		||||
 | 
			
		||||
	Digipeater WIDE2 (probably UNCAN) audio level = 30(15/10)   [NONE]   __|||::__
 | 
			
		||||
	[0.4] N1ZKO-7>T2TS7X,KB1POR-2,UNCAN,WIDE2*:`c6wl!i[/>"4]}[scanning]=<0x0d>
 | 
			
		||||
	MIC-E, Human, Kenwood TH-D72, In Service
 | 
			
		||||
	N 42 43.7800, W 071 26.9100, 0 MPH, course 177, alt 230 ft
 | 
			
		||||
	[scanning]
 | 
			
		||||
 | 
			
		||||
Was sent to server recently so don't do it again.
 | 
			
		||||
 | 
			
		||||
	Rx IGate: Truncated information part at CR.
 | 
			
		||||
	rx_to_ig_allow? 57185 "N1ZKO-7>T2TS7X:`c6wl!i[/>"4]}[scanning]="
 | 
			
		||||
	rx_to_ig_allow? NO. Seen 1 seconds ago.
 | 
			
		||||
	Rx IGate: Drop duplicate of same packet seen recently.
 | 
			
		||||
 | 
			
		||||
We hear it a third time, by a different digipeater.
 | 
			
		||||
 | 
			
		||||
	Digipeater WIDE1 (probably N3LEE-10) audio level = 23(12/6)   [NONE]   __|||||||
 | 
			
		||||
	[0.5] N1ZKO-7>T2TS7X,N3LEE-10,WIDE1*,WIDE2-1:`c6wl!i[/>"4]}[scanning]=<0x0d>
 | 
			
		||||
	MIC-E, Human, Kenwood TH-D72, In Service
 | 
			
		||||
	N 42 43.7800, W 071 26.9100, 0 MPH, course 177, alt 230 ft
 | 
			
		||||
	[scanning]
 | 
			
		||||
 | 
			
		||||
It's a duplicate, so don't send to server.
 | 
			
		||||
 | 
			
		||||
	Rx IGate: Truncated information part at CR.
 | 
			
		||||
	rx_to_ig_allow? 57185 "N1ZKO-7>T2TS7X:`c6wl!i[/>"4]}[scanning]="
 | 
			
		||||
	rx_to_ig_allow? NO. Seen 2 seconds ago.
 | 
			
		||||
	Rx IGate: Drop duplicate of same packet seen recently.
 | 
			
		||||
	Digipeater: Drop redundant packet to channel 0.
 | 
			
		||||
 | 
			
		||||
The server sends it to us.
 | 
			
		||||
NOTICE: The CR at the end has been replaced by a space.
 | 
			
		||||
 | 
			
		||||
	[ig>tx] N1ZKO-7>T2TS7X,K1FFK,WA2MJM-15*,qAR,WB2ZII-15:`c6wl!i[/>"4]}[scanning]=<0x20>
 | 
			
		||||
 | 
			
		||||
Should we transmit it?
 | 
			
		||||
No, we sent it recently by the digipeating function (note "bydigi=1").
 | 
			
		||||
 | 
			
		||||
	DEBUG:  ax25_dedupe_crc ignoring trailing space.
 | 
			
		||||
	ig_to_tx_allow? ch0 27598 "N1ZKO-7>T2TS7X:`c6wl!i[/>"4]}[scanning]= "
 | 
			
		||||
	ig_to_tx_allow? NO. Sent 4 seconds ago. bydigi=1
 | 
			
		||||
	Tx IGate: Drop duplicate packet transmitted recently.
 | 
			
		||||
	[0L] WB2OSZ-14>APDW13,WIDE1-1:}W1AST>TRPR4T,TCPIP,WB2OSZ-14*:`d=Ml!3>/"4N}
 | 
			
		||||
	[rx>ig] #
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define IG2TX_DEDUPE_TIME 60		/* Do not send duplicate within 60 seconds. */
 | 
			
		||||
#define IG2TX_HISTORY_MAX 50		/* Remember the last 50 sent from server to radio. */
 | 
			
		||||
 | 
			
		||||
/* Ideally this should be a critical region because */
 | 
			
		||||
/* it is being written by two threads but I'm not that concerned. */
 | 
			
		||||
 | 
			
		||||
static int ig2tx_insert_next;
 | 
			
		||||
static time_t ig2tx_time_stamp[IG2TX_HISTORY_MAX];
 | 
			
		||||
static unsigned short ig2tx_checksum[IG2TX_HISTORY_MAX];
 | 
			
		||||
static unsigned char ig2tx_chan[IG2TX_HISTORY_MAX];
 | 
			
		||||
static unsigned short ig2tx_bydigi[IG2TX_HISTORY_MAX];
 | 
			
		||||
 | 
			
		||||
static void ig_to_tx_init (void)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -1523,15 +1762,40 @@ static void ig_to_tx_init (void)
 | 
			
		|||
	for (n=0; n<IG2TX_HISTORY_MAX; n++) {
 | 
			
		||||
	  ig2tx_time_stamp[n] = 0;
 | 
			
		||||
	  ig2tx_checksum[n] = 0;
 | 
			
		||||
	  ig2tx_chan[n] = 0xff;
 | 
			
		||||
	  ig2tx_bydigi[n] = 0;
 | 
			
		||||
	}
 | 
			
		||||
	ig2tx_insert_next = 0;
 | 
			
		||||
}
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
static void ig_to_tx_remember (packet_t pp)
 | 
			
		||||
void ig_to_tx_remember (packet_t pp, int chan, int bydigi)
 | 
			
		||||
{
 | 
			
		||||
       	ig2tx_time_stamp[ig2tx_insert_next] = time(NULL);
 | 
			
		||||
        ig2tx_checksum[ig2tx_insert_next] = ax25_dedupe_crc(pp);
 | 
			
		||||
	time_t now = time(NULL);
 | 
			
		||||
	unsigned short crc = ax25_dedupe_crc(pp);
 | 
			
		||||
 | 
			
		||||
	if (s_debug >= 3) {
 | 
			
		||||
	  char src[AX25_MAX_ADDR_LEN];
 | 
			
		||||
	  char dest[AX25_MAX_ADDR_LEN];
 | 
			
		||||
	  unsigned char *pinfo;
 | 
			
		||||
	  int info_len;
 | 
			
		||||
 | 
			
		||||
	  ax25_get_addr_with_ssid(pp, AX25_SOURCE, src);
 | 
			
		||||
	  ax25_get_addr_with_ssid(pp, AX25_DESTINATION, dest);
 | 
			
		||||
	  info_len = ax25_get_info (pp, &pinfo);
 | 
			
		||||
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("ig_to_tx_remember [%d] = ch%d d%d %d %d \"%s>%s:%s\"\n",
 | 
			
		||||
			ig2tx_insert_next,
 | 
			
		||||
			chan, bydigi,
 | 
			
		||||
			(int)(now), crc,
 | 
			
		||||
			src, dest, pinfo);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ig2tx_time_stamp[ig2tx_insert_next] = now;
 | 
			
		||||
	ig2tx_checksum[ig2tx_insert_next] = crc;
 | 
			
		||||
	ig2tx_chan[ig2tx_insert_next] = chan;
 | 
			
		||||
	ig2tx_bydigi[ig2tx_insert_next] = bydigi;
 | 
			
		||||
 | 
			
		||||
        ig2tx_insert_next++;
 | 
			
		||||
        if (ig2tx_insert_next >= IG2TX_HISTORY_MAX) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1539,25 +1803,51 @@ static void ig_to_tx_remember (packet_t pp)
 | 
			
		|||
        }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ig_to_tx_allow (packet_t pp)
 | 
			
		||||
static int ig_to_tx_allow (packet_t pp, int chan)
 | 
			
		||||
{
 | 
			
		||||
	unsigned short crc = ax25_dedupe_crc(pp);
 | 
			
		||||
	time_t now = time(NULL);
 | 
			
		||||
	int j;
 | 
			
		||||
	int count_1, count_5;
 | 
			
		||||
 | 
			
		||||
	if (s_debug >= 2) {
 | 
			
		||||
	  char src[AX25_MAX_ADDR_LEN];
 | 
			
		||||
	  char dest[AX25_MAX_ADDR_LEN];
 | 
			
		||||
	  unsigned char *pinfo;
 | 
			
		||||
	  int info_len;
 | 
			
		||||
 | 
			
		||||
	  ax25_get_addr_with_ssid(pp, AX25_SOURCE, src);
 | 
			
		||||
	  ax25_get_addr_with_ssid(pp, AX25_DESTINATION, dest);
 | 
			
		||||
	  info_len = ax25_get_info (pp, &pinfo);
 | 
			
		||||
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("ig_to_tx_allow? ch%d %d \"%s>%s:%s\"\n", chan, crc, src, dest, pinfo);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Consider transmissions on this channel only by either digi or IGate. */
 | 
			
		||||
 | 
			
		||||
	for (j=0; j<IG2TX_HISTORY_MAX; j++) {
 | 
			
		||||
	  if (ig2tx_time_stamp[j] >= now - IG2TX_DEDUPE_TIME && ig2tx_checksum[j] == crc) {
 | 
			
		||||
	  if (ig2tx_checksum[j] == crc && ig2tx_chan[j] == chan && ig2tx_time_stamp[j] >= now - IG2TX_DEDUPE_TIME) {
 | 
			
		||||
	    if (s_debug >= 2) {
 | 
			
		||||
	      text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	      // could be multiple entries and this might not be the most recent.
 | 
			
		||||
	      dw_printf ("ig_to_tx_allow? NO. Sent %d seconds ago. bydigi=%d\n", (int)(now - ig2tx_time_stamp[j]), ig2tx_bydigi[j]);
 | 
			
		||||
	    }
 | 
			
		||||
	    text_color_set(DW_COLOR_INFO);
 | 
			
		||||
	    dw_printf ("Tx IGate: Drop duplicate packet transmitted recently.\n");
 | 
			
		||||
	    return 0;
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* IGate transmit counts must not include digipeater transmissions. */
 | 
			
		||||
 | 
			
		||||
	count_1 = 0;
 | 
			
		||||
	count_5 = 0;
 | 
			
		||||
	for (j=0; j<IG2TX_HISTORY_MAX; j++) {
 | 
			
		||||
	  if (ig2tx_time_stamp[j] >= now - 60) count_1++;
 | 
			
		||||
	  if (ig2tx_time_stamp[j] >= now - 300) count_5++;
 | 
			
		||||
	  if (ig2tx_chan[j] == chan && ig2tx_bydigi[j] == 0) {
 | 
			
		||||
	    if (ig2tx_time_stamp[j] >= now - 60) count_1++;
 | 
			
		||||
	    if (ig2tx_time_stamp[j] >= now - 300) count_5++;
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (count_1 >= save_igate_config_p->tx_limit_1) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1571,6 +1861,11 @@ static int ig_to_tx_allow (packet_t pp)
 | 
			
		|||
	  return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (s_debug >= 2) {
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("ig_to_tx_allow? YES\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
 | 
			
		||||
} /* end ig_to_tx_allow */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										13
									
								
								igate.h
								
								
								
								
							
							
						
						
									
										13
									
								
								igate.h
								
								
								
								
							| 
						 | 
				
			
			@ -55,13 +55,24 @@ struct igate_config_s {
 | 
			
		|||
	int tx_limit_5;			/* Max. packets to transmit in 5 minutes. */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define IGATE_TX_LIMIT_1_DEFAULT 6
 | 
			
		||||
#define IGATE_TX_LIMIT_1_MAX     20
 | 
			
		||||
 | 
			
		||||
#define IGATE_TX_LIMIT_5_DEFAULT 20
 | 
			
		||||
#define IGATE_TX_LIMIT_5_MAX     80
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Call this once at startup */
 | 
			
		||||
 | 
			
		||||
void igate_init (struct audio_s *p_audio_config, struct igate_config_s *p_igate_config, struct digi_config_s *p_digi_config);
 | 
			
		||||
void igate_init (struct audio_s *p_audio_config, struct igate_config_s *p_igate_config, struct digi_config_s *p_digi_config, int debug_level);
 | 
			
		||||
 | 
			
		||||
/* Call this with each packet received from the radio. */
 | 
			
		||||
 | 
			
		||||
void igate_send_rec_packet (int chan, packet_t recv_pp);
 | 
			
		||||
 | 
			
		||||
/* This when digipeater transmits.  Set bydigi to 1 . */
 | 
			
		||||
 | 
			
		||||
void ig_to_tx_remember (packet_t pp, int chan, int bydigi);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										12
									
								
								kiss.c
								
								
								
								
							
							
						
						
									
										12
									
								
								kiss.c
								
								
								
								
							| 
						 | 
				
			
			@ -303,7 +303,7 @@ void kiss_init (struct misc_config_s *mc)
 | 
			
		|||
	    text_color_set(DW_COLOR_INFO);
 | 
			
		||||
	    dw_printf ("Converted nullmodem device '%s'", mc->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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										15
									
								
								kiss_frame.c
								
								
								
								
							
							
						
						
									
										15
									
								
								kiss_frame.c
								
								
								
								
							| 
						 | 
				
			
			@ -70,10 +70,8 @@
 | 
			
		|||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -91,7 +89,7 @@ void hex_dump (unsigned char *p, int len);
 | 
			
		|||
static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										229
									
								
								latlong.c
								
								
								
								
							
							
						
						
									
										229
									
								
								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 */
 | 
			
		||||
							
								
								
									
										74
									
								
								log.c
								
								
								
								
							
							
						
						
									
										74
									
								
								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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										44
									
								
								log2gpx.c
								
								
								
								
							
							
						
						
									
										44
									
								
								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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -77,9 +77,13 @@ 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.
 | 
			
		||||
.P
 | 
			
		||||
i = IGate
 | 
			
		||||
.RE
 | 
			
		||||
.RE
 | 
			
		||||
.PD
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								morse.c
								
								
								
								
							
							
						
						
									
										2
									
								
								morse.c
								
								
								
								
							| 
						 | 
				
			
			@ -36,7 +36,7 @@
 | 
			
		|||
#include <assert.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <sys/time.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										311
									
								
								multi_modem.c
								
								
								
								
							
							
						
						
									
										311
									
								
								multi_modem.c
								
								
								
								
							| 
						 | 
				
			
			@ -98,23 +98,15 @@ static struct audio_s          *save_audio_config_p;
 | 
			
		|||
// Candidates for further processing.
 | 
			
		||||
 | 
			
		||||
static struct {
 | 
			
		||||
 | 
			
		||||
	packet_t packet_p;
 | 
			
		||||
	alevel_t alevel;
 | 
			
		||||
	retry_t retries;
 | 
			
		||||
	int age;
 | 
			
		||||
	unsigned int crc;
 | 
			
		||||
	int score;
 | 
			
		||||
} candidate[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS];
 | 
			
		||||
 | 
			
		||||
} candidate[MAX_CHANS][MAX_SUBCHANS];
 | 
			
		||||
#define MAX_STORED_CRC 256
 | 
			
		||||
 | 
			
		||||
typedef struct crc_s {
 | 
			
		||||
	struct crc_s* nextp;	/* Next pointer to maintain a queue. */
 | 
			
		||||
	unsigned int crc;
 | 
			
		||||
} *crc_t;
 | 
			
		||||
 | 
			
		||||
static crc_t crc_queue_of_last_to_app[MAX_CHANS];
 | 
			
		||||
 | 
			
		||||
#define PROCESS_AFTER_BITS 2
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -163,12 +155,14 @@ void multi_modem_init (struct audio_s *pa)
 | 
			
		|||
	      save_audio_config_p->achan[chan].baud = DEFAULT_BAUD;
 | 
			
		||||
	    }
 | 
			
		||||
	    process_age[chan] = PROCESS_AFTER_BITS * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].baud;
 | 
			
		||||
	    crc_queue_of_last_to_app[chan] = NULL;
 | 
			
		||||
	    //crc_queue_of_last_to_app[chan] = NULL;
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
 | 
			
		||||
//Add a crc to the end of the queue and returns the numbers of CRC stored in the queue
 | 
			
		||||
int crc_queue_append (unsigned int crc, unsigned int chan) {
 | 
			
		||||
	crc_t plast;
 | 
			
		||||
| 
						 | 
				
			
			@ -257,6 +251,7 @@ unsigned char is_crc_in_queue(unsigned int chan, unsigned int crc) {
 | 
			
		|||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
#endif /* if 0 */
 | 
			
		||||
 | 
			
		||||
/*------------------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -281,7 +276,12 @@ unsigned char is_crc_in_queue(unsigned int chan, unsigned int crc) {
 | 
			
		|||
 *		slicers, using different levels, each with its own HDLC decoder.
 | 
			
		||||
 *		We now have a separate variable, num_demod, which could be 1
 | 
			
		||||
 *		while num_subchan is larger.
 | 
			
		||||
 *		
 | 
			
		||||
 *
 | 
			
		||||
 * Version 1.3:	Go back to num_subchan with single meaning of number of demodulators.
 | 
			
		||||
 *		We now have separate independent variable, num_slicers, for the
 | 
			
		||||
 *		mark/space imbalance compensation.
 | 
			
		||||
 *		num_demod, while probably more descriptive, should not exist anymore.
 | 
			
		||||
 *
 | 
			
		||||
 *------------------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -290,25 +290,48 @@ void multi_modem_process_sample (int chan, int audio_sample)
 | 
			
		|||
{
 | 
			
		||||
	int d;
 | 
			
		||||
	int subchan;
 | 
			
		||||
	static int i = 0;	/* for interleaving among multiple demodulators. */
 | 
			
		||||
 | 
			
		||||
// TODO: temp debug, remove this.
 | 
			
		||||
 | 
			
		||||
	assert (save_audio_config_p->achan[chan].num_subchan > 0 && save_audio_config_p->achan[chan].num_subchan <= MAX_SUBCHANS);
 | 
			
		||||
	assert (save_audio_config_p->achan[chan].num_slicers > 0 && save_audio_config_p->achan[chan].num_slicers <= MAX_SLICERS);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/* Formerly one loop. */
 | 
			
		||||
	/* 1.2: We can feed one demodulator but end up with multiple outputs. */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	for (d = 0; d < save_audio_config_p->achan[chan].num_demod; d++) {
 | 
			
		||||
	if (save_audio_config_p->achan[chan].interleave > 1) {
 | 
			
		||||
 | 
			
		||||
	  demod_process_sample(chan, d, audio_sample);
 | 
			
		||||
// TODO: temp debug, remove this.
 | 
			
		||||
 | 
			
		||||
	  assert (save_audio_config_p->achan[chan].interleave == save_audio_config_p->achan[chan].num_subchan);
 | 
			
		||||
	  demod_process_sample(chan, i, audio_sample);
 | 
			
		||||
	  i++;
 | 
			
		||||
	  if (i >= save_audio_config_p->achan[chan].interleave) i = 0;
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  /* Send same thing to all. */
 | 
			
		||||
	  for (d = 0; d < save_audio_config_p->achan[chan].num_subchan; d++) {
 | 
			
		||||
	    demod_process_sample(chan, d, audio_sample);
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
 | 
			
		||||
	  int slice;
 | 
			
		||||
 | 
			
		||||
	  if (candidate[chan][subchan].packet_p != NULL) {
 | 
			
		||||
	    candidate[chan][subchan].age++;
 | 
			
		||||
	    if (candidate[chan][subchan].age > process_age[chan]) {
 | 
			
		||||
	      pick_best_candidate (chan);
 | 
			
		||||
	  for (slice = 0; slice < save_audio_config_p->achan[chan].num_slicers; slice++) {
 | 
			
		||||
 | 
			
		||||
	    if (candidate[chan][subchan][slice].packet_p != NULL) {
 | 
			
		||||
	      candidate[chan][subchan][slice].age++;
 | 
			
		||||
	      if (candidate[chan][subchan][slice].age > process_age[chan]) {
 | 
			
		||||
	        pick_best_candidate (chan);
 | 
			
		||||
	      }
 | 
			
		||||
	    }
 | 
			
		||||
	  }  
 | 
			
		||||
	}}
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -320,7 +343,8 @@ void multi_modem_process_sample (int chan, int audio_sample)
 | 
			
		|||
 *		FCS and acceptable size.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	chan	- Audio channel number, 0 or 1.
 | 
			
		||||
 *		subchan	- Which modem/decoder found it.
 | 
			
		||||
 *		subchan	- Which modem found it.
 | 
			
		||||
 *		slice	- Which slice found it.
 | 
			
		||||
 *		fbuf	- Pointer to first byte in HDLC frame.
 | 
			
		||||
 *		flen	- Number of bytes excluding the FCS.
 | 
			
		||||
 *		alevel	- Audio level, range of 0 - 100.
 | 
			
		||||
| 
						 | 
				
			
			@ -414,73 +438,31 @@ void multi_modem_process_sample (int chan, int audio_sample)
 | 
			
		|||
	than one.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
void multi_modem_process_rec_frame (int chan, int subchan, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries)  
 | 
			
		||||
{	
 | 
			
		||||
void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries)
 | 
			
		||||
{
 | 
			
		||||
	packet_t pp;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
			
		||||
	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
			
		||||
	assert (slice >= 0 && slice < MAX_SUBCHANS);
 | 
			
		||||
 | 
			
		||||
	pp = ax25_from_frame (fbuf, flen, alevel);
 | 
			
		||||
 | 
			
		||||
	if (pp == NULL) {
 | 
			
		||||
	  text_color_set(DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("Unexpected internal problem, %s %d\n", __FILE__, __LINE__);
 | 
			
		||||
	  return;	/* oops!  why would it fail? */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * If single modem/deocder, push it thru and forget about all this foolishness.
 | 
			
		||||
 * If only one demodulator/slicer, push it thru and forget about all this foolishness.
 | 
			
		||||
 */
 | 
			
		||||
	if (save_audio_config_p->achan[chan].num_subchan == 1) {
 | 
			
		||||
	  dlq_append (DLQ_REC_FRAME, chan, subchan, pp, alevel, retries, "");
 | 
			
		||||
	  return;
 | 
			
		||||
	}
 | 
			
		||||
	if (save_audio_config_p->achan[chan].num_subchan == 1 &&
 | 
			
		||||
	    save_audio_config_p->achan[chan].num_slicers == 1) {
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Special handing for two separated bit errors.
 | 
			
		||||
 * See description earlier.
 | 
			
		||||
 *
 | 
			
		||||
 * Not combined with others to find the best score.
 | 
			
		||||
 * Either pass it along or drop if duplicate.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
	if (retries >= RETRY_SWAP_TWO_SEP) {
 | 
			
		||||
	  int mycrc;
 | 
			
		||||
	  char spectrum[MAX_SUBCHANS+1];
 | 
			
		||||
	  int dropped = 0;
 | 
			
		||||
 | 
			
		||||
	  memset (spectrum, 0, sizeof(spectrum));
 | 
			
		||||
	  memset (spectrum, '_', (size_t)save_audio_config_p->achan[chan].num_subchan);
 | 
			
		||||
	  spectrum[subchan] = '.';
 | 
			
		||||
 | 
			
		||||
	  mycrc = ax25_m_m_crc(pp);
 | 
			
		||||
/* Smetimes recovered packet is not the latest one send to the app:
 | 
			
		||||
 * It can be a packet sent to the app before the latest one because of the processing time ...
 | 
			
		||||
 * So we check if the crc of current packet has already been received in the queue of others crc
 | 
			
		||||
 */
 | 
			
		||||
	  dropped = is_crc_in_queue(chan, mycrc);
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("\n%s\n%d.%d: ptr=%p, retry=%d, age=, crc=%04x, score=  , dropped =%d\n", 
 | 
			
		||||
		spectrum, chan, subchan, pp, (int)retries, mycrc,dropped);
 | 
			
		||||
#endif	   
 | 
			
		||||
	  if (dropped) {
 | 
			
		||||
	     /* Same as last one.  Drop it. */
 | 
			
		||||
	     ax25_delete (pp);
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	     dw_printf ("Drop duplicate.\n");
 | 
			
		||||
#endif
 | 
			
		||||
	     return;
 | 
			
		||||
	   }
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	  dw_printf ("Send the best one along.\n");
 | 
			
		||||
#endif
 | 
			
		||||
	  dlq_append (DLQ_REC_FRAME, chan, subchan, pp, alevel, retries, spectrum);
 | 
			
		||||
	  if (crc_queue_append(mycrc, chan) > MAX_STORED_CRC)
 | 
			
		||||
	    crc_queue_remove(chan);
 | 
			
		||||
	  dlq_append (DLQ_REC_FRAME, chan, subchan, slice, pp, alevel, retries, "");
 | 
			
		||||
	  return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -488,17 +470,17 @@ void multi_modem_process_rec_frame (int chan, int subchan, unsigned char *fbuf,
 | 
			
		|||
/*
 | 
			
		||||
 * Otherwise, save them up for a few bit times so we can pick the best.
 | 
			
		||||
 */
 | 
			
		||||
	if (candidate[chan][subchan].packet_p != NULL) {
 | 
			
		||||
	if (candidate[chan][subchan][slice].packet_p != NULL) {
 | 
			
		||||
	  /* Oops!  Didn't expect it to be there. */
 | 
			
		||||
	  ax25_delete (candidate[chan][subchan].packet_p);
 | 
			
		||||
	  candidate[chan][subchan].packet_p = NULL;
 | 
			
		||||
	  ax25_delete (candidate[chan][subchan][slice].packet_p);
 | 
			
		||||
	  candidate[chan][subchan][slice].packet_p = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	candidate[chan][subchan].packet_p = pp;
 | 
			
		||||
	candidate[chan][subchan].alevel = alevel;
 | 
			
		||||
	candidate[chan][subchan].retries = retries;
 | 
			
		||||
	candidate[chan][subchan].age = 0;
 | 
			
		||||
	candidate[chan][subchan].crc = ax25_m_m_crc(pp);
 | 
			
		||||
	candidate[chan][subchan][slice].packet_p = pp;
 | 
			
		||||
	candidate[chan][subchan][slice].alevel = alevel;
 | 
			
		||||
	candidate[chan][subchan][slice].retries = retries;
 | 
			
		||||
	candidate[chan][subchan][slice].age = 0;
 | 
			
		||||
	candidate[chan][subchan][slice].crc = ax25_m_m_crc(pp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -519,56 +501,82 @@ void multi_modem_process_rec_frame (int chan, int subchan, unsigned char *fbuf,
 | 
			
		|||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
/* This is a suitable order for interleaved "G" demodulators. */
 | 
			
		||||
/* Opposite order would be suitable for multi-frequency although */
 | 
			
		||||
/* multiple slicers are of questionable value for HF SSB. */
 | 
			
		||||
 | 
			
		||||
#define subchan_from_n(x) ((x) % save_audio_config_p->achan[chan].num_subchan)
 | 
			
		||||
#define slice_from_n(x)   ((x) / save_audio_config_p->achan[chan].num_subchan)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void pick_best_candidate (int chan) 
 | 
			
		||||
{
 | 
			
		||||
	int subchan;
 | 
			
		||||
	int best_subchan, best_score;
 | 
			
		||||
	char spectrum[MAX_SUBCHANS+1];
 | 
			
		||||
	int k;
 | 
			
		||||
	int best_n, best_score;
 | 
			
		||||
	char spectrum[MAX_SUBCHANS*MAX_SLICERS+1];
 | 
			
		||||
	int n, j, k;
 | 
			
		||||
	int num_bars = save_audio_config_p->achan[chan].num_slicers * save_audio_config_p->achan[chan].num_subchan;
 | 
			
		||||
 | 
			
		||||
	memset (spectrum, 0, sizeof(spectrum));
 | 
			
		||||
 | 
			
		||||
	for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
 | 
			
		||||
	for (n = 0; n < num_bars; n++) {
 | 
			
		||||
	  j = subchan_from_n(n);
 | 
			
		||||
	  k = slice_from_n(n);
 | 
			
		||||
 | 
			
		||||
	  /* Build the spectrum display. */
 | 
			
		||||
 | 
			
		||||
	  if (candidate[chan][subchan].packet_p == NULL) {
 | 
			
		||||
	    spectrum[subchan] = '_';
 | 
			
		||||
	  if (candidate[chan][j][k].packet_p == NULL) {
 | 
			
		||||
	    spectrum[n] = '_';
 | 
			
		||||
	  }
 | 
			
		||||
	  else if (candidate[chan][subchan].retries == RETRY_NONE) {
 | 
			
		||||
	    spectrum[subchan] = '|';
 | 
			
		||||
	  else if (candidate[chan][j][k].retries == RETRY_NONE) {
 | 
			
		||||
	    spectrum[n] = '|';
 | 
			
		||||
	  }
 | 
			
		||||
	  else if (candidate[chan][subchan].retries == RETRY_SWAP_SINGLE) {
 | 
			
		||||
	    spectrum[subchan] = ':';
 | 
			
		||||
	  else if (candidate[chan][j][k].retries == RETRY_INVERT_SINGLE) {
 | 
			
		||||
	    spectrum[n] = ':';
 | 
			
		||||
	  }
 | 
			
		||||
	  else  {
 | 
			
		||||
	    spectrum[subchan] = '.';
 | 
			
		||||
	    spectrum[n] = '.';
 | 
			
		||||
	  }
 | 
			
		||||
 | 
			
		||||
	  /* Begining score depends on effort to get a valid frame CRC. */
 | 
			
		||||
 | 
			
		||||
	  candidate[chan][subchan].score = RETRY_MAX * 1000 - ((int)candidate[chan][subchan].retries * 1000);
 | 
			
		||||
	  candidate[chan][j][k].score = RETRY_MAX * 1000 - ((int)candidate[chan][j][k].retries * 1000);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	  /* Bump it up slightly if others nearby have the same CRC. */
 | 
			
		||||
	  
 | 
			
		||||
	  for (k = 0; k < save_audio_config_p->achan[chan].num_subchan; k++) {
 | 
			
		||||
	    if (k != subchan && candidate[chan][k].packet_p != NULL) {
 | 
			
		||||
	      if (candidate[chan][k].crc == candidate[chan][subchan].crc) {
 | 
			
		||||
	        candidate[chan][subchan].score += (MAX_SUBCHANS+1) - abs(subchan-k);
 | 
			
		||||
	/* Bump it up slightly if others nearby have the same CRC. */
 | 
			
		||||
 | 
			
		||||
	for (n = 0; n < num_bars; n++) {
 | 
			
		||||
	  int m;
 | 
			
		||||
 | 
			
		||||
	  j = subchan_from_n(n);
 | 
			
		||||
	  k = slice_from_n(n);
 | 
			
		||||
 | 
			
		||||
	  if (candidate[chan][j][k].packet_p != NULL) {
 | 
			
		||||
 | 
			
		||||
	    for (m = 0; m < num_bars; m++) {
 | 
			
		||||
 | 
			
		||||
	      int mj = subchan_from_n(m);
 | 
			
		||||
	      int mk = slice_from_n(m);
 | 
			
		||||
 | 
			
		||||
	      if (m != n && candidate[chan][mj][mk].packet_p != NULL) {
 | 
			
		||||
	        if (candidate[chan][j][k].crc == candidate[chan][mj][mk].crc) {
 | 
			
		||||
	          candidate[chan][j][k].score += (num_bars+1) - abs(m-n);
 | 
			
		||||
	        }
 | 
			
		||||
	      }
 | 
			
		||||
	    }
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
  
 | 
			
		||||
	best_subchan = 0;
 | 
			
		||||
	best_n = 0;
 | 
			
		||||
	best_score = 0;
 | 
			
		||||
 | 
			
		||||
	for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
 | 
			
		||||
	  if (candidate[chan][subchan].packet_p != NULL) {
 | 
			
		||||
	    if (candidate[chan][subchan].score > best_score) {
 | 
			
		||||
	       best_score = candidate[chan][subchan].score;
 | 
			
		||||
	       best_subchan = subchan;
 | 
			
		||||
	for (n = 0; n < num_bars; n++) {
 | 
			
		||||
	  j = subchan_from_n(n);
 | 
			
		||||
	  k = slice_from_n(n);
 | 
			
		||||
 | 
			
		||||
	  if (candidate[chan][j][k].packet_p != NULL) {
 | 
			
		||||
	    if (candidate[chan][j][k].score > best_score) {
 | 
			
		||||
	       best_score = candidate[chan][j][k].score;
 | 
			
		||||
	       best_n = n;
 | 
			
		||||
	    }
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -577,20 +585,22 @@ static void pick_best_candidate (int chan)
 | 
			
		|||
	text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	dw_printf ("\n%s\n", spectrum);
 | 
			
		||||
 | 
			
		||||
	for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
 | 
			
		||||
	for (n = 0; n < num_bars; n++) {
 | 
			
		||||
	  j = subchan_from_n(n);
 | 
			
		||||
	  k = slice_from_n(n);
 | 
			
		||||
 | 
			
		||||
	  if (candidate[chan][subchan].packet_p == NULL) {
 | 
			
		||||
	    dw_printf ("%d.%d: ptr=%p\n", chan, subchan,
 | 
			
		||||
		candidate[chan][subchan].packet_p);
 | 
			
		||||
	  if (candidate[chan][j][k].packet_p == NULL) {
 | 
			
		||||
	    dw_printf ("%d.%d.%d: ptr=%p\n", chan, j, k,
 | 
			
		||||
		candidate[chan][j][k].packet_p);
 | 
			
		||||
	  }
 | 
			
		||||
	  else {
 | 
			
		||||
	    dw_printf ("%d.%d: ptr=%p, retry=%d, age=%3d, crc=%04x, score=%d  %s\n", chan, subchan,
 | 
			
		||||
		candidate[chan][subchan].packet_p, 
 | 
			
		||||
		(int)(candidate[chan][subchan].retries), 
 | 
			
		||||
		candidate[chan][subchan].age,
 | 
			
		||||
		candidate[chan][subchan].crc,
 | 
			
		||||
		candidate[chan][subchan].score,
 | 
			
		||||
		subchan == best_subchan ? "***" : "");
 | 
			
		||||
	    dw_printf ("%d.%d.%d: ptr=%p, retry=%d, age=%3d, crc=%04x, score=%d  %s\n", chan, j, k,
 | 
			
		||||
		candidate[chan][j][k].packet_p,
 | 
			
		||||
		(int)(candidate[chan][j][k].retries),
 | 
			
		||||
		candidate[chan][j][k].age,
 | 
			
		||||
		candidate[chan][j][k].crc,
 | 
			
		||||
		candidate[chan][j][k].score,
 | 
			
		||||
		(n == best_n) ? "***" : "");
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -599,75 +609,38 @@ static void pick_best_candidate (int chan)
 | 
			
		|||
 * send the best one along.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#if 1		// v1.2 dev F, Reverse original order.  Delete rejects THEN process the best one.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/* Delete those not chosen. */
 | 
			
		||||
 | 
			
		||||
	for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
 | 
			
		||||
	  if (subchan != best_subchan && candidate[chan][subchan].packet_p != NULL) {
 | 
			
		||||
	    ax25_delete (candidate[chan][subchan].packet_p);
 | 
			
		||||
	    candidate[chan][subchan].packet_p = NULL;
 | 
			
		||||
	for (n = 0; n < num_bars; n++) {
 | 
			
		||||
	  j = subchan_from_n(n);
 | 
			
		||||
	  k = slice_from_n(n);
 | 
			
		||||
	  if (n != best_n && candidate[chan][j][k].packet_p != NULL) {
 | 
			
		||||
	    ax25_delete (candidate[chan][j][k].packet_p);
 | 
			
		||||
	    candidate[chan][j][k].packet_p = NULL;
 | 
			
		||||
	  }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Pass along one. */
 | 
			
		||||
 | 
			
		||||
	dlq_append (DLQ_REC_FRAME, chan, best_subchan, 
 | 
			
		||||
		candidate[chan][best_subchan].packet_p, 
 | 
			
		||||
		candidate[chan][best_subchan].alevel, 
 | 
			
		||||
		(int)(candidate[chan][best_subchan].retries), 
 | 
			
		||||
 | 
			
		||||
	j = subchan_from_n(best_n);
 | 
			
		||||
	k = slice_from_n(best_n);
 | 
			
		||||
 | 
			
		||||
	dlq_append (DLQ_REC_FRAME, chan, j, k,
 | 
			
		||||
		candidate[chan][j][k].packet_p,
 | 
			
		||||
		candidate[chan][j][k].alevel,
 | 
			
		||||
		(int)(candidate[chan][j][k].retries),
 | 
			
		||||
		spectrum);
 | 
			
		||||
	if (crc_queue_append(candidate[chan][best_subchan].crc, chan) > MAX_STORED_CRC)
 | 
			
		||||
	    crc_queue_remove(chan);
 | 
			
		||||
 | 
			
		||||
	/* Someone else owns it now and will delete it later. */
 | 
			
		||||
	candidate[chan][best_subchan].packet_p = NULL;
 | 
			
		||||
	candidate[chan][j][k].packet_p = NULL;
 | 
			
		||||
 | 
			
		||||
	/* Clear in preparation for next time. */
 | 
			
		||||
 | 
			
		||||
	for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
 | 
			
		||||
	memset (candidate, 0, sizeof(candidate));
 | 
			
		||||
 | 
			
		||||
	  candidate[chan][subchan].alevel.rec = 0;
 | 
			
		||||
	  candidate[chan][subchan].alevel.mark = 0;
 | 
			
		||||
	  candidate[chan][subchan].alevel.space = 0;
 | 
			
		||||
 | 
			
		||||
	  candidate[chan][subchan].retries = 0;
 | 
			
		||||
	  candidate[chan][subchan].age = 0;
 | 
			
		||||
	  candidate[chan][subchan].crc = 0;
 | 
			
		||||
	}
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
	dlq_append (DLQ_REC_FRAME, chan, best_subchan, 
 | 
			
		||||
		candidate[chan][best_subchan].packet_p, 
 | 
			
		||||
		candidate[chan][best_subchan].alevel, 
 | 
			
		||||
		(int)(candidate[chan][best_subchan].retries), 
 | 
			
		||||
		spectrum);
 | 
			
		||||
	if (crc_queue_append(candidate[chan][best_subchan].crc, chan) > MAX_STORED_CRC)
 | 
			
		||||
	    crc_queue_remove(chan);
 | 
			
		||||
	/* Someone else will delete so don't do it below. */
 | 
			
		||||
	candidate[chan][best_subchan].packet_p = NULL;
 | 
			
		||||
 | 
			
		||||
	/* Clear out in preparation for next time. */
 | 
			
		||||
 | 
			
		||||
	for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
 | 
			
		||||
	  if (candidate[chan][subchan].packet_p != NULL) {
 | 
			
		||||
	    ax25_delete (candidate[chan][subchan].packet_p);
 | 
			
		||||
	    candidate[chan][subchan].packet_p = NULL;
 | 
			
		||||
	  }
 | 
			
		||||
 | 
			
		||||
	  candidate[chan][subchan].alevel.rec = 0;
 | 
			
		||||
	  candidate[chan][subchan].alevel.mark = 0;
 | 
			
		||||
	  candidate[chan][subchan].alevel.space = 0;
 | 
			
		||||
 | 
			
		||||
	  candidate[chan][subchan].retries = 0;
 | 
			
		||||
	  candidate[chan][subchan].age = 0;
 | 
			
		||||
	  candidate[chan][subchan].crc = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
} /* end pick_best_candidate */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* end multi_modem.c */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,6 @@ void multi_modem_init (struct audio_s *pmodem);
 | 
			
		|||
 | 
			
		||||
void multi_modem_process_sample (int c, int audio_sample);
 | 
			
		||||
 | 
			
		||||
void multi_modem_process_rec_frame (int chan, int subchan, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries);
 | 
			
		||||
 | 
			
		||||
void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										330
									
								
								nmea.c
								
								
								
								
							
							
						
						
									
										330
									
								
								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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										17
									
								
								pfilter.c
								
								
								
								
							
							
						
						
									
										17
									
								
								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);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										100
									
								
								ptt.c
								
								
								
								
							
							
						
						
									
										100
									
								
								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 <stdio.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/time.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										13
									
								
								recv.c
								
								
								
								
							
							
						
						
									
										13
									
								
								recv.c
								
								
								
								
							| 
						 | 
				
			
			@ -287,11 +287,11 @@ void recv_process (void)
 | 
			
		|||
	dlq_type_t type;
 | 
			
		||||
	int chan;
 | 
			
		||||
	int subchan;
 | 
			
		||||
	int slice;
 | 
			
		||||
	packet_t pp;
 | 
			
		||||
	alevel_t alevel;
 | 
			
		||||
	retry_t retries;
 | 
			
		||||
	char spectrum[MAX_SUBCHANS+1];
 | 
			
		||||
 | 
			
		||||
	char spectrum[MAX_SUBCHANS*MAX_SLICERS+1];
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -299,18 +299,17 @@ void recv_process (void)
 | 
			
		|||
#if DEBUG
 | 
			
		||||
	  text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	  dw_printf ("recv_process: woke up\n");
 | 
			
		||||
#endif
 | 
			
		||||
	  
 | 
			
		||||
#endif 
 | 
			
		||||
 | 
			
		||||
	  ok = dlq_remove (&type, &chan, &subchan, &slice, &pp, &alevel, &retries, spectrum, sizeof(spectrum));
 | 
			
		||||
 | 
			
		||||
	  ok = dlq_remove (&type, &chan, &subchan, &pp, &alevel, &retries, 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", 
 | 
			
		||||
				ok, (int)type, chan, pp);
 | 
			
		||||
#endif
 | 
			
		||||
	  if (ok) {
 | 
			
		||||
	   
 | 
			
		||||
		app_process_rec_packet (chan, subchan, pp, alevel, retries, spectrum);  
 | 
			
		||||
		app_process_rec_packet (chan, subchan, slice, pp, alevel, retries, spectrum);
 | 
			
		||||
	  }
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	  else {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,7 +46,6 @@
 | 
			
		|||
#include <assert.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
//#include <sys/time.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
| 
						 | 
				
			
			@ -99,6 +98,8 @@ static void * redecode_thread (void *arg);
 | 
			
		|||
void redecode_init (struct audio_s *p_audio_config)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
	HANDLE redecode_th;
 | 
			
		||||
#else
 | 
			
		||||
| 
						 | 
				
			
			@ -151,7 +152,7 @@ void redecode_init (struct audio_s *p_audio_config)
 | 
			
		|||
	text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
	dw_printf ("redecode_init: finished \n");
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
} /* end redecode_init */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -175,6 +176,8 @@ void redecode_init (struct audio_s *p_audio_config)
 | 
			
		|||
 *
 | 
			
		||||
 *--------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
static unsigned redecode_thread (void *arg)
 | 
			
		||||
#else
 | 
			
		||||
| 
						 | 
				
			
			@ -242,7 +245,7 @@ static void * redecode_thread (void *arg)
 | 
			
		|||
 | 
			
		||||
} /* end redecode_thread */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										145
									
								
								rrbb.c
								
								
								
								
							
							
						
						
									
										145
									
								
								rrbb.c
								
								
								
								
							| 
						 | 
				
			
			@ -23,17 +23,14 @@
 | 
			
		|||
 * File:	rrbb.c
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:	Raw Received Bit Buffer.
 | 
			
		||||
 *		Implementation of an array of bits used to hold data out of
 | 
			
		||||
 *		An array of bits used to hold data out of
 | 
			
		||||
 *		the demodulator before feeding it into the HLDC decoding.
 | 
			
		||||
 *
 | 
			
		||||
 * Version 1.0:	Let's try something new.
 | 
			
		||||
 *		Rather than storing a single bit from the demodulator
 | 
			
		||||
 *		output, let's store a value which we can try later
 | 
			
		||||
 *		comparing to threshold values besides 0.
 | 
			
		||||
 *
 | 
			
		||||
 * Version 1.2: Save initial state of 9600 baud descrambler so we can
 | 
			
		||||
 *		attempt bit fix up on G3RUH/K9NG scrambled data.
 | 
			
		||||
 *
 | 
			
		||||
 * Version 1.3:	Store as bytes rather than packing 8 bits per byte.
 | 
			
		||||
 *
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#define RRBB_C
 | 
			
		||||
| 
						 | 
				
			
			@ -49,44 +46,9 @@
 | 
			
		|||
#include "rrbb.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define MAGIC1 0x12344321
 | 
			
		||||
#define MAGIC2 0x56788765
 | 
			
		||||
 | 
			
		||||
static const unsigned int masks[SOI] = {
 | 
			
		||||
	0x00000001,
 | 
			
		||||
	0x00000002,
 | 
			
		||||
	0x00000004,
 | 
			
		||||
	0x00000008,
 | 
			
		||||
	0x00000010,
 | 
			
		||||
	0x00000020,
 | 
			
		||||
	0x00000040,
 | 
			
		||||
	0x00000080,
 | 
			
		||||
	0x00000100,
 | 
			
		||||
	0x00000200,
 | 
			
		||||
	0x00000400,
 | 
			
		||||
	0x00000800,
 | 
			
		||||
	0x00001000,
 | 
			
		||||
	0x00002000,
 | 
			
		||||
	0x00004000,
 | 
			
		||||
	0x00008000,
 | 
			
		||||
	0x00010000,
 | 
			
		||||
	0x00020000,
 | 
			
		||||
	0x00040000,
 | 
			
		||||
	0x00080000,
 | 
			
		||||
	0x00100000,
 | 
			
		||||
	0x00200000,
 | 
			
		||||
	0x00400000,
 | 
			
		||||
	0x00800000,
 | 
			
		||||
	0x01000000,
 | 
			
		||||
	0x02000000,
 | 
			
		||||
	0x04000000,
 | 
			
		||||
	0x08000000,
 | 
			
		||||
	0x10000000,
 | 
			
		||||
	0x20000000,
 | 
			
		||||
	0x40000000,
 | 
			
		||||
	0x80000000 };
 | 
			
		||||
 | 
			
		||||
static int new_count = 0;
 | 
			
		||||
static int delete_count = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -102,6 +64,8 @@ static int delete_count = 0;
 | 
			
		|||
 *
 | 
			
		||||
 *		subchan	- Which demodulator of the channel.
 | 
			
		||||
 *
 | 
			
		||||
 *		slice	- multiple thresholds per demodulator.
 | 
			
		||||
 *
 | 
			
		||||
 *		is_scrambled - Is data scrambled? (true, false)
 | 
			
		||||
 *
 | 
			
		||||
 *		descram_state - State of data descrambler.
 | 
			
		||||
| 
						 | 
				
			
			@ -114,21 +78,20 @@ static int delete_count = 0;
 | 
			
		|||
 *
 | 
			
		||||
 ***********************************************************************************/
 | 
			
		||||
 | 
			
		||||
rrbb_t rrbb_new (int chan, int subchan, int is_scrambled, int descram_state, int prev_descram)
 | 
			
		||||
rrbb_t rrbb_new (int chan, int subchan, int slice, int is_scrambled, int descram_state, int prev_descram)
 | 
			
		||||
{
 | 
			
		||||
	rrbb_t result;
 | 
			
		||||
 | 
			
		||||
	assert (SOI == 8 * sizeof(unsigned int));
 | 
			
		||||
 | 
			
		||||
	assert (chan >= 0 && chan < MAX_CHANS);
 | 
			
		||||
	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 | 
			
		||||
 | 
			
		||||
	assert (slice >= 0 && slice < MAX_SLICERS);
 | 
			
		||||
 | 
			
		||||
	result = malloc(sizeof(struct rrbb_s));
 | 
			
		||||
 | 
			
		||||
	result->magic1 = MAGIC1;
 | 
			
		||||
	result->chan = chan;
 | 
			
		||||
	result->subchan = subchan;
 | 
			
		||||
	result->slice = slice;
 | 
			
		||||
	result->magic2 = MAGIC2;
 | 
			
		||||
 | 
			
		||||
	new_count++;
 | 
			
		||||
| 
						 | 
				
			
			@ -181,6 +144,7 @@ void rrbb_clear (rrbb_t b, int is_scrambled, int descram_state, int prev_descram
 | 
			
		|||
	b->prev_descram = prev_descram;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/***********************************************************************************
 | 
			
		||||
 *
 | 
			
		||||
 * Name:	rrbb_append_bit	
 | 
			
		||||
| 
						 | 
				
			
			@ -192,35 +156,7 @@ void rrbb_clear (rrbb_t b, int is_scrambled, int descram_state, int prev_descram
 | 
			
		|||
 *
 | 
			
		||||
 ***********************************************************************************/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// TODO:  Forget about bit packing and just use bytes.
 | 
			
		||||
//  We have hundreds of MB.  Why waste time to save a couple KB?
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void rrbb_append_bit (rrbb_t b, int val)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int di, mi;
 | 
			
		||||
	
 | 
			
		||||
	assert (b != NULL);
 | 
			
		||||
	assert (b->magic1 == MAGIC1);
 | 
			
		||||
	assert (b->magic2 == MAGIC2);
 | 
			
		||||
 | 
			
		||||
	if (b->len >= MAX_NUM_BITS) {
 | 
			
		||||
	  return;	/* Silently discard if full. */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	di = b->len / SOI;
 | 
			
		||||
	mi = b->len % SOI;
 | 
			
		||||
 | 
			
		||||
	if (val) {
 | 
			
		||||
	  b->data[di] |= masks[mi];
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  b->data[di] &= ~ masks[mi];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b->len++;
 | 
			
		||||
}
 | 
			
		||||
/* Definition in header file so it can be inlined. */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/***********************************************************************************
 | 
			
		||||
| 
						 | 
				
			
			@ -279,46 +215,9 @@ int rrbb_get_len (rrbb_t b)
 | 
			
		|||
 *		
 | 
			
		||||
 ***********************************************************************************/
 | 
			
		||||
 | 
			
		||||
int rrbb_get_bit (rrbb_t b, unsigned int ind)
 | 
			
		||||
{
 | 
			
		||||
	assert (b != NULL);
 | 
			
		||||
	assert (b->magic1 == MAGIC1);
 | 
			
		||||
	assert (b->magic2 == MAGIC2);
 | 
			
		||||
/* Definition in header file so it can be inlined. */
 | 
			
		||||
 | 
			
		||||
	assert (ind < b->len);
 | 
			
		||||
 | 
			
		||||
	if (b->data[ind / SOI] & masks[ind % SOI]) {
 | 
			
		||||
	  return 1;
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	  return 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int rrbb_get_computed_bit (rrbb_t b, unsigned int ind)
 | 
			
		||||
{
 | 
			
		||||
	return b->computed_data[ind];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int rrbb_compute_bits (rrbb_t b)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i,val;
 | 
			
		||||
 | 
			
		||||
	assert (b != NULL);
 | 
			
		||||
	assert (b->magic1 == MAGIC1);
 | 
			
		||||
	assert (b->magic2 == MAGIC2);
 | 
			
		||||
 | 
			
		||||
	for (i=0;i<b->len;i++) {
 | 
			
		||||
	  if (b->data[i / SOI] & masks[i % SOI]) {
 | 
			
		||||
	    val = 1;
 | 
			
		||||
	  }
 | 
			
		||||
	  else {
 | 
			
		||||
	    val = 0;
 | 
			
		||||
	  }
 | 
			
		||||
	  b->computed_data[i] = val;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/***********************************************************************************
 | 
			
		||||
| 
						 | 
				
			
			@ -457,6 +356,28 @@ int rrbb_get_subchan (rrbb_t b)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/***********************************************************************************
 | 
			
		||||
 *
 | 
			
		||||
 * Name:	rrbb_get_slice	
 | 
			
		||||
 *
 | 
			
		||||
 * Purpose:	Get slice number from which bit buffer was received.
 | 
			
		||||
 *
 | 
			
		||||
 * Inputs:	b	Handle for bit array.
 | 
			
		||||
 *		
 | 
			
		||||
 ***********************************************************************************/
 | 
			
		||||
 | 
			
		||||
int rrbb_get_slice (rrbb_t b)
 | 
			
		||||
{
 | 
			
		||||
	assert (b != NULL);
 | 
			
		||||
	assert (b->magic1 == MAGIC1);
 | 
			
		||||
	assert (b->magic2 == MAGIC2);
 | 
			
		||||
 | 
			
		||||
	assert (b->slice >= 0 && b->slice < MAX_SLICERS);
 | 
			
		||||
 | 
			
		||||
	return (b->slice);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/***********************************************************************************
 | 
			
		||||
 *
 | 
			
		||||
 * Name:	rrbb_set_audio_level	
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										42
									
								
								rrbb.h
								
								
								
								
							
							
						
						
									
										42
									
								
								rrbb.h
								
								
								
								
							| 
						 | 
				
			
			@ -4,10 +4,11 @@
 | 
			
		|||
#define RRBB_H
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef short slice_t;
 | 
			
		||||
#define FASTER13 1		// Don't pack 8 samples per byte.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef RRBB_C
 | 
			
		||||
//typedef short slice_t;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Maximum size (in bytes) of an AX.25 frame including the 2 octet FCS. 
 | 
			
		||||
| 
						 | 
				
			
			@ -23,13 +24,14 @@ typedef short slice_t;
 | 
			
		|||
 | 
			
		||||
#define MAX_NUM_BITS (MAX_FRAME_LEN * 8 * 6 / 5)
 | 
			
		||||
 | 
			
		||||
#define SOI 32
 | 
			
		||||
 | 
			
		||||
typedef struct rrbb_s {
 | 
			
		||||
	int magic1;
 | 
			
		||||
	struct rrbb_s* nextp;	/* Next pointer to maintain a queue. */
 | 
			
		||||
 | 
			
		||||
	int chan;		/* Radio channel from which it was received. */
 | 
			
		||||
	int subchan;		/* Which modem when more than one per channel. */
 | 
			
		||||
	int slice;		/* Which slicer. */
 | 
			
		||||
 | 
			
		||||
	alevel_t alevel;	/* Received audio level at time of frame capture. */
 | 
			
		||||
	unsigned int len;	/* Current number of samples in array. */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -37,38 +39,37 @@ typedef struct rrbb_s {
 | 
			
		|||
	int descram_state;	/* Descrambler state before first data bit of frame. */
 | 
			
		||||
	int prev_descram;	/* Previous descrambled bit. */
 | 
			
		||||
 | 
			
		||||
	unsigned int data[(MAX_NUM_BITS+SOI-1)/SOI];
 | 
			
		||||
	unsigned int computed_data[MAX_NUM_BITS];
 | 
			
		||||
	unsigned char fdata[MAX_NUM_BITS];
 | 
			
		||||
 | 
			
		||||
	int magic2;
 | 
			
		||||
} *rrbb_t;
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
/* Hide the implementation. */
 | 
			
		||||
 | 
			
		||||
typedef void *rrbb_t;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
rrbb_t rrbb_new (int chan, int subchan, int is_scrambled, int descram_state, int prev_descram);
 | 
			
		||||
rrbb_t rrbb_new (int chan, int subchan, int slice, int is_scrambled, int descram_state, int prev_descram);
 | 
			
		||||
 | 
			
		||||
void rrbb_clear (rrbb_t b, int is_scrambled, int descram_state, int prev_descram);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void rrbb_append_bit (rrbb_t b, int val);
 | 
			
		||||
static __attribute__((always_inline)) void rrbb_append_bit (rrbb_t b, const unsigned char val)
 | 
			
		||||
{
 | 
			
		||||
	if (b->len >= MAX_NUM_BITS) {
 | 
			
		||||
	  return;	/* Silently discard if full. */
 | 
			
		||||
	}
 | 
			
		||||
	b->fdata[b->len] = val;
 | 
			
		||||
	b->len++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static __attribute__((always_inline)) unsigned char rrbb_get_bit (const rrbb_t b, const int ind)
 | 
			
		||||
{
 | 
			
		||||
	return (b->fdata[ind]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void rrbb_chop8 (rrbb_t b);
 | 
			
		||||
 | 
			
		||||
int rrbb_get_len (rrbb_t b);
 | 
			
		||||
 | 
			
		||||
int rrbb_get_bit (rrbb_t b, unsigned int ind);
 | 
			
		||||
unsigned int rrbb_get_computed_bit (rrbb_t b, unsigned int ind);
 | 
			
		||||
int rrbb_compute_bits (rrbb_t b);
 | 
			
		||||
 | 
			
		||||
//void rrbb_flip_bit (rrbb_t b, unsigned int ind);
 | 
			
		||||
 | 
			
		||||
void rrbb_delete (rrbb_t b);
 | 
			
		||||
| 
						 | 
				
			
			@ -78,6 +79,7 @@ rrbb_t rrbb_get_nextp (rrbb_t b);
 | 
			
		|||
 | 
			
		||||
int rrbb_get_chan (rrbb_t b);
 | 
			
		||||
int rrbb_get_subchan (rrbb_t b);
 | 
			
		||||
int rrbb_get_slice (rrbb_t b);
 | 
			
		||||
 | 
			
		||||
void rrbb_set_audio_level (rrbb_t b, alevel_t alevel);
 | 
			
		||||
alevel_t rrbb_get_audio_level (rrbb_t b);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
#
 | 
			
		||||
# Sample configuration for SDR read-only IGate.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# We might not have an audio output device so set to null.
 | 
			
		||||
# We will override the input half on the command line.
 | 
			
		||||
ADEVICE null null
 | 
			
		||||
CHANNEL 0
 | 
			
		||||
MYCALL xxx
 | 
			
		||||
 | 
			
		||||
# First you need to specify the name of a Tier 2 server.  
 | 
			
		||||
# The current preferred way is to use one of these regional rotate addresses:
 | 
			
		||||
 | 
			
		||||
#	noam.aprs2.net 		- for North America
 | 
			
		||||
#	soam.aprs2.net		- for South America
 | 
			
		||||
#	euro.aprs2.net		- for Europe and Africa
 | 
			
		||||
#	asia.aprs2.net 		- for Asia
 | 
			
		||||
#	aunz.aprs2.net		- for Oceania 
 | 
			
		||||
 | 
			
		||||
IGSERVER noam.aprs2.net
 | 
			
		||||
 | 
			
		||||
# You also need to specify your login name and passcode. 
 | 
			
		||||
# Contact the author if you can't figure out how to generate the passcode.
 | 
			
		||||
 
 | 
			
		||||
IGLOGIN xxx 123456
 | 
			
		||||
 | 
			
		||||
# That's all you need for a receive only IGate which relays
 | 
			
		||||
# messages from the local radio channel to the global servers.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										92
									
								
								server.c
								
								
								
								
							
							
						
						
									
										92
									
								
								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        !"<C.tqlHrq!!_
 | 
			
		||||
		*/
 | 
			
		||||
	      {
 | 
			
		||||
	      
 | 
			
		||||
		packet_t pp;
 | 
			
		||||
		int pid = cmd.datakind_hi & 0xff;
 | 
			
		||||
		int pid = cmd.hdr.kind_hi & 0xff;
 | 
			
		||||
		(void)(pid);
 | 
			
		||||
			/* The AGW protocol spec says, */
 | 
			
		||||
			/* "AX.25 PID 0x00 or 0xF0 for AX.25 0xCF NETROM and others" */
 | 
			
		||||
 | 
			
		||||
			/* BUG: In theory, the AX.25 PID octet should be set from this. */
 | 
			
		||||
			/* All examples seen (above) have 0. */
 | 
			
		||||
			/* The AX.25 protocol spec doesn't list 0 as a valid value. */
 | 
			
		||||
			/* We always send 0xf0, meaning no layer 3. */
 | 
			
		||||
			/* Maybe we should have an ax25_set_pid function for cases when */
 | 
			
		||||
			/* it is neither 0 nor 0xf0. */
 | 
			
		||||
 | 
			
		||||
		This is not right.
 | 
			
		||||
		It needs to be more like "V" Transmit UI data frame
 | 
			
		||||
		except there are no digipeaters involved.
 | 
			
		||||
	      	char stemp[AX25_MAX_PACKET_LEN];
 | 
			
		||||
		packet_t pp;
 | 
			
		||||
 | 
			
		||||
		pp = ax25_from_frame ((unsigned char *)cmd.data, cmd.hdr.data_len, -1);
 | 
			
		||||
	      	strlcpy (stemp, cmd.hdr.call_from, sizeof(stemp));
 | 
			
		||||
	      	strlcat (stemp, ">", sizeof(stemp));
 | 
			
		||||
	      	strlcat (stemp, cmd.hdr.call_to, sizeof(stemp));
 | 
			
		||||
 | 
			
		||||
		if (pp != NULL) {
 | 
			
		||||
		  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  */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										237
									
								
								symbols-new.txt
								
								
								
								
							
							
						
						
									
										237
									
								
								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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								symbols.c
								
								
								
								
							
							
						
						
									
										14
									
								
								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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										31
									
								
								symbolsX.txt
								
								
								
								
							
							
						
						
									
										31
									
								
								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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										380
									
								
								telemetry.c
								
								
								
								
							
							
						
						
									
										380
									
								
								telemetry.c
								
								
								
								
							| 
						 | 
				
			
			@ -57,10 +57,6 @@
 | 
			
		|||
#include <math.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
 | 
			
		||||
#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.&^<A!.", result);
 | 
			
		||||
	telemetry_data_base91 ("M0XER-3", "cNOv'C?=!-", result);
 | 
			
		||||
	telemetry_data_base91 ("M0XER-3", "n0RS(:>b!+", result);
 | 
			
		||||
	telemetry_data_base91 ("M0XER-3", "x&G=!(8s!,", result);
 | 
			
		||||
	telemetry_data_base91 ("M0XER-3", "DyR.&^<A!.", result, sizeof(result));
 | 
			
		||||
 | 
			
		||||
	if (strcmp(result, "10mW research balloon: Seq=3273, Vbat=4.472 V, Vsolar=0.516 V, Temp=-24.3 C, Sat=13") != 0 ||
 | 
			
		||||
	    strcmp(comment, "") != 0) {
 | 
			
		||||
	  errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 401\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	telemetry_data_base91 ("M0XER-3", "cNOv'C?=!-", result, sizeof(result));
 | 
			
		||||
 | 
			
		||||
	if (strcmp(result, "10mW research balloon: Seq=6051, Vbat=4.271 V, Vsolar=0.580 V, Temp=2.6 C, Sat=12") != 0 ||
 | 
			
		||||
	    strcmp(comment, "") != 0) {
 | 
			
		||||
	  errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 402\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	telemetry_data_base91 ("M0XER-3", "n0RS(:>b!+", result, sizeof(result));
 | 
			
		||||
 | 
			
		||||
	if (strcmp(result, "10mW research balloon: Seq=7022, Vbat=4.509 V, Vsolar=0.662 V, Temp=-2.8 C, Sat=10") != 0 ||
 | 
			
		||||
	    strcmp(comment, "") != 0) {
 | 
			
		||||
	  errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 403\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	telemetry_data_base91 ("M0XER-3", "x&G=!(8s!,", result, sizeof(result));
 | 
			
		||||
 | 
			
		||||
	if (strcmp(result, "10mW research balloon: Seq=7922, Vbat=3.486 V, Vsolar=0.007 V, Temp=-55.7 C, Sat=11") != 0 ||
 | 
			
		||||
	    strcmp(comment, "") != 0) {
 | 
			
		||||
	  errors++; text_color_set(DW_COLOR_ERROR); dw_printf ("Wrong result, test 404\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	// TODO: Should return success/fail so visual inspection is not needed. 
 | 
			
		||||
/* final score. */
 | 
			
		||||
 | 
			
		||||
	exit (0);
 | 
			
		||||
	if (errors != 0) {
 | 
			
		||||
	  text_color_set (DW_COLOR_ERROR);
 | 
			
		||||
	  dw_printf ("\nTEST FAILED with %d errors.\n", errors);
 | 
			
		||||
	  exit (EXIT_FAILURE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	text_color_set (DW_COLOR_REC);
 | 
			
		||||
	dw_printf ("\nTEST WAS SUCCESSFUL.\n");
 | 
			
		||||
	exit (EXIT_SUCCESS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										29
									
								
								tocalls.txt
								
								
								
								
							
							
						
						
									
										29
									
								
								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]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										395
									
								
								tt_text.c
								
								
								
								
							
							
						
						
									
										395
									
								
								tt_text.c
								
								
								
								
							| 
						 | 
				
			
			@ -162,6 +162,7 @@ static const char grid[10][10][3] =
 | 
			
		|||
#include <assert.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
 | 
			
		||||
#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 */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										20
									
								
								tt_text.h
								
								
								
								
							
							
						
						
									
										20
									
								
								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 */
 | 
			
		||||
| 
						 | 
				
			
			@ -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));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										10
									
								
								ttcalc.c
								
								
								
								
							
							
						
						
									
										10
									
								
								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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										89
									
								
								walk96.c
								
								
								
								
							
							
						
						
									
										89
									
								
								walk96.c
								
								
								
								
							| 
						 | 
				
			
			@ -32,47 +32,43 @@
 | 
			
		|||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#if __WIN32__
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#else
 | 
			
		||||
#define __USE_XOPEN2KXSI 1
 | 
			
		||||
#define __USE_XOPEN 1
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <termios.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <sys/errno.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
#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<HOWLONG; n++) {
 | 
			
		||||
 | 
			
		||||
	  dwgps_info_t info;
 | 
			
		||||
	  dwfix_t fix;
 | 
			
		||||
 | 
			
		||||
	  fix = dwgps_read (&info);
 | 
			
		||||
 | 
			
		||||
	  if (fix > DWFIX_2D) {
 | 
			
		||||
	    walk96 (fix, info.dlat, info.dlon, info.speed_knots, info.track, info.altitude);
 | 
			
		||||
	  }
 | 
			
		||||
	  else if (fix < 0) {
 | 
			
		||||
	    text_color_set (DW_COLOR_ERROR);
 | 
			
		||||
            dw_printf ("Can't communicate with GPS receiver.\n");
 | 
			
		||||
	    exit (EXIT_FAILURE);
 | 
			
		||||
	  }
 | 
			
		||||
	  else  {
 | 
			
		||||
	    text_color_set (DW_COLOR_ERROR);
 | 
			
		||||
            dw_printf ("GPS fix not available.\n");
 | 
			
		||||
	  }
 | 
			
		||||
	  SLEEP_SEC(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	SLEEP_SEC(20);
 | 
			
		||||
 | 
			
		||||
	// Exit out of KISS mode.
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										20
									
								
								xmit.c
								
								
								
								
							
							
						
						
									
										20
									
								
								xmit.c
								
								
								
								
							| 
						 | 
				
			
			@ -59,13 +59,6 @@
 | 
			
		|||
#include <math.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
//#include <sys/time.h>
 | 
			
		||||
//#include <time.h>
 | 
			
		||||
 | 
			
		||||
//#if __WIN32__
 | 
			
		||||
//#include <windows.h>
 | 
			
		||||
//#endif
 | 
			
		||||
 | 
			
		||||
#include "direwolf.h"
 | 
			
		||||
#include "ax25_pad.h"
 | 
			
		||||
#include "textcolor.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -296,7 +289,6 @@ void xmit_init (struct audio_s *p_modem, int debug_xmit_packet)
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*-------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * Name:        xmit_set_txdelay
 | 
			
		||||
| 
						 | 
				
			
			@ -449,7 +441,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) {
 | 
			
		||||
| 
						 | 
				
			
			@ -613,6 +605,7 @@ static void xmit_ax25_frames (int c, int p, packet_t pp)
 | 
			
		|||
	dw_printf ("%s", stemp);			/* stations followed by : */
 | 
			
		||||
	ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp));
 | 
			
		||||
	dw_printf ("\n");
 | 
			
		||||
	(void)ax25_check_addresses (pp);
 | 
			
		||||
 | 
			
		||||
/* Optional hex dump of packet. */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -676,6 +669,7 @@ static void xmit_ax25_frames (int c, int p, packet_t pp)
 | 
			
		|||
	 dw_printf ("%s", stemp);			/* stations followed by : */
 | 
			
		||||
	 ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp));
 | 
			
		||||
	 dw_printf ("\n");
 | 
			
		||||
	 (void)ax25_check_addresses (pp);
 | 
			
		||||
 | 
			
		||||
	 if (g_debug_xmit_packet) {
 | 
			
		||||
	    text_color_set(DW_COLOR_DEBUG);
 | 
			
		||||
| 
						 | 
				
			
			@ -852,16 +846,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 +872,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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue