Version 1.1

modified:   .gitattributes
	modified:   APRStt-Implementation-Notes.pdf
	modified:   CHANGES.txt
	modified:   Makefile.linux
	modified:   Makefile.win
	new file:   Raspberry-Pi-APRS-Tracker.pdf
	modified:   Raspberry-Pi-APRS.pdf
	modified:   User-Guide.pdf
	modified:   aclients.c
	modified:   aprs_tt.c
	modified:   aprs_tt.h
	modified:   atest.c
	modified:   audio.c
	modified:   audio.h
	modified:   audio_win.c
	modified:   ax25_pad.c
	modified:   ax25_pad.h
	modified:   beacon.c
	modified:   beacon.h
	modified:   config.c
	modified:   config.h
	modified:   decode_aprs.c
	modified:   decode_aprs.h
	modified:   demod_afsk.c
	modified:   digipeater.c
	modified:   digipeater.h
	modified:   direwolf.c
	modified:   direwolf.conf
	modified:   direwolf.h
	modified:   dsp.c
	modified:   dwgps.c
	modified:   encode_aprs.c
	modified:   encode_aprs.h
	modified:   fsk_demod_state.h
	new file:   grm_sym.h
	modified:   hdlc_rec.c
	modified:   hdlc_rec2.c
	modified:   hdlc_rec2.h
	modified:   kiss.c
	modified:   kiss_frame.c
	modified:   kiss_frame.h
	modified:   kissnet.c
	modified:   latlong.c
	modified:   latlong.h
	new file:   log.c
	new file:   log.h
	new file:   log2gpx.c
	new file:   mgn_icon.h
	modified:   misc/README-dire-wolf.txt
	modified:   multi_modem.c
	new file:   nmea.c
	new file:   nmea.h
	modified:   ptt.c
	modified:   rdq.c
	modified:   regex/README-dire-wolf.txt
	new file:   rpack.h
	modified:   rrbb.c
	modified:   rrbb.h
	modified:   server.c
	modified:   symbols-new.txt
	modified:   symbols.c
	modified:   symbolsX.txt
	new file:   telemetry.c
	new file:   telemetry.h
	modified:   textcolor.c
	modified:   tocalls.txt
	modified:   utm/README.txt
	modified:   version.h
	modified:   xmit.c
	modified:   xmit.h
This commit is contained in:
WB2OSZ 2015-07-26 21:05:48 -04:00
parent 8978f2de6c
commit b5d1d1c3dd
70 changed files with 9335 additions and 2252 deletions

15
.gitattributes vendored
View File

@ -15,3 +15,18 @@
*.PDF diff=astextplain *.PDF diff=astextplain
*.rtf diff=astextplain *.rtf diff=astextplain
*.RTF diff=astextplain *.RTF diff=astextplain
# My rules to remove any doubt
*.c text
*.cpp text
*.h text
*.pl text
Makefile* text
*.txt text
*.desktop text
*.conf text
*.rc text
*.ico binary
*.png binary

Binary file not shown.

View File

@ -3,6 +3,78 @@ Revision history
---------------- ----------------
-----------
Version 1.1 -- December 2014
-----------
* Changes since beta test version.
It is still highly recommended, but no longer mandatory, that
beaconing be enabled for digipeating to work.
* Known problems.
Sometimes kissattach fails to connect with "direwolf -p".
The User Guide and Raspberry Pi APRS document have a couple work-arounds.
-----------
Version 1.1 -- Beta Test 1 -- November 2014
-----------
* New Features:
Logging of received packets and utility to convert log file
into GPX format.
AGW network port formerly allowed only one connection at a
time. It can now accept 3 client applications at the same time.
(Same has not yet been done for network KISS port.)
Frequency / offset / tone standard formats are now recognized.
Non-standard attempts, in the comment, are often detected and
a message suggests the correct format.
Telemetry is now recognized. Messages are printed for
usage that does not adhere to the published standard.
Tracker function transmits location from GPS position.
New configuration file options: TBEACON and SMARTBEACONING.
(For Linux only. Warning - has not been well tested.)
Experimental packet regeneration feature for HF use.
Will be documented later if proves to be useful...
Several enhancements for trying to fix incorrect CRC.
- Additional types of attempts to fix a bad CRC.
- Optimized code to reduce execution time.
- Improved detection of duplicate packets from different fixup attempts.
- Set limit on number of packets in fix up later queue.
Beacon positions can be specified in either latitude / longitude
or UTM coordinates.
* Bugs fixed:
For Windows version, maximum serial port was COM9.
It is now possible to use COM10 and higher.
Fixed issue with KISS protocol decoder state that showed up
only with "binary" data in packets (e.g. RMS Express).
An extra 00 byte was being appended to packets from AGW
network protocol 'K' messages.
Invalid data from an AGW client application could cause an
application crash.
OSS (audio interface for non-Linux versions of Unix) should
be better now.
----------- -----------
Version 1.0a May 2014 Version 1.0a May 2014
----------- -----------
@ -36,6 +108,13 @@ Improved support for UTF-8 character set.
Improved troubleshooting display for APRStt macros. Improved troubleshooting display for APRStt macros.
In earlier versions, the DTMF decoder was always active because it
took a negligible amount of CPU time. Unfortunately this sometimes
resulted in too many false positives from some other types of digital
transmissions heard on HF. Starting in version 1.0, the DTMF decoder
is enabled only when the APRStt gateway is configured.
----------- -----------
@ -107,7 +186,7 @@ APRStt-Implementation-Notes.pdf
----------- -----------
Version 0.6 Version 0.6 February 2013
----------- -----------
@ -152,7 +231,7 @@ run this in another window:
----------- -----------
Version 0.5 Version 0.5 March 2012
----------- -----------
@ -160,7 +239,7 @@ More error checking and messages for invalid APRS data.
----------- -----------
Version 0.4 Version 0.4 September 2011
----------- -----------
First general availability. First general availability.

View File

@ -2,7 +2,7 @@
# Makefile for Linux version of Dire Wolf. # Makefile for Linux version of Dire Wolf.
# #
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx
CC = gcc CC = gcc
@ -81,18 +81,30 @@ CC = gcc
arch := $(shell echo | gcc -E -dM - | grep __i386__) arch := $(shell echo | gcc -E -dM - | grep __i386__)
ifneq ($(arch),) ifneq ($(arch),)
CFLAGS := -DUSE_ALSA -O3 -march=pentium3 -pthread # You might see improvement with -march fine tuned to your hardware.
# Probably should keep pentium3 if you will be redistributing binaries
# to other people.
CFLAGS := -O3 -march=pentium3 -pthread -Iutm
else else
CFLAGS := -DUSE_ALSA -O3 -pthread CFLAGS := -O3 -pthread -Iutm
endif endif
# Uncomment following lines to enable GPS interface.
# DO NOT USE THIS. Still needs more work. # If you want to use OSS (for FreeBSD, OpenBSD) instead of
# ALSA (for Linux), comment out the two lines below.
CFLAGS += -DUSE_ALSA
LDLIBS += -lasound
# Uncomment following lines to enable GPS interface & tracker function.
#CFLAGS += -DENABLE_GPS #CFLAGS += -DENABLE_GPS
#LDLIBS += -lgps #LDLIBS += -lgps
# Name of current directory. # Name of current directory.
# Used to generate zip file name for distribution. # Used to generate zip file name for distribution.
@ -107,9 +119,9 @@ direwolf : direwolf.o config.o demod.o dsp.o demod_afsk.o demod_9600.o hdlc_rec
decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \ decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
gen_tone.o audio.o digipeater.o dedupe.o tq.o xmit.o \ gen_tone.o audio.o digipeater.o dedupe.o tq.o xmit.o \
ptt.o beacon.o dwgps.o encode_aprs.o latlong.o encode_aprs.o latlong.o textcolor.o \ ptt.o beacon.o dwgps.o encode_aprs.o latlong.o encode_aprs.o latlong.o textcolor.o \
dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o \ dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o log.o telemetry.o \
utm.a utm.a
$(CC) $(CFLAGS) -o $@ $^ -lpthread -lrt -lasound $(LDLIBS) -lm $(CC) $(CFLAGS) -o $@ $^ -lpthread -lrt $(LDLIBS) -lm
# Optimization for slow processors. # Optimization for slow processors.
@ -132,40 +144,51 @@ LatLong-UTMconversion.o : utm/LatLong-UTMconversion.c
$(CC) $(CFLAGS) -c -o $@ $^ $(CC) $(CFLAGS) -c -o $@ $^
# Optional install step. # Optional installation into /usr/local/...
# Needs to be run as root or with sudo.
# TODO: Review file locations. # TODO: Review file locations.
# TODO: Handle Linux variations correctly.
# The Raspberry Pi has ~/Desktop but Ubuntu does not.
# For now, just put reference to it at the end so only last step fails.
install : direwolf decode_aprs tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png direwolf.desktop install : direwolf decode_aprs tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png direwolf.desktop
sudo install direwolf /usr/local/bin install direwolf /usr/local/bin
sudo install decode_aprs /usr/local/bin install decode_aprs /usr/local/bin
sudo install text2tt /usr/local/bin install text2tt /usr/local/bin
sudo install tt2text /usr/local/bin install tt2text /usr/local/bin
sudo install ll2utm /usr/local/bin install ll2utm /usr/local/bin
sudo install utm2ll /usr/local/bin install utm2ll /usr/local/bin
sudo install aclients /usr/local/bin install aclients /usr/local/bin
sudo install -D --mode=644 tocalls.txt /usr/share/direwolf/tocalls.txt install log2gpx /usr/local/bin
sudo install -D --mode=644 symbols-new.txt /usr/share/direwolf/symbols-new.txt install -D --mode=644 tocalls.txt /usr/share/direwolf/tocalls.txt
sudo install -D --mode=644 symbolsX.txt /usr/share/direwolf/symbolsX.txt install -D --mode=644 symbols-new.txt /usr/share/direwolf/symbols-new.txt
sudo install -D --mode=644 dw-icon.png /usr/share/direwolf/dw-icon.png install -D --mode=644 symbolsX.txt /usr/share/direwolf/symbolsX.txt
sudo install -D --mode=644 direwolf.desktop /usr/share/applications/direwolf.desktop install -D --mode=644 dw-icon.png /usr/share/direwolf/dw-icon.png
cp direwolf.conf ~ install -D --mode=644 direwolf.desktop /usr/share/applications/direwolf.desktop
install -D --mode=644 CHANGES.txt /usr/local/share/doc/direwolf/CHANGES.txt
install -D --mode=644 LICENSE-dire-wolf.txt /usr/local/share/doc/direwolf/LICENSE-dire-wolf.txt
install -D --mode=644 LICENSE-other.txt /usr/local/share/doc/direwolf/LICENSE-other.txt
install -D --mode=644 User-Guide.pdf /usr/local/share/doc/direwolf/User-Guide.pdf
install -D --mode=644 Raspberry-Pi-APRS.pdf /usr/local/share/doc/direwolf/Raspberry-Pi-APRS.pdf
install -D --mode=644 Raspberry-Pi-APRS-Tracker.pdf /usr/local/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf
install -D --mode=644 APRStt-Implementation-Notes.pdf /usr/local/share/doc/direwolf/APRStt-Implementation-Notes.pdf
install -D --mode=644 Quick-Start-Guide-Windows.pdf /usr/local/share/doc/direwolf/Quick-Start-Guide-Windows.pdf
# The Raspberry Pi has ~/Desktop but Ubuntu does not.
# TODO: Handle Linux variations correctly.
install-rpi : dw-start.sh
cp dw-start.sh ~ cp dw-start.sh ~
sudo install -D --mode=644 CHANGES.txt /usr/local/share/doc/direwolf/CHANGES.txt
sudo install -D --mode=644 LICENSE-dire-wolf.txt /usr/local/share/doc/direwolf/LICENSE-dire-wolf.txt
sudo install -D --mode=644 LICENSE-other.txt /usr/local/share/doc/direwolf/LICENSE-other.txt
sudo install -D --mode=644 User-Guide.pdf /usr/local/share/doc/direwolf/User-Guide.pdf
sudo install -D --mode=644 Raspberry-Pi-APRS.pdf /usr/local/share/doc/direwolf/Raspberry-Pi-APRS.pdf
sudo install -D --mode=644 APRStt-Implementation-Notes.pdf /usr/local/share/doc/direwolf/APRStt-Implementation-Notes.pdf
sudo install -D --mode=644 Quick-Start-Guide-Windows.pdf /usr/local/share/doc/direwolf/Quick-Start-Guide-Windows.pdf
ln -f -s /usr/share/applications/direwolf.desktop ~/Desktop/direwolf.desktop ln -f -s /usr/share/applications/direwolf.desktop ~/Desktop/direwolf.desktop
install-conf : direwolf.conf
cp direwolf.conf ~
# Separate application to decode raw data. # Separate application to decode raw data.
decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c latlong.c log.c telemetry.o
$(CC) $(CFLAGS) -o decode_aprs -DTEST $^ -lm $(CC) $(CFLAGS) -o decode_aprs -DTEST $^ -lm
@ -182,23 +205,29 @@ tt2text : tt_text.c
# Convert between Latitude/Longitude and UTM coordinates. # Convert between Latitude/Longitude and UTM coordinates.
ll2utm : ll2utm.c utm.a ll2utm : ll2utm.c utm.a
$(CC) $(CFLAGS) -I utm -o $@ $^ -lm $(CC) $(CFLAGS) -o $@ $^ -lm
utm2ll : utm2ll.c utm.a utm2ll : utm2ll.c utm.a
$(CC) $(CFLAGS) -I utm -o $@ $^ -lm $(CC) $(CFLAGS) -o $@ $^ -lm
# Convert from log file to GPX.
log2gpx : log2gpx.c
$(CC) $(CFLAGS) -o $@ $^ -lm
# Test application to generate sound. # Test application to generate sound.
gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c textcolor.c gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c textcolor.c
$(CC) $(CFLAGS) -o $@ $^ -lasound -lm $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -lm
demod.o : tune.h demod.o : tune.h
demod_afsk.o : tune.h demod_afsk.o : tune.h
demod_9600.o : tune.h demod_9600.o : tune.h
testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o fcs_calc.c ax25_pad.c decode_aprs.c symbols.c tune.h textcolor.c testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \
fcs_calc.c ax25_pad.c decode_aprs.c telemetry.c latlong.c symbols.c tune.h textcolor.c
$(CC) $(CFLAGS) -o atest $^ -lm $(CC) $(CFLAGS) -o atest $^ -lm
./atest 02_Track_2.wav | grep "packets decoded in" > atest.out ./atest 02_Track_2.wav | grep "packets decoded in" > atest.out
@ -206,8 +235,9 @@ testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o
# Unit test for AFSK demodulator # Unit test for AFSK demodulator
atest : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c atest : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \
$(CC) $(CFLAGS) -o $@ $^ -lm fcs_calc.c ax25_pad.c decode_aprs.c telemetry.c latlong.c symbols.c textcolor.c
$(CC) $(CFLAGS) -o $@ $^ -lm -lrt
time ./atest ../direwolf-0.2/02_Track_2.wav time ./atest ../direwolf-0.2/02_Track_2.wav
# Unit test for inner digipeater algorithm # Unit test for inner digipeater algorithm
@ -233,6 +263,14 @@ udptest : udp_test.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec
./udptest ./udptest
# Unit test for telemetry decoding.
etest : telemetry.c ax25_pad.c fcs_calc.c textcolor.c misc.a regex.a
$(CC) $(CFLAGS) -o $@ $^ -lm -lrt
./etest
# Multiple AGWPE network or serial port clients to test TNCs side by side. # Multiple AGWPE network or serial port clients to test TNCs side by side.
aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.c aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.c
@ -241,7 +279,7 @@ aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.c
SRCS = direwolf.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c multi_modem.c fcs_calc.c ax25_pad.c decode_aprs.c symbols.c \ SRCS = direwolf.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c multi_modem.c fcs_calc.c ax25_pad.c decode_aprs.c symbols.c \
server.c kiss.c kissnet.c kiss_frame.c hdlc_send.c fcs_calc.c gen_tone.c audio.c \ server.c kiss.c kissnet.c kiss_frame.c hdlc_send.c fcs_calc.c gen_tone.c audio.c \
digipeater.c dedupe.c tq.c xmit.c beacon.c encode_aprs.c latlong.c encode_aprs.c latlong.c digipeater.c dedupe.c tq.c xmit.c beacon.c encode_aprs.c latlong.c encode_aprs.c latlong.c telemetry.c log.c
depend : $(SRCS) depend : $(SRCS)
@ -256,12 +294,12 @@ clean :
# Package it up for distribution. # Package it up for distribution.
dist-src : CHANGES.txt User-Guide.pdf Quick-Start-Guide-Windows.pdf Raspberry-Pi-APRS.pdf \ dist-src : CHANGES.txt User-Guide.pdf Quick-Start-Guide-Windows.pdf Raspberry-Pi-APRS.pdf \
direwolf.desktop dw-start.sh Raspberry-Pi-APRS-Tracker.pdf direwolf.desktop dw-start.sh
rm -f fsk_fast_filter.h rm -f fsk_fast_filter.h
echo " " > tune.h echo " " > tune.h
rm -f ../$z-src.zip rm -f ../$z-src.zip
(cd .. ; zip $z-src.zip $z/CHANGES.txt $z/LICENSE* \ (cd .. ; zip $z-src.zip $z/CHANGES.txt $z/LICENSE* \
$z/User-Guide.pdf $z/Quick-Start-Guide-Windows.pdf $z/Raspberry-Pi-APRS.pdf \ $z/User-Guide.pdf $z/Quick-Start-Guide-Windows.pdf $z/Raspberry-Pi-APRS.pdf Raspberry-Pi-APRS-Tracker.pdf \
$z/Makefile* $z/*.c $z/*.h $z/regex/* $z/misc/* $z/utm/* \ $z/Makefile* $z/*.c $z/*.h $z/regex/* $z/misc/* $z/utm/* \
$z/*.conf $z/tocalls.txt $z/symbols-new.txt $z/symbolsX.txt \ $z/*.conf $z/tocalls.txt $z/symbols-new.txt $z/symbolsX.txt \
$z/dw-icon.png $z/dw-icon.rc $z/dw-icon.ico \ $z/dw-icon.png $z/dw-icon.rc $z/dw-icon.ico \
@ -277,11 +315,25 @@ dist-src : CHANGES.txt User-Guide.pdf Quick-Start-Guide-Windows.pdf Raspberry-P
#Raspberry-Pi-APRS.pdf : Raspberry-Pi-APRS.docx #Raspberry-Pi-APRS.pdf : Raspberry-Pi-APRS.docx
# echo "***** Raspberry-Pi-APRS.pdf is out of date *****" # echo "***** Raspberry-Pi-APRS.pdf is out of date *****"
#Raspberry-Pi-APRS-Tracker.pdf : Raspberry-Pi-APRS-Tracker.docx
# echo "***** Raspberry-Pi-APRS-Tracker.pdf is out of date *****"
backup : backup :
mkdir /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"` mkdir /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"`
cp -r . /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"` cp -r . /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"`
#
# The locations below appear to be the most recent.
# The copy at http://www.aprs.org/tocalls.txt is out of date.
#
tocalls-symbols :
wget http://www.aprs.org/aprs11/tocalls.txt -O tocalls.txt
wget http://www.aprs.org/symbols/symbols-new.txt -O symbols-new.txt
wget http://www.aprs.org/symbols/symbolsX.txt -O symbolsX.txt
# #
# The following is updated by "make depend" # The following is updated by "make depend"
# #

View File

@ -16,7 +16,7 @@
# #
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx
# People say we need -mthreads option for threads to work properly. # People say we need -mthreads option for threads to work properly.
@ -25,8 +25,8 @@ all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients
#TODO: put -Ofast back in. #TODO: put -Ofast back in.
CC = gcc CC = gcc
#CFLAGS = -g -Wall -Ofast -march=pentium3 -msse -Iregex -mthreads -DUSE_REGEX_STATIC #CFLAGS = -g -Wall -Ofast -march=pentium3 -msse -Iregex -Iutm -mthreads -DUSE_REGEX_STATIC
CFLAGS = -g -Wall -march=pentium3 -msse -Iregex -mthreads -DUSE_REGEX_STATIC CFLAGS = -g -Wall -march=pentium3 -msse -Iregex -Iutm -mthreads -DUSE_REGEX_STATIC
AR = ar AR = ar
@ -77,7 +77,7 @@ direwolf : direwolf.o config.o demod.o dsp.o demod_afsk.o demod_9600.o hdlc_rec.
decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \ decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
gen_tone.o audio_win.o digipeater.o dedupe.o tq.o xmit.o \ gen_tone.o audio_win.o digipeater.o dedupe.o tq.o xmit.o \
ptt.o beacon.o dwgps.o encode_aprs.o latlong.o textcolor.o \ ptt.o beacon.o dwgps.o encode_aprs.o latlong.o textcolor.o \
dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o \ dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o log.o telemetry.o \
dw-icon.o regex.a misc.a utm.a dw-icon.o regex.a misc.a utm.a
$(CC) $(CFLAGS) -g -o $@ $^ -lwinmm -lws2_32 $(CC) $(CFLAGS) -g -o $@ $^ -lwinmm -lws2_32
@ -137,7 +137,7 @@ strcasestr.o : misc/strcasestr.c
# Separate application to decode raw data. # Separate application to decode raw data.
decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c regex.a misc.a decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c latlong.c log.o telemetry.o regex.a misc.a utm.a
$(CC) $(CFLAGS) -o decode_aprs -DTEST $^ $(CC) $(CFLAGS) -o decode_aprs -DTEST $^
@ -153,10 +153,16 @@ tt2text : tt_text.c
# Convert between Latitude/Longitude and UTM coordinates. # Convert between Latitude/Longitude and UTM coordinates.
ll2utm : ll2utm.c utm.a ll2utm : ll2utm.c utm.a
$(CC) $(CFLAGS) -I utm -o $@ $^ $(CC) $(CFLAGS) -o $@ $^
utm2ll : utm2ll.c utm.a utm2ll : utm2ll.c utm.a
$(CC) $(CFLAGS) -I utm -o $@ $^ $(CC) $(CFLAGS) -o $@ $^
# Convert from log file to GPX.
log2gpx : log2gpx.c misc/strsep.c misc/strtok_r.c
$(CC) $(CFLAGS) -o $@ $^
# Test application to generate sound. # Test application to generate sound.
@ -172,7 +178,7 @@ demod_afsk.o : tune.h
testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \ testagc : 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 symbols.c textcolor.c regex.a misc.a \ rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c telemetry.c regex.a misc.a \
fsk_demod_agc.h fsk_demod_agc.h
rm -f atest.exe rm -f atest.exe
$(CC) $(CFLAGS) -DNOFIX -o atest $^ $(CC) $(CFLAGS) -DNOFIX -o atest $^
@ -183,7 +189,7 @@ noisy3.wav : gen_packets
./gen_packets -B 300 -n 100 -o noisy3.wav ./gen_packets -B 300 -n 100 -o noisy3.wav
testagc3 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \ testagc3 : 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 symbols.c textcolor.c regex.a misc.a \ rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c telemetry.c regex.a misc.a \
tune.h tune.h
rm -f atest.exe rm -f atest.exe
$(CC) $(CFLAGS) -o atest $^ $(CC) $(CFLAGS) -o atest $^
@ -194,7 +200,7 @@ noisy96.wav : gen_packets
./gen_packets -B 9600 -n 100 -o noisy96.wav ./gen_packets -B 9600 -n 100 -o noisy96.wav
testagc9 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \ testagc9 : 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 symbols.c textcolor.c regex.a misc.a \ rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c telemetry.c regex.a misc.a \
tune.h tune.h
rm -f atest.exe rm -f atest.exe
$(CC) $(CFLAGS) -o atest $^ $(CC) $(CFLAGS) -o atest $^
@ -206,14 +212,14 @@ testagc9 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.
atest : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \ 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 symbols.c textcolor.c misc.a regex.a \ 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 fsk_fast_filter.h
$(CC) $(CFLAGS) -o $@ $^ $(CC) $(CFLAGS) -o $@ $^
echo " " > tune.h echo " " > tune.h
./atest ..\\direwolf-0.2\\02_Track_2.wav ./atest ..\\direwolf-0.2\\02_Track_2.wav
atest9 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \ 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 symbols.c textcolor.c misc.a regex.a \ 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 fsk_fast_filter.h
$(CC) $(CFLAGS) -o $@ $^ $(CC) $(CFLAGS) -o $@ $^
./atest9 -B 9600 ../walkabout9600.wav | grep "packets decoded in" >atest.out ./atest9 -B 9600 ../walkabout9600.wav | grep "packets decoded in" >atest.out
@ -247,6 +253,14 @@ udptest : udp_test.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec
./udptest ./udptest
# Unit test for telemetry decoding.
etest : telemetry.c ax25_pad.c fcs_calc.c textcolor.c misc.a regex.a
$(CC) $(CFLAGS) -DTEST -o $@ $^
./etest
# Multiple AGWPE network or serial port clients to test TNCs side by side. # Multiple AGWPE network or serial port clients to test TNCs side by side.
aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.c misc.a regex.a aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.c misc.a regex.a
@ -258,7 +272,7 @@ SRCS = direwolf.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c \
fcs_calc.c ax25_pad.c decode_aprs.c symbols.c \ fcs_calc.c ax25_pad.c decode_aprs.c symbols.c \
server.c kiss.c kissnet.c kiss_frame.c hdlc_send.c fcs_calc.c gen_tone.c audio_win.c \ server.c kiss.c kissnet.c kiss_frame.c hdlc_send.c fcs_calc.c gen_tone.c audio_win.c \
digipeater.c dedupe.c tq.c xmit.c beacon.c \ digipeater.c dedupe.c tq.c xmit.c beacon.c \
encode_aprs.c latlong.c \ encode_aprs.c latlong.c telemetry.c \
dtmf.c aprs_tt.c tt_text.c igate.c dtmf.c aprs_tt.c tt_text.c igate.c
@ -275,15 +289,15 @@ clean :
dist-win : direwolf.exe decode_aprs.exe CHANGES.txt User-Guide.pdf Quick-Start-Guide-Windows.pdf \ dist-win : direwolf.exe decode_aprs.exe CHANGES.txt User-Guide.pdf Quick-Start-Guide-Windows.pdf \
Raspberry-Pi-APRS.pdf APRStt-Implementation-Notes.pdf Raspberry-Pi-APRS.pdf Raspberry-Pi-APRS-Tracker.pdf APRStt-Implementation-Notes.pdf
rm -f ../$z-win.zip rm -f ../$z-win.zip
zip ../$z-win.zip CHANGES.txt User-Guide.pdf Quick-Start-Guide-Windows.pdf \ zip ../$z-win.zip CHANGES.txt User-Guide.pdf Quick-Start-Guide-Windows.pdf \
Raspberry-Pi-APRS.pdf APRStt-Implementation-Notes.pdf LICENSE* *.conf \ Raspberry-Pi-APRS.pdf Raspberry-Pi-APRS-Tracker.pdf APRStt-Implementation-Notes.pdf LICENSE* *.conf \
direwolf.exe decode_aprs.exe tocalls.txt symbols-new.txt symbolsX.txt \ direwolf.exe decode_aprs.exe tocalls.txt symbols-new.txt symbolsX.txt \
text2tt.exe tt2text.exe ll2utm.exe utm2ll.exe aclients.exe text2tt.exe tt2text.exe ll2utm.exe utm2ll.exe aclients.exe log2gpx.exe
dist-src : CHANGES.txt User-Guide.pdf Quick-Start-Guide-Windows.pdf Raspberry-Pi-APRS.pdf \ dist-src : CHANGES.txt User-Guide.pdf Quick-Start-Guide-Windows.pdf Raspberry-Pi-APRS.pdf \
APRStt-Implementation-Notes.pdf \ Raspberry-Pi-APRS-Tracker.pdf APRStt-Implementation-Notes.pdf \
direwolf.desktop dw-start.sh \ direwolf.desktop dw-start.sh \
tocalls.txt symbols-new.txt symbolsX.txt tocalls.txt symbols-new.txt symbolsX.txt
rm -f fsk_fast_filter.h rm -f fsk_fast_filter.h
@ -292,7 +306,7 @@ dist-src : CHANGES.txt User-Guide.pdf Quick-Start-Guide-Windows.pdf Raspberry-Pi
(cd .. ; zip $z-src.zip \ (cd .. ; zip $z-src.zip \
$z/CHANGES.txt $z/LICENSE* \ $z/CHANGES.txt $z/LICENSE* \
$z/User-Guide.pdf $z/Quick-Start-Guide-Windows.pdf \ $z/User-Guide.pdf $z/Quick-Start-Guide-Windows.pdf \
$z/Raspberry-Pi-APRS.pdf $z/APRStt-Implementation-Notes.pdf \ $z/Raspberry-Pi-APRS.pdf $z/Raspberry-Pi-APRS-Tracker.pdf $z/APRStt-Implementation-Notes.pdf \
$z/Makefile* $z/*.c $z/*.h $z/regex/* $z/misc/* $z/utm/* \ $z/Makefile* $z/*.c $z/*.h $z/regex/* $z/misc/* $z/utm/* \
$z/*.conf $z/tocalls.txt $z/symbols-new.txt $z/symbolsX.txt \ $z/*.conf $z/tocalls.txt $z/symbols-new.txt $z/symbolsX.txt \
$z/dw-icon.png $z/dw-icon.rc $z/dw-icon.ico \ $z/dw-icon.png $z/dw-icon.rc $z/dw-icon.ico \
@ -309,6 +323,10 @@ Quick-Start-Guide-Windows.pdf : Quick-Start-Guide-Windows.docx
Raspberry-Pi-APRS.pdf : Raspberry-Pi-APRS.docx Raspberry-Pi-APRS.pdf : Raspberry-Pi-APRS.docx
echo "***** Raspberry-Pi-APRS.pdf is out of date *****" echo "***** Raspberry-Pi-APRS.pdf is out of date *****"
Raspberry-Pi-APRS-Tracker.pdf : Raspberry-Pi-APRS-Tracker.docx
echo "***** Raspberry-Pi-APRS-Tracker.pdf is out of date *****"
APRStt-Implementation-Notes.pdf : APRStt-Implementation-Notes.docx APRStt-Implementation-Notes.pdf : APRStt-Implementation-Notes.docx
echo "***** APRStt-Implementation-Notes.pdf is out of date *****" echo "***** APRStt-Implementation-Notes.pdf is out of date *****"
@ -317,6 +335,16 @@ backup :
mkdir /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"` mkdir /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"`
cp -r . /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"` cp -r . /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"`
#
# The locations below appear to be the most recent.
# The copy at http://www.aprs.org/tocalls.txt is out of date.
#
tocalls-symbols :
wget http://www.aprs.org/aprs11/tocalls.txt -O tocalls.txt
wget http://www.aprs.org/symbols/symbols-new.txt -O symbols-new.txt
wget http://www.aprs.org/symbols/symbolsX.txt -O symbolsX.txt
# #
# The following is updated by "make depend" # The following is updated by "make depend"
# #

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -519,7 +519,7 @@ static void * client_thread_net (void *arg)
#if __WIN32__ #if __WIN32__
send (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd), 0); send (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd), 0);
#else #else
(void)write (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd)); err = write (server_sock, (char*)(&mon_cmd), sizeof(mon_cmd));
#endif #endif
@ -649,6 +649,8 @@ static void * client_thread_serial (void *arg)
DCB dcb; DCB dcb;
int ok; int ok;
// Bug: Won't work for ports above COM9.
// http://support.microsoft.com/kb/115831
fd = CreateFile(port[my_index], GENERIC_READ | GENERIC_WRITE, fd = CreateFile(port[my_index], GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL); 0, NULL, OPEN_EXISTING, 0, NULL);
@ -671,6 +673,7 @@ static void * client_thread_serial (void *arg)
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx */ /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx */
dcb.DCBlength = sizeof(DCB);
dcb.BaudRate = 9600; dcb.BaudRate = 9600;
dcb.fBinary = 1; dcb.fBinary = 1;
dcb.fParity = 0; dcb.fParity = 0;

View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2013,2014 John Langner, WB2OSZ // Copyright (C) 2013, 2014 John Langner, WB2OSZ
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -32,6 +32,9 @@
* *
*---------------------------------------------------------------*/ *---------------------------------------------------------------*/
#define APRS_TT_C 1
// TODO: clean up terminolgy. // TODO: clean up terminolgy.
// "Message" has a specific meaning in APRS and this is not it. // "Message" has a specific meaning in APRS and this is not it.
// Touch Tone sequence might be appropriate. // Touch Tone sequence might be appropriate.
@ -872,6 +875,7 @@ static int parse_symbol (char *e)
* *
* Outputs: m_latitude * Outputs: m_latitude
* m_longitude * m_longitude
* m_dao
* *
* Returns: 0 for success or one of the TT_ERROR_... codes. * Returns: 0 for success or one of the TT_ERROR_... codes.
* *
@ -915,6 +919,9 @@ static int parse_location (char *e)
m_dao[3] = e[1]; /* Type of location. e.g. !TB6! */ m_dao[3] = e[1]; /* Type of location. e.g. !TB6! */
/* Will be changed by point types. */ /* Will be changed by point types. */
/* 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);
@ -1320,7 +1327,13 @@ static void raw_tt_data_to_app (int chan, char *msg)
* thru the multi modem duplicate processing. * thru the multi modem duplicate processing.
*/ */
if (pp != NULL) {
app_process_rec_packet (chan, -1, pp, -2, RETRY_NONE, "tt"); app_process_rec_packet (chan, -1, pp, -2, RETRY_NONE, "tt");
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Could not convert \"%s\" into APRS packet.\n", raw_tt_msg);
}
#endif #endif
} }

View File

@ -4,6 +4,7 @@
#ifndef APRS_TT_H #ifndef APRS_TT_H
#define APRS_TT_H 1 #define APRS_TT_H 1
/* /*
* For holding location format specifications from config file. * For holding location format specifications from config file.
* Same thing is also useful for macro definitions. * Same thing is also useful for macro definitions.
@ -52,7 +53,7 @@ struct ttloc_s {
}; };
/* /*
* Configuratin options for APRStt. * Configuration options for APRStt.
*/ */
#define TT_MAX_XMITS 10 #define TT_MAX_XMITS 10
@ -78,6 +79,8 @@ struct tt_config_s {
}; };
void aprs_tt_init (struct tt_config_s *p_config); void aprs_tt_init (struct tt_config_s *p_config);
void aprs_tt_button (int chan, char button); void aprs_tt_button (int chan, char button);
@ -95,6 +98,10 @@ void aprs_tt_button (int chan, char button);
#define TT_ERROR_NO_CALL 9 /* No call or object name included. */ #define TT_ERROR_NO_CALL 9 /* No call or object name included. */
#define APRSTT_LOC_DESC_LEN 32 /* Need at least 26 */
void aprs_tt_dao_to_desc (char *dao, char *str);
#endif #endif
/* end aprs_tt.h */ /* end aprs_tt.h */

64
atest.c
View File

@ -145,14 +145,64 @@ int main (int argc, char *argv[])
modem.samples_per_sec = DEFAULT_SAMPLES_PER_SEC; modem.samples_per_sec = DEFAULT_SAMPLES_PER_SEC;
modem.bits_per_sample = DEFAULT_BITS_PER_SAMPLE; modem.bits_per_sample = DEFAULT_BITS_PER_SAMPLE;
// Results v0.9:
//
// fix_bits = 0 971 packets, 69 sec
// fix_bits = SINGLE 990 64
// fix_bits = DOUBLE 992 65
// fix_bits = TRIPLE 992 67
// fix_bits = TWO_SEP 1004 476
// Essentially no difference in time for those with order N time.
// Time increases greatly for the one with order N^2 time.
// Results: version 1.1, decoder C, modem.fix_bits = RETRY_MAX - 1
//
// 971 NONE
// +19 SINGLE
// +2 DOUBLE
// +12 TWO_SEP
// +3 REMOVE_MANY
// ----
// 1007 Total in 1008 sec. More than twice as long as earlier version.
// Results: version 1.1, decoders ABC, modem.fix_bits = RETRY_MAX - 1
//
// 976 NONE
// +21 SINGLE
// +1 DOUBLE
// +22 TWO_SEP
// +1 MANY
// +3 REMOVE_MANY
// ----
// 1024 Total in 2042 sec.
// About 34 minutes of CPU time for a roughly 40 minute CD.
// Many computers wouldn't be able to keep up.
// The SINGLE and TWO_SEP techniques are the most effective.
// Should we reorder the enum values so that TWO_SEP
// comes after SINGLE? That way "FIX_BITS 2" would
// use the two most productive techniques and not waste
// time on the others. People with plenty of CPU power
// to spare can still specify larger numbers for the other
// techniques with less return on investment.
// TODO: tabulate results from atest2.c.txt
/* TODO: should have a command line option for this. */ /* TODO: should have a command line option for this. */
/* Results v0.9: 971/69, 990/64, 992/65, 992/67, 1004/476 */
modem.fix_bits = RETRY_NONE; modem.fix_bits = RETRY_NONE;
modem.fix_bits = RETRY_SINGLE; //modem.fix_bits = RETRY_SWAP_SINGLE;
modem.fix_bits = RETRY_DOUBLE; //modem.fix_bits = RETRY_SWAP_DOUBLE;
//modem.fix_bits = RETRY_TRIPLE; //modem.fix_bits = RETRY_SWAP_TRIPLE;
//modem.fix_bits = RETRY_TWO_SEP; //modem.fix_bits = RETRY_INSERT_DOUBLE;
modem.fix_bits = RETRY_SWAP_TWO_SEP;
//modem.fix_bits = RETRY_REMOVE_MANY;
//modem.fix_bits = RETRY_MAX - 1;
for (channel=0; channel<MAX_CHANS; channel++) { for (channel=0; channel<MAX_CHANS; channel++) {
@ -162,6 +212,7 @@ int main (int argc, char *argv[])
modem.space_freq[channel] = DEFAULT_SPACE_FREQ; modem.space_freq[channel] = DEFAULT_SPACE_FREQ;
modem.baud[channel] = DEFAULT_BAUD; modem.baud[channel] = DEFAULT_BAUD;
strcpy (modem.profiles[channel], "C"); strcpy (modem.profiles[channel], "C");
//strcpy (modem.profiles[channel], "ABC");
// temp // temp
// strcpy (modem.profiles[channel], "F"); // strcpy (modem.profiles[channel], "F");
modem.num_subchan[channel] = strlen(modem.profiles[channel]); modem.num_subchan[channel] = strlen(modem.profiles[channel]);
@ -334,7 +385,6 @@ int main (int argc, char *argv[])
/* process_rec_frame, below, is called. */ /* process_rec_frame, below, is called. */
} }
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
printf ("\n\n"); printf ("\n\n");
printf ("%d packets decoded in %d seconds.\n", packets_decoded, (int)(time(NULL) - start_time)); printf ("%d packets decoded in %d seconds.\n", packets_decoded, (int)(time(NULL) - start_time));
@ -415,7 +465,7 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, int alevel, ret
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
printf ("\n"); printf ("\n");
printf("DECODED[%d] ", packets_decoded );
if (h != AX25_SOURCE) { if (h != AX25_SOURCE) {
printf ("Digipeater "); printf ("Digipeater ");
} }

78
audio.c
View File

@ -50,15 +50,13 @@
* *
* http://www.alsa-project.org/main/index.php/Asoundrc * http://www.alsa-project.org/main/index.php/Asoundrc
* *
* Credits: Fabrice FAURE contributed code for the SDR UDP interface. * Credits: Release 1.0: Fabrice FAURE contributed code for the SDR UDP interface.
* *
* Discussion here: http://gqrx.dk/doc/streaming-audio-over-udp * Discussion here: http://gqrx.dk/doc/streaming-audio-over-udp
* *
* Release 1.1: Gabor Berczi provided fixes for the OSS code
* which had fallen into decay.
* *
* Future: Will probably rip out the OSS code.
* ALSA was added to Linux kernel 10 years ago.
* Cygwin doesn't have it but I see no reason to support Cygwin
* now that we have a native Windows version.
* *
*---------------------------------------------------------------*/ *---------------------------------------------------------------*/
@ -81,8 +79,16 @@
#if USE_ALSA #if USE_ALSA
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#else #else
#ifdef __OpenBSD__
#include <soundcard.h>
#else
#include <sys/soundcard.h> #include <sys/soundcard.h>
#endif #endif
#endif
#ifdef __FreeBSD__
#include <errno.h>
#endif
#include "direwolf.h" #include "direwolf.h"
#include "audio.h" #include "audio.h"
@ -101,6 +107,7 @@ static int set_alsa_params (snd_pcm_t *handle, struct audio_s *pa, char *name, c
//static void alsa_select_device (char *pick_dev, int direction, char *result); //static void alsa_select_device (char *pick_dev, int direction, char *result);
#else #else
static int set_oss_params (int fd, struct audio_s *pa);
static int oss_audio_device_fd = -1; /* Single device, both directions. */ static int oss_audio_device_fd = -1; /* Single device, both directions. */
#endif #endif
@ -180,11 +187,11 @@ int audio_open (struct audio_s *pa)
int err; int err;
int chan; int chan;
#if USE_ALSA
char audio_in_name[30]; char audio_in_name[30];
char audio_out_name[30]; char audio_out_name[30];
#if USE_ALSA
assert (audio_in_handle == NULL); assert (audio_in_handle == NULL);
assert (audio_out_handle == NULL); assert (audio_out_handle == NULL);
@ -234,11 +241,10 @@ int audio_open (struct audio_s *pa)
outbuf_ptr = NULL; outbuf_ptr = NULL;
outbuf_len = 0; outbuf_len = 0;
#if USE_ALSA
/* /*
* Determine the type of audio input. * Determine the type of audio input.
*/ */
audio_in_type = AUDIO_IN_TYPE_SOUNDCARD; audio_in_type = AUDIO_IN_TYPE_SOUNDCARD;
if (strcasecmp(pa->adevice_in, "stdin") == 0 || strcmp(pa->adevice_in, "-") == 0) { if (strcasecmp(pa->adevice_in, "stdin") == 0 || strcmp(pa->adevice_in, "-") == 0) {
@ -246,7 +252,7 @@ int audio_open (struct audio_s *pa)
/* Change - to stdin for readability. */ /* Change - to stdin for readability. */
strcpy (pa->adevice_in, "stdin"); strcpy (pa->adevice_in, "stdin");
} }
else if (strncasecmp(pa->adevice_in, "udp:", 4) == 0) { if (strncasecmp(pa->adevice_in, "udp:", 4) == 0) {
audio_in_type = AUDIO_IN_TYPE_SDR_UDP; audio_in_type = AUDIO_IN_TYPE_SDR_UDP;
/* Supply default port if none specified. */ /* Supply default port if none specified. */
if (strcasecmp(pa->adevice_in,"udp") == 0 || if (strcasecmp(pa->adevice_in,"udp") == 0 ||
@ -285,7 +291,7 @@ int audio_open (struct audio_s *pa)
* Soundcard - ALSA. * Soundcard - ALSA.
*/ */
case AUDIO_IN_TYPE_SOUNDCARD: case AUDIO_IN_TYPE_SOUNDCARD:
#if USE_ALSA
err = snd_pcm_open (&audio_in_handle, audio_in_name, SND_PCM_STREAM_CAPTURE, 0); err = snd_pcm_open (&audio_in_handle, audio_in_name, SND_PCM_STREAM_CAPTURE, 0);
if (err < 0) { if (err < 0) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
@ -296,6 +302,23 @@ int audio_open (struct audio_s *pa)
inbuf_size_in_bytes = set_alsa_params (audio_in_handle, pa, audio_in_name, "input"); inbuf_size_in_bytes = set_alsa_params (audio_in_handle, pa, audio_in_name, "input");
break; break;
#else // OSS
oss_audio_device_fd = open (pa->adevice_in, O_RDWR);
if (oss_audio_device_fd < 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("%s:\n", pa->adevice_in);
// sprintf (message, "Could not open audio device %s", pa->adevice_in);
// perror (message);
return (-1);
}
outbuf_size_in_bytes = inbuf_size_in_bytes = set_oss_params (oss_audio_device_fd, pa);
if (inbuf_size_in_bytes <= 0 || outbuf_size_in_bytes <= 0) {
return (-1);
}
#endif
/* /*
* UDP. * UDP.
@ -353,6 +376,8 @@ int audio_open (struct audio_s *pa)
/* /*
* Output device. Only "soundcard" is supported at this time. * Output device. Only "soundcard" is supported at this time.
*/ */
#if USE_ALSA
err = snd_pcm_open (&audio_out_handle, audio_out_name, SND_PCM_STREAM_PLAYBACK, 0); err = snd_pcm_open (&audio_out_handle, audio_out_name, SND_PCM_STREAM_PLAYBACK, 0);
if (err < 0) { if (err < 0) {
@ -368,34 +393,7 @@ int audio_open (struct audio_s *pa)
return (-1); return (-1);
} }
#endif
#else /* end of ALSA case */
#error OSS support will probably be removed. Complain if you still care about OSS.
oss_audio_device_fd = open (pa->adevice_in, O_RDWR);
if (oss_audio_device_fd < 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("%s:\n", pa->adevice_in);
sprintf (message, "Could not open audio device %s", pa->adevice_in);
perror (message);
return (-1);
}
outbuf_size_in_bytes = inbuf_size_in_bytes = set_oss_params (oss_audio_device_fd, pa);
if (inbuf_size_in_bytes <= 0 || outbuf_size_in_bytes <= 0) {
return (-1);
}
#endif /* end of OSS case */
/* /*
* Finally allocate buffer for each direction. * Finally allocate buffer for each direction.
@ -811,7 +809,7 @@ int audio_get (void)
this_time = time(NULL); this_time = time(NULL);
if (this_time >= last_time + duration) { if (this_time >= last_time + duration) {
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("\nPast %d seconds, %d audio samples, %d errors.\n\n", dw_printf ("\nPast %d seconds, %d audio samples processed, %d errors.\n\n",
duration, sample_count, error_count); duration, sample_count, error_count);
last_time = this_time; last_time = this_time;
sample_count = 0; sample_count = 0;

View File

@ -22,7 +22,8 @@
enum ptt_method_e { enum ptt_method_e {
PTT_METHOD_NONE, /* VOX or no transmit. */ PTT_METHOD_NONE, /* VOX or no transmit. */
PTT_METHOD_SERIAL, /* Serial port RTS or DTR. */ PTT_METHOD_SERIAL, /* Serial port RTS or DTR. */
PTT_METHOD_GPIO }; /* General purpos I/O. */ PTT_METHOD_GPIO, /* General purpose I/O, Linux only. */
PTT_METHOD_LPT }; /* Parallel printer port, Linux only. */
typedef enum ptt_method_e ptt_method_t; typedef enum ptt_method_e ptt_method_t;
@ -94,6 +95,9 @@ struct audio_s {
int ptt_gpio[MAX_CHANS]; /* GPIO number. */ int ptt_gpio[MAX_CHANS]; /* GPIO number. */
int ptt_lpt_bit[MAX_CHANS]; /* Bit number for parallel printer port. */
/* Bit 0 = pin 2, ..., bit 7 = pin 9. */
int ptt_invert[MAX_CHANS]; /* Invert the output. */ int ptt_invert[MAX_CHANS]; /* Invert the output. */
int slottime[MAX_CHANS]; /* Slot time in 10 mS units for persistance algorithm. */ int slottime[MAX_CHANS]; /* Slot time in 10 mS units for persistance algorithm. */
@ -153,7 +157,7 @@ struct audio_s {
#define DEFAULT_BITS_PER_SAMPLE 16 #define DEFAULT_BITS_PER_SAMPLE 16
#define DEFAULT_FIX_BITS RETRY_SINGLE #define DEFAULT_FIX_BITS RETRY_SWAP_SINGLE
/* /*
* Standard for AFSK on VHF FM. * Standard for AFSK on VHF FM.

View File

@ -744,7 +744,7 @@ int audio_get (void)
this_time = time(NULL); this_time = time(NULL);
if (this_time >= last_time + duration) { if (this_time >= last_time + duration) {
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("\nPast %d seconds, %d audio samples, %d errors.\n\n", dw_printf ("\nPast %d seconds, %d audio samples processed, %d errors.\n\n",
duration, sample_count, error_count); duration, sample_count, error_count);
last_time = this_time; last_time = this_time;
sample_count = 0; sample_count = 0;

File diff suppressed because it is too large Load Diff

View File

@ -49,7 +49,8 @@
* #define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * 7 + 2 + AX25_MAX_INFO_LEN) * #define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * 7 + 2 + AX25_MAX_INFO_LEN)
*/ */
/* the more general case. */ /* The more general case. */
/* An AX.25 frame can have a control byte and no protocol. */
#define AX25_MIN_PACKET_LEN ( 2 * 7 + 1 ) #define AX25_MIN_PACKET_LEN ( 2 * 7 + 1 )
@ -77,20 +78,17 @@ struct packet_s {
struct packet_s *nextp; /* Pointer to next in queue. */ struct packet_s *nextp; /* Pointer to next in queue. */
int num_addr; /* Number of elements used in two below. */ int num_addr; /* Number of addresses in frame. */
/* Range of 0 .. AX25_MAX_ADDRS. */ /* Range of AX25_MIN_ADDRS .. AX25_MAX_ADDRS for AX.25. */
/* It will be 0 if it doesn't look like AX.25. */
/* -1 is used temporarily at allocation to mean */
/* not determined yet. */
char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN];
/* Contains the address without the ssid. */
/* Why is it larger than 7? */
/* Messages from an IGate server can have longer */
/* addresses after qAC. Up to 9 observed so far. */
/* usual human readable form. e.g. WB20SZ-15 */
unsigned char ssid_etc[AX25_MAX_ADDRS]; /* SSID octet from each address. */
/* /*
* The 7th octet of each address contains:
*
* Bits: H R R SSID 0 * Bits: H R R SSID 0
* *
* H for digipeaters set to 0 intially. * H for digipeaters set to 0 intially.
@ -118,13 +116,12 @@ struct packet_s {
#define SSID_LAST_MASK 0x01 #define SSID_LAST_MASK 0x01
int the_rest_len; /* Frame length minus the address part. */ int frame_len; /* Frame length without CRC. */
unsigned char frame_data[AX25_MAX_PACKET_LEN+1];
/* Raw frame contents, without the CRC. */
unsigned char the_rest[2 + 3 + AX25_MAX_INFO_LEN + 1];
/* The rest after removing the addresses. */
/* Includes control, protocol ID, Information, */
/* and throw in one more for a character */
/* string nul terminator. */
int magic2; /* Will get stomped on if above overflows. */ int magic2; /* Will get stomped on if above overflows. */
}; };
@ -158,7 +155,8 @@ typedef struct packet_s *packet_t;
static inline int ax25_get_control_offset (packet_t this_p) static inline int ax25_get_control_offset (packet_t this_p)
{ {
return (0); //return (0);
return (this_p->num_addr*7);
} }
static inline int ax25_get_num_control (packet_t this_p) static inline int ax25_get_num_control (packet_t this_p)
@ -174,7 +172,7 @@ static inline int ax25_get_num_control (packet_t this_p)
static inline int ax25_get_pid_offset (packet_t this_p) static inline int ax25_get_pid_offset (packet_t this_p)
{ {
return (ax25_get_num_control(this_p)); return (ax25_get_control_offset (this_p) + ax25_get_num_control(this_p));
} }
static int ax25_get_num_pid (packet_t this_p) static int ax25_get_num_pid (packet_t this_p)
@ -182,12 +180,12 @@ static int ax25_get_num_pid (packet_t this_p)
int c; int c;
int pid; int pid;
c = this_p->the_rest[ax25_get_control_offset(this_p)]; c = this_p->frame_data[ax25_get_control_offset(this_p)];
if ( (c & 0x01) == 0 || /* I xxxx xxx0 */ if ( (c & 0x01) == 0 || /* I xxxx xxx0 */
c == 0x03 || c == 0x13) { /* UI 000x 0011 */ c == 0x03 || c == 0x13) { /* UI 000x 0011 */
pid = this_p->the_rest[ax25_get_pid_offset(this_p)]; pid = this_p->frame_data[ax25_get_pid_offset(this_p)];
if (pid == 0xff) { if (pid == 0xff) {
return (2); /* pid 1111 1111 means another follows. */ return (2); /* pid 1111 1111 means another follows. */
} }
@ -210,17 +208,20 @@ static int ax25_get_num_pid (packet_t this_p)
static inline int ax25_get_info_offset (packet_t this_p) static inline int ax25_get_info_offset (packet_t this_p)
{ {
return (ax25_get_num_control(this_p) + ax25_get_num_pid(this_p)); return (ax25_get_control_offset (this_p) + ax25_get_num_control(this_p) + ax25_get_num_pid(this_p));
} }
static int ax25_get_num_info (packet_t this_p) static int ax25_get_num_info (packet_t this_p)
{ {
int len; int len;
len = this_p->the_rest_len - ax25_get_num_control(this_p) - ax25_get_num_pid(this_p); /* assuming AX.25 frame. */
len = this_p->frame_len - this_p->num_addr * 7 - ax25_get_num_control(this_p) - ax25_get_num_pid(this_p);
if (len < 0) { if (len < 0) {
len = 0; /* print error? */ len = 0; /* print error? */
} }
return (len); return (len);
} }
@ -278,6 +279,8 @@ extern void ax25_format_addrs (packet_t pp, char *);
extern int ax25_pack (packet_t pp, unsigned char result[AX25_MAX_PACKET_LEN]); extern int ax25_pack (packet_t pp, unsigned char result[AX25_MAX_PACKET_LEN]);
extern void ax25_hex_dump (packet_t this_p);
extern int ax25_is_aprs (packet_t pp); extern int ax25_is_aprs (packet_t pp);
extern int ax25_get_control (packet_t this_p); extern int ax25_get_control (packet_t this_p);

159
beacon.c
View File

@ -57,6 +57,7 @@
#include "beacon.h" #include "beacon.h"
#include "latlong.h" #include "latlong.h"
#include "dwgps.h" #include "dwgps.h"
#include "log.h"
@ -83,6 +84,16 @@ static unsigned __stdcall beacon_thread (void *arg);
static void * beacon_thread (void *arg); static void * beacon_thread (void *arg);
#endif #endif
static int g_tracker_debug_level = 0; // 1 for data from gps.
// 2 + Smart Beaconing logic.
// 3 + Send transmissions to log file.
void beacon_tracker_set_debug (int level)
{
g_tracker_debug_level = level;
}
/*------------------------------------------------------------------- /*-------------------------------------------------------------------
@ -141,7 +152,7 @@ void beacon_init (struct misc_config_s *pconfig, struct digi_config_s *pdigi)
* table entry should be ignored later on. * table entry should be ignored later on.
*/ */
for (j=0; j<g_misc_config_p->num_beacons; j++) { for (j=0; j<g_misc_config_p->num_beacons; j++) {
int chan = g_misc_config_p->beacon[j].chan; int chan = g_misc_config_p->beacon[j].sendto_chan;
if (chan < 0) chan = 0; /* For IGate, use channel 0 call. */ if (chan < 0) chan = 0; /* For IGate, use channel 0 call. */
@ -177,7 +188,7 @@ void beacon_init (struct misc_config_s *pconfig, struct digi_config_s *pdigi)
case BEACON_TRACKER: case BEACON_TRACKER:
#if defined(GPS_ENABLED) || defined(DEBUG_SIM) #if defined(ENABLE_GPS) || defined(DEBUG_SIM)
g_using_gps++; g_using_gps++;
#else #else
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
@ -322,8 +333,6 @@ void beacon_init (struct misc_config_s *pconfig, struct digi_config_s *pdigi)
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
#define KNOTS_TO_MPH 1.150779
#define MIN(x,y) ((x) < (y) ? (x) : (y)) #define MIN(x,y) ((x) < (y) ? (x) : (y))
@ -360,7 +369,8 @@ static void * beacon_thread (void *arg)
float my_course = 0; /* degrees */ float my_course = 0; /* degrees */
float my_speed_knots = 0; float my_speed_knots = 0;
float my_speed_mph = 0; float my_speed_mph = 0;
float my_alt = 0; /* meters */ float my_alt_m = G_UNKNOWN; /* meters */
int my_alt_ft = G_UNKNOWN;
/* /*
* SmartBeaconing state. * SmartBeaconing state.
@ -439,15 +449,40 @@ static void * beacon_thread (void *arg)
fprintf (stderr, "Can't read /tmp/cs.\n"); fprintf (stderr, "Can't read /tmp/cs.\n");
} }
fix = 3; fix = 3;
my_speed_mph = KNOTS_TO_MPH * my_speed_knots; my_speed_mph = DW_KNOTS_TO_MPH(my_speed_knots);
my_lat = 42.99; my_lat = 42.99;
my_lon = 71.99; my_lon = 71.99;
my_alt = 100; my_alt_m = 100;
#else #else
if (g_using_gps) { if (g_using_gps) {
fix = dwgps_read (&my_lat, &my_lon, &my_speed_knots, &my_course, &my_alt); fix = dwgps_read (&my_lat, &my_lon, &my_speed_knots, &my_course, &my_alt_m);
my_speed_mph = KNOTS_TO_MPH * my_speed_knots; my_speed_mph = DW_KNOTS_TO_MPH(my_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);
}
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);
}
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. */ /* Don't complain here for no fix. */
/* Possibly at the point where about to transmit. */ /* Possibly at the point where about to transmit. */
@ -461,13 +496,25 @@ static void * beacon_thread (void *arg)
if (my_speed_mph > g_misc_config_p->sb_fast_speed) { if (my_speed_mph > g_misc_config_p->sb_fast_speed) {
sb_every = g_misc_config_p->sb_fast_rate; 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) { else if (my_speed_mph < g_misc_config_p->sb_slow_speed) {
sb_every = g_misc_config_p->sb_slow_rate; 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 { else {
/* Can't divide by 0 assuming sb_slow_speed > 0. */ /* 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; sb_every = ( g_misc_config_p->sb_fast_rate * g_misc_config_p->sb_fast_speed ) / my_speed_mph;
if (g_tracker_debug_level >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("my speed %.1f mph, interval = %d sec\n", my_speed_mph, sb_every);
}
} }
#if DEBUG_SIM #if DEBUG_SIM
@ -493,6 +540,13 @@ static void * beacon_thread (void *arg)
if (heading_change(my_course, sb_prev_course) > turn_threshold && if (heading_change(my_course, sb_prev_course) > turn_threshold &&
now >= sb_prev_time + g_misc_config_p->sb_turn_time) { 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. */ /* Send it now. */
for (j=0; j<g_misc_config_p->num_beacons; j++) { for (j=0; j<g_misc_config_p->num_beacons; j++) {
if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) { if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) {
@ -517,6 +571,7 @@ static void * beacon_thread (void *arg)
char beacon_text[AX25_MAX_PACKET_LEN]; char beacon_text[AX25_MAX_PACKET_LEN];
packet_t pp = NULL; packet_t pp = NULL;
char mycall[AX25_MAX_ADDR_LEN]; char mycall[AX25_MAX_ADDR_LEN];
int alt_ft;
/* /*
* Obtain source call for the beacon. * Obtain source call for the beacon.
@ -524,15 +579,15 @@ static void * beacon_thread (void *arg)
* When sending to IGate server, use call from first radio channel. * When sending to IGate server, use call from first radio channel.
* *
* Check added in version 1.0a. Previously used index of -1. * Check added in version 1.0a. Previously used index of -1.
*
* Version 1.1 - channel should now be 0 for IGate.
* Type of destination is encoded separately.
*/ */
strcpy (mycall, "NOCALL"); strcpy (mycall, "NOCALL");
if (g_misc_config_p->beacon[j].chan == -1) { assert (g_misc_config_p->beacon[j].sendto_chan >= 0);
strcpy (mycall, g_digi_config_p->mycall[0]);
} strcpy (mycall, g_digi_config_p->mycall[g_misc_config_p->beacon[j].sendto_chan]);
else {
strcpy (mycall, g_digi_config_p->mycall[g_misc_config_p->beacon[j].chan]);
}
if (strlen(mycall) == 0 || strcmp(mycall, "NOCALL") == 0) { if (strlen(mycall) == 0 || strcmp(mycall, "NOCALL") == 0) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
@ -542,13 +597,22 @@ static void * beacon_thread (void *arg)
/* /*
* Prepare the monitor format header. * Prepare the monitor format header.
*
* src > dest [ , via ]
*/ */
strcpy (beacon_text, mycall); strcpy (beacon_text, mycall);
strcat (beacon_text, ">"); strcat (beacon_text, ">");
if (g_misc_config_p->beacon[j].dest != NULL) {
strcat (beacon_text, g_misc_config_p->beacon[j].dest);
}
else {
sprintf (stemp, "%s%1d%1d", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION); sprintf (stemp, "%s%1d%1d", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION);
strcat (beacon_text, stemp); strcat (beacon_text, stemp);
if (g_misc_config_p->beacon[j].via) { }
if (g_misc_config_p->beacon[j].via != NULL) {
strcat (beacon_text, ","); strcat (beacon_text, ",");
strcat (beacon_text, g_misc_config_p->beacon[j].via); strcat (beacon_text, g_misc_config_p->beacon[j].via);
} }
@ -561,7 +625,10 @@ static void * beacon_thread (void *arg)
case BEACON_POSITION: case BEACON_POSITION:
encode_position (g_misc_config_p->beacon[j].compress, g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon, 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,
g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol,
g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir, g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
0, 0, /* course, speed */ 0, 0, /* course, speed */
@ -594,8 +661,9 @@ static void * beacon_thread (void *arg)
if (coarse == 0) { if (coarse == 0) {
coarse = 360; coarse = 360;
} }
encode_position (g_misc_config_p->beacon[j].compress, encode_position (g_misc_config_p->beacon[j].messaging,
my_lat, my_lon, g_misc_config_p->beacon[j].compress,
my_lat, my_lon, my_alt_ft,
g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol,
g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir, g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
coarse, (int)roundf(my_speed_knots), coarse, (int)roundf(my_speed_knots),
@ -617,6 +685,34 @@ static void * beacon_thread (void *arg)
else { else {
g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every; 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 */
/* the signals. */
if (g_tracker_debug_level >= 3) {
decode_aprs_t A;
memset (&A, 0, sizeof(A));
A.g_freq = G_UNKNOWN;
A.g_offset = G_UNKNOWN;
A.g_tone = G_UNKNOWN;
A.g_dcs = G_UNKNOWN;
strcpy (A.g_src, mycall);
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_course = coarse;
A.g_altitude = my_alt_ft;
/* Fake channel of 999 to distinguish from real data. */
log_write (999, &A, NULL, 0, 0);
}
} }
else { else {
g_misc_config_p->beacon[j].next = now + 2; g_misc_config_p->beacon[j].next = now + 2;
@ -650,18 +746,33 @@ static void * beacon_thread (void *arg)
if (pp != NULL) { if (pp != NULL) {
/* Send to IGate server or radio. */ /* Send to desired destination. */
switch (g_misc_config_p->beacon[j].sendto_type) {
case SENDTO_IGATE:
if (g_misc_config_p->beacon[j].chan == -1) {
#if 1 #if 1
text_color_set(DW_COLOR_XMIT); text_color_set(DW_COLOR_XMIT);
dw_printf ("[ig] %s\n", beacon_text); dw_printf ("[ig] %s\n", beacon_text);
#endif #endif
igate_send_rec_packet (0, pp); igate_send_rec_packet (0, pp);
ax25_delete (pp); ax25_delete (pp);
} break;
else {
tq_append (g_misc_config_p->beacon[j].chan, TQ_PRIO_1_LO, pp); case SENDTO_XMIT:
default:
tq_append (g_misc_config_p->beacon[j].sendto_chan, TQ_PRIO_1_LO, pp);
break;
case SENDTO_RECV:
// TODO: Put into receive queue rather than calling directly.
app_process_rec_packet (g_misc_config_p->beacon[j].sendto_chan, 0, pp, -1, 0, "");
break;
} }
} }
else { else {

View File

@ -2,3 +2,5 @@
/* beacon.h */ /* beacon.h */
void beacon_init (struct misc_config_s *pconfig, struct digi_config_s *pdigi); void beacon_init (struct misc_config_s *pconfig, struct digi_config_s *pdigi);
void beacon_tracker_set_debug (int level);

301
config.c
View File

@ -54,6 +54,7 @@
#include "igate.h" #include "igate.h"
#include "latlong.h" #include "latlong.h"
#include "symbols.h" #include "symbols.h"
#include "LatLong-UTMconversion.h"
//#include "tq.h" //#include "tq.h"
@ -415,6 +416,7 @@ void config_init (char *fname, struct audio_s *p_modem,
strcpy (p_modem->ptt_device[channel], ""); strcpy (p_modem->ptt_device[channel], "");
p_modem->ptt_line[channel] = PTT_LINE_RTS; p_modem->ptt_line[channel] = PTT_LINE_RTS;
p_modem->ptt_gpio[channel] = 0; p_modem->ptt_gpio[channel] = 0;
p_modem->ptt_lpt_bit[channel] = 0;
p_modem->ptt_invert[channel] = 0; p_modem->ptt_invert[channel] = 0;
p_modem->slottime[channel] = DEFAULT_SLOTTIME; p_modem->slottime[channel] = DEFAULT_SLOTTIME;
@ -477,6 +479,8 @@ void config_init (char *fname, struct audio_s *p_modem,
//strcpy (p_misc_config->nullmodem, DEFAULT_NULLMODEM); //strcpy (p_misc_config->nullmodem, DEFAULT_NULLMODEM);
strcpy (p_misc_config->nullmodem, ""); strcpy (p_misc_config->nullmodem, "");
strcpy (p_misc_config->nmea_port, "");
strcpy (p_misc_config->logdir, "");
/* /*
@ -642,11 +646,21 @@ void config_init (char *fname, struct audio_s *p_modem,
continue; continue;
} }
else { else {
// Definitely set for current channel.
// Set for other channels which have not been set yet.
int c;
for (c = 0; c < MAX_CHANS; c++) {
if (c == channel || strlen(p_digi_config->mycall[c]) == 0 || strcasecmp(p_digi_config->mycall[c], "NOCALL") == 0) {
char *p; char *p;
strncpy (p_digi_config->mycall[channel], t, sizeof(p_digi_config->mycall[channel])-1); strncpy (p_digi_config->mycall[c], t, sizeof(p_digi_config->mycall[c])-1);
for (p = p_digi_config->mycall[channel]; *p != '\0'; p++) { for (p = p_digi_config->mycall[c]; *p != '\0'; p++) {
if (islower(*p)) { if (islower(*p)) {
*p = toupper(*p); /* silently force upper case. */ *p = toupper(*p); /* silently force upper case. */
} }
@ -654,6 +668,8 @@ void config_init (char *fname, struct audio_s *p_modem,
// TODO: additional checks if valid // TODO: additional checks if valid
} }
} }
}
}
/* /*
@ -898,6 +914,7 @@ void config_init (char *fname, struct audio_s *p_modem,
* *
* PTT serial-port [-]rts-or-dtr * PTT serial-port [-]rts-or-dtr
* PTT GPIO [-]gpio-num * PTT GPIO [-]gpio-num
* PTT LPT [-]bit-num
*/ */
else if (strcasecmp(t, "PTT") == 0) { else if (strcasecmp(t, "PTT") == 0) {
@ -910,7 +927,60 @@ void config_init (char *fname, struct audio_s *p_modem,
continue; continue;
} }
if (strcasecmp(t, "GPIO") != 0) { if (strcasecmp(t, "GPIO") == 0) {
/* GPIO case, Linux only. */
#if __WIN32__
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: PTT with GPIO is only available on Linux.\n", line);
#else
t = strtok (NULL, " ,\t\n\r");
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: Missing GPIO number.\n", line);
continue;
}
if (*t == '-') {
p_modem->ptt_gpio[channel] = atoi(t+1);
p_modem->ptt_invert[channel] = 1;
}
else {
p_modem->ptt_gpio[channel] = atoi(t);
p_modem->ptt_invert[channel] = 0;
}
p_modem->ptt_method[channel] = PTT_METHOD_GPIO;
#endif
}
else if (strcasecmp(t, "LPT") == 0) {
/* Parallel printer case, x86 Linux only. */
#if ( defined(__i386__) || defined(__x86_64__) ) && ( defined(__linux__) || defined(__unix__) )
t = strtok (NULL, " ,\t\n\r");
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: Missing LPT bit number.\n", line);
continue;
}
if (*t == '-') {
p_modem->ptt_lpt_bit[channel] = atoi(t+1);
p_modem->ptt_invert[channel] = 1;
}
else {
p_modem->ptt_lpt_bit[channel] = atoi(t);
p_modem->ptt_invert[channel] = 0;
}
p_modem->ptt_method[channel] = PTT_METHOD_LPT;
#else
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: PTT with LPT is only available on x86 Linux.\n", line);
#endif
}
else {
/* serial port case. */ /* serial port case. */
@ -949,34 +1019,7 @@ void config_init (char *fname, struct audio_s *p_modem,
p_modem->ptt_method[channel] = PTT_METHOD_SERIAL; p_modem->ptt_method[channel] = PTT_METHOD_SERIAL;
} }
else {
/* GPIO case, Linux only. */
// TODO:
#if 0
//#if __WIN32__
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: PTT with GPIO is only available on Linux.\n", line);
#else
t = strtok (NULL, " ,\t\n\r");
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: Missing GPIO number.\n", line);
continue;
}
if (*t == '-') {
p_modem->ptt_gpio[channel] = atoi(t+1);
p_modem->ptt_invert[channel] = 1;
}
else {
p_modem->ptt_gpio[channel] = atoi(t);
p_modem->ptt_invert[channel] = 0;
}
p_modem->ptt_method[channel] = PTT_METHOD_GPIO;
#endif
}
} }
@ -1193,6 +1236,48 @@ void config_init (char *fname, struct audio_s *p_modem,
} }
} }
/*
* REGEN - Signal regeneration.
*/
else if (strcasecmp(t, "regen") == 0) {
int from_chan, to_chan;
//int e;
//char message[100];
t = strtok (NULL, " ,\t\n\r");
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file: Missing FROM-channel on line %d.\n", line);
continue;
}
from_chan = atoi(t);
if (from_chan < 0 || from_chan > p_digi_config->num_chans-1) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file: FROM-channel must be in range of 0 to %d on line %d.\n",
p_digi_config->num_chans-1, line);
continue;
}
t = strtok (NULL, " ,\t\n\r");
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file: Missing TO-channel on line %d.\n", line);
continue;
}
to_chan = atoi(t);
if (to_chan < 0 || to_chan > p_digi_config->num_chans-1) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file: TO-channel must be in range of 0 to %d on line %d.\n",
p_digi_config->num_chans-1, line);
continue;
}
p_digi_config->regen[from_chan][to_chan] = 1;
}
/* /*
* ==================== APRStt gateway ==================== * ==================== APRStt gateway ====================
@ -2016,6 +2101,36 @@ void config_init (char *fname, struct audio_s *p_modem,
} }
} }
/*
* NMEA - Device name for communication with NMEA device.
*/
else if (strcasecmp(t, "nmea") == 0) {
t = strtok (NULL, " ,\t\n\r");
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file: Missing device name for NMEA port on line %d.\n", line);
continue;
}
else {
strncpy (p_misc_config->nmea_port, t, sizeof(p_misc_config->nmea_port)-1);
}
}
/*
* LOGDIR - Directory name for storing log files. Use "." for current working directory.
*/
else if (strcasecmp(t, "logdir") == 0) {
t = strtok (NULL, " ,\t\n\r");
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file: Missing directory name for LOGDIR on line %d.\n", line);
continue;
}
else {
strncpy (p_misc_config->logdir, t, sizeof(p_misc_config->logdir)-1);
}
}
/* /*
* FIX_BITS - Attempt to fix frames with bad FCS. * FIX_BITS - Attempt to fix frames with bad FCS.
*/ */
@ -2029,7 +2144,7 @@ void config_init (char *fname, struct audio_s *p_modem,
continue; continue;
} }
n = atoi(t); n = atoi(t);
if (n >= RETRY_NONE && n <= RETRY_TWO_SEP) { if (n >= RETRY_NONE && n <= RETRY_REMOVE_TWO_SEP) {
p_modem->fix_bits = (retry_t)n; p_modem->fix_bits = (retry_t)n;
} }
else { else {
@ -2050,6 +2165,7 @@ void config_init (char *fname, struct audio_s *p_modem,
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file, line %d: Old style 'BEACON' has been replaced with new commands.\n", line); dw_printf ("Config file, line %d: Old style 'BEACON' has been replaced with new commands.\n", line);
dw_printf ("Use PBEACON, OBEACON, or CBEACON instead.\n");
} }
@ -2067,7 +2183,21 @@ void config_init (char *fname, struct audio_s *p_modem,
strcasecmp(t, "OBEACON") == 0 || strcasecmp(t, "OBEACON") == 0 ||
strcasecmp(t, "TBEACON") == 0 || strcasecmp(t, "TBEACON") == 0 ||
strcasecmp(t, "CBEACON") == 0) { strcasecmp(t, "CBEACON") == 0) {
#if __WIN32__
if (strcasecmp(t, "TBEACON") == 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file, line %d: TBEACON is available only in Linux version.\n", line);
continue;
}
#endif
#ifndef ENABLE_GPS
if (strcasecmp(t, "TBEACON") == 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file, line %d: Rebuild with GPS support for TBEACON to be available.\n", line);
continue;
}
#endif
if (p_misc_config->num_beacons < MAX_BEACONS) { if (p_misc_config->num_beacons < MAX_BEACONS) {
memset (&(p_misc_config->beacon[p_misc_config->num_beacons]), 0, sizeof(struct beacon_s)); memset (&(p_misc_config->beacon[p_misc_config->num_beacons]), 0, sizeof(struct beacon_s));
@ -2201,12 +2331,14 @@ void config_init (char *fname, struct audio_s *p_modem,
b = 0; b = 0;
for (k=0; k<p_misc_config->num_beacons; k++) { for (k=0; k<p_misc_config->num_beacons; k++) {
if (p_misc_config->beacon[p_misc_config->num_beacons].chan == j) b++; if (p_misc_config->beacon[p_misc_config->num_beacons].sendto_chan == j) b++;
} }
if (b == 0) { if (b == 0) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file: Beaconing should be configured for channel %d when digipeating is enabled.\n", i); dw_printf ("Config file: Beaconing should be configured for channel %d when digipeating is enabled.\n", i);
p_digi_config->enabled[i][j] = 0; // It's a recommendation, not a requirement.
// Was there some good reason to turn it off in earlier version?
//p_digi_config->enabled[i][j] = 0;
} }
} }
} }
@ -2245,16 +2377,22 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line)
int q; int q;
char temp_symbol[100]; char temp_symbol[100];
int ok; int ok;
char zone[8];
double easting = G_UNKNOWN;
double northing = G_UNKNOWN;
strcpy (temp_symbol, ""); strcpy (temp_symbol, "");
strcpy (zone, "");
b->chan = 0; b->sendto_type = SENDTO_XMIT;
b->sendto_chan = 0;
b->delay = 60; b->delay = 60;
b->every = 600; b->every = 600;
//b->delay = 6; // TODO: temp. remove //b->delay = 6; // temp test.
//b->every = 3600; //b->every = 3600;
b->lat = G_UNKNOWN; b->lat = G_UNKNOWN;
b->lon = G_UNKNOWN; b->lon = G_UNKNOWN;
b->alt_m = G_UNKNOWN;
b->symtab = '/'; b->symtab = '/';
b->symbol = '-'; /* house */ b->symbol = '-'; /* house */
@ -2353,10 +2491,31 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line)
} }
else if (strcasecmp(keyword, "SENDTO") == 0) { else if (strcasecmp(keyword, "SENDTO") == 0) {
if (value[0] == 'i' || value[0] == 'I') { if (value[0] == 'i' || value[0] == 'I') {
b->chan = -1; b->sendto_type = SENDTO_IGATE;
b->sendto_chan = 0;
}
else if (value[0] == 'r' || value[0] == 'R') {
b->sendto_type = SENDTO_RECV;
b->sendto_chan = atoi(value+1);
}
else if (value[0] == 't' || value[0] == 'T' || value[0] == 'x' || value[0] == 'X') {
b->sendto_type = SENDTO_XMIT;
b->sendto_chan = atoi(value+1);
} }
else { else {
b->chan = atoi(value); b->sendto_type = SENDTO_XMIT;
b->sendto_chan = atoi(value);
}
}
else if (strcasecmp(keyword, "DEST") == 0) {
b->dest = strdup(value);
for (p = b->dest; *p != '\0'; p++) {
if (islower(*p)) {
*p = toupper(*p); /* silently force upper case. */
}
}
if (strlen(b->dest) > 9) {
b->dest[9] = '\0';
} }
} }
else if (strcasecmp(keyword, "VIA") == 0) { else if (strcasecmp(keyword, "VIA") == 0) {
@ -2379,6 +2538,18 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line)
else if (strcasecmp(keyword, "LONG") == 0 || strcasecmp(keyword, "LON") == 0) { else if (strcasecmp(keyword, "LONG") == 0 || strcasecmp(keyword, "LON") == 0) {
b->lon = parse_ll (value, LON, line); b->lon = parse_ll (value, LON, line);
} }
else if (strcasecmp(keyword, "ALT") == 0 || strcasecmp(keyword, "ALTITUDE") == 0) {
b->alt_m = atof(value);
}
else if (strcasecmp(keyword, "ZONE") == 0) {
strncpy(zone, value, sizeof(zone));
}
else if (strcasecmp(keyword, "EAST") == 0 || strcasecmp(keyword, "EASTING") == 0) {
easting = atof(value);
}
else if (strcasecmp(keyword, "NORTH") == 0 || strcasecmp(keyword, "NORTHING") == 0) {
northing = atof(value);
}
else if (strcasecmp(keyword, "SYMBOL") == 0) { else if (strcasecmp(keyword, "SYMBOL") == 0) {
/* Defer processing in case overlay appears later. */ /* Defer processing in case overlay appears later. */
strcpy (temp_symbol, value); strcpy (temp_symbol, value);
@ -2419,6 +2590,9 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line)
else if (strcasecmp(keyword, "COMPRESS") == 0 || strcasecmp(keyword, "COMPRESSED") == 0) { else if (strcasecmp(keyword, "COMPRESS") == 0 || strcasecmp(keyword, "COMPRESSED") == 0) {
b->compress = atoi(value); b->compress = atoi(value);
} }
else if (strcasecmp(keyword, "MESSAGING") == 0) {
b->messaging = atoi(value);
}
else { else {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file, line %d: Invalid option keyword, %s.\n", line, keyword); dw_printf ("Config file, line %d: Invalid option keyword, %s.\n", line, keyword);
@ -2426,6 +2600,59 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line)
} }
} }
/*
* Convert UTM coordintes to lat / long.
*/
if (strlen(zone) > 0 || easting != G_UNKNOWN || northing != G_UNKNOWN) {
if (strlen(zone) > 0 && easting != G_UNKNOWN && northing != G_UNKNOWN) {
int znum;
char *zlet;
znum = strtoul(zone, &zlet, 10);
if (znum >= 1 && znum <= 60) {
//printf ("zlet = %c 0x%02x\n", *zlet, *zlet);
if (*zlet == '\0' || strchr ("CDEFGHJKLMNPQRSTUVWX", *zlet) != NULL) {
if (easting >= 0 && easting <= 999999) {
if (northing >= 0 && northing <= 9999999) {
UTMtoLL (WSG84, northing, easting, zone, &b->lat, &b->lon);
// printf ("config UTM debug: latitude = %.6f, longitude = %.6f\n", b->lat, b->lon);
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file, line %d: Northing value is out of range.\n", line);
}
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file, line %d: Easting value is out of range.\n", line);
}
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file, line %d: Latitudinal band must be one of CDEFGHJKLMNPQRSTUVWX.\n", line);
}
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file, line %d: UTM zone is out of range.\n", line);
}
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file, line %d: When any of ZONE, EASTING, NORTHING specifed, they must all be specified.\n", line);
}
}
/* /*
* Process symbol now that we have any later overlay. * Process symbol now that we have any later overlay.
*/ */

View File

@ -25,6 +25,9 @@
enum beacon_type_e { BEACON_IGNORE, BEACON_POSITION, BEACON_OBJECT, BEACON_TRACKER, BEACON_CUSTOM }; enum beacon_type_e { BEACON_IGNORE, BEACON_POSITION, BEACON_OBJECT, BEACON_TRACKER, BEACON_CUSTOM };
enum sendto_type_e { SENDTO_XMIT, SENDTO_IGATE, SENDTO_RECV };
#define MAX_BEACONS 30 #define MAX_BEACONS 30
struct misc_config_s { struct misc_config_s {
@ -40,6 +43,11 @@ struct misc_config_s {
char nullmodem[40]; /* Serial port name for our end of the */ char nullmodem[40]; /* Serial port name for our end of the */
/* virtual null modem for native Windows apps. */ /* virtual null modem for native Windows apps. */
char nmea_port[40]; /* Serial port name for NMEA communication with GPS */
/* receiver and/or mapping application. */
char logdir[80]; /* Directory for saving activity logs. */
int sb_configured; /* TRUE if SmartBeaconing is configured. */ int sb_configured; /* TRUE if SmartBeaconing is configured. */
int sb_fast_speed; /* MPH */ int sb_fast_speed; /* MPH */
int sb_fast_rate; /* seconds */ int sb_fast_rate; /* seconds */
@ -58,7 +66,18 @@ struct misc_config_s {
int lineno; /* Line number from config file for later error messages. */ int lineno; /* Line number from config file for later error messages. */
int chan; /* Send to Channel for transmission. -1 for IGate. */ enum sendto_type_e sendto_type;
/* SENDTO_XMIT - Usually beacons go to a radio transmitter. */
/* chan, below is the channel number. */
/* SENDTO_IGATE - Send to IGate, probably to announce my position */
/* rather than relying on someone else to hear */
/* me on the radio and report me. */
/* SENDTO_RECV - Pretend this was heard on the specified */
/* radio channel. Mostly for testing. It is a */
/* convenient way to send packets to attached apps. */
int sendto_chan; /* Transmit or simulated receive channel for above. Should be 0 for IGate. */
int delay; /* Seconds to delay before first transmission. */ int delay; /* Seconds to delay before first transmission. */
@ -68,6 +87,9 @@ struct misc_config_s {
time_t next; /* Unix time to transmit next one. */ time_t next; /* Unix time to transmit next one. */
char *dest; /* NULL or explicit AX.25 destination to use */
/* instead of the software version such as APDW11. */
int compress; /* Use more compact form? */ int compress; /* Use more compact form? */
char objname[10]; /* Object name. Any printable characters. */ char objname[10]; /* Object name. Any printable characters. */
@ -77,8 +99,12 @@ struct misc_config_s {
char *custom_info; /* Info part for handcrafted custom beacon. */ char *custom_info; /* Info part for handcrafted custom beacon. */
/* Ignore the rest below if this is set. */ /* Ignore the rest below if this is set. */
int messaging; /* Set messaging attribute for position report. */
/* i.e. Data Type Indicator of '=' rather than '!' */
double lat; /* Latitude and longitude. */ double lat; /* Latitude and longitude. */
double lon; double lon;
float alt_m; /* Altitude in meters. */
char symtab; /* Symbol table: / or \ or overlay character. */ char symtab; /* Symbol table: / or \ or overlay character. */
char symbol; /* Symbol code. */ char symbol; /* Symbol code. */

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,106 @@
/* decode_aprs.h */ /* decode_aprs.h */
extern void decode_aprs (packet_t pp);
#ifndef DECODE_APRS_H
#define DECODE_APRS_H 1
#ifndef G_UNKNOWN
#include "latlong.h"
#endif
#ifndef AX25_MAX_ADDR_LEN
#include "ax25_pad.h"
#endif
#ifndef APRSTT_LOC_DESC_LEN
#include "aprs_tt.h"
#endif
typedef struct decode_aprs_s {
char g_src[AX25_MAX_ADDR_LEN];
char g_msg_type[60]; /* Message type. Telemetry descriptions get pretty long. */
char g_symbol_table; /* The Symbol Table Identifier character selects one */
/* of the two Symbol Tables, or it may be used as */
/* single-character (alpha or numeric) overlay, as follows: */
/* / Primary Symbol Table (mostly stations) */
/* \ Alternate Symbol Table (mostly Objects) */
/* 0-9 Numeric overlay. Symbol from Alternate Symbol */
/* Table (uncompressed lat/long data format) */
/* a-j Numeric overlay. Symbol from Alternate */
/* Symbol Table (compressed lat/long data */
/* format only). i.e. a-j maps to 0-9 */
/* A-Z Alpha overlay. Symbol from Alternate Symbol Table */
char g_symbol_code; /* Where the Symbol Table Identifier is 0-9 or A-Z (or a-j */
/* with compressed position data only), the symbol comes from */
/* the Alternate Symbol Table, and is overlaid with the */
/* identifier (as a single digit or a capital letter). */
char g_aprstt_loc[APRSTT_LOC_DESC_LEN]; /* APRStt location from !DAO! */
double g_lat, g_lon; /* Location, degrees. Negative for South or West. */
/* Set to G_UNKNOWN if missing or error. */
char g_maidenhead[9]; /* 4 or 6 (or 8?) character maidenhead locator. */
char g_name[20]; /* Object or item name. */
float g_speed; /* Speed in MPH. */
float g_course; /* 0 = North, 90 = East, etc. */
int g_power; /* Transmitter power in watts. */
int g_height; /* Antenna height above average terrain, feet. */
int g_gain; /* Antenna gain in dB. */
char g_directivity[10]; /* Direction of max signal strength */
float g_range; /* Precomputed radio range in miles. */
float g_altitude; /* Feet above median sea level. */
char g_mfr[80]; /* Manufacturer or application. */
char g_mic_e_status[30]; /* MIC-E message. */
double g_freq; /* Frequency, MHz */
float g_tone; /* CTCSS tone, Hz, one fractional digit */
int g_dcs; /* Digital coded squelch, print as 3 octal digits. */
int g_offset; /* Transmit offset, KHz */
char g_weather[500]; /* Weather. Can get quite long. Rethink max size. */
char g_telemetry[256]; /* Telemetry data. Rethink max size. */
char g_comment[256]; /* Comment. */
} decode_aprs_t;
extern void decode_aprs (decode_aprs_t *A, packet_t pp);
extern void decode_aprs_print (decode_aprs_t *A);
#endif

View File

@ -92,12 +92,7 @@ static inline float z (float x, float y)
__attribute__((hot)) __attribute__((hot))
static inline void push_sample (float val, float *buff, int size) static inline void push_sample (float val, float *buff, int size)
{ {
int j; memmove(buff+1,buff,(size-1)*sizeof(float));
// TODO: memmove any faster?
for (j = size - 1; j >= 1; j--) {
buff[j] = buff[j-1];
}
buff[0] = val; buff[0] = val;
} }

View File

@ -459,6 +459,44 @@ static packet_t digipeat_match (packet_t pp, char *mycall_rec, char *mycall_xmit
/*------------------------------------------------------------------------------
*
* Name: digi_regen
*
* Purpose: Send regenerated copy of what we received.
*
* Inputs: chan - Radio channel where it was received.
*
* pp - Packet object.
*
* Returns: None.
*
* Description: TODO...
*
*------------------------------------------------------------------------------*/
void digi_regen (int from_chan, packet_t pp)
{
int to_chan;
packet_t result;
// dw_printf ("digi_regen()\n");
assert (from_chan >= 0 && from_chan < my_config.num_chans);
for (to_chan=0; to_chan<my_config.num_chans; to_chan++) {
if (my_config.regen[from_chan][to_chan]) {
result = ax25_dup (pp);
if (result != NULL) {
// TODO: if AX.25 and has been digipeated, put in HI queue?
tq_append (to_chan, TQ_PRIO_1_LO, result);
}
}
}
} /* end dig_regen */
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* Name: main * Name: main
@ -509,7 +547,7 @@ static void test (char *in, char *out)
if (strcmp(in, rec) != 0) { if (strcmp(in, rec) != 0) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Text/internal/text error %s -> %s\n", in, rec); dw_printf ("Text/internal/text error-1 %s -> %s\n", in, rec);
} }
/* /*
@ -527,7 +565,7 @@ static void test (char *in, char *out)
if (strcmp(in, rec) != 0) { if (strcmp(in, rec) != 0) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("internal/frame/internal/text error %s -> %s\n", in, rec); dw_printf ("internal/frame/internal/text error-2 %s -> %s\n", in, rec);
} }
/* /*

View File

@ -41,6 +41,8 @@ struct digi_config_s {
enum preempt_e { PREEMPT_OFF, PREEMPT_DROP, PREEMPT_MARK, PREEMPT_TRACE } preempt[MAX_CHANS][MAX_CHANS]; enum preempt_e { PREEMPT_OFF, PREEMPT_DROP, PREEMPT_MARK, PREEMPT_TRACE } preempt[MAX_CHANS][MAX_CHANS];
int regen[MAX_CHANS][MAX_CHANS]; // Regenerate packet.
// Sort of like digipeating but passed along unchanged.
}; };
/* /*
@ -56,6 +58,9 @@ extern void digipeater_init (struct digi_config_s *p_digi_config);
extern void digipeater (int from_chan, packet_t pp); extern void digipeater (int from_chan, packet_t pp);
void digi_regen (int from_chan, packet_t pp);
#endif #endif
/* end digipeater.h */ /* end digipeater.h */

View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2011, 2012, 2013 John Langner, WB2OSZ // Copyright (C) 2011, 2012, 2013, 2014 John Langner, WB2OSZ
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -42,6 +42,7 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <signal.h> #include <signal.h>
#include <ctype.h>
#if __WIN32__ #if __WIN32__
#else #else
@ -49,7 +50,11 @@
#include <fcntl.h> #include <fcntl.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#ifdef __OpenBSD__
#include <soundcard.h>
#else
#include <sys/soundcard.h> #include <sys/soundcard.h>
#endif
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netdb.h> #include <netdb.h>
@ -72,6 +77,7 @@
#include "server.h" #include "server.h"
#include "kiss.h" #include "kiss.h"
#include "kissnet.h" #include "kissnet.h"
#include "nmea.h"
#include "gen_tone.h" #include "gen_tone.h"
#include "digipeater.h" #include "digipeater.h"
#include "tq.h" #include "tq.h"
@ -86,8 +92,12 @@
#include "igate.h" #include "igate.h"
#include "symbols.h" #include "symbols.h"
#include "dwgps.h" #include "dwgps.h"
#include "nmea.h"
#include "log.h"
//static int idx_decoded = 0;
#if __WIN32__ #if __WIN32__
static BOOL cleanup_win (int); static BOOL cleanup_win (int);
#else #else
@ -130,8 +140,10 @@ static void __cpuid(int cpuinfo[4], int infotype){
static struct audio_s modem; static struct audio_s modem;
static int d_u_opt = 0; /* "-d u" command line option. */ static int d_u_opt = 0; /* "-d u" command line option to print UTF-8 also in hexadecimal. */
static int d_p_opt = 0; /* "-d p" option for dumping packets over radio. */
static struct misc_config_s misc_config;
int main (int argc, char *argv[]) int main (int argc, char *argv[])
@ -145,11 +157,18 @@ int main (int argc, char *argv[])
struct digi_config_s digi_config; struct digi_config_s digi_config;
struct tt_config_s tt_config; struct tt_config_s tt_config;
struct igate_config_s igate_config; struct igate_config_s igate_config;
struct misc_config_s misc_config;
int r_opt = 0, n_opt = 0, b_opt = 0, B_opt = 0, D_opt = 0; /* Command line options. */ int r_opt = 0, n_opt = 0, b_opt = 0, B_opt = 0, D_opt = 0; /* Command line options. */
char l_opt[80];
char input_file[80]; char input_file[80];
int t_opt = 1; /* Text color option. */ int t_opt = 1; /* Text color option. */
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. */
strcpy(l_opt, "");
#if __WIN32__ #if __WIN32__
@ -195,12 +214,9 @@ int main (int argc, char *argv[])
text_color_init(t_opt); text_color_init(t_opt);
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
//dw_printf ("Dire Wolf version %d.%d (%s) Beta Test 2\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); //dw_printf ("Dire Wolf version %d.%d (%s) Beta Test 1\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
//dw_printf ("Dire Wolf version %d.%d (%s) Development version\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); //dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "M", __DATE__);
dw_printf ("Dire Wolf version %d.%d, December 2014\n", MAJOR_VERSION, MINOR_VERSION);
// Note "a" for fix with beacon sent to IGate Server.
dw_printf ("Dire Wolf version %d.%da\n", MAJOR_VERSION, MINOR_VERSION);
#if __WIN32__ #if __WIN32__
@ -272,6 +288,7 @@ int main (int argc, char *argv[])
int this_option_optind = optind ? optind : 1; int this_option_optind = optind ? optind : 1;
int option_index = 0; int option_index = 0;
int c; int c;
char *p;
static struct option long_options[] = { static struct option long_options[] = {
{"future1", 1, 0, 0}, {"future1", 1, 0, 0},
{"future2", 0, 0, 0}, {"future2", 0, 0, 0},
@ -281,7 +298,7 @@ int main (int argc, char *argv[])
/* ':' following option character means arg is required. */ /* ':' following option character means arg is required. */
c = getopt_long(argc, argv, "B:D:c:pxr:b:n:d:t:U", c = getopt_long(argc, argv, "B:D:c:pxr:b:n:d:t:Ul:",
long_options, &option_index); long_options, &option_index);
if (c == -1) if (c == -1)
break; break;
@ -380,13 +397,27 @@ int main (int argc, char *argv[])
case 'd': /* Set debug option. */ case 'd': /* Set debug option. */
switch (optarg[0]) { /* New in 1.1. Can combine multiple such as "-d pkk" */
for (p=optarg; *p!='\0'; p++) {
switch (*p) {
case 'a': server_set_debug(1); break; case 'a': server_set_debug(1); break;
case 'k': kiss_serial_set_debug (1); break;
case 'n': kiss_net_set_debug (1); break; case 'k': d_k_opt++; kiss_serial_set_debug (d_k_opt); break;
case 'n': d_n_opt++; kiss_net_set_debug (d_n_opt); break;
case 'u': d_u_opt = 1; break; case 'u': d_u_opt = 1; break;
// separate out gps & waypoints.
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.
default: break; default: break;
} }
}
break; break;
case 't': /* Was handled earlier. */ case 't': /* Was handled earlier. */
@ -403,6 +434,10 @@ int main (int argc, char *argv[])
exit (0); exit (0);
break; break;
case 'l': /* -l for log file directory name */
strncpy (l_opt, optarg, sizeof(l_opt)-1);
break;
default: default:
@ -471,6 +506,10 @@ int main (int argc, char *argv[])
modem.decimate[0] = D_opt; modem.decimate[0] = D_opt;
} }
if (strlen(l_opt) > 0) {
strncpy (misc_config.logdir, l_opt, sizeof(misc_config.logdir)-1);
}
misc_config.enable_kiss_pt = enable_pseudo_terminal; misc_config.enable_kiss_pt = enable_pseudo_terminal;
if (strlen(input_file) > 0) { if (strlen(input_file) > 0) {
@ -521,7 +560,7 @@ int main (int argc, char *argv[])
* Initialize the transmit queue. * Initialize the transmit queue.
*/ */
xmit_init (&modem); xmit_init (&modem, d_p_opt);
/* /*
* If -x option specified, transmit alternating tones for transmitter * If -x option specified, transmit alternating tones for transmitter
@ -565,6 +604,11 @@ int main (int argc, char *argv[])
*/ */
kiss_init (&misc_config); kiss_init (&misc_config);
/*
* Open port for communication with GPS.
*/
nmea_init (&misc_config);
/* /*
* Create thread for trying to salvage frames with bad FCS. * Create thread for trying to salvage frames with bad FCS.
*/ */
@ -576,6 +620,8 @@ int main (int argc, char *argv[])
beacon_init (&misc_config, &digi_config); beacon_init (&misc_config, &digi_config);
log_init(misc_config.logdir);
/* /*
* Get sound samples and decode them. * Get sound samples and decode them.
* Use hot attribute for all functions called for every audio sample. * Use hot attribute for all functions called for every audio sample.
@ -662,6 +708,7 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, int alevel, ret
assert (chan >= 0 && chan < MAX_CHANS); assert (chan >= 0 && chan < MAX_CHANS);
assert (subchan >= -1 && subchan < MAX_SUBCHANS); assert (subchan >= -1 && subchan < MAX_SUBCHANS);
assert (pp != NULL); // 1.1J+
ax25_format_addrs (pp, stemp); ax25_format_addrs (pp, stemp);
@ -687,6 +734,8 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, int alevel, ret
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("\n"); dw_printf ("\n");
//idx_decoded++;
//dw_printf ("DECODED[%d] " , idx_decoded);
if (h != -1 && h != AX25_SOURCE) { if (h != -1 && h != AX25_SOURCE) {
dw_printf ("Digipeater "); dw_printf ("Digipeater ");
@ -720,6 +769,8 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, int alevel, ret
} }
} }
// Display non-APRS packets in a different color. // Display non-APRS packets in a different color.
// Display subchannel only when multiple modems configured for channel. // Display subchannel only when multiple modems configured for channel.
@ -746,10 +797,17 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, int alevel, ret
} }
dw_printf ("%s", stemp); /* stations followed by : */ dw_printf ("%s", stemp); /* stations followed by : */
ax25_safe_print ((char *)pinfo, info_len, 0);
// for APRS we generally want to display non-ASCII to see UTF-8.
// for other, probably want to restrict to ASCII only because we are
// more likely to have compressed data than UTF-8 text.
// TODO: Might want to use d_u_opt for transmitted frames too.
ax25_safe_print ((char *)pinfo, info_len, ( ! ax25_is_aprs(pp)) && ( ! d_u_opt) );
dw_printf ("\n"); dw_printf ("\n");
// Display in pure ASCII if non-ASCII characters and "-d u" option specified. // Also display in pure ASCII if non-ASCII characters and "-d u" option specified.
if (d_u_opt) { if (d_u_opt) {
@ -767,11 +825,43 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, int alevel, ret
} }
} }
/* Optional hex dump of packet. */
if (d_p_opt) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("------\n");
ax25_hex_dump (pp);
dw_printf ("------\n");
}
/* Decode the contents of APRS frames and display in human-readable form. */ /* Decode the contents of APRS frames and display in human-readable form. */
if (ax25_is_aprs(pp)) { if (ax25_is_aprs(pp)) {
decode_aprs (pp);
decode_aprs_t A;
decode_aprs (&A, pp);
//Print it all out in human readable format.
decode_aprs_print (&A);
// Send to log file.
log_write (chan, &A, pp, alevel, retries);
// Convert to NMEA waypoint sentence.
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),
A.g_comment);
} }
}
/* Send to another application if connected. */ /* Send to another application if connected. */
@ -791,6 +881,11 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, int alevel, ret
igate_send_rec_packet (chan, pp); igate_send_rec_packet (chan, pp);
} }
/* Send out a regenerated copy. Applies to all types, not just APRS. */
digi_regen (chan, pp);
/* Note that packet can be modified in place so this is the last thing we should do with it. */ /* Note that packet can be modified in place so this is the last thing we should do with it. */
/* Again, use only those with correct CRC. */ /* Again, use only those with correct CRC. */
/* We don't want to spread corrupted data! */ /* We don't want to spread corrupted data! */
@ -800,6 +895,7 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, int alevel, ret
digipeater (chan, pp); digipeater (chan, pp);
} }
ax25_delete (pp); ax25_delete (pp);
} /* end app_process_rec_packet */ } /* end app_process_rec_packet */
@ -814,6 +910,7 @@ static BOOL cleanup_win (int ctrltype)
if (ctrltype == CTRL_C_EVENT || ctrltype == CTRL_CLOSE_EVENT) { if (ctrltype == CTRL_C_EVENT || ctrltype == CTRL_CLOSE_EVENT) {
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("\nQRT\n"); dw_printf ("\nQRT\n");
log_term ();
ptt_term (); ptt_term ();
dwgps_term (); dwgps_term ();
SLEEP_SEC(1); SLEEP_SEC(1);
@ -829,8 +926,10 @@ static void cleanup_linux (int x)
{ {
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("\nQRT\n"); dw_printf ("\nQRT\n");
log_term ();
ptt_term (); ptt_term ();
dwgps_term (); dwgps_term ();
SLEEP_SEC(1);
exit(0); exit(0);
} }
@ -848,6 +947,7 @@ static void usage (char **argv)
dw_printf ("Usage: direwolf [options]\n"); dw_printf ("Usage: direwolf [options]\n");
dw_printf ("Options:\n"); dw_printf ("Options:\n");
dw_printf (" -c fname Configuration file name.\n"); dw_printf (" -c fname Configuration file name.\n");
dw_printf (" -l logdir Directory name for log files. Use . for current.\n");
dw_printf (" -r n Audio sample rate, per sec.\n"); dw_printf (" -r n Audio sample rate, per sec.\n");
dw_printf (" -n n Number of audio channels, 1 or 2.\n"); dw_printf (" -n n Number of audio channels, 1 or 2.\n");
@ -857,11 +957,13 @@ static void usage (char **argv)
dw_printf (" If > 2400, K9NG/G3RUH style encoding is used.\n"); dw_printf (" If > 2400, K9NG/G3RUH style encoding is used.\n");
dw_printf (" Otherwise, AFSK tones are set to 1200 & 2200.\n"); dw_printf (" Otherwise, AFSK tones are set to 1200 & 2200.\n");
dw_printf (" -d Debug communication with client application, one of\n"); dw_printf (" -d Debug options:\n");
dw_printf (" a a = AGWPE network protocol.\n"); dw_printf (" a a = AGWPE network protocol client.\n");
dw_printf (" k k = KISS serial port.\n"); dw_printf (" k k = KISS serial port client.\n");
dw_printf (" n n = KISS network.\n"); dw_printf (" n n = KISS network client.\n");
dw_printf (" u u = Display non-ASCII text in hexadecimal.\n"); dw_printf (" u u = Display non-ASCII text in hexadecimal.\n");
dw_printf (" p p = dump Packets in hexadecimal.\n");
dw_printf (" t t = gps Tracker.\n");
dw_printf (" -t n Text colors. 1=normal, 0=disabled.\n"); dw_printf (" -t n Text colors. 1=normal, 0=disabled.\n");

View File

@ -24,7 +24,7 @@
# (3) DIGIPEATER - configure digipeating rules. # (3) DIGIPEATER - configure digipeating rules.
# #
# Look for lines starting with DIGIPEATER. # Look for lines starting with DIGIPEATER.
# Most people will probably use the first example. # Most people will probably use the given example.
# Just remove the "#" from the start of the line # Just remove the "#" from the start of the line
# to enable it. # to enable it.
# #
@ -115,6 +115,7 @@
# ADEVICE - plughw:1,0 # ADEVICE - plughw:1,0
# ADEVICE UDP:7355 default # ADEVICE UDP:7355 default
# #
# This is the sound card audio sample rate. # This is the sound card audio sample rate.
# The default is 44100. Other standard values are 22050 or 11025. # The default is 44100. Other standard values are 22050 or 11025.
@ -165,6 +166,8 @@ CHANNEL 0
MYCALL NOCALL MYCALL NOCALL
# #
# VHF FM operation normally uses 1200 baud data with AFSK tones of 1200 and 2200 Hz. # VHF FM operation normally uses 1200 baud data with AFSK tones of 1200 and 2200 Hz.
# #
@ -277,7 +280,7 @@ TXTAIL 10
# #
# Dire Wolf acts as a virtual TNC and can communicate with # Dire Wolf acts as a virtual TNC and can communicate with
# two different protocols: # two different protocols:
# - the “AGW TCPIP Socket Interface” - default port 8000 # - the "AGW TCPIP Socket Interface" - default port 8000
# - KISS TNC via serial port # - KISS TNC via serial port
# - KISS protocol over TCP socket - default port 8001 # - KISS protocol over TCP socket - default port 8001
# #
@ -321,15 +324,12 @@ KISSPORT 8001
# #
# Version 0.6 adds a new feature where it is sometimes possible # It is sometimes possible to recover frames with a bad FCS.
# to recover frames with a bad FCS. Several levels of effort
# are possible.
# #
# 0 [NONE] - Don't try to repair. # 0 [NONE] - Don't try to repair.
# 1 [SINGLE] - Attempt to fix single bit error. (default) # 1 [SINGLE] - Attempt to fix single bit error. (default)
# 2 [DOUBLE] - Also attempt to fix two adjacent bits. # 2 [DOUBLE] - Also attempt to fix two adjacent bits.
# 3 [TRIPLE] - Also attempt to fix three adjacent bits. # ... see User Guide for more values and in-depth discussion.
# 4 [TWO_SEP] - Also attempt to fix two non-adjacent (separated) bits.
# #
FIX_BITS 1 FIX_BITS 1
@ -358,10 +358,16 @@ FIX_BITS 1
# The others are kept local. # The others are kept local.
# #
#PBEACON delay=00:10 every=0: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=00:15 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=00:15 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=10:15 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=10:15 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=20:15 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=20:15 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=00:15 every=10 overlay=S symbol="digi" zone=19T easting=306130 northing=4726010
# #
# Modify this for your particular situation before removing # Modify this for your particular situation before removing
# the # comment character from the beginning of the lines above. # the # comment character from the beginning of the lines above.
@ -374,97 +380,15 @@ FIX_BITS 1
# # # #
############################################################# #############################################################
#
# Digipeating is activated with commands of the form:
#
# DIGIPEAT from-chan to-chan aliases wide [ preemptive ]
#
# where,
#
# from-chan is the channel where the packet is received.
#
# to-chan is the channel where the packet is to be re-transmitted.
#
# aliases is a pattern for digipeating ONCE. Anything matching
# this pattern is effectively treated like WIDE1-1.
# 'MYCALL' for the receiving channel is an implied
# member of this list.
#
# wide is the pattern for normal WIDEn-N digipeating
# where the ssid is decremented.
#
# preemptive is the "preemptive" digipeating option. See
# User Guide for more details.
#
# Pattern matching uses "extended regular expressions." Rather than listing
# all the different possibilities (such as "WIDE3-3,WIDE4-4,WIDE5-5,WIDE6-6,WIDE7-7"),
# a pattern can be specified such as "^WIDE[34567]-[1-7]$". This means:
#
# ^ beginning of call. Without this, leading characters
# don't need to match and ZWIDE3-3 would end up matching.
#
# WIDE is an exact literal match of upper case letters W I D E.
#
# [34567] means ANY ONE of the characters listed.
#
# - is an exact literal match of the "-" character (when not
# found inside of []).
#
# [1-7] is an alternative form where we have a range of characters
# rather than listing them all individually.
#
# $ means end of call. Without this, trailing characters don't
# need to match. As an example, we would end up matching
# WIDE3-15 besides WIDE3-1.
#
# Google "Extended Regular Expressions" for more information.
#
#
# If the first unused digipeater field, in the received packet,
# matches the first pattern, it is treated the same way you
# would expect WIDE1-1 to behave.
#
# The digipeater name is replaced by MYCALL of the destination channel.
#
# Example: W1ABC>APRS,WIDE7-7
# Becomes: W1ABC>APRS,WB2OSZ-5*
#
# In this example, we trap large values of N as recommended in
# http://www.aprs.org/fix14439.html
#
#
# If not caught by the first pattern, see if it matches the second pattern.
#
# Matches will be processed with the usual WIDEn-N rules.
#
# If N >= 2, the N value is decremented and MYCALL (of the destination
# channel) is inserted if enough room.
#
# Example: W1ABC>APRS,WIDE2-2
# Becomes: W1ABC>APRS,WB2OSZ-5*,WIDE2-1
#
# If N = 1, we don't want to keep WIDEn-0 in the digipeater list so
# the station is replaced by MYCALL.
#
# Example: W1ABC>APRS,W9XYZ*,WIDE2-1
# Becomes: W1ABC>APRS,W9XYZ,WB2OSZ-5*
#
#-------------------------------------------------------
# ---------- Example 1: Typical digipeater ----------
#-------------------------------------------------------
# #
# For most common situations, use something like this by removing # For most common situations, use something like this by removing
# the "#" from the beginning of the line. # the "#" from the beginning of the line below.
# To disable digipeating, put # at the beginning of the line.
# #
# DIGIPEAT 0 0 ^WIDE[3-7]-[1-7]$|^TEST$ ^WIDE[12]-[12]$ TRACE #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.
############################################################# #############################################################
@ -512,7 +436,7 @@ FIX_BITS 1
#IGFILTER m/50 #IGFILTER m/50
# Finally, we dont want to flood the radio channel. # Finally, we don't want to flood the radio channel.
# The IGate function will limit the number of packets transmitted # The IGate function will limit the number of packets transmitted
# during 1 minute and 5 minute intervals. If a limit would # during 1 minute and 5 minute intervals. If a limit would
# be exceeded, the packet is dropped and message is displayed in red. # be exceeded, the packet is dropped and message is displayed in red.
@ -530,7 +454,7 @@ IGTXLIMIT 6 10
# Dire Wolf can receive DTMF (commonly known as Touch Tone) # Dire Wolf can receive DTMF (commonly known as Touch Tone)
# messages and convert them to packet objects. # messages and convert them to packet objects.
# #
# See "APRStt-Implementation-Notes" document for details. # See separate "APRStt-Implementation-Notes" document for details.
# #
# #
@ -571,8 +495,8 @@ TTUTM B6xxxyyy 19T 10 300000 4720000
TTCORRAL 37^55.50N 81^7.00W 0^0.02N TTCORRAL 37^55.50N 81^7.00W 0^0.02N
# Compact messages - Fixed locations xx and object yyy where # Compact messages - Fixed locations xx and object yyy where
# Object numbers 100 199 = bicycle # Object numbers 100 - 199 = bicycle
# Object numbers 200 299 = fire truck # Object numbers 200 - 299 = fire truck
# Others = dog # Others = dog
TTMACRO xx1yy B9xx*AB166*AA2B4C5B3B0A1yy TTMACRO xx1yy B9xx*AB166*AA2B4C5B3B0A1yy

View File

@ -29,7 +29,6 @@
#define SLEEP_MS(n) usleep((n)*1000) #define SLEEP_MS(n) usleep((n)*1000)
#endif #endif
#endif
#if __WIN32__ #if __WIN32__
#define PTW32_STATIC_LIB #define PTW32_STATIC_LIB
@ -37,3 +36,22 @@
#else #else
#include <pthread.h> #include <pthread.h>
#endif #endif
/* Not sure where to put these. */
/* Prefix with DW_ because /usr/include/gps.h uses a couple of these names. */
#define DW_METERS_TO_FEET(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 3.2808399)
#define DW_FEET_TO_METERS(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.3048)
#define DW_KM_TO_MILES(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.621371192)
#define DW_KNOTS_TO_MPH(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 1.15077945)
#define DW_KNOTS_TO_METERS_PER_SEC(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.51444444444)
#define DW_MPH_TO_KNOTS(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.868976)
#define DW_MPH_TO_METERS_PER_SEC(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.44704)
#define DW_MBAR_TO_INHG(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.0295333727)
#endif /* ifndef DIREWOLF_H */

22
dsp.c
View File

@ -128,7 +128,7 @@ float window (bp_window_t type, int size, int j)
void gen_lowpass (float fc, float *lp_filter, int filter_size, bp_window_t wtype) void gen_lowpass (float fc, float *lp_filter, int filter_size, bp_window_t wtype)
{ {
int j; int j;
float lp_sum; float G;
#if DEBUG1 #if DEBUG1
@ -165,12 +165,12 @@ void gen_lowpass (float fc, float *lp_filter, int filter_size, bp_window_t wtype
/* /*
* Normalize lowpass for unity gain. * Normalize lowpass for unity gain.
*/ */
lp_sum = 0; G = 0;
for (j=0; j<filter_size; j++) { for (j=0; j<filter_size; j++) {
lp_sum += lp_filter[j]; G += lp_filter[j];
} }
for (j=0; j<filter_size; j++) { for (j=0; j<filter_size; j++) {
lp_filter[j] = lp_filter[j] / lp_sum; lp_filter[j] = lp_filter[j] / G;
} }
} }
@ -198,7 +198,8 @@ void gen_lowpass (float fc, float *lp_filter, int filter_size, bp_window_t wtype
void gen_bandpass (float f1, float f2, float *bp_filter, int filter_size, bp_window_t wtype) void gen_bandpass (float f1, float f2, float *bp_filter, int filter_size, bp_window_t wtype)
{ {
int j; int j;
float bp_sum; float w;
float G;
#if DEBUG1 #if DEBUG1
@ -235,13 +236,18 @@ void gen_bandpass (float f1, float f2, float *bp_filter, int filter_size, bp_win
/* /*
* Normalize bandpass for unity gain. * Normalize bandpass for unity gain.
* TODO: This is not right.
* Can't use same technique as for lowpass.
* Instead compute gain in middle of passband.
* See http://dsp.stackexchange.com/questions/4693/fir-filter-gain
*/ */
bp_sum = 0; w = 2 * M_PI * (f1 + f2) / 2;
G = 0;
for (j=0; j<filter_size; j++) { for (j=0; j<filter_size; j++) {
bp_sum += bp_filter[j]; G += bp_filter[j]; // TBD
} }
for (j=0; j<filter_size; j++) { for (j=0; j<filter_size; j++) {
bp_filter[j] = bp_filter[j] / bp_sum; bp_filter[j] = bp_filter[j] / G;
} }
} }

100
dwgps.c
View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2013 John Langner, WB2OSZ // Copyright (C) 2013, 2014 John Langner, WB2OSZ
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -108,6 +108,10 @@ int dwgps_init (void)
#if __WIN32__ #if __WIN32__
/*
* Windows version. Not implemented yet.
*/
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("GPS interface not yet available in Windows version.\n"); dw_printf ("GPS interface not yet available in Windows version.\n");
init_status = INIT_FAILED; init_status = INIT_FAILED;
@ -117,11 +121,39 @@ int dwgps_init (void)
int err; 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); err = gps_open (GPSD_SHARED_MEMORY, NULL, &gpsdata);
if (err != 0) { if (err != 0) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Unable to connect to GPS receiver.\n"); dw_printf ("Unable to connect to GPSD shared memory interface, status=%d.\n", err);
if (err == NL_NOHOST) { 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 ("Shared memory interface is not enabled in libgps.\n");
dw_printf ("Download the gpsd source and build with 'shm_export=True' option.\n"); dw_printf ("Download the gpsd source and build with 'shm_export=True' option.\n");
} }
@ -133,8 +165,31 @@ int dwgps_init (void)
} }
init_status = INIT_SUCCESS; init_status = INIT_SUCCESS;
return (0); return (0);
#else #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); text_color_set(DW_COLOR_ERROR);
dw_printf ("GPS interface not enabled in this version.\n"); dw_printf ("GPS interface not enabled in this version.\n");
dw_printf ("See documentation on how to rebuild with ENABLE_GPS.\n"); dw_printf ("See documentation on how to rebuild with ENABLE_GPS.\n");
@ -160,7 +215,7 @@ int dwgps_init (void)
* *palt - Altitude, meters. * *palt - Altitude, meters.
* *
* Returns: -1 = error * Returns: -1 = error
* 0 = data not available (no fix) * 0 = location currently not available (no fix)
* 2 = 2D fix, lat/lon, speed, and course are set. * 2 = 2D fix, lat/lon, speed, and course are set.
* 3 - 3D fix, altitude is also set. * 3 - 3D fix, altitude is also set.
* *
@ -184,12 +239,42 @@ int dwgps_read (double *plat, double *plon, float *pspeed, float *pcourse, float
return (-1); return (-1);
} }
#if USE_GPS_SHM
/*
* Shared memory version.
*/
err = gps_read (&gpsdata); err = gps_read (&gpsdata);
#if DEBUG #if DEBUG
dw_printf ("gps_read returns %d bytes\n", err); dw_printf ("gps_read returns %d bytes\n", err);
#endif #endif
#else
/*
* Socket version.
*/
// Wait for up to 1000 milliseconds.
// This should only happen in the beaconing thread so
// I'm not worried about other functions hanging.
if (gps_waiting(&gpsdata, 1000)) {
err = gps_read (&gpsdata);
}
else {
gps_stream(&gpsdata, WATCH_ENABLE | WATCH_JSON, NULL);
sleep (1);
}
#endif
if (err > 0) { if (err > 0) {
/* Data is available. */
if (gpsdata.status >= STATUS_FIX && gpsdata.fix.mode >= MODE_2D) { if (gpsdata.status >= STATUS_FIX && gpsdata.fix.mode >= MODE_2D) {
*plat = gpsdata.fix.latitude; *plat = gpsdata.fix.latitude;
@ -208,10 +293,12 @@ int dwgps_read (double *plat, double *plon, float *pspeed, float *pcourse, float
return (0); return (0);
} }
else if (err == 0) { else if (err == 0) {
/* No data available */
/* No data available at the present time. */
return (0); return (0);
} }
else { else {
/* More serious error. */ /* More serious error. */
return (-1); return (-1);
} }
@ -244,6 +331,10 @@ void dwgps_term (void) {
#elif ENABLE_GPS #elif ENABLE_GPS
if (init_status == INIT_SUCCESS) { if (init_status == INIT_SUCCESS) {
#ifndef USE_GPS_SHM
gps_stream(&gpsdata, WATCH_DISABLE, NULL);
#endif
gps_close (&gpsdata); gps_close (&gpsdata);
} }
#else #else
@ -264,6 +355,7 @@ void dwgps_term (void) {
* Description: Compile with -DTEST option. * Description: Compile with -DTEST option.
* *
* gcc -DTEST dwgps.c textcolor.c -lgps * gcc -DTEST dwgps.c textcolor.c -lgps
* ./a.out
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/

View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2013 John Langner, WB2OSZ // Copyright (C) 2013, 2014 John Langner, WB2OSZ
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -443,9 +443,12 @@ static int frequency_spec (float freq, float tone, float offset, char *presult)
* *
* Purpose: Construct info part for position report format. * Purpose: Construct info part for position report format.
* *
* Inputs: compressed - Send in compressed form? * Inputs: messaging - This determines whether the data type indicator
* is set to '!' (false) or '=' (true).
* compressed - Send in compressed form?
* lat - Latitude. * lat - Latitude.
* lon - Longitude. * lon - Longitude.
* alt_ft - Altitude in feet.
* symtab - Symbol table id or overlay. * symtab - Symbol table id or overlay.
* symbol - Symbol id. * symbol - Symbol id.
* *
@ -494,7 +497,7 @@ typedef struct aprs_compressed_pos_s {
} aprs_compressed_pos_t; } aprs_compressed_pos_t;
int encode_position (int compressed, double lat, double lon, int encode_position (int messaging, int compressed, double lat, double lon, int alt_ft,
char symtab, char symbol, char symtab, char symbol,
int power, int height, int gain, char *dir, int power, int height, int gain, char *dir,
int course, int speed, int course, int speed,
@ -507,7 +510,7 @@ int encode_position (int compressed, double lat, double lon,
if (compressed) { if (compressed) {
aprs_compressed_pos_t *p = (aprs_compressed_pos_t *)presult; aprs_compressed_pos_t *p = (aprs_compressed_pos_t *)presult;
p->dti = '!'; p->dti = messaging ? '=' : '!';
set_comp_position (symtab, symbol, lat, lon, set_comp_position (symtab, symbol, lat, lon,
power, height, gain, power, height, gain,
course, speed, course, speed,
@ -517,7 +520,7 @@ int encode_position (int compressed, double lat, double lon,
else { else {
aprs_ll_pos_t *p = (aprs_ll_pos_t *)presult; aprs_ll_pos_t *p = (aprs_ll_pos_t *)presult;
p->dti = '!'; p->dti = messaging ? '=' : '!';
set_norm_position (symtab, symbol, lat, lon, &(p->pos)); set_norm_position (symtab, symbol, lat, lon, &(p->pos));
result_len = 1 + sizeof (p->pos); result_len = 1 + sizeof (p->pos);
@ -540,6 +543,19 @@ int encode_position (int compressed, double lat, double lon,
presult[result_len] = '\0'; presult[result_len] = '\0';
/* Altitude. Can be anywhere in comment. */
if (alt_ft != G_UNKNOWN) {
char salt[12];
/* Not clear if altitude can be negative. */
/* Be sure it will be converted to 6 digits. */
if (alt_ft < 0) alt_ft = 0;
if (alt_ft > 999999) alt_ft = 999999;
sprintf (salt, "/A=%06d", alt_ft);
strcat (presult, salt);
result_len += strlen(salt);
}
/* Finally, comment text. */ /* Finally, comment text. */
if (comment != NULL) { if (comment != NULL) {
@ -696,17 +712,13 @@ int encode_object (char *name, int compressed, time_t thyme, double lat, double
* *
* Description: Just a smattering, not an organized test. * Description: Just a smattering, not an organized test.
* *
* $ rm a.exe ; gcc -DEN_MAIN encode_aprs.c latlong.c ; ./a.exe * $ rm a.exe ; gcc -DEN_MAIN encode_aprs.c latlong.c textcolor.c ; ./a.exe
* *
*----------------------------------------------------------------*/ *----------------------------------------------------------------*/
#if EN_MAIN #if EN_MAIN
void text_color_set ( enum dw_color_e c )
{
return;
}
int main (int argc, char *argv[]) int main (int argc, char *argv[])
{ {
@ -716,75 +728,86 @@ int main (int argc, char *argv[])
/*********** Position ***********/ /*********** Position ***********/
encode_position (0, 42+34.61/60, -(71+26.47/60), 'D', '&', encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result); 0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result);
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&") != 0) dw_printf ("ERROR!\n"); if (strcmp(result, "!4234.61ND07126.47W&") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
/* with PHG. */ /* with PHG. */
encode_position (0, 42+34.61/60, -(71+26.47/60), 'D', '&', encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
50, 100, 6, "N", 0, 0, 0, 0, 0, NULL, result); 50, 100, 6, "N", 0, 0, 0, 0, 0, NULL, result);
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&PHG7368") != 0) dw_printf ("ERROR!\n"); if (strcmp(result, "!4234.61ND07126.47W&PHG7368") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
/* with freq. */ /* with freq. */
encode_position (0, 42+34.61/60, -(71+26.47/60), 'D', '&', encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 0, 0, 146.955, 74.4, -0.6, NULL, result); 0, 0, 0, NULL, 0, 0, 146.955, 74.4, -0.6, NULL, result);
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&146.955MHz T074 -060 ") != 0) dw_printf ("ERROR!\n"); if (strcmp(result, "!4234.61ND07126.47W&146.955MHz T074 -060 ") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
/* with course/speed, freq, and comment! */ /* with course/speed, freq, and comment! */
encode_position (0, 42+34.61/60, -(71+26.47/60), 'D', '&', encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 180, 55, 146.955, 74.4, -0.6, "River flooding", result); 0, 0, 0, NULL, 180, 55, 146.955, 74.4, -0.6, "River flooding", result);
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz T074 -060 River flooding") != 0) dw_printf ("ERROR!\n"); if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz T074 -060 River flooding") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
/* Course speed, no tone, + offset */ /* Course speed, no tone, + offset */
encode_position (0, 42+34.61/60, -(71+26.47/60), 'D', '&', encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 180, 55, 146.955, 0, 0.6, "River flooding", result); 0, 0, 0, NULL, 180, 55, 146.955, 0, 0.6, "River flooding", result);
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz Toff +060 River flooding") != 0) dw_printf ("ERROR!\n"); if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz Toff +060 River flooding") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
/* Course speed, no tone, + offset + altitude */
encode_position (0, 42+34.61/60, -(71+26.47/60), 12345, 'D', '&',
0, 0, 0, NULL, 180, 55, 146.955, 0, 0.6, "River flooding", result);
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__);
/*********** Compressed position. ***********/ /*********** Compressed position. ***********/
encode_position (1, 42+34.61/60, -(71+26.47/60), 'D', '&', encode_position (1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result); 0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result);
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, "!D8yKC<Hn[& ") != 0) dw_printf ("ERROR!\n"); if (strcmp(result, "!D8yKC<Hn[& !") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
/* with PHG. In this case it is converted to precomputed radio range. */ /* with PHG. In this case it is converted to precomputed radio range. TODO: check on this. Is 27.4 correct? */
encode_position (0, 42+34.61/60, -(71+26.47/60), 'D', '&', encode_position (1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
50, 100, 6, "N", 0, 0, 0, 0, 0, NULL, result); 50, 100, 6, "N", 0, 0, 0, 0, 0, NULL, result);
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&PHG7368 TBD ???") != 0) dw_printf ("ERROR!\n"); if (strcmp(result, "!D8yKC<Hn[&{CG") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
/* with course/speed, freq, and comment! */ /* with course/speed, freq, and comment! TODO: check on this 55 knots should be 63 MPH. we get 62. */
encode_position (0, 42+34.61/60, -(71+26.47/60), 'D', '&', encode_position (1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 180, 55, 146.955, 74.4, -0.6, "River flooding", result); 0, 0, 0, NULL, 180, 55, 146.955, 74.4, -0.6, "River flooding", result);
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz T074 -060 River flooding") != 0) dw_printf ("ERROR!\n"); 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
// TODO: test alt; cs+alt
/*********** Object. ***********/ /*********** Object. ***********/
encode_object ("WB1GOF-C", 0, 0, 42+34.61/60, -(71+26.47/60), 'D', '&', encode_object ("WB1GOF-C", 0, 0, 42+34.61/60, -(71+26.47/60), 'D', '&',
0, 0, 0, NULL, result); 0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result);
dw_printf ("%s\n", result); dw_printf ("%s\n", result);
if (strcmp(result, ";WB1GOF-C *111111z4234.61ND07126.47W& TBD???") != 0) dw_printf ("ERROR!\n"); if (strcmp(result, ";WB1GOF-C *111111z4234.61ND07126.47W&") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
// TODO: need more tests.
return(0); return(0);

View File

@ -1,5 +1,5 @@
int encode_position (int compressed, double lat, double lon, int encode_position (int messaging, int compressed, double lat, double lon, int alt_ft,
char symtab, char symbol, char symtab, char symbol,
int power, int height, int gain, char *dir, int power, int height, int gain, char *dir,
int course, int speed, int course, int speed,

View File

@ -2,6 +2,9 @@
#ifndef FSK_DEMOD_STATE_H #ifndef FSK_DEMOD_STATE_H
#include "rpack.h"
/* /*
* Demodulator state. * Demodulator state.
* Different copy is required for each channel & subchannel being processed concurrently. * Different copy is required for each channel & subchannel being processed concurrently.
@ -166,6 +169,47 @@ struct demodulator_state_s
float lev_prev_peak; float lev_prev_peak;
float lev_prev_ave; float lev_prev_ave;
/*
* Special for Rino decoder only.
* One for each possible signal polarity.
*/
#if 1
struct gr_state_s {
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.
float gr_minus_peak; // For automatic gain control.
float gr_plus_peak;
int gr_sync; // Is sync pulse present?
int gr_prev_sync; // Previous state to detect leading edge.
int gr_first_sample; // Index of starting sample index for debugging.
int gr_dcd; // Data carrier detect. i.e. are we
// currently decoding a message.
float gr_early_sum; // For averaging bit values in two regions.
int gr_early_count;
float gr_late_sum;
int gr_late_count;
float gr_sync_sum;
int gr_sync_count;
int gr_bit_count; // Bit index into message.
struct rpack_s rpack; // Collection of bits.
} gr_state[2];
#endif
}; };
#define FSK_DEMOD_STATE_H 1 #define FSK_DEMOD_STATE_H 1

501
grm_sym.h Normal file
View File

@ -0,0 +1,501 @@
/*
* grm_sym.h
*
* Symbol codes for use in $PGRMWPL sentence.
*
* Copied from
* Garmin Device Interface Specification
* May 19, 2006
* Drawing Number: 001-00063-00 Rev. C
*/
typedef unsigned short symbol_type_t;
enum symbol_type_e
{
/*---------------------------------------------------------------
Marine symbols
---------------------------------------------------------------*/
sym_anchor = 0, /* white anchor symbol */
sym_bell = 1, /* white bell symbol */
sym_diamond_grn = 2, /* green diamond symbol */
sym_diamond_red = 3, /* red diamond symbol */
sym_dive1 = 4, /* diver down flag 1 */
sym_dive2 = 5, /* diver down flag 2 */
sym_dollar = 6, /* white dollar symbol */
sym_fish = 7, /* white fish symbol */
sym_fuel = 8, /* white fuel symbol */
sym_horn = 9, /* white horn symbol */
sym_house = 10, /* white house symbol */
sym_knife = 11, /* white knife & fork symbol */
sym_light = 12, /* white light symbol */
sym_mug = 13, /* white mug symbol */
sym_skull = 14, /* white skull and crossbones symbol*/
sym_square_grn = 15, /* green square symbol */
sym_square_red = 16, /* red square symbol */
sym_wbuoy = 17, /* white buoy waypoint symbol */
sym_wpt_dot = 18, /* waypoint dot */
sym_wreck = 19, /* white wreck symbol */
sym_null = 20, /* null symbol (transparent) */
sym_mob = 21, /* man overboard symbol */
sym_buoy_ambr = 22, /* amber map buoy symbol */
sym_buoy_blck = 23, /* black map buoy symbol */
sym_buoy_blue = 24, /* blue map buoy symbol */
sym_buoy_grn = 25, /* green map buoy symbol */
sym_buoy_grn_red = 26, /* green/red map buoy symbol */
sym_buoy_grn_wht = 27, /* green/white map buoy symbol */
sym_buoy_orng = 28, /* orange map buoy symbol */
sym_buoy_red = 29, /* red map buoy symbol */
sym_buoy_red_grn = 30, /* red/green map buoy symbol */
sym_buoy_red_wht = 31, /* red/white map buoy symbol */
sym_buoy_violet = 32, /* violet map buoy symbol */
sym_buoy_wht = 33, /* white map buoy symbol */
sym_buoy_wht_grn = 34, /* white/green map buoy symbol */
sym_buoy_wht_red = 35, /* white/red map buoy symbol */
sym_dot = 36, /* white dot symbol */
sym_rbcn = 37, /* radio beacon symbol */
sym_boat_ramp = 150, /* boat ramp symbol */
sym_camp = 151, /* campground symbol */
sym_restrooms = 152, /* restrooms symbol */
sym_showers = 153, /* shower symbol */
sym_drinking_wtr = 154, /* drinking water symbol */
sym_phone = 155, /* telephone symbol */
sym_1st_aid = 156, /* first aid symbol */
sym_info = 157, /* information symbol */
sym_parking = 158, /* parking symbol */
sym_park = 159, /* park symbol */
sym_picnic = 160, /* picnic symbol */
sym_scenic = 161, /* scenic area symbol */
sym_skiing = 162, /* skiing symbol */
sym_swimming = 163, /* swimming symbol */
sym_dam = 164, /* dam symbol */
sym_controlled = 165, /* controlled area symbol */
sym_danger = 166, /* danger symbol */
sym_restricted = 167, /* restricted area symbol */
sym_null_2 = 168, /* null symbol */
sym_ball = 169, /* ball symbol */
sym_car = 170, /* car symbol */
sym_deer = 171, /* deer symbol */
sym_shpng_cart = 172, /* shopping cart symbol */
sym_lodging = 173, /* lodging symbol */
sym_mine = 174, /* mine symbol */
sym_trail_head = 175, /* trail head symbol */
sym_truck_stop = 176, /* truck stop symbol */
sym_user_exit = 177, /* user exit symbol */
sym_flag = 178, /* flag symbol */
sym_circle_x = 179, /* circle with x in the center */
sym_open_24hr = 180, /* open 24 hours symbol */
sym_fhs_facility = 181, /* U Fishing Hot Spots™ Facility */
sym_bot_cond = 182, /* Bottom Conditions */
sym_tide_pred_stn = 183, /* Tide/Current Prediction Station */
sym_anchor_prohib = 184, /* U anchor prohibited symbol */
sym_beacon = 185, /* U beacon symbol */
sym_coast_guard = 186, /* U coast guard symbol */
sym_reef = 187, /* U reef symbol */
sym_weedbed = 188, /* U weedbed symbol */
sym_dropoff = 189, /* U dropoff symbol */
sym_dock = 190, /* U dock symbol */
sym_marina = 191, /* U marina symbol */
sym_bait_tackle = 192, /* U bait and tackle symbol */
sym_stump = 193, /* U stump symbol */
/*---------------------------------------------------------------
User customizable symbols
The values from sym_begin_custom to sym_end_custom inclusive are
reserved for the identification of user customizable symbols.
---------------------------------------------------------------*/
sym_begin_custom = 7680, /* first user customizable symbol */
sym_end_custom = 8191, /* last user customizable symbol */
/*---------------------------------------------------------------
Land symbols
---------------------------------------------------------------*/
sym_is_hwy = 8192, /* interstate hwy symbol */
sym_us_hwy = 8193, /* us hwy symbol */
sym_st_hwy = 8194, /* state hwy symbol */
sym_mi_mrkr = 8195, /* mile marker symbol */
sym_trcbck = 8196, /* TracBack (feet) symbol */
sym_golf = 8197, /* golf symbol */
sym_sml_cty = 8198, /* small city symbol */
sym_med_cty = 8199, /* medium city symbol */
sym_lrg_cty = 8200, /* large city symbol */
sym_freeway = 8201, /* intl freeway hwy symbol */
sym_ntl_hwy = 8202, /* intl national hwy symbol */
sym_cap_cty = 8203, /* capitol city symbol (star) */
sym_amuse_pk = 8204, /* amusement park symbol */
sym_bowling = 8205, /* bowling symbol */
sym_car_rental = 8206, /* car rental symbol */
sym_car_repair = 8207, /* car repair symbol */
sym_fastfood = 8208, /* fast food symbol */
sym_fitness = 8209, /* fitness symbol */
sym_movie = 8210, /* movie symbol */
sym_museum = 8211, /* museum symbol */
sym_pharmacy = 8212, /* pharmacy symbol */
sym_pizza = 8213, /* pizza symbol */
sym_post_ofc = 8214, /* post office symbol */
sym_rv_park = 8215, /* RV park symbol */
sym_school = 8216, /* school symbol */
sym_stadium = 8217, /* stadium symbol */
sym_store = 8218, /* dept. store symbol */
sym_zoo = 8219, /* zoo symbol */
sym_gas_plus = 8220, /* convenience store symbol */
sym_faces = 8221, /* live theater symbol */
sym_ramp_int = 8222, /* ramp intersection symbol */
sym_st_int = 8223, /* street intersection symbol */
sym_weigh_sttn = 8226, /* inspection/weigh station symbol */
sym_toll_booth = 8227, /* toll booth symbol */
sym_elev_pt = 8228, /* elevation point symbol */
sym_ex_no_srvc = 8229, /* exit without services symbol */
sym_geo_place_mm = 8230, /* Geographic place name, man-made */
sym_geo_place_wtr = 8231, /* Geographic place name, water */
sym_geo_place_lnd = 8232, /* Geographic place name, land */
sym_bridge = 8233, /* bridge symbol */
sym_building = 8234, /* building symbol */
sym_cemetery = 8235, /* cemetery symbol */
sym_church = 8236, /* church symbol */
sym_civil = 8237, /* civil location symbol */
sym_crossing = 8238, /* crossing symbol */
sym_hist_town = 8239, /* historical town symbol */
sym_levee = 8240, /* levee symbol */
sym_military = 8241, /* military location symbol */
sym_oil_field = 8242, /* oil field symbol */
sym_tunnel = 8243, /* tunnel symbol */
sym_beach = 8244, /* beach symbol */
sym_forest = 8245, /* forest symbol */
sym_summit = 8246, /* summit symbol */
sym_lrg_ramp_int = 8247, /* large ramp intersection symbol */
sym_lrg_ex_no_srvc = 8248, /* large exit without services smbl */
sym_badge = 8249, /* police/official badge symbol */
sym_cards = 8250, /* gambling/casino symbol */
sym_snowski = 8251, /* snow skiing symbol */
sym_iceskate = 8252, /* ice skating symbol */
sym_wrecker = 8253, /* tow truck (wrecker) symbol */
sym_border = 8254, /* border crossing (port of entry) */
sym_geocache = 8255, /* geocache location */
sym_geocache_fnd = 8256, /* found geocache */
sym_cntct_smiley = 8257, /* Rino contact symbol, "smiley" */
sym_cntct_ball_cap = 8258, /* Rino contact symbol, "ball cap" */
sym_cntct_big_ears = 8259, /* Rino contact symbol, "big ear" */
sym_cntct_spike = 8260, /* Rino contact symbol, "spike" */
sym_cntct_goatee = 8261, /* Rino contact symbol, "goatee" */
sym_cntct_afro = 8262, /* Rino contact symbol, "afro" */
sym_cntct_dreads = 8263, /* Rino contact symbol, "dreads" */
sym_cntct_female1 = 8264, /* Rino contact symbol, "female 1" */
sym_cntct_female2 = 8265, /* Rino contact symbol, "female 2" */
sym_cntct_female3 = 8266, /* Rino contact symbol, "female 3" */
sym_cntct_ranger = 8267, /* Rino contact symbol, "ranger" */
sym_cntct_kung_fu = 8268, /* Rino contact symbol, "kung fu" */
sym_cntct_sumo = 8269, /* Rino contact symbol, "sumo" */
sym_cntct_pirate = 8270, /* Rino contact symbol, "pirate" */
sym_cntct_biker = 8271, /* Rino contact symbol, "biker" */
sym_cntct_alien = 8272, /* Rino contact symbol, "alien" */
sym_cntct_bug = 8273, /* Rino contact symbol, "bug" */
sym_cntct_cat = 8274, /* Rino contact symbol, "cat" */
sym_cntct_dog = 8275, /* Rino contact symbol, "dog" */
sym_cntct_pig = 8276, /* Rino contact symbol, "pig" */
sym_hydrant = 8282, /* water hydrant symbol */
sym_flag_blue = 8284, /* blue flag symbol */
sym_flag_green = 8285, /* green flag symbol */
sym_flag_red = 8286, /* red flag symbol */
sym_pin_blue = 8287, /* blue pin symbol */
sym_pin_green = 8288, /* green pin symbol */
sym_pin_red = 8289, /* red pin symbol */
sym_block_blue = 8290, /* blue block symbol */
sym_block_green = 8291, /* green block symbol */
sym_block_red = 8292, /* red block symbol */
sym_bike_trail = 8293, /* bike trail symbol */
sym_circle_red = 8294, /* red circle symbol */
sym_circle_green = 8295, /* green circle symbol */
sym_circle_blue = 8296, /* blue circle symbol */
sym_diamond_blue = 8299, /* blue diamond symbol */
sym_oval_red = 8300, /* red oval symbol */
sym_oval_green = 8301, /* green oval symbol */
sym_oval_blue = 8302, /* blue oval symbol */
sym_rect_red = 8303, /* red rectangle symbol */
sym_rect_green = 8304, /* green rectangle symbol */
sym_rect_blue = 8305, /* blue rectangle symbol */
sym_square_blue = 8308, /* blue square symbol */
sym_letter_a_red = 8309, /* red letter 'A' symbol */
sym_letter_b_red = 8310, /* red letter 'B' symbol */
sym_letter_c_red = 8311, /* red letter 'C' symbol */
sym_letter_d_red = 8312, /* red letter 'D' symbol */
sym_letter_a_green = 8313, /* green letter 'A' symbol */
sym_letter_c_green = 8314, /* green letter 'C' symbol */
sym_letter_b_green = 8315, /* green letter 'B' symbol */
sym_letter_d_green = 8316, /* green letter 'D' symbol */
sym_letter_a_blue = 8317, /* blue letter 'A' symbol */
sym_letter_b_blue = 8318, /* blue letter 'B' symbol */
sym_letter_c_blue = 8319, /* blue letter 'C' symbol */
sym_letter_d_blue = 8320, /* blue letter 'D' symbol */
sym_number_0_red = 8321, /* red number '0' symbol */
sym_number_1_red = 8322, /* red number '1' symbol */
sym_number_2_red = 8323, /* red number '2' symbol */
sym_number_3_red = 8324, /* red number '3' symbol */
sym_number_4_red = 8325, /* red number '4' symbol */
sym_number_5_red = 8326, /* red number '5' symbol */
sym_number_6_red = 8327, /* red number '6' symbol */
sym_number_7_red = 8328, /* red number '7' symbol */
sym_number_8_red = 8329, /* red number '8' symbol */
sym_number_9_red = 8330, /* red number '9' symbol */
sym_number_0_green = 8331, /* green number '0' symbol */
sym_number_1_green = 8332, /* green number '1' symbol */
sym_number_2_green = 8333, /* green number '2' symbol */
sym_number_3_green = 8334, /* green number '3' symbol */
sym_number_4_green = 8335, /* green number '4' symbol */
sym_number_5_green = 8336, /* green number '5' symbol */
sym_number_6_green = 8337, /* green number '6' symbol */
sym_number_7_green = 8338, /* green number '7' symbol */
sym_number_8_green = 8339, /* green number '8' symbol */
sym_number_9_green = 8340, /* green number '9' symbol */
sym_number_0_blue = 8341, /* blue number '0' symbol */
sym_number_1_blue = 8342, /* blue number '1' symbol */
sym_number_2_blue = 8343, /* blue number '2' symbol */
sym_number_3_blue = 8344, /* blue number '3' symbol */
sym_number_4_blue = 8345, /* blue number '4' symbol */
sym_number_5_blue = 8346, /* blue number '5' symbol */
sym_number_6_blue = 8347, /* blue number '6' symbol */
sym_number_7_blue = 8348, /* blue number '7' symbol */
sym_number_8_blue = 8349, /* blue number '8' symbol */
sym_number_9_blue = 8350, /* blue number '9' symbol */
sym_triangle_blue = 8351, /* blue triangle symbol */
sym_triangle_green = 8352, /* green triangle symbol */
sym_triangle_red = 8353, /* red triangle symbol */
sym_food_asian = 8359, /* asian food symbol */
sym_food_deli = 8360, /* deli symbol */
sym_food_italian = 8361, /* italian food symbol */
sym_food_seafood = 8362, /* seafood symbol */
sym_food_steak = 8363, /* steak symbol */
/*---------------------------------------------------------------
Aviation symbols
---------------------------------------------------------------*/
sym_airport = 16384, /* airport symbol */
sym_int = 16385, /* intersection symbol */
sym_ndb = 16386, /* non-directional beacon symbol */
sym_vor = 16387, /* VHF omni-range symbol */
sym_heliport = 16388, /* heliport symbol */
sym_private = 16389, /* private field symbol */
sym_soft_fld = 16390, /* soft field symbol */
sym_tall_tower = 16391, /* tall tower symbol */
sym_short_tower = 16392, /* short tower symbol */
sym_glider = 16393, /* glider symbol */
sym_ultralight = 16394, /* ultralight symbol */
sym_parachute = 16395, /* parachute symbol */
sym_vortac = 16396, /* VOR/TACAN symbol */
sym_vordme = 16397, /* VOR-DME symbol */
sym_faf = 16398, /* first approach fix */
sym_lom = 16399, /* localizer outer marker */
sym_map = 16400, /* missed approach point */
sym_tacan = 16401, /* TACAN symbol */
sym_seaplane = 16402, /* Seaplane Base */
};
/*
* Mapping from APRS symbols to Garmin.
*/
// TODO: NEEDS MORE WORK!!!
#define SYMTAB_SIZE 95
#define sym_default sym_diamond_grn
static const symbol_type_t grm_primary_symtab[SYMTAB_SIZE] = {
sym_default, // 00 --no-symbol--
sym_cntct_ranger, // ! 01 Police, Sheriff
sym_default, // " 02 reserved (was rain)
sym_rbcn, // # 03 DIGI (white center)
sym_phone, // $ 04 PHONE
sym_rbcn, // % 05 DX CLUSTER
sym_rbcn, // & 06 HF GATEway
sym_glider, // ' 07 Small AIRCRAFT
sym_rbcn, // ( 08 Mobile Satellite Station
sym_default, // ) 09 Wheelchair (handicapped)
sym_car, // * 10 SnowMobile
sym_1st_aid, // + 11 Red Cross
sym_cntct_ball_cap, // , 12 Boy Scouts
sym_house, // - 13 House QTH (VHF)
sym_default, // . 14 X
sym_default, // / 15 Red Dot
sym_default, // 0 16 # circle (obsolete)
sym_default, // 1 17 TBD
sym_default, // 2 18 TBD
sym_default, // 3 19 TBD
sym_default, // 4 20 TBD
sym_default, // 5 21 TBD
sym_default, // 6 22 TBD
sym_default, // 7 23 TBD
sym_default, // 8 24 TBD
sym_default, // 9 25 TBD
sym_default, // : 26 FIRE
sym_camp, // ; 27 Campground (Portable ops)
sym_cntct_biker, // < 28 Motorcycle
sym_default, // = 29 RAILROAD ENGINE
sym_car, // > 30 CAR
sym_default, // ? 31 SERVER for Files
sym_default, // @ 32 HC FUTURE predict (dot)
sym_1st_aid, // A 33 Aid Station
sym_rbcn, // B 34 BBS or PBBS
sym_boat_ramp, // C 35 Canoe
sym_default, // D 36
sym_default, // E 37 EYEBALL (Eye catcher!)
sym_default, // F 38 Farm Vehicle (tractor)
sym_default, // G 39 Grid Square (6 digit)
sym_lodging, // H 40 HOTEL (blue bed symbol)
sym_rbcn, // I 41 TcpIp on air network stn
sym_default, // J 42
sym_school, // K 43 School
sym_default, // L 44 PC user
sym_default, // M 45 MacAPRS
sym_default, // N 46 NTS Station
sym_parachute, // O 47 BALLOON
sym_cntct_ranger, // P 48 Police
sym_default, // Q 49 TBD
sym_rv_park, // R 50 REC. VEHICLE
sym_glider, // S 51 SHUTTLE
sym_default, // T 52 SSTV
sym_car, // U 53 BUS
sym_cntct_biker, // V 54 ATV
sym_default, // W 55 National WX Service Site
sym_default, // X 56 HELO
sym_default, // Y 57 YACHT (sail)
sym_default, // Z 58 WinAPRS
sym_cntct_smiley, // [ 59 Human/Person (HT)
sym_triangle_green, // \ 60 TRIANGLE(DF station)
sym_default, // ] 61 MAIL/PostOffice(was PBBS)
sym_glider, // ^ 62 LARGE AIRCRAFT
sym_default, // _ 63 WEATHER Station (blue)
sym_rbcn, // ` 64 Dish Antenna
sym_1st_aid, // a 65 AMBULANCE
sym_cntct_biker, // b 66 BIKE
sym_default, // c 67 Incident Command Post
sym_hydrant, // d 68 Fire dept
sym_deer, // e 69 HORSE (equestrian)
sym_hydrant, // f 70 FIRE TRUCK
sym_glider, // g 71 Glider
sym_1st_aid, // h 72 HOSPITAL
sym_default, // i 73 IOTA (islands on the air)
sym_car, // j 74 JEEP
sym_car, // k 75 TRUCK
sym_default, // l 76 Laptop
sym_rbcn, // m 77 Mic-E Repeater
sym_default, // n 78 Node (black bulls-eye)
sym_default, // o 79 EOC
sym_cntct_dog, // p 80 ROVER (puppy, or dog)
sym_default, // q 81 GRID SQ shown above 128 m
sym_rbcn, // r 82 Repeater
sym_default, // s 83 SHIP (pwr boat)
sym_truck_stop, // t 84 TRUCK STOP
sym_truck_stop, // u 85 TRUCK (18 wheeler)
sym_car, // v 86 VAN
sym_drinking_wtr, // w 87 WATER station
sym_default, // x 88 xAPRS (Unix)
sym_tall_tower, // y 89 YAGI @ QTH
sym_default, // z 90 TBD
sym_default, // { 91
sym_default, // | 92 TNC Stream Switch
sym_default, // } 93
sym_default }; // ~ 94 TNC Stream Switch
static const symbol_type_t grm_alternate_symtab[SYMTAB_SIZE] = {
sym_default, // 00 --no-symbol--
sym_default, // ! 01 EMERGENCY (!)
sym_default, // " 02 reserved
sym_default, // # 03 OVERLAY DIGI (green star)
sym_default, // $ 04 Bank or ATM (green box)
sym_default, // % 05 Power Plant with overlay
sym_rbcn, // & 06 I=Igte IGate R=RX T=1hopTX 2=2hopTX
sym_default, // ' 07 Crash (& now Incident sites)
sym_default, // ( 08 CLOUDY (other clouds w ovrly)
sym_hydrant, // ) 09 Firenet MEO, MODIS Earth Obs.
sym_default, // * 10 SNOW (& future ovrly codes)
sym_default, // + 11 Church
sym_cntct_female1, // , 12 Girl Scouts
sym_house, // - 13 House (H=HF) (O = Op Present)
sym_default, // . 14 Ambiguous (Big Question mark)
sym_default, // / 15 Waypoint Destination
sym_default, // 0 16 CIRCLE (E/I/W=IRLP/Echolink/WIRES)
sym_default, // 1 17
sym_default, // 2 18
sym_default, // 3 19
sym_default, // 4 20
sym_default, // 5 21
sym_default, // 6 22
sym_default, // 7 23
sym_default, // 8 24 802.11 or other network node
sym_default, // 9 25 Gas Station (blue pump)
sym_default, // : 26 Hail (& future ovrly codes)
sym_park, // ; 27 Park/Picnic area
sym_default, // < 28 ADVISORY (one WX flag)
sym_rbcn, // = 29 APRStt Touchtone (DTMF users)
sym_car, // > 30 OVERLAYED CAR
sym_default, // ? 31 INFO Kiosk (Blue box with ?)
sym_default, // @ 32 HURICANE/Trop-Storm
sym_default, // A 33 overlayBOX DTMF & RFID & XO
sym_default, // B 34 Blwng Snow (& future codes)
sym_coast_guard, // C 35 Coast Guard
sym_default, // D 36 Drizzle (proposed APRStt)
sym_default, // E 37 Smoke (& other vis codes)
sym_default, // F 38 Freezng rain (&future codes)
sym_default, // G 39 Snow Shwr (& future ovrlys)
sym_default, // H 40 Haze (& Overlay Hazards)
sym_default, // I 41 Rain Shower
sym_default, // J 42 Lightening (& future ovrlys)
sym_rbcn, // K 43 Kenwood HT (W)
sym_light, // L 44 Lighthouse
sym_default, // M 45 MARS (A=Army,N=Navy,F=AF)
sym_default, // N 46 Navigation Buoy
sym_default, // O 47 Rocket
sym_default, // P 48 Parking
sym_default, // Q 49 QUAKE
sym_default, // R 50 Restaurant
sym_rbcn, // S 51 Satellite/Pacsat
sym_default, // T 52 Thunderstorm
sym_default, // U 53 SUNNY
sym_default, // V 54 VORTAC Nav Aid
sym_default, // W 55 # NWS site (NWS options)
sym_pharmacy, // X 56 Pharmacy Rx (Apothicary)
sym_rbcn, // Y 57 Radios and devices
sym_default, // Z 58
sym_default, // [ 59 W.Cloud (& humans w Ovrly)
sym_default, // \ 60 New overlayable GPS symbol
sym_default, // ] 61
sym_glider, // ^ 62 # Aircraft (shows heading)
sym_default, // _ 63 # WX site (green digi)
sym_default, // ` 64 Rain (all types w ovrly)
sym_default, // a 65 ARRL, ARES, WinLINK
sym_default, // b 66 Blwng Dst/Snd (& others)
sym_default, // c 67 CD triangle RACES/SATERN/etc
sym_default, // d 68 DX spot by callsign
sym_default, // e 69 Sleet (& future ovrly codes)
sym_default, // f 70 Funnel Cloud
sym_default, // g 71 Gale Flags
sym_default, // h 72 Store. or HAMFST Hh=HAM store
sym_default, // i 73 BOX or points of Interest
sym_default, // j 74 WorkZone (Steam Shovel)
sym_car, // k 75 Special Vehicle SUV,ATV,4x4
sym_default, // l 76 Areas (box,circles,etc)
sym_default, // m 77 Value Sign (3 digit display)
sym_default, // n 78 OVERLAY TRIANGLE
sym_default, // o 79 small circle
sym_default, // p 80 Prtly Cldy (& future ovrlys)
sym_default, // q 81
sym_restrooms, // r 82 Restrooms
sym_default, // s 83 OVERLAY SHIP/boat (top view)
sym_default, // t 84 Tornado
sym_car, // u 85 OVERLAYED TRUCK
sym_car, // v 86 OVERLAYED Van
sym_default, // w 87 Flooding
sym_wreck, // x 88 Wreck or Obstruction ->X<-
sym_default, // y 89 Skywarn
sym_default, // z 90 OVERLAYED Shelter
sym_default, // { 91 Fog (& future ovrly codes)
sym_default, // | 92 TNC Stream Switch
sym_default, // } 93
sym_default }; // ~ 94 TNC Stream Switch

View File

@ -367,6 +367,7 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int descram
int alevel = demod_get_audio_level (chan, subchan); int alevel = demod_get_audio_level (chan, subchan);
rrbb_set_audio_level (H->rrbb, alevel); rrbb_set_audio_level (H->rrbb, alevel);
rrbb_set_fix_bits (H->rrbb, H->fix_bits);
hdlc_rec2_block (H->rrbb, H->fix_bits); hdlc_rec2_block (H->rrbb, H->fix_bits);
/* Now owned by someone else who will free it. */ /* Now owned by someone else who will free it. */
H->rrbb = rrbb_new (chan, subchan, is_scrambled, descram_state); /* Allocate a new one. */ H->rrbb = rrbb_new (chan, subchan, is_scrambled, descram_state); /* Allocate a new one. */

View File

@ -1,7 +1,6 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2011, 2012, 2013 John Langner, WB2OSZ // Copyright (C) 2011, 2012, 2013, 2014 John Langner, WB2OSZ
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -26,12 +25,32 @@
* else has done the work of pulling it out from between * else has done the work of pulling it out from between
* the special "flag" sequences. * the special "flag" sequences.
* *
*
* New in version 1.1:
*
* Several enhancements provided by Fabrice FAURE:
*
* - Additional types of attempts to fix a bad CRC.
* - Optimized code to reduce execution time.
* - Improved detection of duplicate packets from different fixup attempts.
* - Set limit on number of packets in fix up later queue.
*
* One of the new recovery attempt cases recovers three additional
* packets that were lost before. The one thing I disagree with is
* use of the word "swap" because that sounds like two things
* are being exchanged for each other. I would prefer "flip"
* or "invert" to describe changing a bit to the opposite state.
* I took "swap" out of the user-visible messages but left the
* rest of the source code as provided.
*
*******************************************************************************/ *******************************************************************************/
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
#include <ctype.h> #include <ctype.h>
//Optimize processing by accessing directly to decoded bits
#define RRBB_C 1
#include "direwolf.h" #include "direwolf.h"
#include "hdlc_rec2.h" #include "hdlc_rec2.h"
#include "fcs_calc.h" #include "fcs_calc.h"
@ -40,6 +59,9 @@
#include "rrbb.h" #include "rrbb.h"
#include "rdq.h" #include "rdq.h"
#include "multi_modem.h" #include "multi_modem.h"
//#define DEBUG 1
//#define DEBUGx 1
//#define DEBUG_LATER 1
/* /*
@ -82,13 +104,13 @@ 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, int alevel, retry_conf_t retry_conf);
static int try_decode (rrbb_t block, int chan, int subchan, int alevel, retry_t bits_flipped, int flip_a, int flip_b, int flip_c);
static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int alevel, retry_t fix_bits); static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int alevel, retry_t fix_bits);
static int sanity_check (unsigned char *buf, int blen, retry_t bits_flipped); static int sanity_check (unsigned char *buf, int blen, retry_t bits_flipped);
#if DEBUG #if DEBUG_LATER
static double dtime_now (void); static double dtime_now (void);
#endif #endif
@ -161,8 +183,19 @@ void hdlc_rec2_block (rrbb_t block, retry_t fix_bits)
rrbb_set_slice_val (block, 0); rrbb_set_slice_val (block, 0);
#else /* not SLICENDICE */ #else /* not SLICENDICE */
/* Create an empty retry configuration */
retry_conf_t retry_cfg;
ok = try_decode (block, chan, subchan, alevel, RETRY_NONE, -1, -1, -1); /* By default we don't try to alter any bits */
retry_cfg.type = RETRY_TYPE_NONE;
retry_cfg.mode = RETRY_MODE_CONTIGUOUS;
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);
if (ok) { if (ok) {
#if DEBUG #if DEBUG
@ -183,7 +216,7 @@ void hdlc_rec2_block (rrbb_t block, retry_t fix_bits)
* Put in queue for retrying later at lower priority. * Put in queue for retrying later at lower priority.
*/ */
if (fix_bits < RETRY_TWO_SEP) { if (fix_bits < RETRY_SWAP_TWO_SEP) {
rrbb_delete (block); rrbb_delete (block);
return; return;
} }
@ -196,20 +229,29 @@ void hdlc_rec2_block (rrbb_t block, retry_t fix_bits)
static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int alevel, retry_t fix_bits) static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int alevel, retry_t fix_bits)
{ {
int ok; int ok;
int len, i; int len, i,j;
len = rrbb_get_len(block); len = rrbb_get_len(block);
/* Prepare the retry configuration */
retry_conf_t retry_cfg;
/* Will modify only contiguous bits*/
retry_cfg.mode = RETRY_MODE_CONTIGUOUS;
/* /*
* Try fixing one bit. * Try fixing one bit.
*/ */
if (fix_bits < RETRY_SINGLE) { if (fix_bits < RETRY_SWAP_SINGLE) {
return 0; return 0;
} }
/* Try to swap one bit */
retry_cfg.type = RETRY_TYPE_SWAP;
retry_cfg.retry = RETRY_SWAP_SINGLE;
retry_cfg.u_bits.contig.nr_bits = 1;
for (i=0; i<len; i++) { for (i=0; i<len; i++) {
ok = try_decode (block, chan, subchan, alevel, RETRY_SINGLE, i, -1, -1); /* 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);
if (ok) { if (ok) {
#if DEBUG #if DEBUG
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
@ -222,12 +264,17 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int alevel
/* /*
* Try fixing two adjacent bits. * Try fixing two adjacent bits.
*/ */
if (fix_bits < RETRY_DOUBLE) { if (fix_bits < RETRY_SWAP_DOUBLE) {
return 0; return 0;
} }
/* Try to swap two contiguous bits */
retry_cfg.retry = RETRY_SWAP_DOUBLE;
retry_cfg.u_bits.contig.nr_bits = 2;
for (i=0; i<len-1; i++) { for (i=0; i<len-1; i++) {
ok = try_decode (block, chan, subchan, alevel, RETRY_DOUBLE, i, i+1, -1); retry_cfg.u_bits.contig.bit_idx = i;
ok = try_decode (block, chan, subchan, alevel, retry_cfg);
if (ok) { if (ok) {
#if DEBUG #if DEBUG
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
@ -240,13 +287,16 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int alevel
/* /*
* Try fixing adjacent three bits. * Try fixing adjacent three bits.
*/ */
if (fix_bits < RETRY_TRIPLE) { if (fix_bits < RETRY_SWAP_TRIPLE) {
return 0; return 0;
} }
/* Try to swap three contiguous bits */
retry_cfg.retry = RETRY_SWAP_TRIPLE;
retry_cfg.u_bits.contig.nr_bits = 3;
len = rrbb_get_len(block);
for (i=0; i<len-2; i++) { for (i=0; i<len-2; i++) {
ok = try_decode (block, chan, subchan, alevel, RETRY_TRIPLE, i, i+1, i+2); retry_cfg.u_bits.contig.bit_idx = i;
ok = try_decode (block, chan, subchan, alevel, retry_cfg);
if (ok) { if (ok) {
#if DEBUG #if DEBUG
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
@ -256,71 +306,362 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int alevel
} }
} }
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);
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);
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);
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);
if (!ok) {
retry_cfg.insert_value=1;
ok = try_decode (block, chan, subchan, alevel, retry_cfg);
}
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);
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; return 0;
} }
void hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, int alevel) void hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, int alevel)
{ {
int ok; int ok;
int len, i; int len, i, j;
#if DEBUG retry_t fix_bits;
#if DEBUG_LATER
double tstart, tend; double tstart, tend;
#endif #endif
retry_conf_t retry_cfg;
len = rrbb_get_len(block); len = rrbb_get_len(block);
fix_bits = rrbb_get_fix_bits (block);
if (fix_bits < RETRY_SWAP_TWO_SEP) {
return ;
}
retry_cfg.mode = RETRY_MODE_SEPARATED;
/* /*
* Two non-adjacent ("separated") single bits. * 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. Test takes 4 times longer to run.
* *
* Ran up to 4.82 seconds for 1040 bits before giving up. * 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. * Processing time is order N squared so time goes up rapidly with larger frames.
*/ */
retry_cfg.type = RETRY_TYPE_SWAP;
retry_cfg.retry = RETRY_SWAP_TWO_SEP;
retry_cfg.u_bits.sep.bit_idx_c = -1;
#if DEBUG #ifdef DEBUG_LATER
tstart = dtime_now(); tstart = dtime_now();
dw_printf ("*** Try flipping TWO SEPARATED BITS %d bits\n", len);
#endif #endif
len = rrbb_get_len(block); len = rrbb_get_len(block);
for (i=0; i<len-2; i++) { for (i=0; i<len-2; i++) {
retry_cfg.u_bits.sep.bit_idx_a = i;
int j; int j;
ok = 0; ok = 0;
for (j=i+2; j<len; j++) { for (j=i+2; j<len; j++) {
ok = try_decode (block, chan, subchan, alevel, RETRY_TWO_SEP, i, j, -1); retry_cfg.u_bits.sep.bit_idx_b = j;
if (ok) ok = try_decode (block, chan, subchan, alevel, retry_cfg);
if (ok) {
break; break;
} }
}
if (ok) { if (ok) {
#if DEBUG #if DEBUG
tend = dtime_now();
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("*** Success by flipping TWO SEPARATED bits %d and %d of %d *** %.3f sec.\n", i, j, len, tend-tstart); dw_printf ("*** Success by flipping TWO SEPARATED bits %d and %d of %d \n", i, j, len);
#endif #endif
return; return;
} }
} }
#if DEBUGx #if DEBUG_LATER
tend = dtime_now(); tend = dtime_now();
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("*** No luck flipping TWO SEPARATED bits of %d *** %.3f sec.\n", len, tend-tstart); dw_printf ("*** No luck flipping TWO SEPARATED bits of %d *** %.3f sec.\n", len, tend-tstart);
#endif #endif
if (fix_bits < RETRY_SWAP_MANY) {
return ;
}
/* 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);
#endif
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);
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 ;
}
}
}
#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) {
return ;
}
/* 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);
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 ;
}
}
}
#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) {
return ;
}
/*
* 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);
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;
}
}
#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
return; return;
} }
/* 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 */
inline static char is_contig_bit_modified(int bit_idx, retry_conf_t retry_conf) {
int cont_bit_idx = retry_conf.u_bits.contig.bit_idx;
int cont_nr_bits = retry_conf.u_bits.contig.nr_bits;
if (bit_idx >= cont_bit_idx && (bit_idx < cont_bit_idx + cont_nr_bits ))
return 1;
else
return 0;
}
/* Check if the specified index of bit has been modified with the current type of configuration in separated bit index mode
* Provide a specific implementation for separated mode to optimize number of tests done in the loop */
inline static char is_sep_bit_modified(int bit_idx, retry_conf_t retry_conf) {
if (bit_idx == retry_conf.u_bits.sep.bit_idx_a ||
bit_idx == retry_conf.u_bits.sep.bit_idx_b ||
bit_idx == retry_conf.u_bits.sep.bit_idx_c)
return 1;
else
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];
}
static int try_decode (rrbb_t block, int chan, int subchan, int alevel, retry_conf_t retry_conf)
static int try_decode (rrbb_t block, int chan, int subchan, int alevel, retry_t bits_flipped, int flip_a, int flip_b, int flip_c)
{ {
struct hdlc_state_s H; struct hdlc_state_s H;
int blen; /* Block length in bits. */ int blen; /* Block length in bits. */
int i; int i;
int raw; /* From demodulator. */ unsigned int raw; /* From demodulator. */
int dbit; /* Data bit after undoing NRZI. */ int crc_failed = 1;
int retry_conf_mode = retry_conf.mode;
int retry_conf_type = retry_conf.type;
int retry_conf_retry = retry_conf.retry;
H.prev_raw = rrbb_get_bit (block, 0); /* Actually last bit of the */ H.prev_raw = get_bit (block, 0); /* Actually last bit of the */
/* opening flag so we can derive the */ /* opening flag so we can derive the */
/* first data bit. */ /* first data bit. */
@ -328,8 +669,8 @@ static int try_decode (rrbb_t block, int chan, int subchan, int alevel, retry_t
/* This is the last bit of the "flag" pattern. */ /* This is the last bit of the "flag" pattern. */
/* If it was corrupted we wouldn't have detected */ /* If it was corrupted we wouldn't have detected */
/* the start of frame. */ /* the start of frame. */
if (retry_conf.mode == RETRY_MODE_CONTIGUOUS && is_contig_bit_modified(0, retry_conf) ||
if (0 == flip_a || 0 == flip_b || 0 == flip_c){ retry_conf.mode == RETRY_MODE_SEPARATED && is_sep_bit_modified(0, retry_conf)) {
H.prev_raw = ! H.prev_raw; H.prev_raw = ! H.prev_raw;
} }
@ -338,90 +679,127 @@ static int try_decode (rrbb_t block, int chan, int subchan, int alevel, retry_t
H.olen = 0; H.olen = 0;
H.frame_len = 0; H.frame_len = 0;
blen = rrbb_get_len (block); 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 #if DEBUGx
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
if (retry_conf.type == RETRY_TYPE_NONE)
dw_printf ("try_decode: blen=%d\n", blen); dw_printf ("try_decode: blen=%d\n", blen);
#endif #endif
for (i=1; i<blen; i++) { for (i=1; i<blen; i++) {
/* Get the value for the current bit */
raw = rrbb_get_bit (block, i); raw = get_bit (block, i);
/* If swap two sep mode , swap the bit if needed */
if (i == flip_a || i == flip_b || i == flip_c){ if (retry_conf_retry == RETRY_SWAP_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 this is the bit to swap */
if (is_contig_bit_modified(i, retry_conf))
raw = ! raw; raw = ! raw;
} }
/* } else {
* Using NRZI encoding, }
* A '0' bit is represented by an inversion since previous bit.
* A '1' bit is represented by no change.
*/
dbit = (raw == H.prev_raw);
H.prev_raw = raw;
/* /*
* Octets are sent LSB first. * Octets are sent LSB first.
* Shift the most recent 8 bits thru the pattern detector. * Shift the most recent 8 bits thru the pattern detector.
*/ */
H.pat_det >>= 1; H.pat_det >>= 1;
if (dbit) {
H.pat_det |= 0x80;
}
if (H.pat_det == 0x7e) { /*
/* The special pattern 01111110 indicates beginning and ending of a frame. */ * Using NRZI encoding,
#if DEBUGx * A '0' bit is represented by an inversion since previous bit.
text_color_set(DW_COLOR_DEBUG); * A '1' bit is represented by no change.
dw_printf ("try_decode: found flag, i=%d\n", i); * Note: this code can be factorized with the raw != H.prev_raw code at the cost of processing time
#endif */
return 0; if (raw == H.prev_raw) {
} H.pat_det |= 0x80;
else if (H.pat_det == 0xfe) { /* Valid data will never have 7 one bits in a row: exit. */
/* Valid data will never have 7 one bits in a row. */ if (H.pat_det == 0xfe) {
#if DEBUGx #if DEBUGx
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("try_decode: found abort, i=%d\n", i); dw_printf ("try_decode: found abort, i=%d\n", i);
#endif #endif
return 0; return 0;
} }
else if ( (H.pat_det & 0xfc) == 0x7c ) { H.oacc >>= 1;
H.oacc |= 0x80;
} else {
H.prev_raw = raw;
/* The special pattern 01111110 indicates beginning and ending of a frame: exit. */
if (H.pat_det == 0x7e) {
#if DEBUGx
text_color_set(DW_COLOR_DEBUG);
dw_printf ("try_decode: found flag, i=%d\n", i);
#endif
return 0;
/* /*
* If we have five '1' bits in a row, followed by a '0' bit, * If we have five '1' bits in a row, followed by a '0' bit,
* *
* 0111110xx * 011111xx
* *
* the current '0' bit should be discarded because it was added for * the current '0' bit should be discarded because it was added for
* "bit stuffing." * "bit stuffing."
*/ */
;
} else { } else if ( (H.pat_det >> 2) == 0x1f ) {
continue;
}
H.oacc >>= 1;
}
/* /*
* In all other cases, accumulate bits into octets, and complete octets * Now accumulate bits into octets, and complete octets
* into the frame buffer. * into the frame buffer.
*/ */
H.oacc >>= 1;
if (dbit) {
H.oacc |= 0x80;
}
H.olen++; H.olen++;
if (H.olen == 8) { if (H.olen & 8) {
H.olen = 0; H.olen = 0;
if (H.frame_len < MAX_FRAME_LEN) { if (H.frame_len < MAX_FRAME_LEN) {
H.frame_buf[H.frame_len] = H.oacc; H.frame_buf[H.frame_len] = H.oacc;
H.frame_len++; H.frame_len++;
}
}
}
}
}
} /* end of loop on all bits in block */ } /* end of loop on all bits in block */
/* /*
* Do we have a minimum number of complete bytes? * Do we have a minimum number of complete bytes?
*/ */
@ -436,6 +814,7 @@ static int try_decode (rrbb_t block, int chan, int subchan, int alevel, retry_t
unsigned short actual_fcs, expected_fcs; unsigned short actual_fcs, expected_fcs;
#if DEBUGx #if DEBUGx
if (retry_conf.type == RETRY_TYPE_NONE) {
int j; int j;
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("NEW WAY: frame len = %d\n", H.frame_len); dw_printf ("NEW WAY: frame len = %d\n", H.frame_len);
@ -443,6 +822,8 @@ static int try_decode (rrbb_t block, int chan, int subchan, int alevel, retry_t
dw_printf (" %02x", H.frame_buf[j]); dw_printf (" %02x", H.frame_buf[j]);
} }
dw_printf ("\n"); dw_printf ("\n");
}
#endif #endif
/* Check FCS, low byte first, and process... */ /* Check FCS, low byte first, and process... */
@ -457,7 +838,7 @@ static int try_decode (rrbb_t block, int chan, int subchan, int alevel, retry_t
expected_fcs = fcs_calc (H.frame_buf, H.frame_len - 2); expected_fcs = fcs_calc (H.frame_buf, H.frame_len - 2);
if (actual_fcs == expected_fcs && sanity_check (H.frame_buf, H.frame_len - 2, bits_flipped)) { if (actual_fcs == expected_fcs && sanity_check (H.frame_buf, H.frame_len - 2, retry_conf.retry)) {
// TODO: Shouldn't be necessary to pass chan, subchan, alevel into // TODO: Shouldn't be necessary to pass chan, subchan, alevel into
@ -468,10 +849,47 @@ static int try_decode (rrbb_t block, int chan, int subchan, int alevel, retry_t
assert (rrbb_get_subchan(block) == subchan); assert (rrbb_get_subchan(block) == subchan);
assert (rrbb_get_audio_level(block) == alevel); assert (rrbb_get_audio_level(block) == alevel);
multi_modem_process_rec_frame (chan, subchan, H.frame_buf, H.frame_len - 2, alevel, bits_flipped); /* len-2 to remove FCS. */ multi_modem_process_rec_frame (chan, subchan, H.frame_buf, H.frame_len - 2, alevel, retry_conf.retry); /* len-2 to remove FCS. */
return 1; /* success */ return 1; /* success */
} else {
goto failure;
} }
} else {
crc_failed = 0;
goto failure;
} }
failure:
#if DEBUGx
if (retry_conf.type == RETRY_TYPE_NONE ) {
int j;
text_color_set(DW_COLOR_ERROR);
if (crc_failed)
dw_printf ("CRC failed\n");
if (H.olen != 0)
dw_printf ("Bad olen: %d \n", H.olen);
else if (H.frame_len < MIN_FRAME_LEN) {
dw_printf ("Frame too small\n");
goto end;
}
dw_printf ("FAILURE with frame: frame len = %d\n", H.frame_len);
dw_printf ("\n");
for (j=0; j<H.frame_len; j++) {
dw_printf (" %02x", H.frame_buf[j]);
}
dw_printf ("\nDEC\n");
for (j=0; j<H.frame_len; j++) {
dw_printf ("%c", H.frame_buf[j]>>1);
}
dw_printf ("\nORIG\n");
for (j=0; j<H.frame_len; j++) {
dw_printf ("%c", H.frame_buf[j]);
}
dw_printf ("\n");
}
#endif
end:
return 0; /* failure. */ return 0; /* failure. */
} /* end try_decode */ } /* end try_decode */
@ -634,7 +1052,6 @@ static int sanity_check (unsigned char *buf, int blen, retry_t bits_flipped)
/* only use this to calculate elapsed time. */ /* only use this to calculate elapsed time. */
#if DEBUG
static double dtime_now (void) static double dtime_now (void)
{ {
@ -658,4 +1075,3 @@ static double dtime_now (void)
#endif #endif
} }
#endif

View File

@ -3,15 +3,59 @@
#define HDLC_REC2_H 1 #define HDLC_REC2_H 1
#include "rrbb.h"
#include "ax25_pad.h" /* for packet_t */ #include "ax25_pad.h" /* for packet_t */
#include "rrbb.h"
typedef enum retry_e { typedef enum retry_e {
RETRY_NONE=0, RETRY_NONE=0,
RETRY_SINGLE=1, RETRY_SWAP_SINGLE=1,
RETRY_DOUBLE=2, RETRY_SWAP_DOUBLE=2,
RETRY_TRIPLE=3, RETRY_SWAP_TRIPLE=3,
RETRY_TWO_SEP=4 } retry_t; 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;
typedef enum retry_mode_e {
RETRY_MODE_CONTIGUOUS=0,
RETRY_MODE_SEPARATED=1,
} retry_mode_t;
typedef enum retry_type_e {
RETRY_TYPE_NONE=0,
RETRY_TYPE_SWAP=1,
RETRY_TYPE_REMOVE=2,
RETRY_TYPE_INSERT=3} retry_type_t;
typedef struct retry_conf_s {
retry_t retry;
retry_mode_t mode;
retry_type_t type;
union {
struct {
int bit_idx_a; /* */
int bit_idx_b; /* */
int bit_idx_c; /* */
} sep; /* RETRY_MODE_SEPARATED */
struct {
int bit_idx;
int nr_bits;
} contig; /* RETRY_MODE_CONTIGUOUS */
} u_bits;
int insert_value;
} retry_conf_t;
#if defined(DIREWOLF_C) || defined(ATEST_C) || defined(UDPTEST_C) #if defined(DIREWOLF_C) || defined(ATEST_C) || defined(UDPTEST_C)
@ -20,7 +64,15 @@ static const char * retry_text[] = {
"SINGLE", "SINGLE",
"DOUBLE", "DOUBLE",
"TRIPLE", "TRIPLE",
"TWO_SEP" }; "REMOVE_SINGLE",
"REMOVE_DOUBLE",
"REMOVE_TRIPLE",
"INSERT_SINGLE",
"INSERT_DOUBLE",
"TWO_SEP",
"MANY",
"REMOVE_MANY",
"REMOVE_SEP"};
#endif #endif
void hdlc_rec2_block (rrbb_t block, retry_t fix_bits); void hdlc_rec2_block (rrbb_t block, retry_t fix_bits);

201
kiss.c
View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2011,2013 John Langner, WB2OSZ // Copyright (C) 2011, 2013, 2014 John Langner, WB2OSZ
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -129,8 +129,12 @@
#include <termios.h> #include <termios.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#ifdef __OpenBSD__
#include <errno.h>
#else
#include <sys/errno.h> #include <sys/errno.h>
#endif #endif
#endif
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
@ -164,14 +168,16 @@ static kiss_frame_t kf; /* Accumulated KISS frame and state of decoder. */
static MYFDTYPE pt_master_fd = MYFDERROR; /* File descriptor for my end. */ static MYFDTYPE pt_master_fd = MYFDERROR; /* File descriptor for my end. */
static MYFDTYPE pt_slave_fd = MYFDERROR; /* File descriptor for pseudo terminal */ static char pt_slave_name[32]; /* Pseudo terminal slave name */
/* for use by application. */ /* like /dev/pts/999 */
/* /*
* Symlink to pseudo terminal name which changes. * Symlink to pseudo terminal name which changes.
*/ */
#define DEV_KISS_TNC "/tmp/kisstnc" #define TMP_KISSTNC_SYMLINK "/tmp/kisstnc"
#endif #endif
@ -186,9 +192,14 @@ static MYFDTYPE nullmodem_fd = MYFDERROR;
#endif #endif
// TODO: define in one place, use everywhere.
#if __WIN32__
#define THREAD_F unsigned __stdcall
#else
#define THREAD_F void *
#endif
static THREAD_F kiss_listen_thread (void *arg);
static void * kiss_listen_thread (void *arg);
@ -262,7 +273,7 @@ void kiss_init (struct misc_config_s *mc)
pt_master_fd = kiss_open_pt (); pt_master_fd = kiss_open_pt ();
if (pt_master_fd != MYFDERROR) { if (pt_master_fd != MYFDERROR) {
e = pthread_create (&kiss_pterm_listen_tid, (pthread_attr_t*)NULL, kiss_listen_thread, (void*)(long)pt_master_fd); e = pthread_create (&kiss_pterm_listen_tid, (pthread_attr_t*)NULL, kiss_listen_thread, NULL);
if (e != 0) { if (e != 0) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
perror("Could not create kiss listening thread for Linux pseudo terminal"); perror("Could not create kiss listening thread for Linux pseudo terminal");
@ -300,14 +311,14 @@ void kiss_init (struct misc_config_s *mc)
if (nullmodem_fd != MYFDERROR) { if (nullmodem_fd != MYFDERROR) {
#if __WIN32__ #if __WIN32__
kiss_nullmodem_listen_th = _beginthreadex (NULL, 0, kiss_listen_thread, (void*)(long)nullmodem_fd, 0, NULL); kiss_nullmodem_listen_th = (HANDLE)_beginthreadex (NULL, 0, kiss_listen_thread, NULL, 0, NULL);
if (kiss_nullmodem_listen_th == NULL) { if (kiss_nullmodem_listen_th == NULL) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Could not create kiss nullmodem thread\n"); dw_printf ("Could not create kiss nullmodem thread\n");
return; return;
} }
#else #else
e = pthread_create (&kiss_nullmodem_listen_tid, NULL, kiss_listen_thread, (void*)(long)nullmodem_fd); e = pthread_create (&kiss_nullmodem_listen_tid, NULL, kiss_listen_thread, NULL);
if (e != 0) { if (e != 0) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
perror("Could not create kiss listening thread for Windows virtual COM port."); perror("Could not create kiss listening thread for Windows virtual COM port.");
@ -341,7 +352,7 @@ void kiss_init (struct misc_config_s *mc)
static MYFDTYPE kiss_open_pt (void) static MYFDTYPE kiss_open_pt (void)
{ {
int fd; int fd;
char *slave_device; char *pts;
struct termios ts; struct termios ts;
int e; int e;
//int flags; //int flags;
@ -357,12 +368,13 @@ static MYFDTYPE kiss_open_pt (void)
if (fd == MYFDERROR if (fd == MYFDERROR
|| grantpt (fd) == MYFDERROR || grantpt (fd) == MYFDERROR
|| unlockpt (fd) == MYFDERROR || unlockpt (fd) == MYFDERROR
|| (slave_device = ptsname (fd)) == NULL) { || (pts = ptsname (fd)) == NULL) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR - Could not create pseudo terminal for KISS TNC.\n"); dw_printf ("ERROR - Could not create pseudo terminal for KISS TNC.\n");
return (MYFDERROR); return (MYFDERROR);
} }
strcpy (pt_slave_name, pts);
e = tcgetattr (fd, &ts); e = tcgetattr (fd, &ts);
if (e != 0) { if (e != 0) {
@ -416,9 +428,29 @@ static MYFDTYPE kiss_open_pt (void)
} }
#endif #endif
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf("Virtual KISS TNC is available on %s\n", slave_device); dw_printf("Virtual KISS TNC is available on %s\n", pt_slave_name);
dw_printf("WARNING - Dire Wolf will hang eventually if nothing is reading from it.\n"); dw_printf("WARNING - Dire Wolf will hang eventually if nothing is reading from it.\n");
#if 1
// Sample code shows this. Why would we open it here?
// On Ubuntu, the slave side disappears after a few
// seconds if no one opens it. Same on Raspian which
// is also based on Debian.
// Need to revisit this.
MYFDTYPE pt_slave_fd;
pt_slave_fd = open(pt_slave_name, O_RDWR|O_NOCTTY);
if (pt_slave_fd < 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Can't open %s\n", pt_slave_name);
perror ("");
return MYFDERROR;
}
#endif
/* /*
* The device name is not the same every time. * The device name is not the same every time.
* This is inconvenient for the application because it might * This is inconvenient for the application because it might
@ -427,31 +459,21 @@ static MYFDTYPE kiss_open_pt (void)
* does not need to change when the pseudo terminal name changes. * does not need to change when the pseudo terminal name changes.
*/ */
//TODO: remove symlink on exit. unlink (TMP_KISSTNC_SYMLINK);
unlink (DEV_KISS_TNC);
if (symlink (slave_device, DEV_KISS_TNC) == 0) {
dw_printf ("Created symlink %s -> %s\n", DEV_KISS_TNC, slave_device); // TODO: Is this removed when application exits?
if (symlink (pt_slave_name, TMP_KISSTNC_SYMLINK) == 0) {
dw_printf ("Created symlink %s -> %s\n", TMP_KISSTNC_SYMLINK, pt_slave_name);
} }
else { else {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Failed to create symlink %s\n", DEV_KISS_TNC); dw_printf ("Failed to create symlink %s\n", TMP_KISSTNC_SYMLINK);
perror (""); perror ("");
} }
#if 1
// Sample code shows this. Why would we open it here?
// On Ubuntu, the slave side disappears after a few
// seconds if no one opens it.
pt_slave_fd = open(slave_device, O_RDWR|O_NOCTTY);
if (pt_slave_fd < 0)
return MYFDERROR;
#endif
return (fd); return (fd);
} }
#endif #endif
@ -471,7 +493,7 @@ static MYFDTYPE kiss_open_nullmodem (char *devicename)
MYFDTYPE fd; MYFDTYPE fd;
DCB dcb; DCB dcb;
int ok; int ok;
char bettername[50];
#if DEBUG #if DEBUG
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
@ -487,8 +509,20 @@ static MYFDTYPE kiss_open_nullmodem (char *devicename)
// Read http://support.microsoft.com/kb/156932 // Read http://support.microsoft.com/kb/156932
// Bug fix in release 1.1 - Need to munge name for COM10 and up.
// http://support.microsoft.com/kb/115831
fd = CreateFile(devicename, GENERIC_READ | GENERIC_WRITE, strcpy (bettername, devicename);
if (strncasecmp(devicename, "COM", 3) == 0) {
int n;
n = atoi(devicename+3);
if (n >= 10) {
strcpy (bettername, "\\\\.\\");
strcat (bettername, devicename);
}
}
fd = CreateFile(bettername, GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (fd == MYFDERROR) { if (fd == MYFDERROR) {
@ -510,7 +544,8 @@ static MYFDTYPE kiss_open_nullmodem (char *devicename)
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx */ /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx */
// dcb.BaudRate ? shouldn't matter dcb.DCBlength = sizeof(DCB);
dcb.BaudRate = CBR_9600; // shouldn't matter
dcb.fBinary = 1; dcb.fBinary = 1;
dcb.fParity = 0; dcb.fParity = 0;
dcb.fOutxCtsFlow = 0; dcb.fOutxCtsFlow = 0;
@ -609,7 +644,7 @@ static MYFDTYPE kiss_open_nullmodem (char *devicename)
void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen) void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen)
{ {
unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN]; unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN + 2];
int kiss_len; int kiss_len;
int j; int j;
int err; int err;
@ -637,31 +672,28 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen)
} }
else { else {
kiss_len = 0;
kiss_buff[kiss_len++] = FEND;
kiss_buff[kiss_len++] = chan << 4;
for (j=0; j<flen; j++) { unsigned char stemp[AX25_MAX_PACKET_LEN + 1];
if (fbuf[j] == FEND) { assert (flen < sizeof(stemp));
kiss_buff[kiss_len++] = FESC;
kiss_buff[kiss_len++] = TFEND;
}
else if (fbuf[j] == FESC) {
kiss_buff[kiss_len++] = FESC;
kiss_buff[kiss_len++] = TFESC;
}
else {
kiss_buff[kiss_len++] = fbuf[j];
}
assert (kiss_len < sizeof (kiss_buff));
}
kiss_buff[kiss_len++] = FEND;
/* This has the escapes but not the surrounding FENDs. */ stemp[0] = (chan << 4) + 0;
memcpy (stemp+1, fbuf, flen);
if (kiss_debug >= 2) {
/* AX.25 frame with the CRC removed. */
text_color_set(DW_COLOR_DEBUG);
dw_printf ("\n");
dw_printf ("Packet content before adding KISS framing and any escapes:\n");
hex_dump ((char*)fbuf, flen);
}
kiss_len = kiss_encapsulate (stemp, flen+1, kiss_buff);
/* This has KISS framing and escapes for sending to client app. */
if (kiss_debug) { if (kiss_debug) {
kiss_debug_print (TO_CLIENT, NULL, kiss_buff+1, kiss_len-2); kiss_debug_print (TO_CLIENT, NULL, kiss_buff, kiss_len);
} }
} }
@ -732,7 +764,7 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen)
//nullmodem_fd = MYFDERROR; //nullmodem_fd = MYFDERROR;
} }
} }
else if (nwritten != flen) else if (nwritten != kiss_len)
{ {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError sending KISS message to client application thru null modem. Only %d of %d written.\n\n", (int)nwritten, kiss_len); dw_printf ("\nError sending KISS message to client application thru null modem. Only %d of %d written.\n\n", (int)nwritten, kiss_len);
@ -770,22 +802,16 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen)
* *
* Purpose: Wait for messages from an application. * Purpose: Wait for messages from an application.
* *
* Inputs: arg - File descriptor for reading. * Global In: nullmodem_fd or pt_master_fd
*
* Outputs: pt_slave_fd - File descriptor for communicating with client app.
* *
* Description: Process messages from the client application. * Description: Process messages from the client application.
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
//TODO: should pass fd by reference so it can be zapped.
//BUG: If we close it here, that fact doesn't get back
// to the main receiving thread.
/* Return one byte (value 0 - 255) or terminate thread on error. */ /* Return one byte (value 0 - 255) or terminate thread on error. */
static int kiss_get (MYFDTYPE fd) static int kiss_get (/* MYFDTYPE fd*/ void )
{ {
unsigned char ch; unsigned char ch;
@ -808,7 +834,7 @@ static int kiss_get (MYFDTYPE fd)
while (n == 0) { while (n == 0) {
if ( ! ReadFile (fd, &ch, 1, &n, &ov_rd)) if ( ! ReadFile (nullmodem_fd, &ch, 1, &n, &ov_rd))
{ {
int err1 = GetLastError(); int err1 = GetLastError();
@ -818,7 +844,7 @@ static int kiss_get (MYFDTYPE fd)
if (WaitForSingleObject (ov_rd.hEvent, INFINITE) == WAIT_OBJECT_0) if (WaitForSingleObject (ov_rd.hEvent, INFINITE) == WAIT_OBJECT_0)
{ {
if ( ! GetOverlappedResult (fd, &ov_rd, &n, 1)) if ( ! GetOverlappedResult (nullmodem_fd, &ov_rd, &n, 1))
{ {
int err3 = GetLastError(); int err3 = GetLastError();
@ -835,8 +861,8 @@ static int kiss_get (MYFDTYPE fd)
{ {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("\nKISS ReadFile error %d. Closing connection.\n\n", err1); dw_printf ("\nKISS ReadFile error %d. Closing connection.\n\n", err1);
//CloseHandle (fd); CloseHandle (nullmodem_fd);
//fd = MYFDERROR; nullmodem_fd = MYFDERROR;
//pthread_exit (NULL); //pthread_exit (NULL);
} }
} }
@ -857,19 +883,33 @@ static int kiss_get (MYFDTYPE fd)
#else /* Linux/Cygwin version */ #else /* Linux/Cygwin version */
int n; int n = 0;
n = read(fd, &ch, (size_t)1); while ( n == 0 ) {
n = read(pt_master_fd, &ch, (size_t)1);
if (n != 1) { if (n != 1) {
//text_color_set(DW_COLOR_ERROR);
//dw_printf ("\nError receiving kiss message from client application. Closing connection %d.\n\n", fd);
close (fd); text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError receiving kiss message from client application. Closing %s.\n\n", pt_slave_name);
perror ("");
fd = MYFDERROR; /* Message added between 1.1 beta test and final version 1.1 */
/* TODO: Determine root cause and find proper solution. */
dw_printf ("This is a known problem that sometimes shows up when using with kissattach.\n");
dw_printf ("There are a couple work-arounds described in the Dire Wolf User Guide\n");
dw_printf ("and the Raspberry Pi APRS documents.\n");
close (pt_master_fd);
pt_master_fd = MYFDERROR;
unlink (TMP_KISSTNC_SYMLINK);
pthread_exit (NULL); pthread_exit (NULL);
} }
}
#endif #endif
@ -897,10 +937,8 @@ static int kiss_get (MYFDTYPE fd)
static void * kiss_listen_thread (void *arg) static THREAD_F kiss_listen_thread (void *arg)
{ {
MYFDTYPE fd = (MYFDTYPE)(long)arg;
unsigned char ch; unsigned char ch;
#if DEBUG #if DEBUG
@ -910,14 +948,15 @@ static void * kiss_listen_thread (void *arg)
while (1) { while (1) {
ch = kiss_get(fd); ch = kiss_get();
kiss_rec_byte (&kf, ch, kiss_debug, kiss_send_rec_packet);
if (kiss_frame (&kf, ch, kiss_debug, kiss_send_rec_packet)) {
kiss_process_msg (&kf, kiss_debug);
} }
} /* while (1) */
return (NULL); /* Unreachable but avoids compiler warning. */ #if __WIN32__
return(0);
#else
return; /* Unreachable but avoids compiler warning. */
#endif
} }
/* end kiss.c */ /* end kiss.c */

View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2013 John Langner, WB2OSZ // Copyright (C) 2013, 2014 John Langner, WB2OSZ
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -84,13 +84,181 @@
#include "tq.h" #include "tq.h"
#include "xmit.h" #include "xmit.h"
/* In server.c. Should probably move to some misc. function file. */
void hex_dump (unsigned char *p, int len);
static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug);
#if TEST
#define dw_printf printf
void text_color_set (dw_color_t c)
{
return;
}
#endif
/*------------------------------------------------------------------- /*-------------------------------------------------------------------
* *
* Name: kiss_frame * Name: kiss_encapsulate
* *
* Purpose: Extract a KISS frame from byte stream. * Purpose: Ecapsulate a frame into KISS format.
*
* Inputs: in - Address of input block.
* First byte is the "type indicator" with type and
* channel but we don't care about that here.
* Note that this is "binary" data and can contain
* nul (0x00) values. Don't treat it like a text string!
*
* ilen - Number of bytes in input block.
*
* Outputs: out - Address where to place the KISS encoded representation.
* The sequence is:
* FEND - Magic frame separator.
* data - with certain byte values replaced so
* FEND will never occur here.
* FEND - Magic frame separator.
*
* Returns: Number of bytes in the output.
* Absolute max length will be twice input plus 2.
*
*-----------------------------------------------------------------*/
int kiss_encapsulate (unsigned char *in, int ilen, unsigned char *out)
{
int olen;
int j;
olen = 0;
out[olen++] = FEND;
for (j=0; j<ilen; j++) {
if (in[j] == FEND) {
out[olen++] = FESC;
out[olen++] = TFEND;
}
else if (in[j] == FESC) {
out[olen++] = FESC;
out[olen++] = TFESC;
}
else {
out[olen++] = in[j];
}
}
out[olen++] = FEND;
return (olen);
} /* end kiss_encapsulate */
/*-------------------------------------------------------------------
*
* Name: kiss_unwrap
*
* Purpose: Extract original data from a KISS frame.
*
* Inputs: in - Address of the received the KISS encoded representation.
* The sequence is:
* FEND - Magic frame separator, optional.
* data - with certain byte values replaced so
* FEND will never occur here.
* FEND - Magic frame separator.
* ilen - Number of bytes in input block.
*
* Inputs: out - Where to put the resulting frame without
* the escapes or FEND.
* First byte is the "type indicator" with type and
* channel but we don't care about that here.
* Note that this is "binary" data and can contain
* nul (0x00) values. Don't treat it like a text string!
*
* Returns: Number of bytes in the output.
*
*-----------------------------------------------------------------*/
static int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out)
{
int olen;
int j;
int escaped_mode;
olen = 0;
escaped_mode = 0;
if (ilen < 2) {
/* Need at least the "type indicator" byte and FEND. */
/* Probably more. */
text_color_set(DW_COLOR_ERROR);
dw_printf ("KISS message less than minimum length.\n");
return (0);
}
if (in[ilen-1] == FEND) {
ilen--; /* Don't try to process below. */
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("KISS frame should end with FEND.\n");
}
if (in[0] == FEND) {
j = 1; /* skip over optional leading FEND. */
}
else {
j = 0;
}
for ( ; j<ilen; j++) {
if (in[j] == FEND) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("KISS frame should not have FEND in the middle.\n");
}
if (escaped_mode) {
if (in[j] == TFESC) {
out[olen++] = FESC;
}
else if (in[j] == TFEND) {
out[olen++] = FEND;
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("KISS protocol error. Found 0x%02x after FESC.\n", in[j]);
}
escaped_mode = 0;
}
else if (in[j] == FESC) {
escaped_mode = 1;
}
else {
out[olen++] = in[j];
}
}
return (olen);
} /* end kiss_unwrap */
#ifndef TEST
/*-------------------------------------------------------------------
*
* Name: kiss_rec_byte
*
* Purpose: Process one byte from a KISS client app.
* *
* Inputs: kf - Current state of building a frame. * Inputs: kf - Current state of building a frame.
* ch - A byte from the input stream. * ch - A byte from the input stream.
@ -100,10 +268,8 @@
* Outputs: kf - Current state is updated. * Outputs: kf - Current state is updated.
* *
* Returns: TRUE when a complete frame is ready for processing. * Returns: TRUE when a complete frame is ready for processing.
// TODO: void later
* *
* Bug: For send, the debug output shows exactly what is
* being sent including the surrounding FEND and any
* escapes. For receive, we don't show those.
* *
*-----------------------------------------------------------------*/ *-----------------------------------------------------------------*/
@ -131,12 +297,18 @@
* Let's try to keep it happy by sending back a command prompt. * Let's try to keep it happy by sending back a command prompt.
*/ */
int kiss_frame (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(int,unsigned char*,int))
void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(int,unsigned char*,int))
{ {
//printf ("kiss_frame ( %c %02x ) \n", ch, ch);
switch (kf->state) { switch (kf->state) {
case KS_SEARCHING: /* Searching for starting FEND. */ case KS_SEARCHING: /* Searching for starting FEND. */
default:
if (ch == FEND) { if (ch == FEND) {
@ -150,8 +322,9 @@ int kiss_frame (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(i
} }
kf->kiss_len = 0; kf->kiss_len = 0;
kf->kiss_msg[kf->kiss_len++] = ch;
kf->state = KS_COLLECTING; kf->state = KS_COLLECTING;
return 0; return;
} }
/* Noise to be rejected. */ /* Noise to be rejected. */
@ -165,7 +338,7 @@ int kiss_frame (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(i
kf->noise[kf->noise_len] = '\0'; kf->noise[kf->noise_len] = '\0';
} }
/* Try to appease it by sending something back. */ /* Try to appease client app by sending something back. */
if (strcasecmp("restart\r", (char*)(kf->noise)) == 0 || if (strcasecmp("restart\r", (char*)(kf->noise)) == 0 ||
strcasecmp("reset\r", (char*)(kf->noise)) == 0) { strcasecmp("reset\r", (char*)(kf->noise)) == 0) {
(*sendfun) (0, (unsigned char *)"\xc0\xc0", -1); (*sendfun) (0, (unsigned char *)"\xc0\xc0", -1);
@ -175,24 +348,51 @@ int kiss_frame (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(i
} }
kf->noise_len = 0; kf->noise_len = 0;
} }
return 0; return;
break;
case KS_COLLECTING: /* Frame collection in progress. */ case KS_COLLECTING: /* Frame collection in progress. */
if (ch == FEND) { if (ch == FEND) {
unsigned char unwrapped[AX25_MAX_PACKET_LEN];
int ulen;
/* End of frame. */ /* End of frame. */
if (kf->kiss_len == 0) { if (kf->kiss_len == 0) {
/* Empty frame. Starting a new one. */
kf->kiss_msg[kf->kiss_len++] = ch;
return;
}
if (kf->kiss_len == 1 && kf->kiss_msg[0] == FEND) {
/* Empty frame. Just go on collecting. */ /* Empty frame. Just go on collecting. */
return 0; return;
} }
kf->kiss_msg[kf->kiss_len++] = ch;
if (debug) { if (debug) {
/* As received over the wire from client app. */
kiss_debug_print (FROM_CLIENT, NULL, kf->kiss_msg, kf->kiss_len); kiss_debug_print (FROM_CLIENT, NULL, kf->kiss_msg, kf->kiss_len);
} }
ulen = kiss_unwrap (kf->kiss_msg, kf->kiss_len, unwrapped);
if (debug >= 2) {
/* Append CRC to this and it goes out over the radio. */
text_color_set(DW_COLOR_DEBUG);
dw_printf ("\n");
dw_printf ("Packet content after removing KISS framing and any escapes:\n");
/* Don't include the "type" indicator. */
/* It contains the radio channel and type should always be 0 here. */
hex_dump (unwrapped+1, ulen-1);
}
kiss_process_msg (unwrapped, ulen, debug);
kf->state = KS_SEARCHING; kf->state = KS_SEARCHING;
return 1; return;
} }
if (kf->kiss_len < MAX_KISS_LEN) { if (kf->kiss_len < MAX_KISS_LEN) {
@ -202,35 +402,15 @@ int kiss_frame (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(i
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("KISS message exceeded maximum length.\n"); dw_printf ("KISS message exceeded maximum length.\n");
} }
return 0; return;
break;
case KS_ESCAPE: /* Expecting TFESC or TFEND. */
if (kf->kiss_len >= MAX_KISS_LEN) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("KISS message exceeded maximum length.\n");
kf->state = KS_COLLECTING;
return 0;
} }
if (ch == TFESC) { return; /* unreachable but suppress compiler warning. */
kf->kiss_msg[kf->kiss_len++] = FESC;
}
else if (ch == TFEND) {
kf->kiss_msg[kf->kiss_len++] = FEND;
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("KISS protocol error. TFESC or TFEND expected.\n");
}
kf->state = KS_COLLECTING; } /* end kiss_rec_byte */
return 0;
}
return 0; /* unreachable but suppress compiler warning. */
} /* end kiss_frame */
/*------------------------------------------------------------------- /*-------------------------------------------------------------------
@ -239,21 +419,23 @@ int kiss_frame (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(i
* *
* Purpose: Process a message from the KISS client. * Purpose: Process a message from the KISS client.
* *
* Inputs: kf - Current state of building a frame. * Inputs: kiss_msg - Kiss frame with FEND and escapes removed.
* Should be complete. * The first byte contains channel and command.
*
* kiss_len - Number of bytes including the command.
* *
* debug - Debug option is selected. * debug - Debug option is selected.
* *
*-----------------------------------------------------------------*/ *-----------------------------------------------------------------*/
void kiss_process_msg (kiss_frame_t *kf, int debug) static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug)
{ {
int port; int port;
int cmd; int cmd;
packet_t pp; packet_t pp;
port = (kf->kiss_msg[0] >> 4) & 0xf; port = (kiss_msg[0] >> 4) & 0xf;
cmd = kf->kiss_msg[0] & 0xf; cmd = kiss_msg[0] & 0xf;
switch (cmd) switch (cmd)
{ {
@ -262,12 +444,12 @@ void kiss_process_msg (kiss_frame_t *kf, int debug)
/* Special hack - Discard apparently bad data from Linux AX25. */ /* Special hack - Discard apparently bad data from Linux AX25. */
if ((port == 2 || port == 8) && if ((port == 2 || port == 8) &&
kf->kiss_msg[1] == 'Q' << 1 && kiss_msg[1] == 'Q' << 1 &&
kf->kiss_msg[2] == 'S' << 1 && kiss_msg[2] == 'S' << 1 &&
kf->kiss_msg[3] == 'T' << 1 && kiss_msg[3] == 'T' << 1 &&
kf->kiss_msg[4] == ' ' << 1 && kiss_msg[4] == ' ' << 1 &&
kf->kiss_msg[15] == 3 && kiss_msg[15] == 3 &&
kf->kiss_msg[16] == 0xcd) { kiss_msg[16] == 0xcd) {
if (debug) { if (debug) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
@ -276,7 +458,16 @@ void kiss_process_msg (kiss_frame_t *kf, int debug)
return; return;
} }
pp = ax25_from_frame (kf->kiss_msg+1, kf->kiss_len-1, -1); // Should really check if single or dual channel mode.
// Do more thoroughly in 1.2.
if (port != 0 && port != 1) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Invalid channel %d from KISS client app.\n", port);
return;
}
pp = ax25_from_frame (kiss_msg+1, kiss_len-1, -1);
if (pp == NULL) { if (pp == NULL) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR - Invalid KISS data frame from client app.\n"); dw_printf ("ERROR - Invalid KISS data frame from client app.\n");
@ -302,35 +493,35 @@ void kiss_process_msg (kiss_frame_t *kf, int debug)
case 1: /* TXDELAY */ case 1: /* TXDELAY */
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("KISS protocol set TXDELAY = %d, port %d\n", kf->kiss_msg[1], port); dw_printf ("KISS protocol set TXDELAY = %d, port %d\n", kiss_msg[1], port);
xmit_set_txdelay (port, kf->kiss_msg[1]); xmit_set_txdelay (port, kiss_msg[1]);
break; break;
case 2: /* Persistence */ case 2: /* Persistence */
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("KISS protocol set Persistence = %d, port %d\n", kf->kiss_msg[1], port); dw_printf ("KISS protocol set Persistence = %d, port %d\n", kiss_msg[1], port);
xmit_set_persist (port, kf->kiss_msg[1]); xmit_set_persist (port, kiss_msg[1]);
break; break;
case 3: /* SlotTime */ case 3: /* SlotTime */
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("KISS protocol set SlotTime = %d, port %d\n", kf->kiss_msg[1], port); dw_printf ("KISS protocol set SlotTime = %d, port %d\n", kiss_msg[1], port);
xmit_set_slottime (port, kf->kiss_msg[1]); xmit_set_slottime (port, kiss_msg[1]);
break; break;
case 4: /* TXtail */ case 4: /* TXtail */
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("KISS protocol set TXtail = %d, port %d\n", kf->kiss_msg[1], port); dw_printf ("KISS protocol set TXtail = %d, port %d\n", kiss_msg[1], port);
xmit_set_txtail (port, kf->kiss_msg[1]); xmit_set_txtail (port, kiss_msg[1]);
break; break;
case 5: /* FullDuplex */ case 5: /* FullDuplex */
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("KISS protocol set FullDuplex = %d, port %d\n", kf->kiss_msg[1], port); dw_printf ("KISS protocol set FullDuplex = %d, port %d\n", kiss_msg[1], port);
break; break;
case 6: /* TNC specific */ case 6: /* TNC specific */
@ -348,7 +539,7 @@ void kiss_process_msg (kiss_frame_t *kf, int debug)
default: default:
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("KISS Invalid command %d\n", cmd); dw_printf ("KISS Invalid command %d\n", cmd);
kiss_debug_print (FROM_CLIENT, NULL, kf->kiss_msg, kf->kiss_len); kiss_debug_print (FROM_CLIENT, NULL, kiss_msg, kiss_len);
break; break;
} }
@ -369,12 +560,6 @@ void kiss_process_msg (kiss_frame_t *kf, int debug)
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
/* In server.c. Should probably move to some misc. function file. */
void hex_dump (unsigned char *p, int len);
void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int msg_len) void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int msg_len)
{ {
const char *direction [2] = { "from", "to" }; const char *direction [2] = { "from", "to" };
@ -390,18 +575,69 @@ void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int
dw_printf ("\n"); dw_printf ("\n");
if (special == NULL) { if (special == NULL) {
unsigned char *p; /* to skip over FEND if present. */
p = pmsg;
if (*p == FEND) p++;
dw_printf ("%s %s %s KISS client application, port %d, total length = %d\n", dw_printf ("%s %s %s KISS client application, port %d, total length = %d\n",
prefix[(int)fromto], function[pmsg[0] & 0xf], direction[(int)fromto], prefix[(int)fromto], function[p[0] & 0xf], direction[(int)fromto],
(pmsg[0] >> 4) & 0xf, msg_len); (p[0] >> 4) & 0xf, msg_len);
} }
else { else {
dw_printf ("%s %s %s KISS client application, total length = %d\n", dw_printf ("%s %s %s KISS client application, total length = %d\n",
prefix[(int)fromto], special, direction[(int)fromto], prefix[(int)fromto], special, direction[(int)fromto],
msg_len); msg_len);
} }
hex_dump ((char*)pmsg, msg_len); hex_dump (pmsg, msg_len);
} /* end kiss_debug_print */ } /* end kiss_debug_print */
#endif
/* Quick unit test for encapsulate & unwrap */
// $ gcc -DTEST kiss_frame.c ; ./a
// Quick KISS test passed OK.
#if TEST
main ()
{
unsigned char din[512];
unsigned char kissed[520];
unsigned char dout[520];
int klen;
int dlen;
int k;
for (k = 0; k < 512; k++) {
if (k < 256) {
din[k] = k;
}
else {
din[k] = 511 - k;
}
}
klen = kiss_encapsulate (din, 512, kissed);
assert (klen == 512 + 6);
dlen = kiss_unwrap (kissed, klen, dout);
assert (dlen == 512);
assert (memcmp(din, dout, 512) == 0);
dlen = kiss_unwrap (kissed+1, klen-1, dout);
assert (dlen == 512);
assert (memcmp(din, dout, 512) == 0);
printf ("Quick KISS test passed OK.\n");
}
#endif
/* end kiss_frame.c */ /* end kiss_frame.c */

View File

@ -12,13 +12,14 @@
#define TFESC 0xDD #define TFESC 0xDD
enum kiss_state_e { enum kiss_state_e {
KS_SEARCHING, /* Looking for FEND to start KISS frame. */ KS_SEARCHING, /* Looking for FEND to start KISS frame. */
KS_COLLECTING, /* In process of collecting KISS frame. */ KS_COLLECTING}; /* In process of collecting KISS frame. */
KS_ESCAPE }; /* FESC found in frame. */
#define MAX_KISS_LEN 2048 /* Spec calls for at least 1024. */ #define MAX_KISS_LEN 2048 /* Spec calls for at least 1024. */
/* Might want to make it longer to accomodate */
/* maximum packet length. */
#define MAX_NOISE_LEN 100 #define MAX_NOISE_LEN 100
@ -27,6 +28,8 @@ typedef struct kiss_frame_s {
enum kiss_state_e state; enum kiss_state_e state;
unsigned char kiss_msg[MAX_KISS_LEN]; unsigned char kiss_msg[MAX_KISS_LEN];
/* Leading FEND is optional. */
/* Contains escapes and ending FEND. */
int kiss_len; int kiss_len;
unsigned char noise[MAX_NOISE_LEN]; unsigned char noise[MAX_NOISE_LEN];
@ -36,9 +39,10 @@ typedef struct kiss_frame_s {
int kiss_frame (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(int,unsigned char*,int)); int kiss_encapsulate (unsigned char *in, int ilen, unsigned char *out);
void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(int,unsigned char*,int));
void kiss_process_msg (kiss_frame_t *kf, int debug);
typedef enum fromto_e { FROM_CLIENT=0, TO_CLIENT=1 } fromto_t; typedef enum fromto_e { FROM_CLIENT=0, TO_CLIENT=1 } fromto_t;

View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2011-2013 John Langner, WB2OSZ // Copyright (C) 2011-2014 John Langner, WB2OSZ
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -125,6 +125,7 @@
static kiss_frame_t kf; /* Accumulated KISS frame and state of decoder. */ static kiss_frame_t kf; /* Accumulated KISS frame and state of decoder. */
// TODO: multiple instances if multiple KISS network clients!
static int client_sock; /* File descriptor for socket for */ static int client_sock; /* File descriptor for socket for */
@ -471,31 +472,28 @@ void kissnet_send_rec_packet (int chan, unsigned char *fbuf, int flen)
} }
else { else {
kiss_len = 0;
kiss_buff[kiss_len++] = FEND;
kiss_buff[kiss_len++] = chan << 4;
for (j=0; j<flen; j++) { unsigned char stemp[AX25_MAX_PACKET_LEN + 1];
if (fbuf[j] == FEND) { assert (flen < sizeof(stemp));
kiss_buff[kiss_len++] = FESC;
kiss_buff[kiss_len++] = TFEND;
}
else if (fbuf[j] == FESC) {
kiss_buff[kiss_len++] = FESC;
kiss_buff[kiss_len++] = TFESC;
}
else {
kiss_buff[kiss_len++] = fbuf[j];
}
assert (kiss_len < sizeof (kiss_buff));
}
kiss_buff[kiss_len++] = FEND;
/* Bug: This has the escapes but not the surrounding FENDs. */ stemp[0] = (chan << 4) + 0;
memcpy (stemp+1, fbuf, flen);
if (kiss_debug >= 2) {
/* AX.25 frame with the CRC removed. */
text_color_set(DW_COLOR_DEBUG);
dw_printf ("\n");
dw_printf ("Packet content before adding KISS framing and any escapes:\n");
hex_dump ((char*)fbuf, flen);
}
kiss_len = kiss_encapsulate (stemp, flen+1, kiss_buff);
/* This has the escapes and the surrounding FENDs. */
if (kiss_debug) { if (kiss_debug) {
kiss_debug_print (TO_CLIENT, NULL, kiss_buff+1, kiss_len-2); kiss_debug_print (TO_CLIENT, NULL, kiss_buff, kiss_len);
} }
} }
@ -658,11 +656,8 @@ static void * kissnet_listen_thread (void *arg)
while (1) { while (1) {
ch = kiss_get(); ch = kiss_get();
kiss_rec_byte (&kf, ch, kiss_debug, kissnet_send_rec_packet);
if (kiss_frame (&kf, ch, kiss_debug, kissnet_send_rec_packet)) {
kiss_process_msg (&kf, kiss_debug);
} }
} /* while (1) */
return (NULL); /* to suppress compiler warning. */ return (NULL); /* to suppress compiler warning. */

242
latlong.c
View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2013 John Langner, WB2OSZ // Copyright (C) 2013,2014 John Langner, WB2OSZ
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -279,3 +279,243 @@ void longitude_to_comp_str (double dlong, char *clon)
clon[2] = x2 + 33; clon[2] = x2 + 33;
clon[3] = x3 + 33; clon[3] = x3 + 33;
} }
/*------------------------------------------------------------------
*
* Name: latitude_to_nmea
*
* Purpose: Convert numeric latitude to strings for NMEA sentence.
*
* Inputs: dlat - Floating point degrees.
*
* Outputs: slat - String in format ddmm.mmmm
* hemi - Hemisphere or empty string.
*
* Returns: None
*
*----------------------------------------------------------------*/
void latitude_to_nmea (double dlat, char *slat, char *hemi)
{
int ideg; /* whole number of degrees. */
double dmin; /* Minutes after removing degrees. */
char smin[10]; /* Minutes in format mm.mmmm */
if (dlat == G_UNKNOWN) {
strcpy (slat, "");
strcpy (hemi, "");
return;
}
if (dlat < -90.) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Latitude is less than -90. Changing to -90.n");
dlat = -90.;
}
if (dlat > 90.) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Latitude is greater than 90. Changing to 90.n");
dlat = 90.;
}
if (dlat < 0) {
dlat = (- dlat);
strcpy (hemi, "S");
}
else {
strcpy (hemi, "N");
}
ideg = (int)dlat;
dmin = (dlat - ideg) * 60.;
sprintf (smin, "%07.4f", dmin);
/* Due to roundoff, 59.99999 could come out as "60.0000" */
if (smin[0] == '6') {
smin[0] = '0';
ideg++;
}
sprintf (slat, "%02d%s", ideg, smin);
} /* end latitude_to_str */
/*------------------------------------------------------------------
*
* Name: longitude_to_nmea
*
* Purpose: Convert numeric longitude to strings for NMEA sentence.
*
* Inputs: dlong - Floating point degrees.
*
* Outputs: slong - String in format dddmm.mmmm
* hemi - Hemisphere or empty string.
*
* Returns: None
*
*----------------------------------------------------------------*/
void longitude_to_nmea (double dlong, char *slong, char *hemi)
{
int ideg; /* whole number of degrees. */
double dmin; /* Minutes after removing degrees. */
char smin[10]; /* Minutes in format mm.mmmm */
if (dlong == G_UNKNOWN) {
strcpy (slong, "");
strcpy (hemi, "");
return;
}
if (dlong < -180.) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("longitude is less than -180. Changing to -180.n");
dlong = -180.;
}
if (dlong > 180.) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("longitude is greater than 180. Changing to 180.n");
dlong = 180.;
}
if (dlong < 0) {
dlong = (- dlong);
strcpy (hemi, "W");
}
else {
strcpy (hemi, "E");
}
ideg = (int)dlong;
dmin = (dlong - ideg) * 60.;
sprintf (smin, "%07.4f", dmin);
/* Due to roundoff, 59.99999 could come out as "60.0000" */
if (smin[0] == '6') {
smin[0] = '0';
ideg++;
}
sprintf (slong, "%03d%s", ideg, smin);
} /* end longitude_to_nmea */
/*------------------------------------------------------------------
*
* Function: latitude_from_nmea
*
* Purpose: Convert NMEA latitude encoding to degrees.
*
* Inputs: pstr - Pointer to numeric string.
* phemi - Pointer to following field. Should be N or S.
*
* Returns: Double precision value in degrees. Negative for South.
*
* Description: Latitude field has
* 2 digits for degrees
* 2 digits for minutes
* period
* Variable number of fractional digits for minutes.
* I've seen 2, 3, and 4 fractional digits.
*
*
* Bugs: Very little validation of data.
*
* Errors: Return constant G_UNKNOWN for any type of error.
* Could we use special "NaN" code?
*
*------------------------------------------------------------------*/
double latitude_from_nmea (char *pstr, char *phemi)
{
double lat;
if ( ! isdigit((unsigned char)(pstr[0]))) return (G_UNKNOWN);
if (pstr[4] != '.') return (G_UNKNOWN);
lat = (pstr[0] - '0') * 10 + (pstr[1] - '0') + atof(pstr+2) / 60.0;
if (lat < 0 || lat > 90) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Error: Latitude not in range of 0 to 90.\n");
}
// Saw this one time:
// $GPRMC,000000,V,0000.0000,0,00000.0000,0,000,000,000000,,*01
// If location is unknown, I think the hemisphere should be
// an empty string. TODO: Check on this.
// 'V' means void, so sentence should be discarded rather than
// trying to extract any data from it.
if (*phemi != 'N' && *phemi != 'S' && *phemi != '\0') {
text_color_set(DW_COLOR_ERROR);
dw_printf("Error: Latitude hemisphere should be N or S.\n");
}
if (*phemi == 'S') lat = ( - lat);
return (lat);
}
/*------------------------------------------------------------------
*
* Function: longitude_from_nmea
*
* Purpose: Convert NMEA longitude encoding to degrees.
*
* Inputs: pstr - Pointer to numeric string.
* phemi - Pointer to following field. Should be E or W.
*
* Returns: Double precision value in degrees. Negative for West.
*
* Description: Longitude field has
* 3 digits for degrees
* 2 digits for minutes
* period
* Variable number of fractional digits for minutes
*
*
* Bugs: Very little validation of data.
*
* Errors: Return constant G_UNKNOWN for any type of error.
* Could we use special "NaN" code?
*
*------------------------------------------------------------------*/
double longitude_from_nmea (char *pstr, char *phemi)
{
double lon;
if ( ! isdigit((unsigned char)(pstr[0]))) return (G_UNKNOWN);
if (pstr[5] != '.') return (G_UNKNOWN);
lon = (pstr[0] - '0') * 100 + (pstr[1] - '0') * 10 + (pstr[2] - '0') + atof(pstr+3) / 60.0;
if (lon < 0 || lon > 180) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Error: Longitude not in range of 0 to 180.\n");
}
if (*phemi != 'E' && *phemi != 'W' && *phemi != '\0') {
text_color_set(DW_COLOR_ERROR);
dw_printf("Error: Longitude hemisphere should be E or W.\n");
}
if (*phemi == 'W') lon = ( - lon);
return (lon);
}

View File

@ -9,5 +9,12 @@
void latitude_to_str (double dlat, int ambiguity, char *slat); void latitude_to_str (double dlat, int ambiguity, char *slat);
void longitude_to_str (double dlong, int ambiguity, char *slong); void longitude_to_str (double dlong, int ambiguity, char *slong);
void latitude_to_comp_str (double dlat, char *clat); void latitude_to_comp_str (double dlat, char *clat);
void longitude_to_comp_str (double dlon, char *clon); void longitude_to_comp_str (double dlon, char *clon);
void latitude_to_nmea (double dlat, char *slat, char *hemi);
void longitude_to_nmea (double dlong, char *slong, char *hemi);
double latitude_from_nmea (char *pstr, char *phemi);
double longitude_from_nmea (char *pstr, char *phemi);

362
log.c Normal file
View File

@ -0,0 +1,362 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2014 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/>.
//
/*------------------------------------------------------------------
*
* File: log.c
*
* Purpose: Save received packets to a log file.
*
* Description: Rather than saving the raw, sometimes rather cryptic and
* unreadable, format, write separated properties into
* CSV format for easy reading and later processing.
*
*
*------------------------------------------------------------------*/
#include <stdio.h>
#include <time.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include "direwolf.h"
#include "ax25_pad.h"
#include "textcolor.h"
#include "decode_aprs.h"
#include "log.h"
/*
* CSV format needs quotes if value contains comma or quote.
*/
static void quote_for_csv (char *out, const char *in) {
const char *p;
char *q = out;
int need_quote = 0;
for (p = in; *p != '\0'; p++) {
if (*p == ',' || *p == '"') {
need_quote = 1;
break;
}
}
if (need_quote) {
*q++ = '"';
for (p = in; *p != '\0'; p++) {
if (*p == '"') {
*q++ = *p;
}
*q++ = *p;
}
*q++ = '"';
*q = '\0';
}
else {
strcpy (out, in);
}
}
/*------------------------------------------------------------------
*
* Function: log_init
*
* Purpose: Initialization at start of application.
*
* Inputs: path - Path of log file directory.
* Use "." for current directory.
* Empty string disables feature.
*
* Global Out: g_log_dir - Save directory here for later use.
* g_log_fp - File pointer for writing.
* g_open_fname - Name of currently open file.
*
*------------------------------------------------------------------*/
static char g_log_dir[80];
static FILE *g_log_fp;
static char g_open_fname[20];
void log_init (char *path)
{
struct stat st;
strcpy (g_log_dir, "");
g_log_fp = NULL;
strcpy (g_open_fname, "");
if (strlen(path) == 0) {
return;
}
if (stat(path,&st) == 0) {
// Exists, but is it a directory?
if (S_ISDIR(st.st_mode)) {
// Specified directory exists.
strcpy (g_log_dir, path);
}
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, ".");
}
}
else {
// Doesn't exist. Try to create it.
// parent directory must exist.
// We don't create multiple levels like "mkdir -p"
#if __WIN32__
if (_mkdir (path) == 0) {
#else
if (mkdir (path, 0777) == 0) {
#endif
// Success.
text_color_set(DW_COLOR_INFO);
dw_printf ("Log file location \"%s\" has been created.\n", path);
strcpy (g_log_dir, path);
}
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, ".");
}
}
}
/*------------------------------------------------------------------
*
* Function: log_write
*
* Purpose: Save information to log file.
*
* Inputs: chan - Radio channel where heard.
*
* A - Explode information from APRS packet.
*
* pp - Received packet object.
*
* alevel - audio level.
*
* retries - Amount of effort to get a good CRC.
*
*------------------------------------------------------------------*/
void log_write (int chan, decode_aprs_t *A, packet_t pp, int alevel, retry_t retries)
{
time_t now; // make 'now' a parameter so we can process historical data ???
char fname[20];
struct tm tm;
if (strlen(g_log_dir) == 0) return;
// Generate the file name from current date, UTC.
now = time(NULL);
gmtime_r (&now, &tm);
// Microsoft doesn't recognize %F as equivalent to %Y-%m-%d
strftime (fname, sizeof(fname), "%Y-%m-%d.log", &tm);
// Close current file if name has changed
if (g_log_fp != NULL && strcmp(fname, g_open_fname) != 0) {
log_term ();
}
// Open for append if not already open.
if (g_log_fp == NULL) {
char full_path[120];
struct stat st;
int already_there;
strcpy (full_path, g_log_dir);
#if __WIN32__
strcat (full_path, "\\");
#else
strcat (full_path, "/");
#endif
strcat (full_path, fname);
// See if it already exists.
// This is used later to write a header if it did not exist already.
already_there = stat(full_path,&st) == 0;
text_color_set(DW_COLOR_INFO);
dw_printf("Opening log file \"%s\".\n", fname);
g_log_fp = fopen (full_path, "a");
if (g_log_fp != NULL) {
strcpy (g_open_fname, 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, "");
return;
}
// Write a header suitable for importing into a spreadsheet
// only if this will be the first line.
if ( ! already_there) {
fprintf (g_log_fp, "chan,utime,isotime,source,heard,level,error,dti,name,symbol,latitude,longitude,speed,course,altitude,frequency,offset,tone,system,status,comment\n");
}
}
if (g_log_fp != NULL) {
char itime[24];
char heard[AX25_MAX_ADDR_LEN+1];
int h;
char stemp[256];
char slat[16], slon[16], sspd[12], scse[12], salt[12];
char sfreq[20], soffs[10], stone[10];
char sdti[10];
char sname[24];
char ssymbol[8];
char smfr[60];
char sstatus[40];
char stelemetry[200];
char scomment[256];
// Microsoft doesn't recognize %T as equivalent to %H:%M:%S
strftime (itime, sizeof(itime), "%Y-%m-%dT%H:%M:%SZ", &tm);
/* Who are we hearing? Original station or digipeater? */
/* Similar code in direwolf.c. Combine into one function? */
strcpy(heard, "");
if (pp != NULL) {
if (ax25_get_num_addr(pp) == 0) {
/* Not AX.25. No station to display below. */
h = -1;
strcpy (heard, "");
}
else {
h = ax25_get_heard(pp);
ax25_get_addr_with_ssid(pp, h, heard);
}
if (h >= AX25_REPEATER_2 &&
strncmp(heard, "WIDE", 4) == 0 &&
isdigit(heard[4]) &&
heard[5] == '\0') {
ax25_get_addr_with_ssid(pp, h-1, heard);
strcat (heard, "?");
}
}
// Might need to quote anything that could contain comma or quote.
strcpy(sdti, "");
if (pp != NULL) {
stemp[0] = ax25_get_dti(pp);
stemp[1] = '\0';
quote_for_csv (sdti, stemp);
}
quote_for_csv (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 (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);
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));
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);
fprintf (g_log_fp, "%d,%d,%s,%s,%s,%d,%d,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
chan, (int)now, itime,
A->g_src, heard, alevel, (int)retries, sdti,
sname, ssymbol,
slat, slon, sspd, scse, salt,
sfreq, soffs, stone,
smfr, sstatus, stelemetry, scomment);
fflush (g_log_fp);
}
} /* end log_write */
/*------------------------------------------------------------------
*
* Function: log_term
*
* Purpose: Close any open log file.
* Called when exiting or when date changes.
*
*------------------------------------------------------------------*/
void log_term (void)
{
if (g_log_fp != NULL) {
text_color_set(DW_COLOR_INFO);
dw_printf("Closing log file \"%s\".\n", g_open_fname);
fclose (g_log_fp);
g_log_fp = NULL;
strcpy (g_open_fname, "");
}
} /* end log_term */
/* end log.c */

16
log.h Normal file
View File

@ -0,0 +1,16 @@
/* log.h */
#include "hdlc_rec2.h" // for retry_t
#include "decode_aprs.h" // for decode_aprs_t
void log_init (char *path);
void log_write (int chan, decode_aprs_t *A, packet_t pp, int alevel, retry_t retries);
void log_term (void);

535
log2gpx.c Normal file
View File

@ -0,0 +1,535 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2014 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/>.
//
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#if __WIN32__
char *strsep(char **stringp, const char *delim);
#endif
/*
* Information we gather for each thing.
*/
typedef struct thing_s {
double lat;
double lon;
float alt; /* Meters above average sea level. */
float course;
float speed; /* Meters per second. */
char time[20+1+3];
char name[9+1+2];
char desc[32]; /* freq/offset/tone something like 146.955 MHz -600k PL 74.4 */
char comment[80]; /* Combined mic-e status and comment text */
} thing_t;
static thing_t *things; /* Dynamically sized array. */
static int max_things; /* Current size. */
static int num_things; /* Number of elements currently in use. */
#define UNKNOWN_VALUE (-999) /* Special value to indicate unknown altitude, speed, course. */
#define KNOTS_TO_METERS_PER_SEC(x) ((x)*0.51444444444)
static void read_csv(FILE *fp);
static void unquote (char *in, char *out);
static int compar(const void *a, const void *b);
static void process_things (int first, int last);
int main (int argc, char *argv[])
{
int first, last;
/*
* Allocate array for data.
* Expand it as needed if initial size is inadequate.
*/
num_things = 0;
max_things = 1000;
things = malloc (max_things * sizeof(thing_t));
/*
* Read files listed or stdin if none.
*/
if (argc == 1) {
read_csv (stdin);
}
else {
int n;
for (n=1; n<argc; n++) {
if (strcmp(argv[n], "-") == 0) {
read_csv (stdin);
}
else {
FILE *fp;
fp = fopen (argv[n], "r");
if (fp != NULL) {
read_csv (fp);
fclose (fp);
}
else {
fprintf (stderr, "Can't open %s for read.\n", argv[n]);
exit (1);
}
}
}
}
if (num_things == 0) {
fprintf (stderr, "Nothing to process.\n");
exit (1);
}
/*
* Sort the data so everything for the same name is adjacent and
* in order of time.
*/
qsort (things, num_things, sizeof(thing_t), compar);
//for (i=0; i<num_things; i++) {
// printf ("%d: %s %.6f %.6f %.1f %s\n",
// i,
// things[i].time,
// things[i].lat,
// things[i].lon,
// things[i].alt,
// things[i].name);
//}
/*
* GPX file header.
*/
printf ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n");
printf ("<gpx version=\"1.1\" creator=\"Dire Wolf\">\n");
/*
* Group together all records for the same entity.
*/
last = first = 0;
while (first < num_things) {
while (last < num_things-1 && strcmp(things[first].name, things[last+1].name) == 0) {
last++;
}
process_things (first, last);
first = last + 1;
}
/*
* GPX file tail.
*/
printf ("</gpx>\n");
exit (0);
}
/*
* Read from given file, already open, into things array.
*/
static void read_csv(FILE *fp)
{
char raw[500];
char csv[500];
int n;
while (fgets(raw, sizeof(raw), fp) != NULL) {
char *next;
char *pchan;
char *putime;
char *pisotime;
char *psource;
char *pheard;
char *plevel;
char *perror;
char *pdti;
char *pname;
char *psymbol;
char *platitude;
char *plongitude;
char *pspeed;
char *pcourse;
char *paltitude;
char *pfreq;
char *poffset;
char *ptone;
char *psystem;
char *pstatus;
char *ptelemetry;
char *pcomment;
n = strlen(raw) - 1;
while (n >= 0 && (raw[n] == '\r' || raw[n] == '\n')) {
raw[n] = '\0';
n--;
}
unquote (raw, csv);
//printf ("%s\n", csv);
/*
* Separate out the fields.
*/
next = csv;
pchan = strsep(&next,"\t");
putime = strsep(&next,"\t");
pisotime = strsep(&next,"\t");
psource = strsep(&next,"\t");
pheard = strsep(&next,"\t");
plevel = strsep(&next,"\t");
perror = strsep(&next,"\t");
pdti = strsep(&next,"\t");
pname = strsep(&next,"\t");
psymbol = strsep(&next,"\t");
platitude = strsep(&next,"\t");
plongitude = strsep(&next,"\t");
pspeed = strsep(&next,"\t"); /* Knots, must convert. */
pcourse = strsep(&next,"\t");
paltitude = strsep(&next,"\t"); /* Meters, already correct units. */
pfreq = strsep(&next,"\t");
poffset = strsep(&next,"\t");
ptone = strsep(&next,"\t");
psystem = strsep(&next,"\t");
pstatus = strsep(&next,"\t");
ptelemetry = strsep(&next,"\t"); /* Currently unused. Add to description? */
pcomment = strsep(&next,"\t");
/*
* Skip header line with names of fields.
*/
if (strcmp(pchan, "chan") == 0) {
continue;
}
/*
* Save only if we have valid data.
* (Some packets don't contain a position.)
*/
if (pisotime != NULL && strlen(pisotime) > 0 &&
pname != NULL && strlen(pname) > 0 &&
platitude != NULL && strlen(platitude) > 0 &&
plongitude != NULL && strlen(plongitude) > 0) {
float speed = UNKNOWN_VALUE;
float course = UNKNOWN_VALUE;
float alt = UNKNOWN_VALUE;
double freq = UNKNOWN_VALUE;
int offset = UNKNOWN_VALUE;
char stemp[16], desc[32], comment[256];
if (pspeed != NULL && strlen(pspeed) > 0) {
speed = KNOTS_TO_METERS_PER_SEC(atof(pspeed));
}
if (pcourse != NULL && strlen(pcourse) > 0) {
course = atof(pcourse);
}
if (paltitude != NULL && strlen(paltitude) > 0) {
alt = atof(platitude);
}
/* combine freq/offset/tone into one description string. */
if (pfreq != NULL && strlen(pfreq) > 0) {
freq = atof(pfreq);
sprintf (desc, "%.3f MHz", freq);
}
else {
strcpy (desc, "");
}
if (poffset != NULL && strlen(poffset) > 0) {
offset = atoi(poffset);
if (offset != 0 && offset % 1000 == 0) {
sprintf (stemp, "%+dM", offset / 1000);
}
else {
sprintf (stemp, "%+dk", offset);
}
if (strlen(desc) > 0) strcat (desc, " ");
strcat (desc, stemp);
}
if (ptone != NULL && strlen(ptone) > 0) {
if (*ptone == 'D') {
sprintf (stemp, "DCS %s", ptone+1);
}
else {
sprintf (stemp, "PL %s", ptone);
}
if (strlen(desc) > 0) strcat (desc, " ");
strcat (desc, stemp);
}
strcpy (comment, "");
if (pstatus != NULL && strlen(pstatus) > 0) {
strcpy (comment, pstatus);
}
if (pcomment != NULL && strlen(pcomment) > 0) {
if (strlen(comment) > 0) strcat (comment, ", ");
strcat (comment, pcomment);
}
if (num_things == max_things) {
/* It's full. Grow the array by 50%. */
max_things += max_things / 2;
things = realloc (things, max_things*sizeof(thing_t));
}
things[num_things].lat = atof(platitude);
things[num_things].lon = atof(plongitude);
things[num_things].speed = speed;
things[num_things].course = course;
things[num_things].alt = alt;
strncpy (things[num_things].time, pisotime, sizeof(things[num_things].time));
strncpy (things[num_things].name, pname, sizeof(things[num_things].name));
strncpy (things[num_things].desc, desc, sizeof(things[num_things].desc));
strncpy (things[num_things].comment, comment, sizeof(things[num_things].comment));
num_things++;
}
}
}
/*
* Compare function for use with qsort.
* Order by name then date/time.
*/
static int compar(const void *a, const void *b)
{
thing_t *ta = (thing_t *)a;
thing_t *tb = (thing_t *)b;
int n;
n = strcmp(ta->name, tb->name);
if (n != 0)
return (n);
return (strcmp(ta->time, tb->time));
}
/*
* Take quoting out of CSV data.
* Replace field separator commas with tabs while retaining
* commas that were part of the original data before quoting.
*/
static void unquote (char *in, char *out)
{
char *p;
char *q = out; /* Mind your p's and q's */
int quoted = 0;
for (p=in; *p!='\0'; p++) {
if (*p == '"') {
if (p == in || ( !quoted && *(p-1) == ',')) {
/* " found at beginning of field */
quoted = 1;
}
else if (*(p+1) == '\0' || (quoted && *(p+1) == ',')) {
/* " found at end of field */
quoted = 0;
}
else {
/* " found somewhere in middle of field. */
/* We expect to be in quoted state and we should have a pair. */
if (quoted && *(p+1) == '"') {
/* Keep one and drop the other. */
*q++ = *p;
p++;
}
else {
/* This shouldn't happen. */
fprintf (stderr, "CSV data quoting is messed up.\n");
*q++ = *p;
}
}
}
else if (*p == ',') {
if (quoted) {
/* Comma in original data. Keep it. */
*q++ = *p;
}
else {
/* Comma is field separator. Replace with tab. */
*q++ = '\t';
}
}
else {
/* copy ordinary character. */
*q++ = *p;
}
}
*q = '\0';
}
/*
* Prepare text values for XML.
* Replace significant characters with "predefined entities."
*/
static void xml_text (char *in, char *out)
{
char *p, *q;
q = out;
for (p = in; *p != '\0'; p++) {
if (*p == '"') {
*q++ = '&';
*q++ = 'q';
*q++ = 'u';
*q++ = 'o';
*q++ = 't';
*q++ = ';';
}
else if (*p == '&') {
*q++ = '&';
*q++ = 'a';
*q++ = 'm';
*q++ = 'p';
*q++ = ';';
}
else if (*p == '\'') {
*q++ = '&';
*q++ = 'a';
*q++ = 'p';
*q++ = 'o';
*q++ = 's';
*q++ = ';';
}
else if (*p == '<') {
*q++ = '&';
*q++ = 'l';
*q++ = 't';
*q++ = ';';
}
else if (*p == '>') {
*q++ = '&';
*q++ = 'g';
*q++ = 't';
*q++ = ';';
}
else {
*q++ = *p;
}
}
*q = '\0';
}
/*
* Process all things with the same name.
* They should be sorted by time.
* For stationary entities, generate just one GPX waypoint.
* For moving entities, generate a GPX track.
*/
static void process_things (int first, int last)
{
//printf ("process %d to %d\n", first, last);
int i;
int moved = 0;
char safe_name[30];
char safe_comment[120];
for (i=first+1; i<=last; i++) {
if (things[i].lat != things[first].lat) moved = 1;
if (things[i].lon != things[first].lon) moved = 1;
}
if (moved) {
/*
* Generate track for moving thing.
*/
xml_text (things[first].name, safe_name);
xml_text (things[first].comment, safe_comment);
printf (" <trk>\n");
printf (" <name>%s</name>\n", safe_name);
printf (" <trkseg>\n");
for (i=first; i<=last; i++) {
printf (" <trkpt lat=\"%.6f\" lon=\"%.6f\">\n", things[i].lat, things[i].lon);
if (things[i].speed != UNKNOWN_VALUE) {
printf (" <speed>%.1f</speed>\n", things[i].speed);
}
if (things[i].course != UNKNOWN_VALUE) {
printf (" <course>%.1f</course>\n", things[i].course);
}
if (things[i].alt != UNKNOWN_VALUE) {
printf (" <ele>%.1f</ele>\n", things[i].alt);
}
if (strlen(things[i].desc) > 0) {
printf (" <desc>%s</desc>\n", things[i].desc);
}
if (strlen(safe_comment) > 0) {
printf (" <cmt>%s</cmt>\n", safe_comment);
}
printf (" <time>%s</time>\n", things[i].time);
printf (" </trkpt>\n");
}
printf (" </trkseg>\n");
printf (" </trk>\n");
/* Also generate waypoint for last location. */
}
// Future possibility?
// <sym>Symbol Name</sym> -- not standardized.
/*
* Generate waypoint for stationary thing or last known position for moving thing.
*/
xml_text (things[last].name, safe_name);
xml_text (things[last].comment, safe_comment);
printf (" <wpt lat=\"%.6f\" lon=\"%.6f\">\n", things[last].lat, things[last].lon);
if (things[last].alt != UNKNOWN_VALUE) {
printf (" <ele>%.1f</ele>\n", things[last].alt);
}
if (strlen(things[i].desc) > 0) {
printf (" <desc>%s</desc>\n", things[i].desc);
}
if (strlen(safe_comment) > 0) {
printf (" <cmt>%s</cmt>\n", safe_comment);
}
printf (" <name>%s</name>\n", safe_name);
printf (" </wpt>\n");
}

267
mgn_icon.h Normal file
View File

@ -0,0 +1,267 @@
/*
* MGN_icon.h
*
* Waypoint icon codes for use in the $PMGNWPL sentence.
*
* Derived from Data Transmission Protocol For Magellan Products version 2.11
*/
#define MGN_crossed_square "a"
#define MGN_box "b"
#define MGN_house "c"
#define MGN_aerial "d"
#define MGN_airport "e"
#define MGN_amusement_park "f"
#define MGN_ATM "g"
#define MGN_auto_repair "h"
#define MGN_boating "I"
#define MGN_camping "j"
#define MGN_exit_ramp "k"
#define MGN_first_aid "l"
#define MGN_nav_aid "m"
#define MGN_buoy "n"
#define MGN_fuel "o"
#define MGN_garden "p"
#define MGN_golf "q"
#define MGN_hotel "r"
#define MGN_hunting_fishing "s"
#define MGN_large_city "t"
#define MGN_lighthouse "u"
#define MGN_major_city "v"
#define MGN_marina "w"
#define MGN_medium_city "x"
#define MGN_museum "y"
#define MGN_obstruction "z"
#define MGN_park "aa"
#define MGN_resort "ab"
#define MGN_restaurant "ac"
#define MGN_rock "ad"
#define MGN_scuba "ae"
#define MGN_RV_service "af"
#define MGN_shooting "ag"
#define MGN_sight_seeing "ah"
#define MGN_small_city "ai"
#define MGN_sounding "aj"
#define MGN_sports_arena "ak"
#define MGN_tourist_info "al"
#define MGN_truck_service "am"
#define MGN_winery "an"
#define MGN_wreck "ao"
#define MGN_zoo "ap"
/*
* Mapping from APRS symbols to Magellan.
*
* This is a bit of a challenge because there
* are no icons for moving objects.
* We can use airport for flying things but
* what about wheeled transportation devices?
*/
// TODO: NEEDS MORE WORK!!!
#define MGN_default MGN_crossed_square
#define SYMTAB_SIZE 95
static const char mgn_primary_symtab[SYMTAB_SIZE][3] = {
MGN_default, // 00 --no-symbol--
MGN_default, // ! 01 Police, Sheriff
MGN_default, // " 02 reserved (was rain)
MGN_aerial, // # 03 DIGI (white center)
MGN_default, // $ 04 PHONE
MGN_aerial, // % 05 DX CLUSTER
MGN_aerial, // & 06 HF GATEway
MGN_airport, // ' 07 Small AIRCRAFT
MGN_aerial, // ( 08 Mobile Satellite Station
MGN_default, // ) 09 Wheelchair (handicapped)
MGN_default, // * 10 SnowMobile
MGN_default, // + 11 Red Cross
MGN_default, // , 12 Boy Scouts
MGN_house, // - 13 House QTH (VHF)
MGN_default, // . 14 X
MGN_default, // / 15 Red Dot
MGN_default, // 0 16 # circle (obsolete)
MGN_default, // 1 17 TBD
MGN_default, // 2 18 TBD
MGN_default, // 3 19 TBD
MGN_default, // 4 20 TBD
MGN_default, // 5 21 TBD
MGN_default, // 6 22 TBD
MGN_default, // 7 23 TBD
MGN_default, // 8 24 TBD
MGN_default, // 9 25 TBD
MGN_default, // : 26 FIRE
MGN_camping, // ; 27 Campground (Portable ops)
MGN_default, // < 28 Motorcycle
MGN_default, // = 29 RAILROAD ENGINE
MGN_default, // > 30 CAR
MGN_default, // ? 31 SERVER for Files
MGN_default, // @ 32 HC FUTURE predict (dot)
MGN_first_aid, // A 33 Aid Station
MGN_aerial, // B 34 BBS or PBBS
MGN_boating, // C 35 Canoe
MGN_default, // D 36
MGN_default, // E 37 EYEBALL (Eye catcher!)
MGN_default, // F 38 Farm Vehicle (tractor)
MGN_default, // G 39 Grid Square (6 digit)
MGN_default, // H 40 HOTEL (blue bed symbol)
MGN_aerial, // I 41 TcpIp on air network stn
MGN_default, // J 42
MGN_default, // K 43 School
MGN_default, // L 44 PC user
MGN_default, // M 45 MacAPRS
MGN_aerial, // N 46 NTS Station
MGN_airport, // O 47 BALLOON
MGN_default, // P 48 Police
MGN_default, // Q 49 TBD
MGN_RV_service, // R 50 REC. VEHICLE
MGN_airport, // S 51 SHUTTLE
MGN_default, // T 52 SSTV
MGN_default, // U 53 BUS
MGN_default, // V 54 ATV
MGN_default, // W 55 National WX Service Site
MGN_default, // X 56 HELO
MGN_boating, // Y 57 YACHT (sail)
MGN_default, // Z 58 WinAPRS
MGN_default, // [ 59 Human/Person (HT)
MGN_default, // \ 60 TRIANGLE(DF station)
MGN_default, // ] 61 MAIL/PostOffice(was PBBS)
MGN_airport, // ^ 62 LARGE AIRCRAFT
MGN_default, // _ 63 WEATHER Station (blue)
MGN_aerial, // ` 64 Dish Antenna
MGN_default, // a 65 AMBULANCE
MGN_default, // b 66 BIKE
MGN_default, // c 67 Incident Command Post
MGN_default, // d 68 Fire dept
MGN_zoo, // e 69 HORSE (equestrian)
MGN_default, // f 70 FIRE TRUCK
MGN_airport, // g 71 Glider
MGN_default, // h 72 HOSPITAL
MGN_default, // i 73 IOTA (islands on the air)
MGN_default, // j 74 JEEP
MGN_default, // k 75 TRUCK
MGN_default, // l 76 Laptop
MGN_aerial, // m 77 Mic-E Repeater
MGN_default, // n 78 Node (black bulls-eye)
MGN_default, // o 79 EOC
MGN_default, // p 80 ROVER (puppy, or dog)
MGN_default, // q 81 GRID SQ shown above 128 m
MGN_aerial, // r 82 Repeater
MGN_default, // s 83 SHIP (pwr boat)
MGN_default, // t 84 TRUCK STOP
MGN_default, // u 85 TRUCK (18 wheeler)
MGN_default, // v 86 VAN
MGN_default, // w 87 WATER station
MGN_aerial, // x 88 xAPRS (Unix)
MGN_aerial, // y 89 YAGI @ QTH
MGN_default, // z 90 TBD
MGN_default, // { 91
MGN_default, // | 92 TNC Stream Switch
MGN_default, // } 93
MGN_default }; // ~ 94 TNC Stream Switch
static const char mgn_alternate_symtab[SYMTAB_SIZE][3] = {
MGN_default, // 00 --no-symbol--
MGN_default, // ! 01 EMERGENCY (!)
MGN_default, // " 02 reserved
MGN_aerial, // # 03 OVERLAY DIGI (green star)
MGN_ATM, // $ 04 Bank or ATM (green box)
MGN_default, // % 05 Power Plant with overlay
MGN_aerial, // & 06 I=Igte IGate R=RX T=1hopTX 2=2hopTX
MGN_default, // ' 07 Crash (& now Incident sites)
MGN_default, // ( 08 CLOUDY (other clouds w ovrly)
MGN_aerial, // ) 09 Firenet MEO, MODIS Earth Obs.
MGN_default, // * 10 SNOW (& future ovrly codes)
MGN_default, // + 11 Church
MGN_default, // , 12 Girl Scouts
MGN_house, // - 13 House (H=HF) (O = Op Present)
MGN_default, // . 14 Ambiguous (Big Question mark)
MGN_default, // / 15 Waypoint Destination
MGN_default, // 0 16 CIRCLE (E/I/W=IRLP/Echolink/WIRES)
MGN_default, // 1 17
MGN_default, // 2 18
MGN_default, // 3 19
MGN_default, // 4 20
MGN_default, // 5 21
MGN_default, // 6 22
MGN_default, // 7 23
MGN_aerial, // 8 24 802.11 or other network node
MGN_fuel, // 9 25 Gas Station (blue pump)
MGN_default, // : 26 Hail (& future ovrly codes)
MGN_park, // ; 27 Park/Picnic area
MGN_default, // < 28 ADVISORY (one WX flag)
MGN_default, // = 29 APRStt Touchtone (DTMF users)
MGN_default, // > 30 OVERLAYED CAR
MGN_tourist_info, // ? 31 INFO Kiosk (Blue box with ?)
MGN_default, // @ 32 HURICANE/Trop-Storm
MGN_default, // A 33 overlayBOX DTMF & RFID & XO
MGN_default, // B 34 Blwng Snow (& future codes)
MGN_boating, // C 35 Coast Guard
MGN_default, // D 36 Drizzle (proposed APRStt)
MGN_default, // E 37 Smoke (& other vis codes)
MGN_default, // F 38 Freezng rain (&future codes)
MGN_default, // G 39 Snow Shwr (& future ovrlys)
MGN_default, // H 40 Haze (& Overlay Hazards)
MGN_default, // I 41 Rain Shower
MGN_default, // J 42 Lightening (& future ovrlys)
MGN_default, // K 43 Kenwood HT (W)
MGN_lighthouse, // L 44 Lighthouse
MGN_default, // M 45 MARS (A=Army,N=Navy,F=AF)
MGN_nav_aid, // N 46 Navigation Buoy
MGN_airport, // O 47 Rocket
MGN_default, // P 48 Parking
MGN_default, // Q 49 QUAKE
MGN_restaurant, // R 50 Restaurant
MGN_aerial, // S 51 Satellite/Pacsat
MGN_default, // T 52 Thunderstorm
MGN_default, // U 53 SUNNY
MGN_default, // V 54 VORTAC Nav Aid
MGN_default, // W 55 # NWS site (NWS options)
MGN_default, // X 56 Pharmacy Rx (Apothicary)
MGN_aerial, // Y 57 Radios and devices
MGN_default, // Z 58
MGN_default, // [ 59 W.Cloud (& humans w Ovrly)
MGN_default, // \ 60 New overlayable GPS symbol
MGN_default, // ] 61
MGN_airport, // ^ 62 # Aircraft (shows heading)
MGN_default, // _ 63 # WX site (green digi)
MGN_default, // ` 64 Rain (all types w ovrly)
MGN_aerial, // a 65 ARRL, ARES, WinLINK
MGN_default, // b 66 Blwng Dst/Snd (& others)
MGN_default, // c 67 CD triangle RACES/SATERN/etc
MGN_default, // d 68 DX spot by callsign
MGN_default, // e 69 Sleet (& future ovrly codes)
MGN_default, // f 70 Funnel Cloud
MGN_default, // g 71 Gale Flags
MGN_default, // h 72 Store. or HAMFST Hh=HAM store
MGN_default, // i 73 BOX or points of Interest
MGN_default, // j 74 WorkZone (Steam Shovel)
MGN_default, // k 75 Special Vehicle SUV,ATV,4x4
MGN_default, // l 76 Areas (box,circles,etc)
MGN_default, // m 77 Value Sign (3 digit display)
MGN_default, // n 78 OVERLAY TRIANGLE
MGN_default, // o 79 small circle
MGN_default, // p 80 Prtly Cldy (& future ovrlys)
MGN_default, // q 81
MGN_default, // r 82 Restrooms
MGN_default, // s 83 OVERLAY SHIP/boat (top view)
MGN_default, // t 84 Tornado
MGN_default, // u 85 OVERLAYED TRUCK
MGN_default, // v 86 OVERLAYED Van
MGN_default, // w 87 Flooding
MGN_wreck, // x 88 Wreck or Obstruction ->X<-
MGN_default, // y 89 Skywarn
MGN_default, // z 90 OVERLAYED Shelter
MGN_default, // { 91 Fog (& future ovrly codes)
MGN_default, // | 92 TNC Stream Switch
MGN_default, // } 93
MGN_default }; // ~ 94 TNC Stream Switch

View File

@ -1,3 +1,4 @@
This is NOT used for the Linux version.
These are part of the standard C library for Linux and Cygwin. These are part of the standard C library for Linux and Cygwin.
For the Windows version we need to include our own copy. For the Windows version we need to include our own copy.

View File

@ -60,8 +60,18 @@
* part is picking the best one when there is more than one * part is picking the best one when there is more than one
* success and discarding the rest. * success and discarding the rest.
* *
* New in version 1.1:
*
* Several enhancements provided by Fabrice FAURE:
*
* Additional types of attempts to fix a bad CRC.
* Optimized code to reduce execution time.
* Improved detection of duplicate packets from
* different fixup attempts.
* Set limit on number of packets in fix up later queue.
*
*------------------------------------------------------------------*/ *------------------------------------------------------------------*/
//#define DEBUG 1
#define DIGIPEATER_C #define DIGIPEATER_C
@ -97,8 +107,14 @@ static struct {
int score; int score;
} candidate[MAX_CHANS][MAX_SUBCHANS]; } candidate[MAX_CHANS][MAX_SUBCHANS];
#define MAX_STORED_CRC 256
static unsigned int crc_of_last_to_app[MAX_CHANS]; 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 #define PROCESS_AFTER_BITS 2
@ -139,12 +155,99 @@ void multi_modem_init (struct audio_s *pmodem)
for (chan=0; chan<modem.num_channels; chan++) { for (chan=0; chan<modem.num_channels; chan++) {
process_age[chan] = PROCESS_AFTER_BITS * modem.samples_per_sec / modem.baud[chan]; process_age[chan] = PROCESS_AFTER_BITS * modem.samples_per_sec / modem.baud[chan];
crc_of_last_to_app[chan] = 0x12345678; crc_queue_of_last_to_app[chan] = NULL;
} }
} }
//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;
crc_t plast1;
crc_t pnext;
crc_t new_crc;
unsigned int nb_crc = 1;
if (chan>=MAX_CHANS) {
return -1;
}
new_crc = (crc_t) malloc (10*sizeof(struct crc_s));
if (!new_crc)
return -1;
new_crc->crc = crc;
new_crc->nextp = NULL;
if (crc_queue_of_last_to_app[chan] == NULL) {
crc_queue_of_last_to_app[chan] = new_crc;
nb_crc = 1;
}
else {
nb_crc = 2;
plast = crc_queue_of_last_to_app[chan];
pnext = plast->nextp;
while (pnext != NULL) {
nb_crc++;
plast = pnext;
pnext = pnext->nextp;
}
plast->nextp = new_crc;
}
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf("Out crc_queue_append nb_crc = %d\n", nb_crc);
#endif
return nb_crc;
}
//Remove the crc from the top of the queue
unsigned int crc_queue_remove (unsigned int chan) {
unsigned int res;
crc_t plast;
crc_t pnext;
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf("In crc_queue_remove\n");
#endif
crc_t removed_crc;
if (chan>=MAX_CHANS) {
return 0;
}
removed_crc = crc_queue_of_last_to_app[chan];
if (removed_crc == NULL) {
return 0;
}
else {
crc_queue_of_last_to_app[chan] = removed_crc->nextp;
res = removed_crc->crc;
free(removed_crc);
}
return res;
}
unsigned char is_crc_in_queue(unsigned int chan, unsigned int crc) {
crc_t plast;
crc_t pnext;
if (crc_queue_of_last_to_app[chan] == NULL) {
return 0;
}
else {
plast = crc_queue_of_last_to_app[chan];
do {
pnext = plast->nextp;
if (plast->crc == crc) {
return 1;
}
plast = pnext;
} while (pnext != NULL);
}
return 0;
}
/*------------------------------------------------------------------------------ /*------------------------------------------------------------------------------
* *
@ -310,22 +413,28 @@ void multi_modem_process_rec_frame (int chan, int subchan, unsigned char *fbuf,
* Either pass it along or drop if duplicate. * Either pass it along or drop if duplicate.
*/ */
if (retries == RETRY_TWO_SEP) { if (retries >= RETRY_SWAP_TWO_SEP) {
int mycrc; int mycrc;
char spectrum[MAX_SUBCHANS+1]; char spectrum[MAX_SUBCHANS+1];
int dropped = 0;
memset (spectrum, 0, sizeof(spectrum)); memset (spectrum, 0, sizeof(spectrum));
memset (spectrum, '_', (size_t)modem.num_subchan[chan]); memset (spectrum, '_', (size_t)modem.num_subchan[chan]);
spectrum[subchan] = '.'; spectrum[subchan] = '.';
mycrc = ax25_m_m_crc(pp); 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 #if DEBUG
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("\n%s\n%d.%d: ptr=%p, retry=%d, age=, crc=%04x, score= \n", 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); spectrum, chan, subchan, pp, (int)retries, mycrc,dropped);
#endif #endif
if (mycrc == crc_of_last_to_app[chan]) { if (dropped) {
/* Same as last one. Drop it. */ /* Same as last one. Drop it. */
ax25_delete (pp); ax25_delete (pp);
#if DEBUG #if DEBUG
@ -338,7 +447,8 @@ void multi_modem_process_rec_frame (int chan, int subchan, unsigned char *fbuf,
dw_printf ("Send the best one along.\n"); dw_printf ("Send the best one along.\n");
#endif #endif
app_process_rec_packet (chan, subchan, pp, alevel, retries, spectrum); app_process_rec_packet (chan, subchan, pp, alevel, retries, spectrum);
crc_of_last_to_app[chan] = mycrc; if (crc_queue_append(mycrc, chan) > MAX_STORED_CRC)
crc_queue_remove(chan);
return; return;
} }
@ -397,7 +507,7 @@ static void pick_best_candidate (int chan)
else if (candidate[chan][subchan].retries == RETRY_NONE) { else if (candidate[chan][subchan].retries == RETRY_NONE) {
spectrum[subchan] = '|'; spectrum[subchan] = '|';
} }
else if (candidate[chan][subchan].retries == RETRY_SINGLE) { else if (candidate[chan][subchan].retries == RETRY_SWAP_SINGLE) {
spectrum[subchan] = ':'; spectrum[subchan] = ':';
} }
else { else {
@ -406,7 +516,7 @@ static void pick_best_candidate (int chan)
/* Begining score depends on effort to get a valid frame CRC. */ /* Begining score depends on effort to get a valid frame CRC. */
candidate[chan][subchan].score = 5000 - ((int)candidate[chan][subchan].retries * 1000); candidate[chan][subchan].score = RETRY_MAX * 1000 - ((int)candidate[chan][subchan].retries * 1000);
/* Bump it up slightly if others nearby have the same CRC. */ /* Bump it up slightly if others nearby have the same CRC. */
@ -461,8 +571,8 @@ static void pick_best_candidate (int chan)
candidate[chan][best_subchan].alevel, candidate[chan][best_subchan].alevel,
(int)(candidate[chan][best_subchan].retries), (int)(candidate[chan][best_subchan].retries),
spectrum); spectrum);
crc_of_last_to_app[chan] = candidate[chan][best_subchan].crc; 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. */ /* Someone else will delete so don't do it below. */
candidate[chan][best_subchan].packet_p = NULL; candidate[chan][best_subchan].packet_p = NULL;

1037
nmea.c Normal file

File diff suppressed because it is too large Load Diff

19
nmea.h Normal file
View File

@ -0,0 +1,19 @@
/*
* Name: nmea.h
*/
#include "ax25_pad.h" /* for packet_t */
#include "config.h" /* for struct misc_config_s */
void nmea_init (struct misc_config_s *misc_config);
void nmea_set_debug (int n);
void nmea_send_waypoint (char *wname_in, double dlat, double dlong, char symtab, char symbol,
float alt, float course, float speed, char *comment);
/* end nmea.h */

183
ptt.c
View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2011,2013 John Langner, WB2OSZ // Copyright (C) 2011,2013,2014 John Langner, WB2OSZ
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -34,6 +34,8 @@
* *
* Version 0.9: Add ability to use GPIO pins on Linux. * Version 0.9: Add ability to use GPIO pins on Linux.
* *
* Version 1.1: Add parallel printer port for x86 Linux only.
*
* References: http://www.robbayer.com/files/serial-win.pdf * References: http://www.robbayer.com/files/serial-win.pdf
* *
* https://www.kernel.org/doc/Documentation/gpio.txt * https://www.kernel.org/doc/Documentation/gpio.txt
@ -86,6 +88,14 @@ typedef int HANDLE;
#endif #endif
#define LPT_IO_ADDR 0x378
#if TEST
#define dw_printf printf
#endif
/*------------------------------------------------------------------- /*-------------------------------------------------------------------
* *
@ -108,6 +118,7 @@ static ptt_method_t ptt_method[MAX_CHANS]; /* Method for PTT signal. */
/* PTT_METHOD_NONE - not configured. Could be using VOX. */ /* PTT_METHOD_NONE - not configured. Could be using VOX. */
/* PTT_METHOD_SERIAL - serial (com) port. */ /* PTT_METHOD_SERIAL - serial (com) port. */
/* PTT_METHOD_GPIO - general purpose I/O. */ /* PTT_METHOD_GPIO - general purpose I/O. */
/* PTT_METHOD_LPT - Parallel printer port. */
static char ptt_device[MAX_CHANS][20]; /* Name of serial port device. */ static char ptt_device[MAX_CHANS][20]; /* Name of serial port device. */
/* e.g. COM1 or /dev/ttyS0. */ /* e.g. COM1 or /dev/ttyS0. */
@ -117,6 +128,10 @@ static ptt_line_t ptt_line[MAX_CHANS]; /* RTS or DTR when using serial port. */
static int ptt_gpio[MAX_CHANS]; /* GPIO number. Only used for Linux. */ static int ptt_gpio[MAX_CHANS]; /* GPIO number. Only used for Linux. */
/* Valid only when ptt_method is PTT_METHOD_GPIO. */ /* Valid only when ptt_method is PTT_METHOD_GPIO. */
int ptt_lpt_bit[MAX_CHANS]; /* Bit number for parallel printer port. */
/* Bit 0 = pin 2, ..., bit 7 = pin 9. */
/* Valid only when ptt_method is PTT_METHOD_LPT. */
static int ptt_invert[MAX_CHANS]; /* Invert the signal. */ static int ptt_invert[MAX_CHANS]; /* Invert the signal. */
/* Normally higher voltage means transmit. */ /* Normally higher voltage means transmit. */
@ -157,16 +172,18 @@ void ptt_init (struct audio_s *p_modem)
strcpy (ptt_device[ch], p_modem->ptt_device[ch]); strcpy (ptt_device[ch], p_modem->ptt_device[ch]);
ptt_line[ch] = p_modem->ptt_line[ch]; ptt_line[ch] = p_modem->ptt_line[ch];
ptt_gpio[ch] = p_modem->ptt_gpio[ch]; ptt_gpio[ch] = p_modem->ptt_gpio[ch];
ptt_lpt_bit[ch] = p_modem->ptt_lpt_bit[ch];
ptt_invert[ch] = p_modem->ptt_invert[ch]; ptt_invert[ch] = p_modem->ptt_invert[ch];
ptt_fd[ch] = INVALID_HANDLE_VALUE; ptt_fd[ch] = INVALID_HANDLE_VALUE;
#if DEBUG #if DEBUG
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("ch=%d, method=%d, device=%s, line=%d, gpio=%d, invert=%d\n", dw_printf ("ch=%d, method=%d, device=%s, line=%d, gpio=%d, lpt_bit=%d, invert=%d\n",
ch, ch,
ptt_method[ch], ptt_method[ch],
ptt_device[ch], ptt_device[ch],
ptt_line[ch], ptt_line[ch],
ptt_gpio[ch], ptt_gpio[ch],
ptt_lpt_bit[ch],
ptt_invert[ch]); ptt_invert[ch]);
#endif #endif
} }
@ -205,8 +222,20 @@ void ptt_init (struct audio_s *p_modem)
} }
else { else {
#if __WIN32__ #if __WIN32__
char bettername[50];
// Bug fix in release 1.1 - Need to munge name for COM10 and up.
// http://support.microsoft.com/kb/115831
fd = CreateFile(ptt_device[ch], strcpy (bettername, ptt_device[ch]);
if (strncasecmp(bettername, "COM", 3) == 0) {
int n;
n = atoi(bettername+3);
if (n >= 10) {
strcpy (bettername, "\\\\.\\");
strcat (bettername, ptt_device[ch]);
}
}
fd = CreateFile(bettername,
GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
#else #else
@ -295,9 +324,10 @@ void ptt_init (struct audio_s *p_modem)
*/ */
if (geteuid() != 0) { if (geteuid() != 0) {
if ( ! (finfo.st_mode & S_IWOTH)) { if ( ! (finfo.st_mode & S_IWOTH)) {
int err;
/* Try to change protection. */ /* Try to change protection. */
system ("sudo chmod go+w /sys/class/gpio/export /sys/class/gpio/unexport"); err = system ("sudo chmod go+w /sys/class/gpio/export /sys/class/gpio/unexport");
if (stat("/sys/class/gpio/export", &finfo) < 0) { if (stat("/sys/class/gpio/export", &finfo) < 0) {
/* Unexpected because we could do it before. */ /* Unexpected because we could do it before. */
@ -327,6 +357,7 @@ void ptt_init (struct audio_s *p_modem)
if (ptt_method[ch] == PTT_METHOD_GPIO) { if (ptt_method[ch] == PTT_METHOD_GPIO) {
char stemp[80]; char stemp[80];
struct stat finfo; struct stat finfo;
int err;
fd = open("/sys/class/gpio/export", O_WRONLY); fd = open("/sys/class/gpio/export", O_WRONLY);
if (fd < 0) { if (fd < 0) {
@ -355,9 +386,9 @@ void ptt_init (struct audio_s *p_modem)
* We only care about "direction" and "value". * We only care about "direction" and "value".
*/ */
sprintf (stemp, "sudo chmod go+rw /sys/class/gpio/gpio%d/direction", ptt_gpio[ch]); sprintf (stemp, "sudo chmod go+rw /sys/class/gpio/gpio%d/direction", ptt_gpio[ch]);
system (stemp); err = system (stemp);
sprintf (stemp, "sudo chmod go+rw /sys/class/gpio/gpio%d/value", ptt_gpio[ch]); sprintf (stemp, "sudo chmod go+rw /sys/class/gpio/gpio%d/value", ptt_gpio[ch]);
system (stemp); err = system (stemp);
sprintf (stemp, "/sys/class/gpio/gpio%d/value", ptt_gpio[ch]); sprintf (stemp, "/sys/class/gpio/gpio%d/value", ptt_gpio[ch]);
@ -414,12 +445,75 @@ void ptt_init (struct audio_s *p_modem)
#endif #endif
/*
* Set up parallel printer port.
* Hardcoded for single port.
* For x86 Linux only.
*/
#if ( defined(__i386__) || defined(__x86_64__) ) && ( defined(__linux__) || defined(__unix__) )
for (ch=0; ch<ptt_num_channels; ch++) {
if (ptt_method[ch] == PTT_METHOD_LPT) {
/* Can't open the same device more than once so we */
/* need more logic to look for the case of both radio */
/* channels using different pins of the same LPT port. */
/* TODO: Needs to be rewritten in a more general manner */
/* if we ever have more than 2 channels. */
if (ch == 1 && strcmp(ptt_device[0],ptt_device[1]) == 0) {
fd = ptt_fd[0];
}
else {
fd = open ("/dev/port", O_RDWR | O_NDELAY);
}
if (fd != INVALID_HANDLE_VALUE) {
ptt_fd[ch] = fd;
}
else {
int e = errno;
text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR - Can't open /dev/port for parallel printer port PTT control.\n");
dw_printf ("%s\n", strerror(errno));
dw_printf ("You probably don't have adequate permissions to access I/O ports.\n");
dw_printf ("Either run direwolf as root or change these permissions:\n");
dw_printf (" sudo chmod go+rw /dev/port\n");
dw_printf (" sudo setcap cap_sys_rawio=ep `which direwolf`\n");
/* Don't try using it later if device open failed. */
ptt_method[ch] = PTT_METHOD_NONE;
}
/*
* Set initial state of PTT off.
* ptt_set will invert output signal if appropriate.
*/
ptt_set (ch, 0);
} /* if parallel printer port method. */
} /* For each channel. */
#endif /* x86 Linux */
/* Why doesn't it transmit? Probably forgot to specify PTT option. */ /* Why doesn't it transmit? Probably forgot to specify PTT option. */
for (ch=0; ch<ptt_num_channels; ch++) { for (ch=0; ch<ptt_num_channels; ch++) {
if(ptt_method[ch] == PTT_METHOD_NONE) { if(ptt_method[ch] == PTT_METHOD_NONE) {
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("Note: PTT not configured for channel %d.\n", ch); dw_printf ("Note: PTT not configured for channel %d. (Ignore this if using VOX.)\n", ch);
} }
} }
} /* end ptt_init */ } /* end ptt_init */
@ -523,6 +617,44 @@ void ptt_set (int chan, int ptt)
} }
#endif #endif
/*
* Using parallel printer port?
*/
#if ( defined(__i386__) || defined(__x86_64__) ) && ( defined(__linux__) || defined(__unix__) )
if (ptt_method[chan] == PTT_METHOD_LPT &&
ptt_fd[chan] != INVALID_HANDLE_VALUE) {
char lpt_data;
ssize_t n;
lseek (ptt_fd[chan], (off_t)LPT_IO_ADDR, SEEK_SET);
if (read (ptt_fd[chan], &lpt_data, (size_t)1) != 1) {
int e = errno;
text_color_set(DW_COLOR_ERROR);
dw_printf ("Error reading current state of LPT for channel %d PTT\n", chan);
dw_printf ("%s\n", strerror(e));
}
if (ptt) {
lpt_data |= ( 1 << ptt_lpt_bit[chan] );
}
else {
lpt_data &= ~ ( 1 << ptt_lpt_bit[chan] );
}
lseek (ptt_fd[chan], (off_t)LPT_IO_ADDR, SEEK_SET);
if (write (ptt_fd[chan], &lpt_data, (size_t)1) != 1) {
int e = errno;
text_color_set(DW_COLOR_ERROR);
dw_printf ("Error writing to LPT for channel %d PTT\n", chan);
dw_printf ("%s\n", strerror(e));
}
}
#endif /* x86 Linux */
} /* end ptt_set */ } /* end ptt_set */
@ -546,6 +678,9 @@ void ptt_term (void)
for (n = 0; n < ptt_num_channels; n++) { for (n = 0; n < ptt_num_channels; n++) {
ptt_set (n, 0); ptt_set (n, 0);
}
for (n = 0; n < ptt_num_channels; n++) {
if (ptt_fd[n] != INVALID_HANDLE_VALUE) { if (ptt_fd[n] != INVALID_HANDLE_VALUE) {
#if __WIN32__ #if __WIN32__
CloseHandle (ptt_fd[n]); CloseHandle (ptt_fd[n]);
@ -572,6 +707,8 @@ void ptt_term (void)
void text_color_set (dw_color_t c) { } void text_color_set (dw_color_t c) { }
#define dw_printf printf
main () main ()
{ {
struct audio_s modem; struct audio_s modem;
@ -656,8 +793,7 @@ main ()
/* Test GPIO */ /* Test GPIO */
#if __WIN32__ #if __arm__
#else
memset (&modem, 0, sizeof(modem)); memset (&modem, 0, sizeof(modem));
modem.num_channels = 1; modem.num_channels = 1;
@ -680,6 +816,35 @@ main ()
ptt_term (); ptt_term ();
#endif #endif
memset (&modem, 0, sizeof(modem));
modem.num_channels = 2;
modem.ptt_method[0] = PTT_METHOD_LPT;
modem.ptt_lpt_bit[0] = 0;
modem.ptt_method[1] = PTT_METHOD_LPT;
modem.ptt_lpt_bit[1] = 1;
dw_printf ("Try LPT bits 0 & 1 a few times...\n");
ptt_init (&modem);
for (n=0; n<8; n++) {
ptt_set (0, n & 1);
ptt_set (1, (n>>1) & 1);
SLEEP_SEC(1);
}
ptt_term ();
/* Parallel printer port. */
#if ( defined(__i386__) || defined(__x86_64__) ) && ( defined(__linux__) || defined(__unix__) )
#endif
} }
#endif #endif

18
rdq.c
View File

@ -44,8 +44,9 @@
static rrbb_t queue_head; /* Head of linked list for queue. */ static rrbb_t queue_head = NULL; /* Head of linked list for queue. */
static int rdq_len = 0;
#define RDQ_UNDERRUN_THRESHOLD 30 /* A warning will be emitted if there are still this number of packets to decode in the queue and we try to add another one */
#if __WIN32__ #if __WIN32__
static CRITICAL_SECTION rdq_cs; /* Critical section for updating queues. */ static CRITICAL_SECTION rdq_cs; /* Critical section for updating queues. */
@ -204,7 +205,11 @@ void rdq_append (rrbb_t rrbb)
} }
rrbb_set_nextp (plast, rrbb); rrbb_set_nextp (plast, rrbb);
} }
rdq_len++;
if (rdq_len > RDQ_UNDERRUN_THRESHOLD) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Too many packets to decode (%d) in the queue, decrease the FIX_BITS value\n", rdq_len);
}
#if __WIN32__ #if __WIN32__
LeaveCriticalSection (&rdq_cs); LeaveCriticalSection (&rdq_cs);
@ -374,7 +379,7 @@ void rdq_wait_while_empty (void)
#if DEBUG #if DEBUG
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("rdq_wait_while_empty () returns\n"); dw_printf ("rdq_wait_while_empty () returns (%d buffers remaining)\n", rdq_len);
#endif #endif
} }
@ -418,7 +423,10 @@ rrbb_t rdq_remove (void)
exit (1); exit (1);
} }
#endif #endif
rdq_len--;
#if DEBUG
dw_printf ("-rdq_len: %d\n", rdq_len);
#endif
if (queue_head == NULL) { if (queue_head == NULL) {
result_p = NULL; result_p = NULL;
} }

View File

@ -1,3 +1,4 @@
This is NOT used for the Linux version.
For Linux and Cygwin, we use the built-in regular expression library. For Linux and Cygwin, we use the built-in regular expression library.
For the Windows version, we need to include our own version. For the Windows version, we need to include our own version.

94
rpack.h Normal file
View File

@ -0,0 +1,94 @@
/*------------------------------------------------------------------
*
* File: rpack.h
*
* Purpose: Definition of Garmin Rino message format.
*
* References: http://www.radio-active.net.au/web3/APRS/Resources/RINO
*
* http://www.radio-active.net.au/web3/APRS/Resources/RINO/OnAir
*
*---------------------------------------------------------------*/
#ifndef RPACK_H
#define RPACK_H 1
#define RPACK_FRAME_LEN 168
#ifdef RPACK_C /* Expose private details */
// Transmission order is LSB first.
struct __attribute__((__packed__)) rpack_s {
int lat; // Latitude.
// Signed integer. Scaled by 2**30/90.
int lon; // Longitude. Same encoding.
char unknown1; // Unproven theory: altitude.
char unknown2;
unsigned name0:6; // 10 character name.
unsigned name1:6; // Bit packing is implementation dependent.
unsigned name2:6; // Should rewrite to be more portable.
unsigned name3:6;
unsigned name4:6;
unsigned name5:6;
unsigned name6:6;
unsigned name7:6;
unsigned name8:6;
unsigned name9:6;
unsigned symbol:5;
unsigned unknown3:7;
// unsigned crc:16; // Safe bet this is CRC for error checking.
unsigned char crc1;
unsigned char crc2;
char dummy[3]; // Total size should be 24 bytes if no gaps.
};
#else /* Show only public interface. */
struct rpack_s {
char stuff[24];
};
#endif
void rpack_set_bit (struct rpack_s *rp, int position, int value);
int rpack_is_valid (struct rpack_s *rp);
int rpack_get_bit (struct rpack_s *rp, int position);
double rpack_get_lat (struct rpack_s *rp);
double rpack_get_lon (struct rpack_s *rp);
int rpack_get_symbol (struct rpack_s *rp);
void rpack_get_name (struct rpack_s *rp, char *str);
#endif
/* end rpack.h */

72
rrbb.c
View File

@ -374,18 +374,13 @@ int rrbb_get_bit (rrbb_t b, unsigned int ind)
#else #else
int rrbb_get_bit (rrbb_t b, unsigned int ind) int rrbb_get_bit (rrbb_t b, unsigned int ind)
{ {
unsigned int di, mi;
assert (b != NULL); assert (b != NULL);
assert (b->magic1 == MAGIC1); assert (b->magic1 == MAGIC1);
assert (b->magic2 == MAGIC2); assert (b->magic2 == MAGIC2);
assert (ind < b->len); assert (ind < b->len);
di = ind / SOI; if (b->data[ind / SOI] & masks[ind % SOI]) {
mi = ind % SOI;
if (b->data[di] & masks[mi]) {
return 1; return 1;
} }
else { else {
@ -393,6 +388,30 @@ int rrbb_get_bit (rrbb_t b, unsigned int ind)
} }
} }
#endif #endif
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;
}
/*********************************************************************************** /***********************************************************************************
@ -572,6 +591,47 @@ int rrbb_get_audio_level (rrbb_t b)
} }
/***********************************************************************************
*
* Name: rrbb_set_fix_bits
*
* Purpose: Set fix bits at time the frame was received.
*
* Inputs: b Handle for bit array.
* a fix_bits.
*
***********************************************************************************/
void rrbb_set_fix_bits (rrbb_t b, int a)
{
assert (b != NULL);
assert (b->magic1 == MAGIC1);
assert (b->magic2 == MAGIC2);
b->fix_bits = a;
}
/***********************************************************************************
*
* Name: rrbb_get_fix_bits
*
* Purpose: Get fix bits at time the frame was received.
*
* Inputs: b Handle for bit array.
*
***********************************************************************************/
int rrbb_get_fix_bits (rrbb_t b)
{
assert (b != NULL);
assert (b->magic1 == MAGIC1);
assert (b->magic2 == MAGIC2);
return (b->fix_bits);
}
/*********************************************************************************** /***********************************************************************************
* *
* Name: rrbb_get_is_scrambled * Name: rrbb_get_is_scrambled

9
rrbb.h
View File

@ -37,6 +37,9 @@ typedef struct rrbb_s {
int subchan; /* Which modem when more than one per channel. */ int subchan; /* Which modem when more than one per channel. */
int audio_level; /* Received audio level at time of frame capture. */ int audio_level; /* Received audio level at time of frame capture. */
unsigned int len; /* Current number of samples in array. */ unsigned int len; /* Current number of samples in array. */
int fix_bits; /* Level of effort to recover from */
/* a bad FCS on the frame. */
int is_scrambled; /* Is data scrambled G3RUH / K9NG style? */ int is_scrambled; /* Is data scrambled G3RUH / K9NG style? */
int descram_state; /* Descrambler state before first data bit of frame. */ int descram_state; /* Descrambler state before first data bit of frame. */
@ -46,6 +49,7 @@ typedef struct rrbb_s {
slice_t data[MAX_NUM_BITS]; slice_t data[MAX_NUM_BITS];
#else #else
unsigned int data[(MAX_NUM_BITS+SOI-1)/SOI]; unsigned int data[(MAX_NUM_BITS+SOI-1)/SOI];
unsigned int computed_data[MAX_NUM_BITS];
#endif #endif
int magic2; int magic2;
} *rrbb_t; } *rrbb_t;
@ -79,6 +83,8 @@ void rrbb_set_slice_val (rrbb_t b, slice_t slice_val);
#endif #endif
int rrbb_get_bit (rrbb_t b, unsigned int ind); 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_flip_bit (rrbb_t b, unsigned int ind);
@ -99,4 +105,7 @@ int rrbb_get_is_scrambled (rrbb_t b);
int rrbb_get_descram_state (rrbb_t b); int rrbb_get_descram_state (rrbb_t b);
int rrbb_get_fix_bits(rrbb_t b);
void rrbb_set_fix_bits(rrbb_t b, int fix_bits);
#endif #endif

362
server.c
View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2011,2012,2013 John Langner, WB2OSZ // Copyright (C) 2011, 2012, 2013, 2014 John Langner, WB2OSZ
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -85,6 +85,12 @@
* Getting Started with Winsock * Getting Started with Winsock
* http://msdn.microsoft.com/en-us/library/windows/desktop/bb530742(v=vs.85).aspx * http://msdn.microsoft.com/en-us/library/windows/desktop/bb530742(v=vs.85).aspx
* *
*
* Major change in 1.1:
*
* Formerly a single client was allowed.
* Now we can have multiple concurrent clients.
*
*---------------------------------------------------------------*/ *---------------------------------------------------------------*/
@ -122,21 +128,42 @@
#include "server.h" #include "server.h"
/*
* Previously, we allowed only one network connection at a time to each port.
* In version 1.1, we allow multiple concurrent client apps to connect.
*/
static int client_sock; /* File descriptor for socket for */ #define MAX_NET_CLIENTS 3
static int client_sock[MAX_NET_CLIENTS];
/* File descriptor for socket for */
/* communication with client application. */ /* communication with client application. */
/* Set to -1 if not connected. */ /* Set to -1 if not connected. */
/* (Don't use SOCKET type because it is unsigned.) */ /* (Don't use SOCKET type because it is unsigned.) */
static int enable_send_raw_to_client; /* Should we send received packets to client app? */ static int enable_send_raw_to_client[MAX_NET_CLIENTS];
static int enable_send_monitor_to_client; /* Should we send received packets to client app in raw form? */
/* Note that it starts as false for a new connection. */
/* the client app must send a command to enable this. */
static int enable_send_monitor_to_client[MAX_NET_CLIENTS];
/* Should we send received packets to client app in monitor form? */
/* Note that it starts as false for a new connection. */
/* the client app must send a command to enable this. */
static int num_channels; /* Number of radio ports. */ static int num_channels; /* Number of radio ports. */
static void * connect_listen_thread (void *arg); // TODO: define in one place, use everywhere.
static void * cmd_listen_thread (void *arg); #if __WIN32__
#define THREAD_F unsigned __stdcall
#else
#define THREAD_F void *
#endif
static THREAD_F connect_listen_thread (void *arg);
static THREAD_F cmd_listen_thread (void *arg);
/* /*
* Message header for AGW protocol. * Message header for AGW protocol.
@ -169,12 +196,13 @@ struct agwpe_s {
* Purpose: Print message to/from client for debugging. * Purpose: Print message to/from client for debugging.
* *
* Inputs: fromto - Direction of message. * Inputs: fromto - Direction of message.
* client - client number, 0 .. MAX_NET_CLIENTS-1
* pmsg - Address of the message block. * pmsg - Address of the message block.
* msg_len - Length of the message. * msg_len - Length of the message.
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
static int debug_client = 0; /* Print information flowing from and to client. */ static int debug_client = 0; /* Debug option: Print information flowing from and to client. */
void server_set_debug (int n) void server_set_debug (int n)
{ {
@ -208,7 +236,7 @@ void hex_dump (unsigned char *p, int len)
typedef enum fromto_e { FROM_CLIENT=0, TO_CLIENT=1 } fromto_t; typedef enum fromto_e { FROM_CLIENT=0, TO_CLIENT=1 } fromto_t;
static void debug_print (fromto_t fromto, struct agwpe_s *pmsg, int msg_len) static void debug_print (fromto_t fromto, int client, struct agwpe_s *pmsg, int msg_len)
{ {
char direction [10]; char direction [10];
char datakind[80]; char datakind[80];
@ -269,15 +297,15 @@ static void debug_print (fromto_t fromto, struct agwpe_s *pmsg, int msg_len)
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("\n"); dw_printf ("\n");
dw_printf ("%s %s %s AGWPE client application, total length = %d\n", dw_printf ("%s %s %s AGWPE client application %d, total length = %d\n",
prefix[(int)fromto], datakind, direction, msg_len); prefix[(int)fromto], datakind, direction, client, msg_len);
dw_printf ("\tportx = %d, port_hi_reserved = %d\n", pmsg->portx, pmsg->port_hi_reserved); dw_printf ("\tportx = %d, port_hi_reserved = %d\n", pmsg->portx, pmsg->port_hi_reserved);
dw_printf ("\tkind_lo = %d = '%c', kind_hi = %d\n", pmsg->kind_lo, pmsg->kind_lo, pmsg->kind_hi); dw_printf ("\tkind_lo = %d = '%c', kind_hi = %d\n", pmsg->kind_lo, pmsg->kind_lo, pmsg->kind_hi);
dw_printf ("\tcall_from = \"%s\", call_to = \"%s\"\n", pmsg->call_from, pmsg->call_to); dw_printf ("\tcall_from = \"%s\", call_to = \"%s\"\n", pmsg->call_from, pmsg->call_to);
dw_printf ("\tdata_len = %d, user_reserved = %d, data =\n", pmsg->data_len, pmsg->user_reserved); dw_printf ("\tdata_len = %d, user_reserved = %d, data =\n", pmsg->data_len, pmsg->user_reserved);
hex_dump ((char*)pmsg + sizeof(struct agwpe_s), pmsg->data_len); hex_dump ((unsigned char*)pmsg + sizeof(struct agwpe_s), pmsg->data_len);
if (msg_len < 36) { if (msg_len < 36) {
text_color_set (DW_COLOR_ERROR); text_color_set (DW_COLOR_ERROR);
@ -303,9 +331,9 @@ static void debug_print (fromto_t fromto, struct agwpe_s *pmsg, int msg_len)
* *
* Outputs: * Outputs:
* *
* Description: This starts two threads: * Description: This starts at least two threads:
* * to listen for a connection from client app. * * one to listen for a connection from client app.
* * to listen for commands from client app. * * one or more to listen for commands from client app.
* so the main application doesn't block while we wait for these. * so the main application doesn't block while we wait for these.
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
@ -313,15 +341,17 @@ static void debug_print (fromto_t fromto, struct agwpe_s *pmsg, int msg_len)
void server_init (struct misc_config_s *mc) void server_init (struct misc_config_s *mc)
{ {
int client;
#if __WIN32__ #if __WIN32__
HANDLE connect_listen_th; HANDLE connect_listen_th;
HANDLE cmd_listen_th; HANDLE cmd_listen_th[MAX_NET_CLIENTS];
#else #else
pthread_t connect_listen_tid; pthread_t connect_listen_tid;
pthread_t cmd_listen_tid; pthread_t cmd_listen_tid[MAX_NET_CLIENTS];
#endif #endif
int e; int e;
int server_port = mc->agwpe_port; int server_port = mc->agwpe_port; /* Usually 8000 but can be changed. */
#if DEBUG #if DEBUG
@ -329,16 +359,18 @@ void server_init (struct misc_config_s *mc)
dw_printf ("server_init ( %d )\n", server_port); dw_printf ("server_init ( %d )\n", server_port);
debug_a = 1; debug_a = 1;
#endif #endif
client_sock = -1; for (client=0; client<MAX_NET_CLIENTS; client++) {
enable_send_raw_to_client = 0; client_sock[client] = -1;
enable_send_monitor_to_client = 0; enable_send_raw_to_client[client] = 0;
enable_send_monitor_to_client[client] = 0;
}
num_channels = mc->num_channels; num_channels = mc->num_channels;
/* /*
* This waits for a client to connect and sets client_sock. * This waits for a client to connect and sets an available client_sock[n].
*/ */
#if __WIN32__ #if __WIN32__
connect_listen_th = _beginthreadex (NULL, 0, connect_listen_thread, (void *)server_port, 0, NULL); connect_listen_th = (HANDLE)_beginthreadex (NULL, 0, connect_listen_thread, (void *)(unsigned int)server_port, 0, NULL);
if (connect_listen_th == NULL) { if (connect_listen_th == NULL) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Could not create AGW connect listening thread\n"); dw_printf ("Could not create AGW connect listening thread\n");
@ -354,23 +386,28 @@ void server_init (struct misc_config_s *mc)
#endif #endif
/* /*
* This reads messages from client when client_sock is valid. * These read messages from client when client_sock[n] is valid.
* Currently we start up a separate thread for each potential connection.
* Possible later refinement. Start one now, others only as needed.
*/ */
for (client = 0; client < MAX_NET_CLIENTS; client++) {
#if __WIN32__ #if __WIN32__
cmd_listen_th = _beginthreadex (NULL, 0, cmd_listen_thread, NULL, 0, NULL); cmd_listen_th[client] = (HANDLE)_beginthreadex (NULL, 0, cmd_listen_thread, (void*)client, 0, NULL);
if (cmd_listen_th == NULL) { if (cmd_listen_th[client] == NULL) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Could not create AGW command listening thread\n"); dw_printf ("Could not create AGW command listening thread\n");
return; return;
} }
#else #else
e = pthread_create (&cmd_listen_tid, NULL, cmd_listen_thread, NULL); e = pthread_create (&cmd_listen_tid[client], NULL, cmd_listen_thread, (void *)(long)client);
if (e != 0) { if (e != 0) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
perror("Could not create AGW command listening thread"); perror("Could not create AGW command listening thread");
return; return;
} }
#endif #endif
}
} }
@ -393,7 +430,7 @@ void server_init (struct misc_config_s *mc)
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
static void * connect_listen_thread (void *arg) static THREAD_F connect_listen_thread (void *arg)
{ {
#if __WIN32__ #if __WIN32__
@ -414,7 +451,7 @@ static void * connect_listen_thread (void *arg)
if (err != 0) { if (err != 0) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf("WSAStartup failed: %d\n", err); dw_printf("WSAStartup failed: %d\n", err);
return (NULL); return (NULL); // TODO: what should this be for Windows?
} }
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) { if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) {
@ -472,13 +509,22 @@ static void * connect_listen_thread (void *arg)
while (1) { while (1) {
while (client_sock > 0) { int client;
SLEEP_SEC(1); /* Already connected. Try again later. */ int c;
client = -1;
for (c = 0; c < MAX_NET_CLIENTS && client < 0; c++) {
if (client_sock[c] <= 0) {
client = c;
}
} }
#define QUEUE_SIZE 5 /*
* Listen for connection if we have not reached maximum.
*/
if (client >= 0) {
if(listen(listen_sock,QUEUE_SIZE) == SOCKET_ERROR) if(listen(listen_sock, MAX_NET_CLIENTS) == SOCKET_ERROR)
{ {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf("Listen failed with error: %d\n", WSAGetLastError()); dw_printf("Listen failed with error: %d\n", WSAGetLastError());
@ -486,11 +532,11 @@ static void * connect_listen_thread (void *arg)
} }
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf("Ready to accept AGW client application on port %s ...\n", server_port_str); dw_printf("Ready to accept AGW client application %d on port %s ...\n", client, server_port_str);
client_sock = accept(listen_sock, NULL, NULL); client_sock[client] = accept(listen_sock, NULL, NULL);
if (client_sock == -1) { if (client_sock[client] == -1) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf("Accept failed with error: %d\n", WSAGetLastError()); dw_printf("Accept failed with error: %d\n", WSAGetLastError());
closesocket(listen_sock); closesocket(listen_sock);
@ -499,18 +545,22 @@ static void * connect_listen_thread (void *arg)
} }
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf("\nConnected to AGW client application ...\n\n"); dw_printf("\nConnected to AGW client application %d ...\n\n", client);
/* /*
* The command to change this is actually a toggle, not explicit on or off. * The command to change this is actually a toggle, not explicit on or off.
* Make sure it has proper state when we get a new connection. * Make sure it has proper state when we get a new connection.
*/ */
enable_send_raw_to_client = 0; enable_send_raw_to_client[client] = 0;
enable_send_monitor_to_client = 0; enable_send_monitor_to_client[client] = 0;
}
else {
SLEEP_SEC(1); /* wait then check again if more clients allowed. */
}
} }
#else #else /* End of Windows case, now Linux */
struct sockaddr_in sockaddr; /* Internet socket address stuct */ struct sockaddr_in sockaddr; /* Internet socket address stuct */
socklen_t sockaddr_size = sizeof(struct sockaddr_in); socklen_t sockaddr_size = sizeof(struct sockaddr_in);
@ -548,13 +598,19 @@ static void * connect_listen_thread (void *arg)
while (1) { while (1) {
while (client_sock > 0) { int client;
SLEEP_SEC(1); /* Already connected. Try again later. */ int c;
client = -1;
for (c = 0; c < MAX_NET_CLIENTS && client < 0; c++) {
if (client_sock[c] <= 0) {
client = c;
}
} }
#define QUEUE_SIZE 5 if (client >= 0) {
if(listen(listen_sock,QUEUE_SIZE) == -1) if(listen(listen_sock,MAX_NET_CLIENTS) == -1)
{ {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
perror ("connect_listen_thread: Listen failed"); perror ("connect_listen_thread: Listen failed");
@ -562,20 +618,23 @@ static void * connect_listen_thread (void *arg)
} }
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf("Ready to accept AGW client application on port %d ...\n", server_port); dw_printf("Ready to accept AGW client application %d on port %d ...\n", client, server_port);
client_sock = accept(listen_sock, (struct sockaddr*)(&sockaddr),&sockaddr_size); client_sock[client] = accept(listen_sock, (struct sockaddr*)(&sockaddr),&sockaddr_size);
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf("\nConnected to AGW client application ...\n\n"); dw_printf("\nConnected to AGW client application %d...\n\n", client);
/* /*
* The command to change this is actually a toggle, not explicit on or off. * The command to change this is actually a toggle, not explicit on or off.
* Make sure it has proper state when we get a new connection. * Make sure it has proper state when we get a new connection.
*/ */
enable_send_raw_to_client = 0; enable_send_raw_to_client[client] = 0;
enable_send_monitor_to_client = 0; enable_send_monitor_to_client[client] = 0;
}
else {
SLEEP_SEC(1); /* wait then check again if more clients allowed. */
}
} }
#endif #endif
} }
@ -616,13 +675,15 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
int err; int err;
int info_len; int info_len;
unsigned char *pinfo; unsigned char *pinfo;
int client;
/* /*
* RAW format * RAW format
*/ */
for (client=0; client<MAX_NET_CLIENTS; client++) {
if (enable_send_raw_to_client if (enable_send_raw_to_client[client] && client_sock[client] > 0){
&& client_sock > 0){
memset (&agwpe_msg.hdr, 0, sizeof(agwpe_msg.hdr)); memset (&agwpe_msg.hdr, 0, sizeof(agwpe_msg.hdr));
@ -642,37 +703,38 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
memcpy (agwpe_msg.data + 1, fbuf, (size_t)flen); memcpy (agwpe_msg.data + 1, fbuf, (size_t)flen);
if (debug_client) { if (debug_client) {
debug_print (TO_CLIENT, &agwpe_msg.hdr, sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len); debug_print (TO_CLIENT, client, &agwpe_msg.hdr, sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len);
} }
#if __WIN32__ #if __WIN32__
err = send (client_sock, (char*)(&agwpe_msg), sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len, 0); err = send (client_sock[client], (char*)(&agwpe_msg), sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len, 0);
if (err == SOCKET_ERROR) if (err == SOCKET_ERROR)
{ {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError %d sending message to AGW client application. Closing connection.\n\n", WSAGetLastError()); dw_printf ("\nError %d sending message to AGW client application. Closing connection.\n\n", WSAGetLastError());
closesocket (client_sock); closesocket (client_sock[client]);
client_sock = -1; client_sock[client] = -1;
WSACleanup(); WSACleanup();
} }
#else #else
err = write (client_sock, &agwpe_msg, sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len); err = write (client_sock[client], &agwpe_msg, sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len);
if (err <= 0) if (err <= 0)
{ {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError sending message to AGW client application. Closing connection.\n\n"); dw_printf ("\nError sending message to AGW client application. Closing connection.\n\n");
close (client_sock); close (client_sock[client]);
client_sock = -1; client_sock[client] = -1;
} }
#endif #endif
} }
}
/* MONITOR format - only for UI frames. */ /* MONITOR format - only for UI frames. */
for (client=0; client<MAX_NET_CLIENTS; client++) {
if (enable_send_monitor_to_client if (enable_send_monitor_to_client[client] && client_sock[client] > 0
&& client_sock > 0
&& ax25_get_control(pp) == AX25_UI_FRAME){ && ax25_get_control(pp) == AX25_UI_FRAME){
time_t clock; time_t clock;
@ -710,30 +772,31 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
agwpe_msg.hdr.data_len = strlen(agwpe_msg.data) + 1 /* include null */ ; agwpe_msg.hdr.data_len = strlen(agwpe_msg.data) + 1 /* include null */ ;
if (debug_client) { if (debug_client) {
debug_print (TO_CLIENT, &agwpe_msg.hdr, sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len); debug_print (TO_CLIENT, client, &agwpe_msg.hdr, sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len);
} }
#if __WIN32__ #if __WIN32__
err = send (client_sock, (char*)(&agwpe_msg), sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len, 0); err = send (client_sock[client], (char*)(&agwpe_msg), sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len, 0);
if (err == SOCKET_ERROR) if (err == SOCKET_ERROR)
{ {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError %d sending message to AGW client application. Closing connection.\n\n", WSAGetLastError()); dw_printf ("\nError %d sending message to AGW client application %d. Closing connection.\n\n", WSAGetLastError(), client);
closesocket (client_sock); closesocket (client_sock[client]);
client_sock = -1; client_sock[client] = -1;
WSACleanup(); WSACleanup();
} }
#else #else
err = write (client_sock, &agwpe_msg, sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len); err = write (client_sock[client], &agwpe_msg, sizeof(agwpe_msg.hdr) + agwpe_msg.hdr.data_len);
if (err <= 0) if (err <= 0)
{ {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError sending message to AGW client application. Closing connection.\n\n"); dw_printf ("\nError sending message to AGW client application %d. Closing connection.\n\n", client);
close (client_sock); close (client_sock[client]);
client_sock = -1; client_sock[client] = -1;
} }
#endif #endif
} }
}
} /* server_send_rec_packet */ } /* server_send_rec_packet */
@ -800,9 +863,9 @@ static int read_from_socket (int fd, char *ptr, int len)
* *
* Purpose: Wait for command messages from an application. * Purpose: Wait for command messages from an application.
* *
* Inputs: arg - Not used. * Inputs: arg - client number, 0 .. MAX_NET_CLIENTS-1
* *
* Outputs: client_sock - File descriptor for communicating with client app. * Outputs: client_sock[n] - File descriptor for communicating with client app.
* *
* Description: Process messages from the client application. * Description: Process messages from the client application.
* Note that the client can go away and come back again and * Note that the client can go away and come back again and
@ -810,11 +873,10 @@ static int read_from_socket (int fd, char *ptr, int len)
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
static void * cmd_listen_thread (void *arg) static THREAD_F cmd_listen_thread (void *arg)
{ {
int n; int n;
struct { struct {
struct agwpe_s hdr; /* Command header. */ struct agwpe_s hdr; /* Command header. */
@ -822,44 +884,85 @@ static void * cmd_listen_thread (void *arg)
/* Maximum for 'V': 1 + 8*10 + 256 */ /* Maximum for 'V': 1 + 8*10 + 256 */
} cmd; } cmd;
int client = (int) arg;
assert (client >= 0 && client < MAX_NET_CLIENTS);
while (1) { while (1) {
while (client_sock <= 0) { while (client_sock[client] <= 0) {
SLEEP_SEC(1); /* Not connected. Try again later. */ SLEEP_SEC(1); /* Not connected. Try again later. */
} }
n = read_from_socket (client_sock, (char *)(&cmd.hdr), sizeof(cmd.hdr)); n = read_from_socket (client_sock[client], (char *)(&cmd.hdr), sizeof(cmd.hdr));
if (n != sizeof(cmd.hdr)) { if (n != sizeof(cmd.hdr)) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError getting message header from client application.\n"); dw_printf ("\nError getting message header from AGW client application %d.\n", client);
dw_printf ("Tried to read %d bytes but got only %d.\n", (int)sizeof(cmd.hdr), n); dw_printf ("Tried to read %d bytes but got only %d.\n", (int)sizeof(cmd.hdr), n);
dw_printf ("Closing connection.\n\n"); dw_printf ("Closing connection.\n\n");
#if __WIN32__ #if __WIN32__
closesocket (client_sock); closesocket (client_sock[client]);
#else #else
close (client_sock); close (client_sock[client]);
#endif #endif
client_sock = -1; client_sock[client] = -1;
continue; continue;
} }
assert (cmd.hdr.data_len >= 0 && cmd.hdr.data_len < sizeof(cmd.data)); /*
* Take some precautions to guard against bad data
* which could cause problems later.
*/
/*
* Call to/from must not exceeed 9 characters.
* It's not guaranteed that unused bytes will contain 0 so we
* don't issue error message in this case.
*/
cmd.hdr.call_from[sizeof(cmd.hdr.call_from)-1] = '\0';
cmd.hdr.call_to[sizeof(cmd.hdr.call_to)-1] = '\0';
/*
* Following data must fit in available buffer.
* Leave room for an extra nul byte terminator at end later.
*/
if (cmd.hdr.data_len < 0 || cmd.hdr.data_len > sizeof(cmd.data) - 1) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nInvalid message from AGW client application %d.\n", client);
dw_printf ("Data Length of %d is out of range.\n", cmd.hdr.data_len);
/* This is a bad situation. */
/* If we tried to read again, the header probably won't be there. */
/* No point in trying to continue reading. */
dw_printf ("Closing connection.\n\n");
#if __WIN32__
closesocket (client_sock[client]);
#else
close (client_sock[client]);
#endif
client_sock[client] = -1;
return NULL;
}
cmd.data[0] = '\0'; cmd.data[0] = '\0';
if (cmd.hdr.data_len > 0) { if (cmd.hdr.data_len > 0) {
n = read_from_socket (client_sock, cmd.data, cmd.hdr.data_len); n = read_from_socket (client_sock[client], cmd.data, cmd.hdr.data_len);
if (n != cmd.hdr.data_len) { if (n != cmd.hdr.data_len) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError getting message data from client application.\n"); dw_printf ("\nError getting message data from AGW client application %d.\n", client);
dw_printf ("Tried to read %d bytes but got only %d.\n", cmd.hdr.data_len, n); dw_printf ("Tried to read %d bytes but got only %d.\n", cmd.hdr.data_len, n);
dw_printf ("Closing connection.\n\n"); dw_printf ("Closing connection.\n\n");
#if __WIN32__ #if __WIN32__
closesocket (client_sock); closesocket (client_sock[client]);
#else #else
close (client_sock); close (client_sock[client]);
#endif #endif
client_sock = -1; client_sock[client] = -1;
return NULL; return NULL;
} }
if (n > 0) { if (n > 0) {
@ -872,7 +975,7 @@ static void * cmd_listen_thread (void *arg)
*/ */
if (debug_client) { if (debug_client) {
debug_print (FROM_CLIENT, &cmd.hdr, sizeof(cmd.hdr) + cmd.hdr.data_len); debug_print (FROM_CLIENT, client, &cmd.hdr, sizeof(cmd.hdr) + cmd.hdr.data_len);
} }
switch (cmd.hdr.kind_lo) { switch (cmd.hdr.kind_lo) {
@ -901,15 +1004,15 @@ static void * cmd_listen_thread (void *arg)
assert (sizeof(reply) == 44); assert (sizeof(reply) == 44);
if (debug_client) { if (debug_client) {
debug_print (TO_CLIENT, &reply.hdr, sizeof(reply)); debug_print (TO_CLIENT, client, &reply.hdr, sizeof(reply));
} }
// TODO: Should have unified function instead of multiple versions everywhere. // TODO: Should have unified function instead of multiple versions everywhere.
#if __WIN32__ #if __WIN32__
send (client_sock, (char*)(&reply), sizeof(reply), 0); send (client_sock[client], (char*)(&reply), sizeof(reply), 0);
#else #else
write (client_sock, &reply, sizeof(reply)); n = write (client_sock[client], &reply, sizeof(reply));
#endif #endif
} }
break; break;
@ -940,13 +1043,13 @@ static void * cmd_listen_thread (void *arg)
assert (reply.hdr.data_len == 100); assert (reply.hdr.data_len == 100);
if (debug_client) { if (debug_client) {
debug_print (TO_CLIENT, &reply.hdr, sizeof(reply)); debug_print (TO_CLIENT, client, &reply.hdr, sizeof(reply));
} }
#if __WIN32__ #if __WIN32__
send (client_sock, (char*)(&reply), sizeof(reply), 0); send (client_sock[client], (char*)(&reply), sizeof(reply), 0);
#else #else
write (client_sock, &reply, sizeof(reply)); n = write (client_sock[client], &reply, sizeof(reply));
#endif #endif
} }
break; break;
@ -991,13 +1094,13 @@ static void * cmd_listen_thread (void *arg)
assert (sizeof(reply) == 48); assert (sizeof(reply) == 48);
if (debug_client) { if (debug_client) {
debug_print (TO_CLIENT, &reply.hdr, sizeof(reply)); debug_print (TO_CLIENT, client, &reply.hdr, sizeof(reply));
} }
#if __WIN32__ #if __WIN32__
send (client_sock, (char*)(&reply), sizeof(reply), 0); send (client_sock[client], (char*)(&reply), sizeof(reply), 0);
#else #else
write (client_sock, &reply, sizeof(reply)); n = write (client_sock[client], &reply, sizeof(reply));
#endif #endif
} }
break; break;
@ -1027,13 +1130,13 @@ static void * cmd_listen_thread (void *arg)
reply.hdr.data_len = strlen(reply.info); reply.hdr.data_len = strlen(reply.info);
if (debug_client) { if (debug_client) {
debug_print (TO_CLIENT, &reply.hdr, sizeof(reply.hdr) + reply.hdr.data_len); debug_print (TO_CLIENT, client, &reply.hdr, sizeof(reply.hdr) + reply.hdr.data_len);
} }
#if __WIN32__ #if __WIN32__
send (client_sock, &reply, sizeof(reply.hdr) + reply.hdr.data_len, 0); send (client_sock[client], &reply, sizeof(reply.hdr) + reply.hdr.data_len, 0);
#else #else
write (client_sock, &reply, sizeof(reply.hdr) + reply.hdr.data_len); write (client_sock[client], &reply, sizeof(reply.hdr) + reply.hdr.data_len);
#endif #endif
#endif #endif
@ -1047,14 +1150,14 @@ static void * cmd_listen_thread (void *arg)
// Actually it is a toggle so we must be sure to clear it for a new connection. // Actually it is a toggle so we must be sure to clear it for a new connection.
enable_send_raw_to_client = ! enable_send_raw_to_client; enable_send_raw_to_client[client] = ! enable_send_raw_to_client[client];
break; break;
case 'm': /* Ask to start receiving Monitor frames */ case 'm': /* Ask to start receiving Monitor frames */
// Actually it is a toggle so we must be sure to clear it for a new connection. // Actually it is a toggle so we must be sure to clear it for a new connection.
enable_send_monitor_to_client = ! enable_send_monitor_to_client; enable_send_monitor_to_client[client] = ! enable_send_monitor_to_client[client];
break; break;
@ -1074,6 +1177,8 @@ static void * cmd_listen_thread (void *arg)
//unsigned char fbuf[AX25_MAX_PACKET_LEN+2]; //unsigned char fbuf[AX25_MAX_PACKET_LEN+2];
//int flen; //int flen;
// We have already assured these do not exceed 9 characters.
strcpy (stemp, cmd.hdr.call_from); strcpy (stemp, cmd.hdr.call_from);
strcat (stemp, ">"); strcat (stemp, ">");
strcat (stemp, cmd.hdr.call_to); strcat (stemp, cmd.hdr.call_to);
@ -1126,10 +1231,22 @@ static void * cmd_listen_thread (void *arg)
// data which is raw ax.25 frame. // data which is raw ax.25 frame.
// //
packet_t pp; packet_t pp;
pp = ax25_from_frame ((unsigned char *)cmd.data+1, cmd.hdr.data_len, -1); // Bug fix in version 1.1:
//
// The first byte of data is described as:
//
// the “TNC” to use
// 00=Port 1
// 16=Port 2
//
// I don't know what that means; we already a port number in the header.
// Anyhow, the original code here added one to cmd.data to get the
// first byte of the frame. Unfortunately, it did not subtract one from
// cmd.hdr.data_len so we ended up sending an extra byte.
pp = ax25_from_frame ((unsigned char *)cmd.data+1, cmd.hdr.data_len - 1, -1);
if (pp == NULL) { if (pp == NULL) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
@ -1177,13 +1294,13 @@ static void * cmd_listen_thread (void *arg)
// That's why more cumbersome size expression is used. // That's why more cumbersome size expression is used.
if (debug_client) { if (debug_client) {
debug_print (TO_CLIENT, &reply.hdr, sizeof(reply.hdr) + sizeof(reply.data)); debug_print (TO_CLIENT, client, &reply.hdr, sizeof(reply.hdr) + sizeof(reply.data));
} }
#if __WIN32__ #if __WIN32__
send (client_sock, (char*)(&reply), sizeof(reply.hdr) + sizeof(reply.data), 0); send (client_sock[client], (char*)(&reply), sizeof(reply.hdr) + sizeof(reply.data), 0);
#else #else
write (client_sock, &reply, sizeof(reply.hdr) + sizeof(reply.data)); n = write (client_sock[client], &reply, sizeof(reply.hdr) + sizeof(reply.data));
#endif #endif
} }
break; break;
@ -1201,7 +1318,7 @@ static void * cmd_listen_thread (void *arg)
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("\n"); dw_printf ("\n");
dw_printf ("Can't process command from AGW client app.\n"); dw_printf ("Can't process command from AGW client app %d.\n", client);
dw_printf ("Connected packet mode is not implemented.\n"); dw_printf ("Connected packet mode is not implemented.\n");
break; break;
@ -1211,9 +1328,10 @@ static void * cmd_listen_thread (void *arg)
Not sure what we might want to do here. Not sure what we might want to do here.
AGWterminal sends this for beacon or ask QRA. AGWterminal sends this for beacon or ask QRA.
None of the other tested applications use it.
<<< Send UNPROTO Information from AGWPE client application, total length = 253 <<< Send UNPROTO Information from AGWPE client application 0, total length = 253
portx = 0, port_hi_reserved = 0 portx = 0, port_hi_reserved = 0
kind_lo = 77 = 'M', kind_hi = 0 kind_lo = 77 = 'M', kind_hi = 0
call_from = "SV2AGW-1", call_to = "BEACON" call_from = "SV2AGW-1", call_to = "BEACON"
@ -1222,21 +1340,43 @@ static void * cmd_listen_thread (void *arg)
010: 73 20 74 68 65 20 6e 65 77 20 41 47 57 20 50 61 s the new AGW Pa 010: 73 20 74 68 65 20 6e 65 77 20 41 47 57 20 50 61 s the new AGW Pa
020: 63 6b 65 74 20 45 6e 67 69 6e 65 20 77 69 6e 73 cket Engine wins 020: 63 6b 65 74 20 45 6e 67 69 6e 65 20 77 69 6e 73 cket Engine wins
<<< Send UNPROTO Information from AGWPE client application, total length = 37 <<< Send UNPROTO Information from AGWPE client application 0, total length = 37
portx = 0, port_hi_reserved = 0 portx = 0, port_hi_reserved = 0
kind_lo = 77 = 'M', kind_hi = 0 kind_lo = 77 = 'M', kind_hi = 0
call_from = "SV2AGW-1", call_to = "QRA" call_from = "SV2AGW-1", call_to = "QRA"
data_len = 1, user_reserved = 32218432, data = data_len = 1, user_reserved = 32218432, data =
000: 0d . 000: 0d .
{
packet_t pp;
int pid = cmd.datakind_hi & 0xff;
/* "AX.25 PID 0x00 or 0xF0 for AX.25 0xCF NETROM and others" */
This is not right.
It needs to be more like "V" Transmit UI data frame
except there are no digipeaters involved.
pp = ax25_from_frame ((unsigned char *)cmd.data, cmd.hdr.data_len, -1);
if (pp != NULL) {
tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp);
ax25_set_pid (pp, pid);
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Failed to create frame from AGW 'M' message.\n");
}
}
break; break;
#endif #endif
default: default:
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("--- Unexpected Command from application using AGW protocol:\n"); dw_printf ("--- Unexpected Command from application %d using AGW protocol:\n", client);
debug_print (FROM_CLIENT, &cmd.hdr, sizeof(cmd.hdr) + cmd.hdr.data_len); debug_print (FROM_CLIENT, client, &cmd.hdr, sizeof(cmd.hdr) + cmd.hdr.data_len);
break; break;
} }

View File

@ -1,4 +1,4 @@
APRS SYMBOL OVERLAY and EXTENSION TABLES in APRS 1.2 20 May 2014 APRS SYMBOL OVERLAY and EXTENSION TABLES in APRS 1.2 28 Aug 2014
--------------------------------------------------------------------- ---------------------------------------------------------------------
BACKGROUND: This file addresses new additions proposals (OVERLAYS) BACKGROUND: This file addresses new additions proposals (OVERLAYS)
@ -13,6 +13,7 @@ CORRECT one.
UPDATES/REVISIONS/CORRECTIONS: UPDATES/REVISIONS/CORRECTIONS:
28 Aug 14 Added numerous OpenAPRS symbols see "(new Aug 2014)"
20 May 14 Changed Da to DSTAR (2700 of them) from Dutch Ares 20 May 14 Changed Da to DSTAR (2700 of them) from Dutch Ares
19 May 14 Added Submarine&torpedo to ships and lots of Aircraft 19 May 14 Added Submarine&torpedo to ships and lots of Aircraft
search for "(new may 2014)" search for "(new may 2014)"
@ -113,6 +114,11 @@ letting that define a new graphic just for that combination.
The following tables will attempt to keep track of these and The following tables will attempt to keep track of these and
any other useful generic applications of overlay characters. any other useful generic applications of overlay characters.
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)
AIRCRAFT AIRCRAFT
/^ = LARGE Aircraft /^ = LARGE Aircraft
\^ = top-view originally intended to point in direction of flight \^ = top-view originally intended to point in direction of flight
@ -121,7 +127,9 @@ E^ = Enemy aircraft (too bad I cant use the original Hostile)
H^ = Hovercraft (new may 2014) H^ = Hovercraft (new may 2014)
J^ = JET (new may 2014) J^ = JET (new may 2014)
M^ = Missle (new may 2014) M^ = Missle (new may 2014)
P^ = Prop (new Aug 2014)
V^ = Vertical takeoff (new may 2014) V^ = Vertical takeoff (new may 2014)
X^ = Experimental (new Aug 2014)
ATM Machine or CURRENCY: #$ ATM Machine or CURRENCY: #$
/$ = original primary Phone /$ = original primary Phone
@ -130,12 +138,31 @@ U$ = US dollars
L$ = Brittish Pound L$ = Brittish Pound
Y$ = Japanese Yen Y$ = Japanese Yen
DEPOT
/D = was originally undefined
\D = was drizzle (moved to ' ovlyD)
AD = Airport (new Aug 2014)
FD = Ferry Landing (new Aug 2014)
HD = Heloport (new Aug 2014)
RD = Rail Depot (new Aug 2014)
BD = Bus Depot (new Aug 2014)
LD = LIght Rail or Subway (new Aug 2014)
SD = Seaport Depot (new Aug 2014)
EMERGENCY: #!
/! = Police/Sheriff, etc
\! = Emergency!
E! = ELT or EPIRB (new Aug 2014)
V! = Volcanic Eruption or Lava (new Aug 2014)
POWER PLANT: #% POWER PLANT: #%
/% = DX cluster <= the original primary table definition /% = DX cluster <= the original primary table definition
C% = Coal C% = Coal
E% = Emergency (new Aug 2014)
G% = Geothermal G% = Geothermal
H% = Hydroelectric H% = Hydroelectric
N% = Nuclear N% = Nuclear
P% = Portable (new Aug 2014)
S% = Solar S% = Solar
T% = Turbine T% = Turbine
W% = Wind W% = Wind
@ -148,7 +175,8 @@ T& = TX igate with path set to 1 hop only)
2& = TX igate with path set to 2 hops (not generally good idea) 2& = TX igate with path set to 2 hops (not generally good idea)
INCIDENT SITES: #' INCIDENT SITES: #'
\' = Airplane Crash Site <= the original primary deifinition /' = Small Aircraft (original primary symbol)
\' = Airplane Crash Site <= the original alternate deifinition
A' = Automobile crash site A' = Automobile crash site
H' = Hazardous incident H' = Hazardous incident
M' = Multi-Vehicle crash site M' = Multi-Vehicle crash site
@ -199,9 +227,10 @@ ADVISORIES: #< (new expansion possibilities)
/< = motorcycle /< = motorcycle
\< = Advisory (single gale flag) \< = Advisory (single gale flag)
CARS: #> CARS: #> (Vehicles)
/> = normal car (side view) /> = normal car (side view)
\> = Top view and symbol POINTS in direction of travel \> = Top view and symbol POINTS in direction of travel
#> = Reserve overlays 1-9 for numbered cars (new Aug 2014)
E> = Electric E> = Electric
H> = Hybrid H> = Hybrid
S> = Solar powered S> = Solar powered
@ -270,13 +299,18 @@ Wa = WinLink
CIVIL DEFENSE or TRIANGLE: #c CIVIL DEFENSE or TRIANGLE: #c
/c = Incident Command Post /c = Incident Command Post
\c = Civil Defense \c = Civil Defense
Dc = Decontamination (new Aug 2014)
Rc = RACES Rc = RACES
Sc = SATERN mobile canteen Sc = SATERN mobile canteen
BUILDINGS: #h BUILDINGS: #h
/h = Hospital /h = Hospital
\h = Ham Store ** <= now used for HAMFESTS \h = Ham Store ** <= now used for HAMFESTS
Fh = HamFest (new Aug 2014)
Hh = Home Dept etc.. Hh = Home Dept etc..
Mh = Morgue
Ch = Clinic
Th = Triage
SPECIAL VEHICLES: #k SPECIAL VEHICLES: #k
/k = truck /k = truck
@ -287,17 +321,33 @@ Ak = ATV (all terrain vehicle)
SHIPS: #s SHIPS: #s
/s = Power boat (ship) side view /s = Power boat (ship) side view
\s = Overlay Boat (Top view) \s = Overlay Boat (Top view)
Cs = receive as Canoe but still transmit canoe as /C 6s = Shipwreck ("deep6") (new Aug 2014)
Bs = Pleasure Boat
Cs = Cargo
Ds = Diving
Es = Emergency or Medical transport
Fs = Fishing
Hs = High-speed Craft
Js = Jet Ski Js = Jet Ski
Ks = Kayak Ls = Law enforcement
Hs = Hovercraft (new may 2014) Ms = Miltary
Ts = Torpedo (new may 2014) Os = Oil Rig
Us = sUbmarine U-boat (new may 2014) Ps = Pilot Boat (new Aug 2014)
Qs = Torpedo
Ss = Search and Rescue
Ts = Tug (new Aug 2014)
Us = Underwater ops or submarine
Ws = Wing-in-Ground effect (or Hovercraft)
Xs = Passenger (paX)(ferry)
Ys = Sailing (large ship)
TRUCKS: #u TRUCKS: #u
/u = Truck (18 wheeler) /u = Truck (18 wheeler)
\u = truck with overlay \u = truck with overlay
Bu = Buldozer/construction (new Aug 2014)
Gu = Gas Gu = Gas
Pu = Plow or SnowPlow (new Aug 2014)
Tu = Tanker Tu = Tanker
Cu = Chlorine Tanker Cu = Chlorine Tanker
Hu = Hazardous Hu = Hazardous

View File

@ -307,20 +307,32 @@ static int new_sym_len = 0; /* Number of elements used. */
void symbols_init (void) void symbols_init (void)
{ {
FILE *fp; FILE *fp;
struct {
char overlay; /*
char symbol; * We only care about lines with this format:
char sp1; *
char equal; * Column 1 - overlay character of / \ upper case or digit
char sp2; * Column 2 - symbol in range of ! thru ~
char description[150]; * Column 3 - space
} stuff; * Column 4 - equal sign
* Column 5 - space
* Column 6 - Start of description.
*/
#define COL1_OVERLAY 0
#define COL2_SYMBOL 1
#define COL3_SP 2
#define COL4_EQUAL 3
#define COL5_SP 4
#define COL6_DESC 5
char stuff[200];
int j; int j;
#define GOOD_LINE(x) ((x.overlay == '/' || x.overlay == '\\' || isupper(x.overlay) || isdigit(x.overlay)) \ #define GOOD_LINE(x) (strlen(x) > 6 && \
&& x.symbol >= '!' && x.symbol <= '~' \ (x[COL1_OVERLAY] == '/' || x[COL1_OVERLAY] == '\\' || isupper(x[COL1_OVERLAY]) || isdigit(x[COL1_OVERLAY])) \
&& x.sp1 == ' ' && x.equal == '=' && x.sp2 == ' ') && x[COL2_SYMBOL] >= '!' && x[COL2_SYMBOL] <= '~' \
&& x[COL3_SP] == ' ' && x[COL4_EQUAL] == '=' && x[COL5_SP] == ' ' && x[COL6_DESC] != ' ')
if (new_sym_ptr != NULL) { if (new_sym_ptr != NULL) {
return; /* was called already. */ return; /* was called already. */
@ -350,7 +362,7 @@ void symbols_init (void)
/* /*
* Count number of interesting lines and allocate storage. * Count number of interesting lines and allocate storage.
*/ */
while (fgets((char*)(&stuff), sizeof(stuff), fp) != NULL) { while (fgets(stuff, sizeof(stuff), fp) != NULL) {
if (GOOD_LINE(stuff)) { if (GOOD_LINE(stuff)) {
new_sym_size++; new_sym_size++;
} }
@ -363,15 +375,15 @@ void symbols_init (void)
*/ */
rewind (fp); rewind (fp);
while (fgets((char*)(&stuff), sizeof(stuff), fp) != NULL) { while (fgets(stuff, sizeof(stuff), fp) != NULL) {
if (GOOD_LINE(stuff)) { if (GOOD_LINE(stuff)) {
for (j = strlen(stuff.description) - 1; j>=0 && stuff.description[j] <= ' '; j--) { for (j = strlen(stuff+COL6_DESC) - 1; j>=0 && stuff[COL6_DESC+j] <= ' '; j--) {
stuff.description[j] = '\0'; stuff[COL6_DESC+j] = '\0';
} }
new_sym_ptr[new_sym_len].overlay = stuff.overlay; new_sym_ptr[new_sym_len].overlay = stuff[COL1_OVERLAY];
new_sym_ptr[new_sym_len].symbol = stuff.symbol; new_sym_ptr[new_sym_len].symbol = stuff[COL2_SYMBOL];
strncpy(new_sym_ptr[new_sym_len].description, stuff.description, NEW_SYM_DESC_LEN); strncpy(new_sym_ptr[new_sym_len].description, stuff+COL6_DESC, NEW_SYM_DESC_LEN);
new_sym_len++; new_sym_len++;
} }
} }

View File

@ -1,20 +1,21 @@
APRS SYMBOLS (Icons) 07 Oct 2013 APRS SYMBOLS (Icons) 28 Aug 2014
----------------------------------------------------------------------- -----------------------------------------------------------------------
WB4APR WB4APR
This original APRS symbol specification is updated periodically with This original APRS symbol specification is updated periodically with
new symbols as they are defined. This is THE master list for APRS. But new symbols as they are defined. This is THE master list for APRS. But
almost all new symbols will be formed as Overlays to the basic set. So almost all new symbols since 2007 will be formed as Overlays to the
be sure to check the symbols-new.txt file noted below! basic set. So be sure to check the symbols-new.txt file noted below!
http://aprs.org/symbols/symbols-new.txt
NEW OVERLAYS ON ALL ALTERNATE SYMBOLS: As of 1 October 2007, the use of NEW OVERLAYS ON ALL ALTERNATE SYMBOLS: As of 1 October 2007, the use of
overlay characters on all alternate symbols was allowed as needed in overlay characters on all alternate symbols was allowed as needed in
order to further expand the APRS symbol set. These overlay expansions order to further expand the APRS symbol set. These overlay expansions
of up to 36 different usages for each of the 94 alternate symbols adds of up to 36 different usages for each of the 94 alternate symbols adds
hundreds of potential new symbols. Since this overlay info will no hundreds of potential new symbols. Since each of the original 94
longer fit in this original document it is found in a new document: symbols can now have up to 36 other definitions, this new overlay info
is now found in the above file.
http://aprs.org/symbols/symbols-new.txt
If an alternate symbol from the table below, contains significant If an alternate symbol from the table below, contains significant
definitions of its overlay characters, then the entry will have an "O" definitions of its overlay characters, then the entry will have an "O"
@ -23,10 +24,17 @@ document. The # symbol indicates the original Overlay subset.
For details on Upgrading your symbol set, please see the background For details on Upgrading your symbol set, please see the background
information on Symbols prepared by Stephen Smith, WA8LMF: information on Symbols prepared by Stephen Smith, WA8LMF:
http://www.ew.usna.edu/~bruninga/aprs/symbols-background.txt http://aprs.org/symbols/symbols-background.txt
UPDATE CHRONOLOGY: UPDATE CHRONOLOGY:
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
Changed #D from "avail" to new family called Depots
Changed name of overlay Car to generic Vehicles w overlays
Edited name of /E Eyeball to include "Events"
19 May 14: Added many new Aircraft and Ship symbols see symbols-new.txt
07 Oct 13: Added JetSki [Js] & Ham Club [C-] ovrlys to symbols-new.txt 07 Oct 13: Added JetSki [Js] & Ham Club [C-] ovrlys to symbols-new.txt
19 Sep 11: Added T & 2 overlay for 1 & 2 hop Message TX Igates 19 Sep 11: Added T & 2 overlay for 1 & 2 hop Message TX Igates
Updated overlay "portable" (;) overlays for events Updated overlay "portable" (;) overlays for events
@ -100,7 +108,9 @@ Alt: >KOSY[^ksuv\ <==[removed /0An]
SYMBOLS.TXT APRS DISPLAY SYMBOLS APRSdos ORIGINAL SYMBOLS.TXT APRS DISPLAY SYMBOLS APRSdos ORIGINAL
====================================================================== ======================================================================
Document dated: 28 Apr 99 FInal APRSdos symbol spec (still updated!) Document dated: 28 Apr 99 FInal APRSdos symbol spec
**************: This file Remains CUrrent and is Updated Frequently
**************: See date and updates at the top of this file.
Author(s): Bob Bruninga, WB4APR <bruninga@usna.edu> Author(s): Bob Bruninga, WB4APR <bruninga@usna.edu>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -113,8 +123,8 @@ tables or may be used as an alphanumeric overlay over some symbols:
& RESERVED for possible AUXILLIARY tables (Aug 09) & RESERVED for possible AUXILLIARY tables (Aug 09)
/ Primary symbol Table (Mostly stations) / Primary symbol Table (Mostly stations)
\ Alternate symbol table (Mostly Objects) \ Alternate symbol table (Mostly Objects)
0-9 Alternate OVERLAY symbol with 0-9 overlayed 0-9 Alternate OVERLAY symbols with 0-9 overlayed
A-Z Alternate OVERLAY symbol with A-Z overlayed A-Z Alternate OVERLAY symbols with A-Z overlayed
For ease of reference we refer to these as the SYMBOL CHARACTERS and For ease of reference we refer to these as the SYMBOL CHARACTERS and
often abbreviate them as "/$" which refers to the Table character "/" often abbreviate them as "/$" which refers to the Table character "/"
@ -128,8 +138,8 @@ symbols through Oct 2007 were:
CIRCLE, SQUARE, CAR, TRUCK, VAN, DIGIS, GATES CIRCLE, SQUARE, CAR, TRUCK, VAN, DIGIS, GATES
Civil-Defense(RACES), NWS sites, WX stations, Triangle Civil-Defense(RACES), NWS sites, WX stations, Triangle
After that, provisions should be made in all software to allow for After 2007, provisions should be made in all software to allow for
overlays on any alternate symbol as they may be used in the future. overlays on *any/all* alternate symbols for use in the future.
SYMBOLS WITH STAND-ALONE GPS TRACKERS: Stand-alone devices that SYMBOLS WITH STAND-ALONE GPS TRACKERS: Stand-alone devices that
transmit raw GPS have no method to convey their symbol. For this transmit raw GPS have no method to convey their symbol. For this
@ -162,16 +172,16 @@ for the stand-alone trackers described above.
/$ XYZ BASIC SYMBOL TABLE \$ XYZ OTHER SYMBOL TABLE (\) /$ XYZ BASIC SYMBOL TABLE \$ XYZ OTHER SYMBOL TABLE (\)
-- --- ------------------------ -- --- ---------------------- -- --- ------------------------ -- --- ----------------------
/! BB Police, Sheriff \! OB EMERGENCY (!) /! BB Police, Sheriff \! OBO EMERGENCY (and overlays)
/" BC reserved (was rain) \" OC reserved /" BC reserved (was rain) \" OC reserved
/# BD DIGI (white center) \# OD# OVERLAY DIGI (green star) /# BD DIGI (white center) \# OD# OVERLAY DIGI (green star)
/$ BE PHONE \$ OEO Bank or ATM (green box) /$ BE PHONE \$ OEO Bank or ATM (green box)
/% BF DX CLUSTER \% OFO Power Plant with overlay /% BF DX CLUSTER \% OFO Power Plant with overlay
/& BG HF GATEway \& OG# I=Igte R=RX T=1hopTX 2=2hopTX /& BG HF GATEway \& OG# I=Igte R=RX T=1hopTX 2=2hopTX
/' BH Small AIRCRAFT (SSID = 7) \' OHO Crash (& now Incident sites) /' BH Small AIRCRAFT (SSID = 7) \' OHO Crash (& now Incident sites)
/( BI Mobile Satellite Station \( OI CLOUDY (other clouds w ovrly) /( BI Mobile Satellite Station \( OIO CLOUDY (other clouds w ovrly)
/) BJ Wheelchair (handicapped) \) OJO Firenet MEO, MODIS Earth Obs. /) BJ Wheelchair (handicapped) \) OJO Firenet MEO, MODIS Earth Obs.
/* BK SnowMobile \* OK SNOW (& future ovrly codes) /* BK SnowMobile \* OK AVAIL (SNOW moved to ` ovly S)
/+ BL Red Cross \+ OL Church /+ BL Red Cross \+ OL Church
/, BM Boy Scouts \, OM Girl Scouts /, BM Boy Scouts \, OM Girl Scouts
/- BN House QTH (VHF) \- ONO House (H=HF) (O = Op Present) /- BN House QTH (VHF) \- ONO House (H=HF) (O = Op Present)
@ -181,38 +191,38 @@ for the stand-alone trackers described above.
/$ XYZ PRIMARY SYMBOL TABLE \$ XYZ ALTERNATE SYMBOL TABLE (\) /$ XYZ PRIMARY SYMBOL TABLE \$ XYZ ALTERNATE SYMBOL TABLE (\)
-- --- ------------------------ -- --- -------------------------- -- --- ------------------------ -- --- --------------------------
/0 P0 # circle (obsolete) \0 A0# CIRCLE (E/I/W=IRLP/Echolink/WIRES) /0 P0 # circle (obsolete) \0 A0# CIRCLE (IRLP/Echolink/WIRES)
/1 P1 TBD (these were numbered) \1 A1 /1 P1 TBD (these were numbered) \1 A1 AVAIL
/2 P2 TBD (circles like pool) \2 A2 /2 P2 TBD (circles like pool) \2 A2 AVAIL
/3 P3 TBD (balls. But with) \3 A3 /3 P3 TBD (balls. But with) \3 A3 AVAIL
/4 P4 TBD (overlays, we can) \4 A4 /4 P4 TBD (overlays, we can) \4 A4 AVAIL
/5 P5 TBD (put all #'s on one) \5 A5 /5 P5 TBD (put all #'s on one) \5 A5 AVAIL
/6 P6 TBD (So 1-9 are available)\6 A6 /6 P6 TBD (So 1-9 are available)\6 A6 AVAIL
/7 P7 TBD (for new uses?) \7 A7 /7 P7 TBD (for new uses?) \7 A7 AVAIL
/8 P8 TBD (They are often used) \8 A8O 802.11 or other network node /8 P8 TBD (They are often used) \8 A8O 802.11 or other network node
/9 P9 TBD (as mobiles at events)\9 A9 Gas Station (blue pump) /9 P9 TBD (as mobiles at events)\9 A9 Gas Station (blue pump)
/: MR FIRE \: NR Hail (& future ovrly codes) /: MR FIRE \: NR AVAIL (Hail ==> ` ovly H)
/; MS Campground (Portable ops) \; NSO Park/Picnic + overlay events /; MS Campground (Portable ops) \; NSO Park/Picnic + overlay events
/< MT Motorcycle (SSID =10) \< NTO ADVISORY (one WX flag) /< MT Motorcycle (SSID =10) \< NTO ADVISORY (one WX flag)
/= MU RAILROAD ENGINE \= NUO APRStt Touchtone (DTMF users) /= MU RAILROAD ENGINE \= NUO APRStt Touchtone (DTMF users)
/> MV CAR (SSID = 9) \> NV# OVERLAYED CAR /> MV CAR (SSID = 9) \> NV# OVERLAYED CARs & Vehicles
/? MW SERVER for Files \? NW INFO Kiosk (Blue box with ?) /? MW SERVER for Files \? NW INFO Kiosk (Blue box with ?)
/@ MX HC FUTURE predict (dot) \@ NX HURICANE/Trop-Storm /@ MX HC FUTURE predict (dot) \@ NX HURICANE/Trop-Storm
/A PA Aid Station \A AA# overlayBOX DTMF & RFID & XO /A PA Aid Station \A AA# overlayBOX DTMF & RFID & XO
/B PB BBS or PBBS \B AB Blwng Snow (& future codes) /B PB BBS or PBBS \B AB AVAIL (BlwngSnow ==> E ovly B
/C PC Canoe \C AC Coast Guard /C PC Canoe \C AC Coast Guard
/D PD \D AD Drizzle (proposed APRStt) /D PD \D ADO DEPOTS (Drizzle ==> ' ovly D)
/E PE EYEBALL (Eye catcher!) \E AE Smoke (& other vis codes) /E PE EYEBALL (Events, etc!) \E AE Smoke (& other vis codes)
/F PF Farm Vehicle (tractor) \F AF Freezng rain (&future codes) /F PF Farm Vehicle (tractor) \F AF AVAIL (FrzngRain ==> `F)
/G PG Grid Square (6 digit) \G AG Snow Shwr (& future ovrlys) /G PG Grid Square (6 digit) \G AG AVAIL (Snow Shwr ==> I ovly S)
/H PH HOTEL (blue bed symbol) \H AHO \Haze (& Overlay Hazards) /H PH HOTEL (blue bed symbol) \H AHO \Haze (& Overlay Hazards)
/I PI TcpIp on air network stn \I AI Rain Shower /I PI TcpIp on air network stn \I AI Rain Shower
/J PJ \J AJ Lightening (& future ovrlys) /J PJ \J AJ AVAIL (Lightening ==> I ovly L)
/K PK School \K AK Kenwood HT (W) /K PK School \K AK Kenwood HT (W)
/L PL PC user (Jan 03) \L AL Lighthouse /L PL PC user (Jan 03) \L AL Lighthouse
/M PM MacAPRS \M AMO MARS (A=Army,N=Navy,F=AF) /M PM MacAPRS \M AMO MARS (A=Army,N=Navy,F=AF)
/N PN NTS Station \N AN Navigation Buoy /N PN NTS Station \N AN Navigation Buoy
/O PO BALLOON (SSID =11) \O AO Rocket (new June 2004) /O PO BALLOON (SSID =11) \O AO Overlay Balloon (Rocket = \O)
/P PP Police \P AP Parking /P PP Police \P AP Parking
/Q PQ TBD \Q AQ QUAKE /Q PQ TBD \Q AQ QUAKE
/R PR REC. VEHICLE (SSID =13) \R ARO Restaurant /R PR REC. VEHICLE (SSID =13) \R ARO Restaurant
@ -223,18 +233,18 @@ for the stand-alone trackers described above.
/W PW National WX Service Site \W AW# # NWS site (NWS options) /W PW National WX Service Site \W AW# # NWS site (NWS options)
/X PX HELO (SSID = 6) \X AX Pharmacy Rx (Apothicary) /X PX HELO (SSID = 6) \X AX Pharmacy Rx (Apothicary)
/Y PY YACHT (sail) (SSID = 5) \Y AYO Radios and devices /Y PY YACHT (sail) (SSID = 5) \Y AYO Radios and devices
/Z PZ WinAPRS \Z AZ /Z PZ WinAPRS \Z AZ AVAIL
/[ HS Human/Person (HT) \[ DSO W.Cloud (& humans w Ovrly) /[ HS Human/Person (HT) \[ DSO W.Cloud (& humans w Ovrly)
/\ HT TRIANGLE(DF station) \\ DTO New overlayable GPS symbol /\ HT TRIANGLE(DF station) \\ DTO New overlayable GPS symbol
/] HU MAIL/PostOffice(was PBBS) \] DU /] HU MAIL/PostOffice(was PBBS) \] DU AVAIL
/^ HV LARGE AIRCRAFT \^ DV# # Aircraft (shows heading) /^ HV LARGE AIRCRAFT \^ DV# other Aircraft ovrlys (2014)
/_ HW WEATHER Station (blue) \_ DW# # WX site (green digi) /_ HW WEATHER Station (blue) \_ DW# # WX site (green digi)
/` HX Dish Antenna \` DX Rain (all types w ovrly) /` HX Dish Antenna \` DX Rain (all types w ovrly)
/$ XYZ LOWER CASE SYMBOL TABLE \$ XYZ SECONDARY SYMBOL TABLE (\) /$ XYZ LOWER CASE SYMBOL TABLE \$ XYZ SECONDARY SYMBOL TABLE (\)
-- --- ------------------------ -- --- -------------------------- -- --- ------------------------ -- --- --------------------------
/a LA AMBULANCE (SSID = 1) \a SA#O ARRL, ARES, WinLINK /a LA AMBULANCE (SSID = 1) \a SA#O ARRL,ARES,WinLINK,Dstar, etc
/b LB BIKE (SSID = 4) \b SB Blwng Dst/Snd (& others) /b LB BIKE (SSID = 4) \b SB AVAIL(Blwng Dst/Snd => E ovly)
/c LC Incident Command Post \c SC#O CD triangle RACES/SATERN/etc /c LC Incident Command Post \c SC#O CD triangle RACES/SATERN/etc
/d LD Fire dept \d SD DX spot by callsign /d LD Fire dept \d SD DX spot by callsign
/e LE HORSE (equestrian) \e SE Sleet (& future ovrly codes) /e LE HORSE (equestrian) \e SE Sleet (& future ovrly codes)
@ -248,27 +258,28 @@ for the stand-alone trackers described above.
/m LM Mic-E Repeater \m SM Value Sign (3 digit display) /m LM Mic-E Repeater \m SM Value Sign (3 digit display)
/n LN Node (black bulls-eye) \n SN# OVERLAY TRIANGLE /n LN Node (black bulls-eye) \n SN# OVERLAY TRIANGLE
/o LO EOC \o SO small circle /o LO EOC \o SO small circle
/p LP ROVER (puppy, or dog) \p SP Prtly Cldy (& future ovrlys) /p LP ROVER (puppy, or dog) \p SP AVAIL (PrtlyCldy => ( ovly P
/q LQ GRID SQ shown above 128 m \q SQ /q LQ GRID SQ shown above 128 m \q SQ AVAIL
/r LR Repeater (Feb 07) \r SR Restrooms /r LR Repeater (Feb 07) \r SR Restrooms
/s LS SHIP (pwr boat) (SSID-8) \s SS# OVERLAY SHIP/boat (top view) /s LS SHIP (pwr boat) (SSID-8) \s SS# OVERLAY SHIP/boats
/t LT TRUCK STOP \t ST Tornado /t LT TRUCK STOP \t ST Tornado
/u LU TRUCK (18 wheeler) \u SU# OVERLAYED TRUCK /u LU TRUCK (18 wheeler) \u SU# OVERLAYED TRUCK
/v LV VAN (SSID = 15) \v SV# OVERLAYED Van /v LV VAN (SSID = 15) \v SV# OVERLAYED Van
/w LW WATER station \w SW Flooding /w LW WATER station \w SWO Flooding (Avalanches/Slides)
/x LX xAPRS (Unix) \x SX Wreck or Obstruction ->X<- /x LX xAPRS (Unix) \x SX Wreck or Obstruction ->X<-
/y LY YAGI @ QTH \y SY Skywarn /y LY YAGI @ QTH \y SY Skywarn
/z LZ TBD \z SZ# OVERLAYED Shelter /z LZ TBD \z SZ# OVERLAYED Shelter
/{ J1 \{ Q1 Fog (& future ovrly codes) /{ J1 \{ Q1 AVAIL? (Fog ==> E ovly F)
/| J2 TNC Stream Switch \| Q2 TNC Stream Switch /| J2 TNC Stream Switch \| Q2 TNC Stream Switch
/} J3 \} Q3 /} J3 \} Q3 AVAIL? (maybe)
/~ J4 TNC Stream Switch \~ Q4 TNC Stream Switch /~ J4 TNC Stream Switch \~ Q4 TNC Stream Switch
HEADING SYMBOLS: Although all symbols are supposed to have a heading HEADING SYMBOLS: Although all symbols are supposed to have a heading
line showing the direction of movement with a length proportional to line showing the direction of movement with a length proportional to
the log of speed, some symbols were desiged as top-down views so that the log of speed, some symbols were desiged as top-down views so that
they could be displayed actually always POINTING in the direction of they could be displayed actually always POINTING in the direction of
movement. These special symbols are: movement. Now All symbols should be oriented (if practical). These
original special symbols were:
\> OVERLAYED CAR \> OVERLAYED CAR
\s Overlayed Ship \s Overlayed Ship
@ -277,10 +288,14 @@ movement. These special symbols are:
/g Glider /g Glider
\n Overlayed Triangle \n Overlayed Triangle
AREA SYMBOLS! You can define BOX/CIRCLE/LINE or TRIANGLE areas in all AREA SYMBOLS! The special symbol \l (lower case L) was special. It
colors, either open or filled in, any size from 60 feet to 100 miles. indicates an area definition. You can define these as a BOX, CIRCLE,
Simply move the cursor to the location, press HOME, move the cursor to LINE or TRIANGLE area in all colors, either open or filled in, any
the lower right corner of the AREA and hit INPUT-ADD-OBJECTS-AREA. size from 60 feet to 100 miles. In APRSdos they were generated auto-
matically by simply moving the cursor to the location, press HOME,
move the cursor to the lower right corner of the AREA and hit INPUT-
ADD-OBJECTS-AREA.
Enter the type of area, and color. NOTE that AREA shapes can only be Enter the type of area, and color. NOTE that AREA shapes can only be
defined by selecting the upper left corner first, then the lower right defined by selecting the upper left corner first, then the lower right
second. The line is an exception. It is still top to bottom, but the second. The line is an exception. It is still top to bottom, but the
@ -294,10 +309,12 @@ cautious in using the color fill option, since all other objects in
that area that occur earlier in your PLIST will be obscured. AND you that area that occur earlier in your PLIST will be obscured. AND you
do NOT know the order of other stations P-lists. do NOT know the order of other stations P-lists.
AREAS FORMAT: The new format for specifying special areas uses the AREAS FORMAT: Use of the special AREAS symbol (/l) triggers special
CSE/SPD field to provide the additional information as follows: processing of the next 7 bytes normally used for CSE and SPD. In
this special case the processing is as follows:
$CSE/SPD... Normal Field description $CSE/SPD... Normal Field description
lTyy/Cxx... Where: l (lower case L) is symbol for "LOCATION SHAPES" lTyy/Cxx... Where: l (lower case L) is symbol for "LOCATION SHAPES"
T is Type of shape: 0=circle, 1=line, 2=elipse T is Type of shape: 0=circle, 1=line, 2=elipse
3=triangle 4=box 3=triangle 4=box
@ -314,7 +331,7 @@ Type of 6 and are drawn down and to the left.
HURRICANES, TROPICAL STORMS and DEPRESSIONS: These symbols will be HURRICANES, TROPICAL STORMS and DEPRESSIONS: These symbols will be
differentiated by colors red, yellos, and blue. Additionally a radius differentiated by colors red, yellos, and blue. Additionally a radius
of Huricane and also Tropical storm winds will also be shown if the of Huricane and also Tropical storm winds will also be shown if the
format detailed in WX.txt is used. special format detailed in WX.txt is used.
SYMBOLS ON MAPS! APRS can also be permanently embedded in maps. To SYMBOLS ON MAPS! APRS can also be permanently embedded in maps. To
embed a symbol in a map, simply make the first four characters of the embed a symbol in a map, simply make the first four characters of the
@ -325,9 +342,12 @@ same location. An example are the VORTAC nav-aids in Alaska. The
Anchorage VORTAC appears as ANC on all maps below 128 miles. The label Anchorage VORTAC appears as ANC on all maps below 128 miles. The label
entry is #\VFANC,LAT,LONG,128. entry is #\VFANC,LAT,LONG,128.
VALUE SIGNPOSTS: Signposts display as a yellow box with a 1-3 letter VALUE SIGNPOSTS: This is another special handling Symbol. Signposts
overlay on them. You specify the 1-3 letter overlay by enclosing them trigger a display as a yellow box with a 1-3 letter overlay on them.
in braces in the comment field. Thus a VALUE Signpost with {55} would The use of this symbol (\m) triggers a search for a 1-3 letter
string of characters encolsed in braces in the comment field.
Thus a VALUE Signpost with {55} in the comment field would
appear as a sign with 55 on it, designed for posting the speed appear as a sign with 55 on it, designed for posting the speed
of traffic past speed measuring devices. APRSdos has a version named of traffic past speed measuring devices. APRSdos has a version named
APRStfc.EXE that monitors traffic speed and posts these speed signs APRStfc.EXE that monitors traffic speed and posts these speed signs
@ -336,21 +356,21 @@ map, they ONLY appear at 8 miles and below AND they do not show any
callsign or name. Only the yellow box and the 3 letters or numbers. callsign or name. Only the yellow box and the 3 letters or numbers.
Select them from the OBJECT menu under VALUE... Select them from the OBJECT menu under VALUE...
APRS 1.2 OVERLAY TYPE SYMBOLS [April 2007]: APRS 1.2 OVERLAY TYPE SYMBOLS EXPANSION! [April 2007]:
------------------------------------------- -------------------------------------------------------
All alternate symbols have the potential to be overlayed. This was All alternate symbols have the potential to be overlayed. This was
the original intent and was only limited to a few due to limitations the original intent and was only limited to a few due to limitations
in Mac and WinAPRS. Those original "numbered" symbols are marked in Mac and WinAPRS. Those original "numbered" symbols are marked
with a # in the table above. But by 2007, it was time to move on. with a # in the table above. But by 2007, it was time to move on.
In APRS 1.2 it is proposed that any ALTENATE symbol can have overlays. In APRS 1.2 it was proposed that any ALTENATE symbol can have overlays.
Kenwood has already responded with the new D710 that can now display Kenwood has already responded with the new D710 that can now display
these overlays on all symbols. these overlays on all symbols.
To help define these hundreds of new symbol combinations, we have To help define these hundreds of new symbol combinations, we have
added a new file called: added a new file called:
http://www.ew.usna.edu/~bruninga/aprs/symbols-new.txt http:aprs.org/symbols/symbols-new.txt
The overlay symbols may be used in two ways. First, simply as an The overlay symbols may be used in two ways. First, simply as an
overlay on a basic symbol type. Most uses of these symbols will be overlay on a basic symbol type. Most uses of these symbols will be

1038
telemetry.c Normal file

File diff suppressed because it is too large Load Diff

15
telemetry.h Normal file
View File

@ -0,0 +1,15 @@
/* telemetry.h */
void telemetry_data_original (char *station, char *info, char *output, char *comment);
void telemetry_data_base91 (char *station, char *cdata, char *output);
void telemetry_name_message (char *station, char *msg);
void telemetry_unit_label_message (char *station, char *msg);
void telemetry_coefficents_message (char *station, char *msg);
void telemetry_bit_sense_message (char *station, char *msg);

View File

@ -118,7 +118,33 @@ static const char dark_green[] = "\e[0;32m" "\e[5;47m";
static const char clear_eos[] = "\e[0J"; static const char clear_eos[] = "\e[0J";
#else /* Linux */ #elif __arm__ /* Linux on Raspberry Pi or similar */
/* We need "blink" (5) rather than the */
/* expected bright/bold (1) to get bright white background. */
/* Makes no sense but I stumbled across that somewhere. */
static const char background_white[] = "\e[5;47m";
/* Whenever a dark color is used, the */
/* background is reset and needs to be set again. */
static const char black[] = "\e[0;30m" "\e[5;47m";
static const char red[] = "\e[1;31m";
static const char green[] = "\e[1;32m";
static const char yellow[] = "\e[1;33m";
static const char blue[] = "\e[1;34m";
static const char magenta[] = "\e[1;35m";
static const char cyan[] = "\e[1;36m";
static const char dark_green[] = "\e[0;32m" "\e[5;47m";
/* Clear from cursor to end of screen. */
static const char clear_eos[] = "\e[0J";
#else /* Other Linux */
static const char background_white[] = "\e[47;1m"; static const char background_white[] = "\e[47;1m";

View File

@ -1,6 +1,10 @@
APRS TO-CALL VERSION NUMBERS 18 Dec 2013 APRS TO-CALL VERSION NUMBERS 02 Sep 2014
------------------------------------------------------------------- -------------------------------------------------------------------
WB4APR WB4APR
02 Sep 14 added APSTMx for W7QO's Balloon trackers
21 Aug 14 added APSMSx for Paul Defrusne's SMS gateway
11 Aug 14 added APCWP8 for John GM7HHB, WinphoneAPRS
18 Dec 13 added APZWKR for GM1WKR NetSked application 18 Dec 13 added APZWKR for GM1WKR NetSked application
22 Oct 13 added APFIxx for APRS.FI OH7LZB, Hessu 22 Oct 13 added APFIxx for APRS.FI OH7LZB, Hessu
23 Aug 13 added APOxxx for OSCAR satellites for AMSAT-LU by LU9DO 23 Aug 13 added APOxxx for OSCAR satellites for AMSAT-LU by LU9DO
@ -56,6 +60,7 @@ a TOCALL number series:
APCLEY EYTraker GPRS/GSM tracker by ZS6EY APCLEY EYTraker GPRS/GSM tracker by ZS6EY
APCLWX EYWeather GPRS/GSM WX station by ZS6EY APCLWX EYWeather GPRS/GSM WX station by ZS6EY
APCLEZ Telit EZ10 GSM application ZS6CEY APCLEZ Telit EZ10 GSM application ZS6CEY
APCWP8 John GM7HHB, WinphoneAPRS
APCYxx Cybiko applications APCYxx Cybiko applications
APD APD4xx UP4DAR platform APD APD4xx UP4DAR platform
APDDxx DV-RPTR Modem and Control Center Software APDDxx DV-RPTR Modem and Control Center Software
@ -133,11 +138,13 @@ a TOCALL number series:
APRTLM used in MIM's and Mic-lites, etc APRTLM used in MIM's and Mic-lites, etc
APRtfc APRStraffic APRtfc APRStraffic
APRSTx APRStt (Touch tone) APRSTx APRStt (Touch tone)
APS APRS+SA, etc APS APSxxx APRS+SA, etc
APSARx ZL4FOX's SARTRACK APSARx ZL4FOX's SARTRACK
APSCxx aprsc APRS-IS core server (OH7LZB, OH2MQK) APSCxx aprsc APRS-IS core server (OH7LZB, OH2MQK)
APSK63 APRS Messenger -over-PSK63 APSK63 APRS Messenger -over-PSK63
APSK25 APRS Messenger GMSK-250 APSK25 APRS Messenger GMSK-250
APSMSx Paul Defrusne's SMS gateway
APSTMx for W7QO's Balloon trackers
APT APTIGR TigerTrack APT APTIGR TigerTrack
APTTxx Tiny Track APTTxx Tiny Track
APT2xx Tiny Track II APT2xx Tiny Track II

View File

@ -1,8 +1,12 @@
Most of the files in this directory copied from The other files in this directory were copied from
http://www.gpsy.com/gpsinfo/geotoutm/ http://www.gpsy.com/gpsinfo/geotoutm/
See attachments to message from Chuck Gantz.
There is no obvious copyright on the source code.
The links in the message are no longer valid.
A few minor modifications were made: A few minor modifications were made:

View File

@ -1,7 +1,7 @@
/* Dire Wolf version 1.0 */ /* Dire Wolf version 1.1 */
#define APP_TOCALL "APDW" #define APP_TOCALL "APDW"
#define MAJOR_VERSION 1 #define MAJOR_VERSION 1
#define MINOR_VERSION 0 #define MINOR_VERSION 1

53
xmit.c
View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2011,2013 John Langner, WB2OSZ // Copyright (C) 2011,2013,2014 John Langner, WB2OSZ
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -110,14 +110,21 @@ static int xmit_bits_per_sec[MAX_CHANS]; /* Data transmission rate. */
/* this case but could be different with other */ /* this case but could be different with other */
/* modulation techniques. */ /* modulation techniques. */
static int g_debug_xmit_packet; /* print packet in hexadecimal form for debugging. */
#define BITS_TO_MS(b,ch) (((b)*1000)/xmit_bits_per_sec[(ch)]) #define BITS_TO_MS(b,ch) (((b)*1000)/xmit_bits_per_sec[(ch)])
#define MS_TO_BITS(ms,ch) (((ms)*xmit_bits_per_sec[(ch)])/1000) #define MS_TO_BITS(ms,ch) (((ms)*xmit_bits_per_sec[(ch)])/1000)
#if __WIN32__
static unsigned __stdcall xmit_thread (void *arg);
#else
static void * xmit_thread (void *arg); static void * xmit_thread (void *arg);
#endif
static int wait_for_clear_channel (int channel, int nowait, int slotttime, int persist); static int wait_for_clear_channel (int channel, int nowait, int slotttime, int persist);
@ -142,7 +149,7 @@ static int wait_for_clear_channel (int channel, int nowait, int slotttime, int p
void xmit_init (struct audio_s *p_modem) void xmit_init (struct audio_s *p_modem, int debug_xmit_packet)
{ {
int j; int j;
#if __WIN32__ #if __WIN32__
@ -159,6 +166,8 @@ void xmit_init (struct audio_s *p_modem)
dw_printf ("xmit_init ( ... )\n"); dw_printf ("xmit_init ( ... )\n");
#endif #endif
g_debug_xmit_packet = debug_xmit_packet;
/* /*
* Push to Talk (PTT) control. * Push to Talk (PTT) control.
*/ */
@ -202,7 +211,7 @@ void xmit_init (struct audio_s *p_modem)
// underrun on the audio output device. // underrun on the audio output device.
#if __WIN32__ #if __WIN32__
xmit_th = _beginthreadex (NULL, 0, xmit_thread, NULL, 0, NULL); xmit_th = (HANDLE)_beginthreadex (NULL, 0, xmit_thread, NULL, 0, NULL);
if (xmit_th == NULL) { if (xmit_th == NULL) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Could not create xmit thread\n"); dw_printf ("Could not create xmit thread\n");
@ -377,7 +386,11 @@ void xmit_set_txtail (int channel, int value)
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
#if __WIN32__
static unsigned __stdcall xmit_thread (void *arg)
#else
static void * xmit_thread (void *arg) static void * xmit_thread (void *arg)
#endif
{ {
packet_t pp; packet_t pp;
unsigned char fbuf[AX25_MAX_PACKET_LEN+2]; unsigned char fbuf[AX25_MAX_PACKET_LEN+2];
@ -455,14 +468,25 @@ static void * xmit_thread (void *arg)
text_color_set(DW_COLOR_XMIT); text_color_set(DW_COLOR_XMIT);
dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L'); dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L');
dw_printf ("%s", stemp); /* stations followed by : */ dw_printf ("%s", stemp); /* stations followed by : */
ax25_safe_print ((char *)pinfo, info_len, 0); ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp));
dw_printf ("\n"); dw_printf ("\n");
flen = ax25_pack (pp, fbuf); /* Optional hex dump of packet. */
assert (flen <= sizeof(fbuf));
if (g_debug_xmit_packet) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("------\n");
ax25_hex_dump (pp);
dw_printf ("------\n");
}
/* /*
* Transmit the frame. * Transmit the frame.
* 1.1J fixed order.
*/ */
flen = ax25_pack (pp, fbuf);
assert (flen >= 1 && flen <= sizeof(fbuf));
num_bits += hdlc_send_frame (c, fbuf, flen); num_bits += hdlc_send_frame (c, fbuf, flen);
numframe = 1; numframe = 1;
ax25_delete (pp); ax25_delete (pp);
@ -483,14 +507,21 @@ static void * xmit_thread (void *arg)
text_color_set(DW_COLOR_XMIT); text_color_set(DW_COLOR_XMIT);
dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L'); dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L');
dw_printf ("%s", stemp); /* stations followed by : */ dw_printf ("%s", stemp); /* stations followed by : */
ax25_safe_print ((char *)pinfo, info_len, 0); ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp));
dw_printf ("\n"); dw_printf ("\n");
flen = ax25_pack (pp, fbuf); if (g_debug_xmit_packet) {
assert (flen <= sizeof(fbuf)); text_color_set(DW_COLOR_DEBUG);
dw_printf ("------\n");
ax25_hex_dump (pp);
dw_printf ("------\n");
}
/* /*
* Transmit the frame. * Transmit the frame.
*/ */
flen = ax25_pack (pp, fbuf);
assert (flen >= 1 && flen <= sizeof(fbuf));
num_bits += hdlc_send_frame (c, fbuf, flen); num_bits += hdlc_send_frame (c, fbuf, flen);
numframe++; numframe++;
ax25_delete (pp); ax25_delete (pp);
@ -559,7 +590,7 @@ static void * xmit_thread (void *arg)
dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L'); dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L');
dw_printf ("%s", stemp); /* stations followed by : */ dw_printf ("%s", stemp); /* stations followed by : */
ax25_safe_print ((char *)pinfo, info_len, 0); ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp));
dw_printf ("\n"); dw_printf ("\n");
ax25_delete (pp); ax25_delete (pp);

2
xmit.h
View File

@ -6,7 +6,7 @@
#include "audio.h" /* for struct audio_s */ #include "audio.h" /* for struct audio_s */
extern void xmit_init (struct audio_s *p_modem); extern void xmit_init (struct audio_s *p_modem, int debug_xmit_packet);
extern void xmit_set_txdelay (int channel, int value); extern void xmit_set_txdelay (int channel, int value);