mirror of https://github.com/wb2osz/direwolf.git
Development snapshot 1.3 dev F.
Changes to be committed: modified: .gitattributes modified: CHANGES.md modified: Makefile modified: Makefile.linux new file: Makefile.macosx modified: Makefile.win modified: aclients.c modified: aprs_tt.c modified: aprs_tt.h modified: atest.c modified: audio.c modified: audio.h new file: audio_portaudio.c new file: audio_stats.c new file: audio_stats.h modified: audio_win.c modified: ax25_pad.c modified: ax25_pad.h modified: beacon.c modified: config.c modified: config.h modified: decode_aprs.c modified: decode_aprs.h modified: demod.c modified: digipeater.c modified: direwolf.c modified: direwolf.h modified: dlq.c modified: doc/README.md modified: doc/Raspberry-Pi-APRS.pdf modified: doc/User-Guide.pdf new file: doc/WA8LMF-TNC-Test-CD-Results.pdf modified: dtime_now.c modified: dtmf.c modified: dw-start.sh modified: encode_aprs.c modified: encode_aprs.h modified: gen_packets.c modified: gen_tone.c modified: gen_tone.h new file: generic.conf modified: grm_sym.h modified: hdlc_rec.c modified: hdlc_rec.h modified: igate.c modified: kiss.c modified: kiss_frame.c modified: kissnet.c modified: latlong.c modified: man1/direwolf.1 modified: mgn_icon.h modified: misc/README-dire-wolf.txt new file: misc/strlcat.c new file: misc/strlcpy.c modified: morse.c new file: morse.h modified: nmea.c modified: pfilter.c modified: ptt.c new file: serial_port.c new file: serial_port.h modified: server.c modified: symbols.c modified: symbols.h new file: telemetry-toolkit/telem-balloon.conf new file: telemetry-toolkit/telem-balloon.pl new file: telemetry-toolkit/telem-bits.pl new file: telemetry-toolkit/telem-data.pl new file: telemetry-toolkit/telem-data91.pl new file: telemetry-toolkit/telem-eqns.pl new file: telemetry-toolkit/telem-m0xer-3.txt new file: telemetry-toolkit/telem-parm.pl new file: telemetry-toolkit/telem-unit.pl new file: telemetry-toolkit/telem-volts.conf new file: telemetry-toolkit/telem-volts.py modified: telemetry.c modified: textcolor.c modified: tq.c modified: tt_text.c modified: tt_text.h modified: tt_user.c modified: tt_user.h modified: utm2ll.c modified: version.h new file: walk96.c modified: xmit.c
This commit is contained in:
parent
558315fe19
commit
dd27f9960b
|
@ -22,7 +22,8 @@
|
|||
*.cpp text
|
||||
*.h text
|
||||
*.pl text
|
||||
Makefile* text
|
||||
*.py text
|
||||
*.sh text
|
||||
*.txt text
|
||||
*.desktop text
|
||||
*.conf text
|
||||
|
@ -30,3 +31,5 @@ Makefile* text
|
|||
|
||||
*.ico binary
|
||||
*.png binary
|
||||
|
||||
Makefile* text
|
35
CHANGES.md
35
CHANGES.md
|
@ -3,6 +3,41 @@
|
|||
|
||||
----------
|
||||
|
||||
## Version 1.3 -- Development snapshot F -- September 2015 ##
|
||||
|
||||
### New Feature: ###
|
||||
|
||||
- Command line option "-a n" to print audio device statistics each n seconds. Previously this was always each 100 seconds on Linux and not available on Windows.
|
||||
|
||||
### Bug Fixed: ###
|
||||
|
||||
- Crashed when receiving packet with comment of a few hundred characters.
|
||||
|
||||
----------
|
||||
|
||||
## Version 1.3 -- Development snapshot E -- August 2015 ##
|
||||
|
||||
### Bug Fixed: ###
|
||||
|
||||
- Crashed when receiving packet with unexpected form of GPS NMEA sentence.
|
||||
|
||||
----------
|
||||
|
||||
## Version 1.3 -- Development snapshot D -- July 2015 ##
|
||||
|
||||
### New Features: ###
|
||||
|
||||
- Enhancements to APRStt gateway including Morse code and speech responses to to APRStt tone sequences.
|
||||
|
||||
- Preliminary support for Mac OS X. NEEDS MORE TESTING!
|
||||
|
||||
- A list of all symbols available can be obtained with the -S
|
||||
command line option.
|
||||
|
||||
- APRS Telemetry Toolkit (incomplete).
|
||||
|
||||
----------
|
||||
|
||||
## Version 1.2 -- June 2015 ##
|
||||
|
||||
### New Features ###
|
||||
|
|
11
Makefile
11
Makefile
|
@ -1,9 +1,16 @@
|
|||
# Select proper Makefile for operating system.
|
||||
# The Windows version is built with the help of Cygwin.
|
||||
|
||||
# In my case, I see CYGWIN_NT-6.1-WOW so we don't check for
|
||||
# equal to some value. Your mileage my vary.
|
||||
|
||||
win := $(shell uname | grep CYGWIN)
|
||||
dar := $(shell uname | grep Darwin)
|
||||
|
||||
ifneq ($(win),)
|
||||
include Makefile.win
|
||||
include Makefile.win
|
||||
else ifeq ($(dar),Darwin)
|
||||
include Makefile.macosx
|
||||
else
|
||||
include Makefile.linux
|
||||
include Makefile.linux
|
||||
endif
|
||||
|
|
124
Makefile.linux
124
Makefile.linux
|
@ -2,7 +2,7 @@
|
|||
# Makefile for Linux version of Dire Wolf.
|
||||
#
|
||||
|
||||
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc direwolf.desktop
|
||||
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc direwolf.desktop direwolf.conf
|
||||
@echo " "
|
||||
@echo "Next step - install with:"
|
||||
@echo " "
|
||||
|
@ -216,10 +216,10 @@ direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_9600.o hd
|
|||
hdlc_rec2.o multi_modem.o redecode.o rdq.o rrbb.o dlq.o \
|
||||
fcs_calc.o ax25_pad.o \
|
||||
decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
|
||||
gen_tone.o audio.o digipeater.o pfilter.o dedupe.o tq.o xmit.o \
|
||||
gen_tone.o audio.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o morse.o \
|
||||
ptt.o beacon.o dwgps.o encode_aprs.o latlong.o encode_aprs.o latlong.o textcolor.o \
|
||||
dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o log.o telemetry.o dtime_now.o \
|
||||
geotranz.a
|
||||
dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o serial_port.o log.o telemetry.o dtime_now.o \
|
||||
misc.a geotranz.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lpthread -lrt $(LDLIBS) -lm
|
||||
|
||||
|
||||
|
@ -263,6 +263,25 @@ utm.o : geotranz/utm.c
|
|||
$(CC) $(CFLAGS) -c -o $@ $^
|
||||
|
||||
|
||||
# Provide our own copy of strlcpy and strlcat because they are not included with Linux.
|
||||
# We don't need the others in that same directory.
|
||||
|
||||
misc.a : strlcpy.o strlcat.o
|
||||
ar -cr $@ $^
|
||||
|
||||
strlcpy.o : misc/strlcpy.c
|
||||
$(CC) $(CFLAGS) -I. -c -o $@ $^
|
||||
|
||||
strlcat.o : misc/strlcat.c
|
||||
$(CC) $(CFLAGS) -I. -c -o $@ $^
|
||||
|
||||
|
||||
|
||||
# Generate apprpriate sample configuration file for this platform.
|
||||
|
||||
direwolf.conf : generic.conf
|
||||
egrep '^C|^L' generic.conf | cut -c2-999 > direwolf.conf
|
||||
|
||||
|
||||
# Where should we install it?
|
||||
|
||||
|
@ -282,7 +301,7 @@ INSTALLDIR := /usr/local
|
|||
# It was hardcoded with lxterminal, /home/pi, and so on.
|
||||
# In version 1.2, try to customize this to match other situations better.
|
||||
|
||||
# TODO1.2: Test this better.
|
||||
# TODO: Test this better.
|
||||
|
||||
|
||||
direwolf.desktop :
|
||||
|
@ -310,7 +329,7 @@ endif
|
|||
# TODO: Review file locations.
|
||||
|
||||
install : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets \
|
||||
tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png direwolf.desktop
|
||||
tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png direwolf.desktop
|
||||
install direwolf $(INSTALLDIR)/bin
|
||||
install decode_aprs $(INSTALLDIR)/bin
|
||||
install text2tt $(INSTALLDIR)/bin
|
||||
|
@ -323,20 +342,21 @@ install : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx ge
|
|||
install atest $(INSTALLDIR)/bin
|
||||
install ttcalc $(INSTALLDIR)/bin
|
||||
install dwespeak.sh $(INSTALLDIR)/bin
|
||||
install telemetry-toolkit/*.p[ly] $(INSTALLDIR)/bin
|
||||
install -D --mode=644 tocalls.txt /usr/share/direwolf/tocalls.txt
|
||||
install -D --mode=644 symbols-new.txt /usr/share/direwolf/symbols-new.txt
|
||||
install -D --mode=644 symbolsX.txt /usr/share/direwolf/symbolsX.txt
|
||||
install -D --mode=644 dw-icon.png /usr/share/direwolf/dw-icon.png
|
||||
install -D --mode=644 direwolf.desktop /usr/share/applications/direwolf.desktop
|
||||
install -D --mode=644 CHANGES.txt $(INSTALLDIR)/share/doc/direwolf/CHANGES.txt
|
||||
install -D --mode=644 README.md $(INSTALLDIR)/share/doc/direwolf/README.md
|
||||
install -D --mode=644 CHANGES.md $(INSTALLDIR)/share/doc/direwolf/CHANGES.md
|
||||
install -D --mode=644 LICENSE-dire-wolf.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-dire-wolf.txt
|
||||
install -D --mode=644 LICENSE-other.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-other.txt
|
||||
install -D --mode=644 User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf
|
||||
install -D --mode=644 Raspberry-Pi-APRS.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf
|
||||
install -D --mode=644 Raspberry-Pi-APRS-Tracker.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf
|
||||
install -D --mode=644 APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf
|
||||
install -D --mode=644 A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf
|
||||
install -D --mode=644 A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf
|
||||
install -D --mode=644 doc/User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf
|
||||
install -D --mode=644 doc/Raspberry-Pi-APRS.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf
|
||||
install -D --mode=644 doc/Raspberry-Pi-APRS-Tracker.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf
|
||||
install -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf
|
||||
install -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf
|
||||
install -D --mode=644 man1/aclients.1 $(INSTALLDIR)/man/man1/aclients.1
|
||||
install -D --mode=644 man1/atest.1 $(INSTALLDIR)/man/man1/atest.1
|
||||
install -D --mode=644 man1/decode_aprs.1 $(INSTALLDIR)/man/man1/decode_aprs.1
|
||||
|
@ -348,15 +368,15 @@ install : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx ge
|
|||
install -D --mode=644 man1/tt2text.1 $(INSTALLDIR)/man/man1/tt2text.1
|
||||
install -D --mode=644 man1/utm2ll.1 $(INSTALLDIR)/man/man1/utm2ll.1
|
||||
@echo " "
|
||||
@echo "If this is your first install, not an upgrade, type this"
|
||||
@echo "to put a copy of the sample configuration file in your home directory:"
|
||||
@echo "If this is your first install, not an upgrade, type this to put a copy"
|
||||
@echo "of the sample configuration file (direwolf.conf) in your home directory:"
|
||||
@echo " "
|
||||
@echo " make install-conf"
|
||||
@echo " "
|
||||
|
||||
|
||||
# TODO: Should we put the sample direwolf.conf file somewhere like
|
||||
# /usr/share/doc/direwolf/examples or /etc/ax25 and add that to the
|
||||
# /usr/share/doc/direwolf/examples and add that to the
|
||||
# end of the search path list?
|
||||
# That would make it easy to see user customizations compared to the
|
||||
# latest sample.
|
||||
|
@ -371,6 +391,8 @@ install : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx ge
|
|||
.PHONY: install-conf
|
||||
install-conf : direwolf.conf
|
||||
cp direwolf.conf ~
|
||||
cp telemetry-toolkit/telem-m0xer-3.txt ~
|
||||
cp telemetry-toolkit/telem-*.conf ~
|
||||
ifneq ($(wildcard $(HOME)/Desktop),)
|
||||
@echo " "
|
||||
@echo "This will add a desktop icon on some systems:"
|
||||
|
@ -389,7 +411,7 @@ install-rpi : dw-start.sh
|
|||
|
||||
# Separate application to decode raw data.
|
||||
|
||||
decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c latlong.c log.c telemetry.o
|
||||
decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c latlong.c log.c telemetry.o tt_text.o misc.a
|
||||
$(CC) $(CFLAGS) -o decode_aprs -DTEST $^ -lm
|
||||
|
||||
|
||||
|
@ -405,22 +427,22 @@ tt2text : tt_text.c
|
|||
|
||||
# Convert between Latitude/Longitude and UTM coordinates.
|
||||
|
||||
ll2utm : ll2utm.c geotranz.a
|
||||
ll2utm : ll2utm.c geotranz.a textcolor.o misc.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lm
|
||||
|
||||
utm2ll : utm2ll.c geotranz.a
|
||||
utm2ll : utm2ll.c geotranz.a textcolor.o misc.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lm
|
||||
|
||||
|
||||
# Convert from log file to GPX.
|
||||
|
||||
log2gpx : log2gpx.c
|
||||
log2gpx : log2gpx.c textcolor.o misc.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lm
|
||||
|
||||
|
||||
# Test application to generate sound.
|
||||
|
||||
gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c textcolor.c dsp.c
|
||||
gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c morse.c textcolor.c dsp.c misc.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -lm
|
||||
|
||||
demod.o : tune.h
|
||||
|
@ -428,7 +450,7 @@ demod_afsk.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 telemetry.c latlong.c symbols.c tune.h textcolor.c
|
||||
fcs_calc.c ax25_pad.c decode_aprs.c telemetry.c latlong.c symbols.c tune.h textcolor.c misc.a
|
||||
$(CC) $(CFLAGS) -o atest $^ -lm
|
||||
./atest 02_Track_2.wav | grep "packets decoded in" > atest.out
|
||||
|
||||
|
@ -437,34 +459,36 @@ testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o
|
|||
|
||||
|
||||
atest : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \
|
||||
fcs_calc.c ax25_pad.c decode_aprs.c telemetry.c latlong.c symbols.c textcolor.c
|
||||
fcs_calc.c ax25_pad.c decode_aprs.c telemetry.c latlong.c symbols.c tt_text.c textcolor.c \
|
||||
misc.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lm -lrt
|
||||
|
||||
# Unit test for inner digipeater algorithm
|
||||
|
||||
|
||||
dtest : digipeater.c pfilter.c ax25_pad.c dedupe.c fcs_calc.c tq.c textcolor.c
|
||||
dtest : digipeater.c pfilter.c ax25_pad.c dedupe.c fcs_calc.c tq.c textcolor.c misc.a
|
||||
$(CC) $(CFLAGS) -DTEST -o $@ $^
|
||||
./dtest
|
||||
|
||||
|
||||
# Unit test for APRStt.
|
||||
|
||||
ttest : aprs_tt.c tt_text.c latlong.c misc.a geotranz.a
|
||||
ttest : aprs_tt.c tt_text.c latlong.c textcolor.o misc.a geotranz.a misc.a
|
||||
$(CC) $(CFLAGS) -DTT_MAIN -o $@ $^
|
||||
|
||||
|
||||
# Unit test for IGate
|
||||
|
||||
|
||||
itest : igate.c textcolor.c ax25_pad.c fcs_calc.c
|
||||
itest : igate.c textcolor.c ax25_pad.c fcs_calc.c textcolor.o misc.a
|
||||
$(CC) $(CFLAGS) -DITEST -o $@ $^
|
||||
./itest
|
||||
|
||||
|
||||
# Unit test for UDP reception with AFSK demodulator
|
||||
|
||||
udptest : udp_test.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c
|
||||
udptest : udp_test.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c rrbb.c
|
||||
fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c misc.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lm -lrt
|
||||
./udptest
|
||||
|
||||
|
@ -472,20 +496,20 @@ udptest : udp_test.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec
|
|||
# Unit test for telemetry decoding.
|
||||
|
||||
|
||||
etest : telemetry.c ax25_pad.c fcs_calc.c textcolor.c misc.a regex.a
|
||||
etest : telemetry.c ax25_pad.c fcs_calc.c textcolor.o misc.a regex.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lm -lrt
|
||||
./etest
|
||||
|
||||
|
||||
# Multiple AGWPE network or serial port clients to test TNCs side by side.
|
||||
|
||||
aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.c
|
||||
aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.o misc.a
|
||||
$(CC) $(CFLAGS) -g -o $@ $^
|
||||
|
||||
|
||||
# Touch Tone to Speech sample application.
|
||||
|
||||
ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o
|
||||
ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a
|
||||
$(CC) $(CFLAGS) -g -o $@ $^
|
||||
|
||||
|
||||
|
@ -503,49 +527,35 @@ clean :
|
|||
# Package it up for distribution.
|
||||
|
||||
.PHONY: dist-src
|
||||
dist-src : CHANGES.txt User-Guide.pdf Raspberry-Pi-APRS.pdf \
|
||||
Raspberry-Pi-APRS-Tracker.pdf APRStt-Implementation-Notes.pdf \
|
||||
A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf \
|
||||
dist-src : README.md CHANGES.md
|
||||
doc/User-Guide.pdf doc/Raspberry-Pi-APRS.pdf \
|
||||
doc/Raspberry-Pi-APRS-Tracker.pdf doc/APRStt-Implementation-Notes.pdf \
|
||||
dw-start.sh dwespeak.bat dwespeak.sh \
|
||||
tocalls.txt symbols-new.txt symbolsX.txt direwolf.spec
|
||||
rm -f fsk_fast_filter.h
|
||||
echo " " > tune.h
|
||||
rm -f ../$z-src.zip
|
||||
egrep '^C|^L' direwolf.txt | cut -c2-999 > direwolf.conf
|
||||
(cd .. ; zip $z-src.zip \
|
||||
$z/CHANGES.txt \
|
||||
$z/README.md \
|
||||
$z/CHANGES.md \
|
||||
$z/LICENSE* \
|
||||
$z/User-Guide.pdf \
|
||||
$z/Raspberry-Pi-APRS.pdf \
|
||||
$z/Raspberry-Pi-APRS-Tracker.pdf \
|
||||
$z/APRStt-Implementation-Notes.pdf \
|
||||
$z/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf \
|
||||
$z/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf \
|
||||
$z/doc/User-Guide.pdf \
|
||||
$z/doc/Raspberry-Pi-APRS.pdf \
|
||||
$z/doc/Raspberry-Pi-APRS-Tracker.pdf \
|
||||
$z/doc/APRStt-Implementation-Notes.pdf \
|
||||
$z/doc/APRS-Telemetry-Toolkit.pdf \
|
||||
$z/Makefile* \
|
||||
$z/*.c $z/*.h \
|
||||
$z/regex/* $z/misc/* $z/geotranz/* \
|
||||
$z/man1/* \
|
||||
$z/direwolf.conf $z/direwolf.txt \
|
||||
$z/generic.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-start.sh $z/direwolf.spec \
|
||||
$z/dwespeak.bat $z/dwespeak.sh )
|
||||
$z/dwespeak.bat $z/dwespeak.sh \
|
||||
$z/telemetry-toolkit/* )
|
||||
|
||||
|
||||
|
||||
#User-Guide.pdf : User-Guide.docx
|
||||
# echo "***** User-Guide.pdf is out of date *****"
|
||||
|
||||
#Raspberry-Pi-APRS.pdf : Raspberry-Pi-APRS.docx
|
||||
# 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 *****"
|
||||
|
||||
#A-Better-APRS-Packet-Demodulator.pdf : A-Better-APRS-Packet-Demodulator.docx
|
||||
# echo "***** A-Better-APRS-Packet-Demodulator.pdf is out of date *****"
|
||||
|
||||
|
||||
#
|
||||
# The locations below appear to be the most recent.
|
||||
# The copy at http://www.aprs.org/tocalls.txt is out of date.
|
||||
|
|
|
@ -0,0 +1,516 @@
|
|||
#
|
||||
# Makefile for Macintosh 10.8+ version of Dire Wolf.
|
||||
#
|
||||
|
||||
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc direwolf.conf
|
||||
@echo " "
|
||||
@echo "Next step install with: "
|
||||
@echo " "
|
||||
@echo " sudo make install"
|
||||
@echo " "
|
||||
@echo "System SDK's (10.8 - 10.10) must be located here to make use of them. "
|
||||
@echo " /Developer/SDKs "
|
||||
@echo " "
|
||||
|
||||
SYS_LIBS :=
|
||||
SYS_MIN :=
|
||||
SDK := $(shell find /Developer -maxdepth 1 -type d -name "SDKs")
|
||||
#$(info $$SDK = ${SDK})
|
||||
ifeq (${SDK},/Developer/SDKs)
|
||||
SDK := $(shell find /Developer/SDKs -maxdepth 1 -type d -name "MacOSX10.8.sdk")
|
||||
ifeq (${SDK},/Developer/SDKs/MacOSX10.8.sdk)
|
||||
SYS_LIBS := -isystem /Developer/SDKs/MacOSX10.8.sdk
|
||||
SYS_MIN := -mmacosx-version-min=10.8
|
||||
else
|
||||
SDK := $(shell find /Developer/SDKs -maxdepth 1 -type d -name "MacOSX10.9.sdk")
|
||||
ifeq (${SDK},/Developer/SDKs/MacOSX10.9.sdk)
|
||||
SYS_LIBS := -isystem /Developer/SDKs/MacOSX10.9.sdk
|
||||
SYS_MIN := -mmacosx-version-min=10.9
|
||||
else
|
||||
SDK := $(shell find /Developer/SDKs -maxdepth 1 -type d -name "MacOSX10.10.sdk")
|
||||
ifeq (${SDK},/Developer/SDKs/MacOSX10.10.sdk)
|
||||
SYS_LIBS := -isystem /Developer/SDKs/MacOSX10.10.sdk
|
||||
SYS_MIN := -mmacosx-version-min=10.10
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
EXTRA_CFLAGS :=
|
||||
DARWIN_CC := $(shell which clang)
|
||||
ifeq (${DARWIN_CC},)
|
||||
DARWIN_CC := $(shell which gcc)
|
||||
EXTRA_CFLAGS :=
|
||||
else
|
||||
EXTRA_CFLAGS := -fvectorize -fslp-vectorize -fslp-vectorize-aggressive -pthread
|
||||
endif
|
||||
|
||||
# Change as required in support of the available libraries
|
||||
|
||||
#CC := $(DARWIN_CC) -m64 $(SYS_LIBS) $(SYS_MIN)
|
||||
CC := $(DARWIN_CC) -m32 $(SYS_LIBS) $(SYS_MIN)
|
||||
CFLAGS := -Os -pthread -Igeotranz $(EXTRA_CFLAGS)
|
||||
# $(info $$CC is [${CC}])
|
||||
|
||||
#
|
||||
# The DSP filters spend a lot of time spinning around in little
|
||||
# loops multiplying and adding arrays of numbers. The Intel "SSE"
|
||||
# instructions, introduced in 1999 with the Pentium III series,
|
||||
# can speed this up considerably.
|
||||
#
|
||||
# SSE2 instructions, added in 2000, don't seem to offer any advantage.
|
||||
#
|
||||
#
|
||||
# Let's take a look at the effect of the compile options.
|
||||
#
|
||||
#
|
||||
# Times are elapsed time to process Track 2 of the TNC test CD.
|
||||
#
|
||||
# i.e. "./atest 02_Track_2.wav"
|
||||
# Default demodulator type is new "E" added for version 1.2.
|
||||
#
|
||||
|
||||
#
|
||||
# ---------- x86 (32 bit) ----------
|
||||
#
|
||||
|
||||
#
|
||||
# gcc 4.6.3 running on Ubuntu 12.04.05.
|
||||
# Intel(R) Celeron(R) CPU 2.53GHz. Appears to have only 32 bit instructions.
|
||||
# Probably from around 2004 or 2005.
|
||||
#
|
||||
# When gcc is generating code for a 32 bit x86 target, it assumes the ancient
|
||||
# i386 processor. This is good for portability but bad for performance.
|
||||
#
|
||||
# The code can run considerably faster by taking advantage of the SSE instructions
|
||||
# available in the Pentium 3 or later.
|
||||
#
|
||||
# seconds options comments
|
||||
# ------ ------- --------
|
||||
# 524
|
||||
# 183 -O2
|
||||
# 182 -O3
|
||||
# 183 -O3 -ffast-math (should be same as -Ofast)
|
||||
# 184 -Ofast
|
||||
# 189 -O3 -ffast-math -march=pentium
|
||||
# 122 -O3 -ffast-math -msse
|
||||
# 122 -O3 -ffast-math -march=pentium -msse
|
||||
# 121 -O3 -ffast-math -march=pentium3 (this implies -msse)
|
||||
# 120 -O3 -ffast-math -march=native
|
||||
#
|
||||
# Note that "-march=native" is essentially the same as "-march=pentium3."
|
||||
#
|
||||
|
||||
# If the compiler is generating code for the i386 target, we can
|
||||
# get much better results by telling it we have at least a Pentium 3.
|
||||
|
||||
CFLAGS += -march=core2 -msse4.1 -std=gnu99
|
||||
#CFLAGS += -march=pentium3 -sse
|
||||
|
||||
#
|
||||
# gcc 4.8.2 running on Ubuntu 14.04.1.
|
||||
# Intel Core 2 Duo from around 2007 or 2008.
|
||||
#
|
||||
# 64 bit target implies that we have SSE and probably even better vector instructions.
|
||||
#
|
||||
# seconds options comments
|
||||
# ------ ------- --------
|
||||
# 245
|
||||
# 75 -01
|
||||
# 72 -02
|
||||
# 71 -03
|
||||
# 73 -O3 -march=native
|
||||
# 42 -O3 -ffast-math
|
||||
# 42 -Ofast (note below)
|
||||
# 40 -O3 -ffast-math -march=native
|
||||
#
|
||||
#
|
||||
# Note that "-Ofast" is a newer option roughly equivalent to "-O3 -ffast-math".
|
||||
# I use the longer form because it is compatible with older compilers.
|
||||
#
|
||||
# Why don't I don't have "-march=native?"
|
||||
# Older compilers don't recognize "native" as one of the valid options.
|
||||
# One article said it was added with gcc 4.2 but I haven't verified that.
|
||||
#
|
||||
|
||||
# Add -ffastmath in only if compiler version recognizes it.
|
||||
|
||||
useffast := $(shell gcc --help -v 2>/dev/null | grep ffast-math)
|
||||
ifneq ($(useffast),)
|
||||
CFLAGS += -ffast-math
|
||||
endif
|
||||
|
||||
#
|
||||
# You would expect "-march=native" to produce the fastest code.
|
||||
# Why don't I use it here?
|
||||
#
|
||||
# 1. In my benchmarks, above, it has a negligible impact if any at all.
|
||||
# 2. Some older versions of gcc don't recognize "native" as a valid choice.
|
||||
# 3. Results are less portable. Not a consideration if you are
|
||||
# building only for your own use but very important for anyone
|
||||
# redistributing a "binary" version.
|
||||
#
|
||||
# If you are planning to distribute the binary version to other
|
||||
# people (in some ham radio software collection, RPM, or DEB package),
|
||||
# avoid # fine tuning it for your particular computer. It could
|
||||
# cause compatibility issues for those with older computers.
|
||||
#
|
||||
|
||||
#CFLAGS += -D_FORTIFY_SOURCE
|
||||
|
||||
# Use PortAudio Library
|
||||
|
||||
# Force static linking of portaudio if the static library is available.
|
||||
PA_LIB_STATIC := $(shell find /opt/local/lib -maxdepth 1 -type f -name "libportaudio.a")
|
||||
#$(info $$PA_LIB_STATIC is [${PA_LIB_STATIC}])
|
||||
ifeq (${PA_LIB_STATIC},)
|
||||
LDLIBS += -L/opt/local/lib -lportaudio
|
||||
else
|
||||
LDLIBS += /opt/local/lib/libportaudio.a
|
||||
endif
|
||||
|
||||
# Include libraries portaudio requires.
|
||||
LDLIBS += -framework CoreAudio -framework AudioUnit -framework AudioToolbox
|
||||
LDLIBS += -framework Foundation -framework CoreServices
|
||||
|
||||
CFLAGS += -DUSE_PORTAUDIO -I/opt/local/include
|
||||
|
||||
# Uncomment following lines to enable GPS interface & tracker function.
|
||||
# Not available for MacOSX.
|
||||
# Although MacPorts has gpsd, wonder if it's the same thing.
|
||||
|
||||
#CFLAGS += -DENABLE_GPS
|
||||
#LDLIBS += -lgps
|
||||
|
||||
# Name of current directory.
|
||||
# Used to generate zip file name for distribution.
|
||||
|
||||
z := $(notdir ${CURDIR})
|
||||
|
||||
|
||||
# Main application.
|
||||
|
||||
direwolf : direwolf.o aprs_tt.o audio_portaudio.o audio_stats.o ax25_pad.o beacon.o \
|
||||
config.o decode_aprs.o dedupe.o demod_9600.o demod_afsk.o \
|
||||
demod.o digipeater.o dlq.o dsp.o dtime_now.o dtmf.o dwgps.o \
|
||||
encode_aprs.o encode_aprs.o fcs_calc.o fcs_calc.o gen_tone.o \
|
||||
geotranz.a hdlc_rec.o hdlc_rec2.o hdlc_send.o igate.o kiss_frame.o \
|
||||
kiss.o kissnet.o latlong.o latlong.o log.o morse.o multi_modem.o \
|
||||
nmea.o serial_port.o pfilter.o ptt.o rdq.o recv.o redecode.o rrbb.o server.o \
|
||||
symbols.o telemetry.o textcolor.o tq.o tt_text.o tt_user.o xmit.o
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lpthread $(LDLIBS) -lm
|
||||
|
||||
|
||||
# Optimization for slow processors.
|
||||
|
||||
demod.o : fsk_fast_filter.h
|
||||
|
||||
demod_afsk.o : fsk_fast_filter.h
|
||||
|
||||
|
||||
fsk_fast_filter.h : demod_afsk.c
|
||||
$(CC) $(CFLAGS) -o gen_fff -DGEN_FFF demod_afsk.c dsp.c textcolor.c -lm
|
||||
./gen_fff > fsk_fast_filter.h
|
||||
|
||||
|
||||
|
||||
# UTM, USNG, MGRS conversions.
|
||||
|
||||
geotranz.a : error_string.o mgrs.o polarst.o tranmerc.o ups.o usng.o utm.o
|
||||
ar -cr $@ $^
|
||||
|
||||
error_string.o : geotranz/error_string.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $^
|
||||
|
||||
mgrs.o : geotranz/mgrs.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $^
|
||||
|
||||
polarst.o : geotranz/polarst.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $^
|
||||
|
||||
tranmerc.o : geotranz/tranmerc.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $^
|
||||
|
||||
ups.o : geotranz/ups.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $^
|
||||
|
||||
usng.o : geotranz/usng.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $^
|
||||
|
||||
utm.o : geotranz/utm.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $^
|
||||
|
||||
|
||||
|
||||
# Generate apprpriate sample configuration file for this platform.
|
||||
|
||||
direwolf.conf : generic.conf
|
||||
egrep '^C|^M' generic.conf | cut -c2-999 > direwolf.conf
|
||||
|
||||
|
||||
# Where should we install it?
|
||||
|
||||
# My understanding, of the convention, is that something you compile
|
||||
# from source, that is not a standard part of the operating system,
|
||||
# should go in /usr/local/bin.
|
||||
|
||||
# This is a step in the right direction but not sufficient to use /usr instead.
|
||||
|
||||
INSTALLDIR := /usr/local
|
||||
|
||||
# TODO: Test this better.
|
||||
|
||||
# Optional installation into /usr/local/...
|
||||
# Needs to be run as root or with sudo.
|
||||
# TODO: Review file locations.
|
||||
|
||||
install : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets \
|
||||
tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png
|
||||
ginstall direwolf $(INSTALLDIR)/bin
|
||||
ginstall decode_aprs $(INSTALLDIR)/bin
|
||||
ginstall text2tt $(INSTALLDIR)/bin
|
||||
ginstall tt2text $(INSTALLDIR)/bin
|
||||
ginstall ll2utm $(INSTALLDIR)/bin
|
||||
ginstall utm2ll $(INSTALLDIR)/bin
|
||||
ginstall aclients $(INSTALLDIR)/bin
|
||||
ginstall log2gpx $(INSTALLDIR)/bin
|
||||
ginstall gen_packets $(INSTALLDIR)/bin
|
||||
ginstall atest $(INSTALLDIR)/bin
|
||||
ginstall ttcalc $(INSTALLDIR)/bin
|
||||
ginstall dwespeak.sh $(INSTALLDIR)/bin
|
||||
ginstall telemetry-toolkit/*.p[ly] $(INSTALLDIR)/bin
|
||||
ginstall -D --mode=644 tocalls.txt $(INSTALLDIR)/share/direwolf/tocalls.txt
|
||||
ginstall -D --mode=644 symbols-new.txt $(INSTALLDIR)/share/direwolf/symbols-new.txt
|
||||
ginstall -D --mode=644 symbolsX.txt $(INSTALLDIR)/share/direwolf/symbolsX.txt
|
||||
ginstall -D --mode=644 dw-icon.png $(INSTALLDIR)/share/direwolf/dw-icon.png
|
||||
ginstall -D --mode=644 README.md $(INSTALLDIR)/share/doc/direwolf/README.md
|
||||
ginstall -D --mode=644 CHANGES.md $(INSTALLDIR)/share/doc/direwolf/CHANGES.md
|
||||
ginstall -D --mode=644 direwolf.conf $(INSTALLDIR)/share/direwolf/config/direwolf.conf
|
||||
ginstall -D --mode=644 LICENSE-dire-wolf.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-dire-wolf.txt
|
||||
ginstall -D --mode=644 LICENSE-other.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-other.txt
|
||||
ginstall -D --mode=644 doc/User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf
|
||||
ginstall -D --mode=644 doc/Raspberry-Pi-APRS.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf
|
||||
ginstall -D --mode=644 doc/Raspberry-Pi-APRS-Tracker.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf
|
||||
ginstall -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf
|
||||
ginstall -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf
|
||||
ginstall -D --mode=644 man1/aclients.1 $(INSTALLDIR)/man/man1/aclients.1
|
||||
ginstall -D --mode=644 man1/atest.1 $(INSTALLDIR)/man/man1/atest.1
|
||||
ginstall -D --mode=644 man1/decode_aprs.1 $(INSTALLDIR)/man/man1/decode_aprs.1
|
||||
ginstall -D --mode=644 man1/direwolf.1 $(INSTALLDIR)/man/man1/direwolf.1
|
||||
ginstall -D --mode=644 man1/gen_packets.1 $(INSTALLDIR)/man/man1/gen_packets.1
|
||||
ginstall -D --mode=644 man1/ll2utm.1 $(INSTALLDIR)/man/man1/ll2utm.1
|
||||
ginstall -D --mode=644 man1/log2gpx.1 $(INSTALLDIR)/man/man1/log2gpx.1
|
||||
ginstall -D --mode=644 man1/text2tt.1 $(INSTALLDIR)/man/man1/text2tt.1
|
||||
ginstall -D --mode=644 man1/tt2text.1 $(INSTALLDIR)/man/man1/tt2text.1
|
||||
ginstall -D --mode=644 man1/utm2ll.1 $(INSTALLDIR)/man/man1/utm2ll.1
|
||||
@echo " "
|
||||
@echo "If this is your first install, not an upgrade, type this to put a copy"
|
||||
@echo "of the sample configuration file (direwolf.conf) in your home directory:"
|
||||
@echo " "
|
||||
@echo " make install-conf"
|
||||
@echo " "
|
||||
|
||||
|
||||
# TODO: Should we put the sample direwolf.conf file somewhere like
|
||||
# /usr/share/doc/direwolf/examples and add that to the
|
||||
# end of the search path list?
|
||||
# That would make it easy to see user customizations compared to the
|
||||
# latest sample.
|
||||
|
||||
# These would be done as ordinary user.
|
||||
|
||||
# The Raspberry Pi has ~/Desktop but Ubuntu does not.
|
||||
|
||||
# TODO: Handle Linux variations correctly.
|
||||
|
||||
|
||||
.PHONY: install-conf
|
||||
install-conf : direwolf.conf
|
||||
cp direwolf.conf ~
|
||||
cp telemetry-toolkit/telem-m0xer-3.txt ~
|
||||
cp telemetry-toolkit/telem-*.conf ~
|
||||
|
||||
|
||||
# Separate application to decode raw data.
|
||||
|
||||
decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c latlong.c log.c telemetry.o tt_text.o
|
||||
$(CC) $(CFLAGS) -o decode_aprs -DTEST $^ -lm
|
||||
|
||||
# Convert between text and touch tone representation.
|
||||
|
||||
text2tt : tt_text.c
|
||||
$(CC) $(CFLAGS) -DENC_MAIN -o text2tt tt_text.c
|
||||
|
||||
tt2text : tt_text.c
|
||||
$(CC) $(CFLAGS) -DDEC_MAIN -o tt2text tt_text.c
|
||||
|
||||
|
||||
# Convert between Latitude/Longitude and UTM coordinates.
|
||||
|
||||
ll2utm : ll2utm.c geotranz.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lm
|
||||
|
||||
utm2ll : utm2ll.c geotranz.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lm
|
||||
|
||||
|
||||
# Convert from log file to GPX.
|
||||
|
||||
log2gpx : log2gpx.c
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lm
|
||||
|
||||
|
||||
# Test application to generate sound.
|
||||
|
||||
gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c morse.c textcolor.c dsp.c
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -lm
|
||||
|
||||
demod.o : tune.h
|
||||
demod_afsk.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 telemetry.c latlong.c symbols.c tune.h textcolor.c
|
||||
$(CC) $(CFLAGS) -o atest $^ -lm
|
||||
./atest 02_Track_2.wav | grep "packets decoded in" > atest.out
|
||||
|
||||
|
||||
# Unit test for AFSK demodulator
|
||||
|
||||
|
||||
atest : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \
|
||||
fcs_calc.c ax25_pad.c decode_aprs.c telemetry.c latlong.c symbols.c textcolor.c tt_text.c
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lm
|
||||
|
||||
# Unit test for inner digipeater algorithm
|
||||
|
||||
|
||||
dtest : digipeater.c pfilter.c ax25_pad.c dedupe.c fcs_calc.c tq.c textcolor.c
|
||||
$(CC) $(CFLAGS) -DTEST -o $@ $^
|
||||
./dtest
|
||||
|
||||
|
||||
# Unit test for APRStt.
|
||||
|
||||
ttest : aprs_tt.c tt_text.c latlong.c misc.a geotranz.a
|
||||
$(CC) $(CFLAGS) -DTT_MAIN -o $@ $^
|
||||
|
||||
|
||||
# Unit test for IGate
|
||||
|
||||
|
||||
itest : igate.c textcolor.c ax25_pad.c fcs_calc.c
|
||||
$(CC) $(CFLAGS) -DITEST -o $@ $^
|
||||
./itest
|
||||
|
||||
|
||||
# Unit test for UDP reception with AFSK demodulator
|
||||
|
||||
udptest : udp_test.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c symbols.c textcolor.c
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lm
|
||||
./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
|
||||
./etest
|
||||
|
||||
|
||||
# Multiple AGWPE network or serial port clients to test TNCs side by side.
|
||||
|
||||
aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.c
|
||||
$(CC) $(CFLAGS) -g -o $@ $^
|
||||
|
||||
|
||||
# Touch Tone to Speech sample application.
|
||||
|
||||
ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o
|
||||
$(CC) $(CFLAGS) -g -o $@ $^
|
||||
|
||||
|
||||
depend : $(wildcard *.c)
|
||||
makedepend -f $(lastword $(MAKEFILE_LIST)) -- $(CFLAGS) -- $^
|
||||
|
||||
|
||||
.PHONY: clean
|
||||
clean :
|
||||
rm -f direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc \
|
||||
fsk_fast_filter.h *.o *.a
|
||||
echo " " > tune.h
|
||||
|
||||
|
||||
.PHONY: dist-mac
|
||||
dist-mac: direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets \
|
||||
tocalls.txt symbols-new.txt symbolsX.txt dw-icon.png
|
||||
rm -f ../direwolf_dist_bin.zip
|
||||
(cd .. ; zip direwolf_dist_bin.zip \
|
||||
$(INSTALLDIR)/bin/direwolf \
|
||||
$(INSTALLDIR)/bin/decode_aprs \
|
||||
$(INSTALLDIR)/bin/text2tt \
|
||||
$(INSTALLDIR)/bin/tt2text \
|
||||
$(INSTALLDIR)/bin/ll2utm \
|
||||
$(INSTALLDIR)/bin/utm2ll \
|
||||
$(INSTALLDIR)/bin/aclients \
|
||||
$(INSTALLDIR)/bin/log2gpx \
|
||||
$(INSTALLDIR)/bin/gen_packets \
|
||||
$(INSTALLDIR)/bin/atest \
|
||||
$(INSTALLDIR)/bin/ttcalc \
|
||||
$(INSTALLDIR)/bin/dwespeak.sh \
|
||||
$(INSTALLDIR)/share/direwolf/tocalls.txt \
|
||||
$(INSTALLDIR)/share/direwolf/config/direwolf.conf \
|
||||
$(INSTALLDIR)/share/direwolf/symbols-new.txt \
|
||||
$(INSTALLDIR)/share/direwolf/symbolsX.txt \
|
||||
$(INSTALLDIR)/share/direwolf/dw-icon.png \
|
||||
$(INSTALLDIR)/share/doc/direwolf/README.md \
|
||||
$(INSTALLDIR)/share/doc/direwolf/CHANGES.md \
|
||||
$(INSTALLDIR)/share/doc/direwolf/LICENSE-dire-wolf.txt \
|
||||
$(INSTALLDIR)/share/doc/direwolf/LICENSE-other.txt \
|
||||
$(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf \
|
||||
$(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf \
|
||||
$(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf \
|
||||
$(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf \
|
||||
$(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf \
|
||||
$(INSTALLDIR)/man/man1/aclients.1 \
|
||||
$(INSTALLDIR)/man/man1/atest.1 \
|
||||
$(INSTALLDIR)/man/man1/decode_aprs.1 \
|
||||
$(INSTALLDIR)/man/man1/direwolf.1 \
|
||||
$(INSTALLDIR)/man/man1/gen_packets.1 \
|
||||
$(INSTALLDIR)/man/man1/ll2utm.1 \
|
||||
$(INSTALLDIR)/man/man1/log2gpx.1 \
|
||||
$(INSTALLDIR)/man/man1/text2tt.1 \
|
||||
$(INSTALLDIR)/man/man1/tt2text.1 \
|
||||
$(INSTALLDIR)/man/man1/utm2ll.1 \
|
||||
)
|
||||
|
||||
# Package it up for distribution.
|
||||
|
||||
.PHONY: dist-src
|
||||
dist-src : README.md CHANGES.md \
|
||||
doc/User-Guide.pdf doc/Raspberry-Pi-APRS.pdf \
|
||||
doc/Raspberry-Pi-APRS-Tracker.pdf doc/APRStt-Implementation-Notes.pdf \
|
||||
dw-start.sh dwespeak.bat dwespeak.sh \
|
||||
tocalls.txt symbols-new.txt symbolsX.txt direwolf.spec
|
||||
rm -f fsk_fast_filter.h
|
||||
echo " " > tune.h
|
||||
rm -f ../$z-src.zip
|
||||
(cd .. ; zip $z-src.zip \
|
||||
$z/README.md \
|
||||
$z/CHANGES.md \
|
||||
$z/LICENSE* \
|
||||
$z/doc/User-Guide.pdf \
|
||||
$z/doc/Raspberry-Pi-APRS.pdf \
|
||||
$z/doc/Raspberry-Pi-APRS-Tracker.pdf \
|
||||
$z/doc/APRStt-Implementation-Notes.pdf \
|
||||
$z/Makefile* \
|
||||
$z/*.c $z/*.h \
|
||||
$z/regex/* $z/misc/* $z/geotranz/* \
|
||||
$z/man1/* \
|
||||
$z/generic.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-start.sh $z/direwolf.spec \
|
||||
$z/dwespeak.bat $z/dwespeak.sh \
|
||||
$z/telemetry-toolkit/* )
|
145
Makefile.win
145
Makefile.win
|
@ -26,7 +26,7 @@ CC := gcc
|
|||
CFLAGS := -Wall -Ofast -march=pentium3 -msse -Iregex -Iutm -Igeotranz -mthreads -DUSE_REGEX_STATIC
|
||||
AR := ar
|
||||
|
||||
#CFLAGS += -g
|
||||
CFLAGS += -g
|
||||
|
||||
#
|
||||
# Let's see impact of various optimization levels.
|
||||
|
@ -73,9 +73,9 @@ direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_9600.o hd
|
|||
hdlc_rec2.o multi_modem.o redecode.o rdq.o rrbb.o dlq.o \
|
||||
fcs_calc.o ax25_pad.o \
|
||||
decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
|
||||
gen_tone.o audio_win.o digipeater.o pfilter.o dedupe.o tq.o xmit.o \
|
||||
gen_tone.o morse.o audio_win.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o \
|
||||
ptt.o beacon.o dwgps.o encode_aprs.o latlong.o textcolor.o \
|
||||
dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o log.o telemetry.o dtime_now.o \
|
||||
dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o serial_port.o log.o telemetry.o dtime_now.o \
|
||||
dw-icon.o regex.a misc.a geotranz.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
|
||||
|
||||
|
@ -139,7 +139,7 @@ regex.o : regex/regex.c
|
|||
# There are also a couple other functions in the misc
|
||||
# subdirectory that are missing on Windows.
|
||||
|
||||
misc.a : strsep.o strtok_r.o strcasestr.o
|
||||
misc.a : strsep.o strtok_r.o strcasestr.o strlcpy.o strlcat.o
|
||||
ar -cr $@ $^
|
||||
|
||||
strsep.o : misc/strsep.c
|
||||
|
@ -151,11 +151,16 @@ strtok_r.o : misc/strtok_r.c
|
|||
strcasestr.o : misc/strcasestr.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $^
|
||||
|
||||
strlcpy.o : misc/strlcpy.c
|
||||
$(CC) $(CFLAGS) -I. -c -o $@ $^
|
||||
|
||||
strlcat.o : misc/strlcat.c
|
||||
$(CC) $(CFLAGS) -I. -c -o $@ $^
|
||||
|
||||
|
||||
# Separate application to decode raw data.
|
||||
|
||||
decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c latlong.c log.o telemetry.o regex.a misc.a geotranz.a
|
||||
decode_aprs : decode_aprs.c symbols.c ax25_pad.c textcolor.c fcs_calc.c latlong.c log.o telemetry.o tt_text.o regex.a misc.a geotranz.a
|
||||
$(CC) $(CFLAGS) -o decode_aprs -DTEST $^
|
||||
|
||||
|
||||
|
@ -170,22 +175,23 @@ tt2text : tt_text.c
|
|||
|
||||
# Convert between Latitude/Longitude and UTM coordinates.
|
||||
|
||||
ll2utm : ll2utm.c geotranz.a
|
||||
ll2utm : ll2utm.c textcolor.c geotranz.a misc.a
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
utm2ll : utm2ll.c geotranz.a
|
||||
utm2ll : utm2ll.c textcolor.c geotranz.a misc.a
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
|
||||
# Convert from log file to GPX.
|
||||
|
||||
log2gpx : log2gpx.c misc/strsep.c misc/strtok_r.c
|
||||
#log2gpx : log2gpx.c misc/strsep.c misc/strtok_r.c
|
||||
log2gpx : log2gpx.c misc.a
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
|
||||
# Test application to generate sound.
|
||||
|
||||
gen_packets : gen_packets.o ax25_pad.o hdlc_send.o fcs_calc.o gen_tone.o textcolor.o dsp.o misc.a regex.a
|
||||
gen_packets : gen_packets.o ax25_pad.o hdlc_send.o fcs_calc.o gen_tone.o morse.o textcolor.o dsp.o misc.a regex.a
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
# For tweaking the demodulator.
|
||||
|
@ -234,7 +240,7 @@ testagc9 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.
|
|||
# Unit test for AFSK demodulator
|
||||
|
||||
atest : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \
|
||||
rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c latlong.c symbols.c textcolor.c telemetry.c misc.a regex.a \
|
||||
rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c latlong.c symbols.c tt_text.c textcolor.c telemetry.c misc.a regex.a \
|
||||
fsk_fast_filter.h
|
||||
echo " " > tune.h
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
@ -291,6 +297,16 @@ ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a regex.a
|
|||
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
|
||||
|
||||
|
||||
# Send GPS location to KISS TNC each second.
|
||||
|
||||
walk96 : walk96.c nmea.c kiss_frame.c \
|
||||
latlong.o encode_aprs.o serial_port.o textcolor.o \
|
||||
ax25_pad.o fcs_calc.o regex.a \
|
||||
misc.a
|
||||
$(CC) $(CFLAGS) -DWALK96 -o $@ $^ -lwinmm -lws2_32
|
||||
|
||||
|
||||
|
||||
|
||||
.PHONY: depend
|
||||
depend : $(wildcard *.c)
|
||||
|
@ -310,21 +326,21 @@ clean :
|
|||
.PHONY: dist-win
|
||||
dist-win : direwolf.exe decode_aprs.exe text2tt.exe tt2text.exe ll2utm.exe utm2ll.exe \
|
||||
aclients.exe log2gpx.exe gen_packets.exe atest.exe ttcalc.exe \
|
||||
direwolf.txt dwespeak.bat \
|
||||
CHANGES.txt User-Guide.pdf \
|
||||
Raspberry-Pi-APRS.pdf APRStt-Implementation-Notes.pdf \
|
||||
A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf \
|
||||
A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf
|
||||
generic.conf dwespeak.bat \
|
||||
README.md CHANGES.md \
|
||||
doc/User-Guide.pdf \
|
||||
doc/Raspberry-Pi-APRS.pdf \
|
||||
doc/APRStt-Implementation-Notes.pdf
|
||||
rm -f ../$z-win.zip
|
||||
egrep '^C|^W' direwolf.txt | cut -c2-999 > direwolf.conf
|
||||
egrep '^C|^W' generic.conf | cut -c2-999 > direwolf.conf
|
||||
unix2dos direwolf.conf
|
||||
zip ../$z-win.zip \
|
||||
CHANGES.txt \
|
||||
User-Guide.pdf \
|
||||
Raspberry-Pi-APRS.pdf \
|
||||
APRStt-Implementation-Notes.pdf \
|
||||
A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf \
|
||||
A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf \
|
||||
zip --junk-paths ../$z-win.zip \
|
||||
README.md \
|
||||
CHANGES.md \
|
||||
doc/User-Guide.pdf \
|
||||
doc/Raspberry-Pi-APRS.pdf \
|
||||
doc/APRStt-Implementation-Notes.pdf \
|
||||
doc/APRS-Telemetry-Toolkit.pdf \
|
||||
LICENSE* \
|
||||
direwolf.conf \
|
||||
direwolf.exe \
|
||||
|
@ -337,67 +353,96 @@ dist-win : direwolf.exe decode_aprs.exe text2tt.exe tt2text.exe ll2utm.exe utm2l
|
|||
gen_packets.exe \
|
||||
atest.exe \
|
||||
ttcalc.exe \
|
||||
dwespeak.bat
|
||||
dwespeak.bat \
|
||||
telemetry-toolkit/*
|
||||
|
||||
.PHONY: dist-src
|
||||
dist-src : CHANGES.txt \
|
||||
User-Guide.pdf \
|
||||
Raspberry-Pi-APRS.pdf \
|
||||
Raspberry-Pi-APRS-Tracker.pdf \
|
||||
APRStt-Implementation-Notes.pdf \
|
||||
A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf \
|
||||
A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf \
|
||||
dist-src : README.md CHANGES.md \
|
||||
doc/User-Guide.pdf \
|
||||
doc/Raspberry-Pi-APRS.pdf \
|
||||
doc/Raspberry-Pi-APRS-Tracker.pdf \
|
||||
doc/APRStt-Implementation-Notes.pdf \
|
||||
doc/APRS-Telemetry-Toolkit.pdf \
|
||||
dw-start.sh dwespeak.bat dwespeak.sh \
|
||||
tocalls.txt symbols-new.txt symbolsX.txt direwolf.spec
|
||||
rm -f fsk_fast_filter.h
|
||||
echo " " > tune.h
|
||||
rm -f ../$z-src.zip
|
||||
egrep '^C|^L' direwolf.txt | cut -c2-999 > direwolf.conf
|
||||
dos2unix direwolf.conf
|
||||
dos2unix generic.conf
|
||||
dos2unix Makefile
|
||||
dos2unix Makefile.linux
|
||||
dos2unix Makefile.macosx
|
||||
dos2unix telemetry-toolkit/telem-balloon.conf
|
||||
dos2unix telemetry-toolkit/telem-balloon.pl
|
||||
dos2unix telemetry-toolkit/telem-bits.pl
|
||||
dos2unix telemetry-toolkit/telem-data.pl
|
||||
dos2unix telemetry-toolkit/telem-data91.pl
|
||||
dos2unix telemetry-toolkit/telem-eqns.pl
|
||||
dos2unix telemetry-toolkit/telem-m0xer-3.txt
|
||||
dos2unix telemetry-toolkit/telem-parm.pl
|
||||
dos2unix telemetry-toolkit/telem-unit.pl
|
||||
dos2unix telemetry-toolkit/telem-volts.py
|
||||
dos2unix telemetry-toolkit/telem-volts.conf
|
||||
(cd .. ; zip $z-src.zip \
|
||||
$z/CHANGES.txt \
|
||||
$z/README.md \
|
||||
$z/CHANGES.md \
|
||||
$z/LICENSE* \
|
||||
$z/User-Guide.pdf \
|
||||
$z/Raspberry-Pi-APRS.pdf \
|
||||
$z/Raspberry-Pi-APRS-Tracker.pdf \
|
||||
$z/APRStt-Implementation-Notes.pdf \
|
||||
$z/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf \
|
||||
$z/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf \
|
||||
$z/Makefile.win $z/Makefile.linux $z/Makefile \
|
||||
$z/doc/User-Guide.pdf \
|
||||
$z/doc/Raspberry-Pi-APRS.pdf \
|
||||
$z/doc/Raspberry-Pi-APRS-Tracker.pdf \
|
||||
$z/doc/APRStt-Implementation-Notes.pdf \
|
||||
$z/doc/APRS-Telemetry-Toolkit.pdf \
|
||||
$z/Makefile.win $z/Makefile.linux $z/Makefile.macosx $z/Makefile \
|
||||
$z/*.c $z/*.h \
|
||||
$z/regex/* $z/misc/* $z/geotranz/* \
|
||||
$z/man1/* \
|
||||
$z/direwolf.conf $z/direwolf.txt \
|
||||
$z/generic.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-start.sh $z/direwolf.spec \
|
||||
$z/dwespeak.bat $z/dwespeak.sh )
|
||||
$z/dwespeak.bat $z/dwespeak.sh \
|
||||
$z/telemetry-toolkit/* )
|
||||
unix2dos Makefile
|
||||
unix2dos Makefile.linux
|
||||
rm direwolf.conf
|
||||
unix2dos Makefile.macosx
|
||||
unix2dos telemetry-toolkit/telem-balloon.conf
|
||||
unix2dos telemetry-toolkit/telem-balloon.pl
|
||||
dos2unix telemetry-toolkit/telem-bits.pl
|
||||
unix2dos telemetry-toolkit/telem-data.pl
|
||||
unix2dos telemetry-toolkit/telem-data91.pl
|
||||
unix2dos telemetry-toolkit/telem-eqns.pl
|
||||
unix2dos telemetry-toolkit/telem-m0xer-3.txt
|
||||
unix2dos telemetry-toolkit/telem-parm.pl
|
||||
unix2dos telemetry-toolkit/telem-unit.pl
|
||||
unix2dos telemetry-toolkit/telem-volts.py
|
||||
unix2dos telemetry-toolkit/telem-volts.conf
|
||||
|
||||
|
||||
|
||||
|
||||
User-Guide.pdf : User-Guide.docx
|
||||
doc/User-Guide.pdf : doc/User-Guide.docx
|
||||
echo "***** User-Guide.pdf is out of date *****"
|
||||
|
||||
Raspberry-Pi-APRS.pdf : Raspberry-Pi-APRS.docx
|
||||
doc/Raspberry-Pi-APRS.pdf : doc/Raspberry-Pi-APRS.docx
|
||||
echo "***** Raspberry-Pi-APRS.pdf is out of date *****"
|
||||
|
||||
Raspberry-Pi-APRS-Tracker.pdf : Raspberry-Pi-APRS-Tracker.docx
|
||||
doc/Raspberry-Pi-APRS-Tracker.pdf : doc/Raspberry-Pi-APRS-Tracker.docx
|
||||
echo "***** Raspberry-Pi-APRS-Tracker.pdf is out of date *****"
|
||||
|
||||
APRStt-Implementation-Notes.pdf : APRStt-Implementation-Notes.docx
|
||||
doc/APRStt-Implementation-Notes.pdf : doc/APRStt-Implementation-Notes.docx
|
||||
echo "***** APRStt-Implementation-Notes.pdf is out of date *****"
|
||||
|
||||
A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf : A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.docx
|
||||
doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf : doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.docx
|
||||
echo "***** A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf is out of date *****"
|
||||
|
||||
A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf : A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.docx
|
||||
doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf : doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.docx
|
||||
echo "***** A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf is out of date *****"
|
||||
|
||||
doc/APRS-Telemetry-Toolkit.pdf : doc/APRS-Telemetry-Toolkit.docx
|
||||
echo "***** APRS-Telemetry-Toolkit.pdf is out of date *****"
|
||||
|
||||
|
||||
|
||||
.PHONY: backup
|
||||
backup :
|
||||
mkdir /cygdrive/e/backup-cygwin-home/`date +"%Y-%m-%d"`
|
||||
|
|
|
@ -582,6 +582,7 @@ static void * client_thread_net (void *arg)
|
|||
use_chan == mon_cmd.portx;
|
||||
memset (&alevel, 0xff, sizeof(alevel));
|
||||
pp = ax25_from_frame ((unsigned char *)(data+1), mon_cmd.data_len-1, alevel);
|
||||
assert (pp != NULL);
|
||||
ax25_format_addrs (pp, result);
|
||||
info_len = ax25_get_info (pp, (unsigned char **)(&pinfo));
|
||||
pinfo[info_len] = '\0';
|
||||
|
|
316
aprs_tt.c
316
aprs_tt.c
|
@ -42,12 +42,12 @@
|
|||
// What do we call the parts separated by * key? Entry? Field?
|
||||
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
@ -64,6 +64,8 @@
|
|||
#include "latlong.h"
|
||||
#include "dlq.h"
|
||||
#include "demod.h" /* for alevel_t & demod_get_audio_level() */
|
||||
#include "tq.h"
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
char *strtok_r(char *str, const char *delim, char **saveptr);
|
||||
|
@ -144,6 +146,8 @@ static struct ttloc_s test_config[] = {
|
|||
{ TTLOC_GRID, "Byyyxxx", .grid.lat0 = 37 + 50./60.0, .grid.lon0 = 81,
|
||||
.grid.lat9 = 37 + 59.99/60.0, .grid.lon9 = 81 + 9.99/60.0 },
|
||||
|
||||
{ TTLOC_MHEAD, "BAxxxxxx", .mhead.prefix = "326129" },
|
||||
|
||||
{ TTLOC_SATSQ, "BAxxxx" },
|
||||
|
||||
{ TTLOC_MACRO, "xxyyy", .macro.definition = "B9xx*AB166*AA2B4C5B3B0Ayyy" },
|
||||
|
@ -310,6 +314,7 @@ static char m_callsign[20]; /* really object name */
|
|||
static char m_symtab_or_overlay;
|
||||
static char m_symbol_code;
|
||||
|
||||
static char m_loc_text[24];
|
||||
static double m_longitude;
|
||||
static double m_latitude;
|
||||
static char m_comment[200];
|
||||
|
@ -325,6 +330,10 @@ static int m_ssid;
|
|||
void aprs_tt_sequence (int chan, char *msg)
|
||||
{
|
||||
int err;
|
||||
char audible_response[1000];
|
||||
packet_t pp;
|
||||
char script_response[1000];
|
||||
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
|
@ -341,15 +350,16 @@ void aprs_tt_sequence (int chan, char *msg)
|
|||
/*
|
||||
* The parse functions will fill these in.
|
||||
*/
|
||||
strcpy (m_callsign, "");
|
||||
strlcpy (m_callsign, "", sizeof(m_callsign));
|
||||
m_symtab_or_overlay = '\\';
|
||||
m_symbol_code = 'A';
|
||||
strlcpy (m_loc_text, "", sizeof(m_loc_text));
|
||||
m_longitude = G_UNKNOWN;
|
||||
m_latitude = G_UNKNOWN;
|
||||
strcpy (m_comment, "");
|
||||
strcpy (m_freq, "");
|
||||
strlcpy (m_comment, "", sizeof(m_comment));
|
||||
strlcpy (m_freq, "", sizeof(m_freq));
|
||||
m_mic_e = ' ';
|
||||
strcpy (m_dao, "!T !"); /* start out unknown */
|
||||
strlcpy (m_dao, "!T !", sizeof(m_dao)); /* start out unknown */
|
||||
m_ssid = 12;
|
||||
|
||||
/*
|
||||
|
@ -371,13 +381,46 @@ void aprs_tt_sequence (int chan, char *msg)
|
|||
*/
|
||||
|
||||
#ifndef TT_MAIN
|
||||
err = tt_user_heard (m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code, m_latitude, m_longitude,
|
||||
err = tt_user_heard (m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code,
|
||||
m_loc_text, m_latitude, m_longitude,
|
||||
m_freq, m_comment, m_mic_e, m_dao);
|
||||
#endif
|
||||
}
|
||||
|
||||
// TODO send response to user.
|
||||
// err == 0 OK, others, suitable error response.
|
||||
|
||||
/*
|
||||
* If a command / script was supplied, run it now.
|
||||
* This can do additional processing and provide a custom audible response.
|
||||
*/
|
||||
|
||||
strlcpy (script_response, "", sizeof(script_response));
|
||||
|
||||
if (strlen(tt_config.ttcmd) > 0) {
|
||||
|
||||
dw_run_cmd (tt_config.ttcmd, 1, script_response, (int)sizeof(script_response));
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Send response to user by constructing packet with SPEECH or MORSE as destination.
|
||||
* Source shouldn't matter because it doesn't get transmitted as AX.25 frame.
|
||||
* Use high priority queue for consistent timing.
|
||||
*/
|
||||
|
||||
snprintf (audible_response, sizeof(audible_response),
|
||||
"APRSTT>%s:%s",
|
||||
tt_config.response[err].method,
|
||||
(strlen(script_response) > 0) ? script_response : tt_config.response[err].mtext);
|
||||
|
||||
pp = ax25_from_text (audible_response, 0);
|
||||
|
||||
if (pp == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error. Couldn't make frame from \"%s\"\n", audible_response);
|
||||
return;
|
||||
}
|
||||
|
||||
tq_append (chan, TQ_PRIO_0_HI, pp);
|
||||
|
||||
|
||||
} /* end aprs_tt_sequence */
|
||||
|
@ -414,42 +457,55 @@ static int parse_fields (char *msg)
|
|||
char stemp[MAX_MSG_LEN+1];
|
||||
char *e;
|
||||
char *save;
|
||||
int err;
|
||||
|
||||
strcpy (stemp, msg);
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//printf ("parse_fields (%s).\n", msg);
|
||||
|
||||
strlcpy (stemp, msg, sizeof(stemp));
|
||||
e = strtok_r (stemp, "*#", &save);
|
||||
while (e != NULL) {
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//printf ("parse_fields () field = %s\n", e);
|
||||
|
||||
switch (*e) {
|
||||
|
||||
case 'A':
|
||||
|
||||
switch (e[1]) {
|
||||
case 'A':
|
||||
parse_object_name (e);
|
||||
err = parse_object_name (e);
|
||||
if (err != 0) return (err);
|
||||
break;
|
||||
case 'B':
|
||||
parse_symbol (e);
|
||||
err = parse_symbol (e);
|
||||
if (err != 0) return (err);
|
||||
break;
|
||||
case 'C':
|
||||
/*
|
||||
* New in 1.2: test for 10 digit callsign.
|
||||
*/
|
||||
if (tt_call10_to_text(e+2,1,stemp) == 0) {
|
||||
strcpy(m_callsign, stemp);
|
||||
strlcpy(m_callsign, stemp, sizeof(m_callsign));
|
||||
}
|
||||
// TODO1.3: else return (?)
|
||||
break;
|
||||
default:
|
||||
parse_callsign (e);
|
||||
err = parse_callsign (e);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
parse_location (e);
|
||||
err = parse_location (e);
|
||||
if (err != 0) return (err);
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
parse_comment (e);
|
||||
err = parse_comment (e);
|
||||
if (err != 0) return (err);
|
||||
break;
|
||||
|
||||
case '0':
|
||||
|
@ -462,7 +518,8 @@ static int parse_fields (char *msg)
|
|||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
expand_macro (e);
|
||||
err = expand_macro (e);
|
||||
if (err != 0) return (err);
|
||||
break;
|
||||
|
||||
case '\0':
|
||||
|
@ -473,7 +530,7 @@ static int parse_fields (char *msg)
|
|||
default:
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Entry does not start with A, B, C, or digit: \"%s\"\n", msg);
|
||||
dw_printf ("Field does not start with A, B, C, or digit: \"%s\"\n", msg);
|
||||
return (TT_ERROR_D_MSG);
|
||||
|
||||
}
|
||||
|
@ -481,6 +538,9 @@ static int parse_fields (char *msg)
|
|||
e = strtok_r (NULL, "*#", &save);
|
||||
}
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//printf ("parse_fields () normal return\n");
|
||||
|
||||
return (0);
|
||||
|
||||
} /* end parse_fields */
|
||||
|
@ -546,7 +606,7 @@ static int expand_macro (char *e)
|
|||
* Substitute values in to the definition.
|
||||
*/
|
||||
|
||||
strcpy (stemp, "");
|
||||
strlcpy (stemp, "", sizeof(stemp));
|
||||
|
||||
for (d = tt_config.ttloc_ptr[ipat].macro.definition; *d != '\0'; d++) {
|
||||
|
||||
|
@ -557,20 +617,20 @@ static int expand_macro (char *e)
|
|||
|
||||
switch (*d) {
|
||||
case 'x':
|
||||
strcat (stemp, xstr);
|
||||
strlcat (stemp, xstr, sizeof(stemp));
|
||||
break;
|
||||
case 'y':
|
||||
strcat (stemp, ystr);
|
||||
strlcat (stemp, ystr, sizeof(stemp));
|
||||
break;
|
||||
case 'z':
|
||||
strcat (stemp, zstr);
|
||||
strlcat (stemp, zstr, sizeof(stemp));
|
||||
break;
|
||||
default:
|
||||
{
|
||||
char c1[2];
|
||||
c1[0] = *d;
|
||||
c1[1] = '\0';
|
||||
strcat (stemp, c1);
|
||||
strlcat (stemp, c1, sizeof(stemp));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -675,7 +735,7 @@ static int parse_callsign (char *e)
|
|||
*/
|
||||
|
||||
if (len == 4 && isdigit(e[1]) && isdigit(e[2]) && isdigit(e[3])) {
|
||||
strcpy (m_callsign, e+1);
|
||||
strlcpy (m_callsign, e+1, sizeof(m_callsign));
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -844,7 +904,7 @@ static int parse_object_name (char *e)
|
|||
static int parse_symbol (char *e)
|
||||
{
|
||||
int len;
|
||||
char nstr[4];
|
||||
char nstr[3];
|
||||
int nn;
|
||||
char stemp[10];
|
||||
|
||||
|
@ -950,6 +1010,8 @@ static int parse_location (char *e)
|
|||
long lerr;
|
||||
double easting, northing;
|
||||
char mh[20];
|
||||
char stemp[32];
|
||||
|
||||
|
||||
assert (*e == 'B');
|
||||
|
||||
|
@ -1020,12 +1082,12 @@ static int parse_location (char *e)
|
|||
if (strlen(xstr) == 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Missing X coordinate.\n");
|
||||
strcpy (xstr, "0");
|
||||
strlcpy (xstr, "0", sizeof(xstr));
|
||||
}
|
||||
if (strlen(ystr) == 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Missing Y coordinate.\n");
|
||||
strcpy (ystr, "0");
|
||||
strlcpy (ystr, "0", sizeof(ystr));
|
||||
}
|
||||
|
||||
lat0 = tt_config.ttloc_ptr[ipat].grid.lat0;
|
||||
|
@ -1046,13 +1108,13 @@ static int parse_location (char *e)
|
|||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Missing X coordinate.\n");
|
||||
/* Avoid divide by zero later. Put in middle of range. */
|
||||
strcpy (xstr, "5");
|
||||
strlcpy (xstr, "5", sizeof(xstr));
|
||||
}
|
||||
if (strlen(ystr) == 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Missing Y coordinate.\n");
|
||||
/* Avoid divide by zero later. Put in middle of range. */
|
||||
strcpy (ystr, "5");
|
||||
strlcpy (ystr, "5", sizeof(ystr));
|
||||
}
|
||||
|
||||
x = atof(xstr);
|
||||
|
@ -1060,7 +1122,17 @@ static int parse_location (char *e)
|
|||
|
||||
y = atof(ystr);
|
||||
northing = y * tt_config.ttloc_ptr[ipat].utm.scale + tt_config.ttloc_ptr[ipat].utm.y_offset;
|
||||
|
||||
|
||||
if (isalpha(tt_config.ttloc_ptr[ipat].utm.latband)) {
|
||||
snprintf (m_loc_text, sizeof(m_loc_text), "%d%c %.0f %.0f", (int)(tt_config.ttloc_ptr[ipat].utm.lzone), tt_config.ttloc_ptr[ipat].utm.latband, easting, northing);
|
||||
}
|
||||
else if (tt_config.ttloc_ptr[ipat].utm.latband == '-') {
|
||||
snprintf (m_loc_text, sizeof(m_loc_text), "%d %.0f %.0f", (int)(- tt_config.ttloc_ptr[ipat].utm.lzone), easting, northing);
|
||||
}
|
||||
else {
|
||||
snprintf (m_loc_text, sizeof(m_loc_text), "%d %.0f %.0f", (int)(tt_config.ttloc_ptr[ipat].utm.lzone), easting, northing);
|
||||
}
|
||||
|
||||
lerr = Convert_UTM_To_Geodetic(tt_config.ttloc_ptr[ipat].utm.lzone,
|
||||
tt_config.ttloc_ptr[ipat].utm.hemi, easting, northing, &lat0, &lon0);
|
||||
|
||||
|
@ -1088,24 +1160,26 @@ static int parse_location (char *e)
|
|||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("MGRS/USNG: Missing X (easting) coordinate.\n");
|
||||
/* Should not be possible to get here. Fake it and carry on. */
|
||||
strcpy (xstr, "5");
|
||||
strlcpy (xstr, "5", sizeof(xstr));
|
||||
}
|
||||
if (strlen(ystr) == 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("MGRS/USNG: Missing Y (northing) coordinate.\n");
|
||||
/* Should not be possible to get here. Fake it and carry on. */
|
||||
strcpy (ystr, "5");
|
||||
strlcpy (ystr, "5", sizeof(ystr));
|
||||
}
|
||||
|
||||
char loc[40];
|
||||
|
||||
strcpy (loc, tt_config.ttloc_ptr[ipat].mgrs.zone);
|
||||
strcat (loc, xstr);
|
||||
strcat (loc, ystr);
|
||||
strlcpy (loc, tt_config.ttloc_ptr[ipat].mgrs.zone, sizeof(loc));
|
||||
strlcat (loc, xstr, sizeof(loc));
|
||||
strlcat (loc, ystr, sizeof(loc));
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("MGRS/USNG location debug: %s\n", loc);
|
||||
|
||||
strlcpy (m_loc_text, loc, sizeof(m_loc_text));
|
||||
|
||||
if (tt_config.ttloc_ptr[ipat].type == TTLOC_MGRS)
|
||||
lerr = Convert_MGRS_To_Geodetic(loc, &lat0, &lon0);
|
||||
else
|
||||
|
@ -1127,17 +1201,48 @@ static int parse_location (char *e)
|
|||
}
|
||||
break;
|
||||
|
||||
case TTLOC_MHEAD:
|
||||
|
||||
|
||||
/* Combine prefix from configuration and digits from user. */
|
||||
|
||||
strlcpy (stemp, tt_config.ttloc_ptr[ipat].mhead.prefix, sizeof(stemp));
|
||||
strlcat (stemp, xstr, sizeof(stemp));
|
||||
|
||||
if (strlen(stemp) != 4 && strlen(stemp) != 6 && strlen(stemp) != 10 && strlen(stemp) != 12) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Expected total of 4, 6, 10, or 12 digits for the Maidenhead Locator \"%s\" + \"%s\"\n",
|
||||
tt_config.ttloc_ptr[ipat].mhead.prefix, xstr);
|
||||
return (TT_ERROR_INVALID_MHEAD);
|
||||
}
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("Case MHEAD: Convert to text \"%s\".\n", stemp);
|
||||
|
||||
if (tt_mhead_to_text (stemp, 0, mh) == 0) {
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("Case MHEAD: Resulting text \"%s\".\n", mh);
|
||||
|
||||
strlcpy (m_loc_text, mh, sizeof(m_loc_text));
|
||||
|
||||
ll_from_grid_square (mh, &m_latitude, &m_longitude);
|
||||
}
|
||||
break;
|
||||
|
||||
case TTLOC_SATSQ:
|
||||
|
||||
if (strlen(xstr) != 4) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Expected 4 digits for the Satellite Square.\n");
|
||||
return (TT_ERROR_SATSQ);
|
||||
return (TT_ERROR_INVALID_SATSQ);
|
||||
}
|
||||
|
||||
/* Convert 4 digits to usual AA99 form, then to location. */
|
||||
|
||||
if (tt_gridsquare_to_text (xstr, 0, mh) == 0) {
|
||||
if (tt_satsq_to_text (xstr, 0, mh) == 0) {
|
||||
|
||||
strlcpy (m_loc_text, mh, sizeof(m_loc_text));
|
||||
|
||||
ll_from_grid_square (mh, &m_latitude, &m_longitude);
|
||||
}
|
||||
break;
|
||||
|
@ -1149,9 +1254,13 @@ static int parse_location (char *e)
|
|||
return (0);
|
||||
}
|
||||
|
||||
/* Send reject sound. */
|
||||
/* Does not match any location specification. */
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Received location \"%s\" does not match any definitions.\n", e);
|
||||
|
||||
/* Send reject sound. */
|
||||
|
||||
return (TT_ERROR_INVALID_LOC);
|
||||
|
||||
} /* end parse_location */
|
||||
|
@ -1198,11 +1307,11 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
|
|||
if (strlen(e) == len) {
|
||||
|
||||
match = 1;
|
||||
strcpy (xstr, "");
|
||||
strcpy (ystr, "");
|
||||
strcpy (zstr, "");
|
||||
strcpy (bstr, "");
|
||||
strcpy (dstr, "");
|
||||
strlcpy (xstr, "", sizeof(xstr));
|
||||
strlcpy (ystr, "", sizeof(ystr));
|
||||
strlcpy (zstr, "", sizeof(zstr));
|
||||
strlcpy (bstr, "", sizeof(bstr));
|
||||
strlcpy (dstr, "", sizeof(dstr));
|
||||
|
||||
for (k=0; k<len; k++) {
|
||||
mc = tt_config.ttloc_ptr[ipat].pattern[k];
|
||||
|
@ -1233,7 +1342,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
|
|||
char stemp[2];
|
||||
stemp[0] = e[k];
|
||||
stemp[1] = '\0';
|
||||
strcat (xstr, stemp);
|
||||
strlcat (xstr, stemp, sizeof(xstr));
|
||||
}
|
||||
else {
|
||||
match = 0;
|
||||
|
@ -1245,7 +1354,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
|
|||
char stemp[2];
|
||||
stemp[0] = e[k];
|
||||
stemp[1] = '\0';
|
||||
strcat (ystr, stemp);
|
||||
strlcat (ystr, stemp, sizeof(ystr));
|
||||
}
|
||||
else {
|
||||
match = 0;
|
||||
|
@ -1257,7 +1366,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
|
|||
char stemp[2];
|
||||
stemp[0] = e[k];
|
||||
stemp[1] = '\0';
|
||||
strcat (zstr, stemp);
|
||||
strlcat (zstr, stemp, sizeof(zstr));
|
||||
}
|
||||
else {
|
||||
match = 0;
|
||||
|
@ -1269,7 +1378,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
|
|||
char stemp[2];
|
||||
stemp[0] = e[k];
|
||||
stemp[1] = '\0';
|
||||
strcat (bstr, stemp);
|
||||
strlcat (bstr, stemp, sizeof(bstr));
|
||||
}
|
||||
else {
|
||||
match = 0;
|
||||
|
@ -1281,7 +1390,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
|
|||
char stemp[2];
|
||||
stemp[0] = e[k];
|
||||
stemp[1] = '\0';
|
||||
strcat (dstr, stemp);
|
||||
strlcat (dstr, stemp, sizeof(dstr));
|
||||
}
|
||||
else {
|
||||
match = 0;
|
||||
|
@ -1324,8 +1433,10 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
|
|||
*
|
||||
* Description: We recognize these different formats:
|
||||
*
|
||||
* Cn - One digit for MIC-E position comment.
|
||||
* Always / plus exactly 10 characters.
|
||||
* Cn - One digit (1-9) predefined status. 0 is reserved for none.
|
||||
* The defaults are derived from the MIC-E position comments
|
||||
* which were always "/" plus exactly 10 characters.
|
||||
* Users can override the defaults with configuration options.
|
||||
*
|
||||
* Cnnnnnn - Six digit frequency reformatted as nnn.nnnMHz
|
||||
*
|
||||
|
@ -1402,7 +1513,7 @@ static void raw_tt_data_to_app (int chan, char *msg)
|
|||
return ;
|
||||
#else
|
||||
char src[10], dest[10];
|
||||
char raw_tt_msg[200];
|
||||
char raw_tt_msg[256];
|
||||
packet_t pp;
|
||||
char *c, *s;
|
||||
int i;
|
||||
|
@ -1416,10 +1527,10 @@ static void raw_tt_data_to_app (int chan, char *msg)
|
|||
// Application version might be useful in case we end up using different
|
||||
// message formats in later versions.
|
||||
|
||||
strcpy (src, "DTMF");
|
||||
sprintf (dest, "%s%d%d", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION);
|
||||
strlcpy (src, "DTMF", sizeof(src));
|
||||
snprintf (dest, sizeof(dest), "%s%d%d", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION);
|
||||
|
||||
sprintf (raw_tt_msg, "%s>%s:t%s", src, dest, msg);
|
||||
snprintf (raw_tt_msg, sizeof(raw_tt_msg), "%s>%s:t%s", src, dest, msg);
|
||||
|
||||
pp = ax25_from_text (raw_tt_msg, 1);
|
||||
|
||||
|
@ -1454,6 +1565,103 @@ static void raw_tt_data_to_app (int chan, char *msg)
|
|||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: dw_run_cmd
|
||||
*
|
||||
* Purpose: Run a command and capture the output.
|
||||
*
|
||||
* Inputs: cmd - The command.
|
||||
*
|
||||
* oneline - 0 = Keep original line separators. Caller
|
||||
* must deal with operating system differences.
|
||||
* 1 = Change CR, LF, TAB to space so result
|
||||
* is one line of text.
|
||||
* 2 = Also remove any trailing whitespace.
|
||||
*
|
||||
* maxresult - Amount of space available for result.
|
||||
*
|
||||
* Outputs: result - Output captured from running command.
|
||||
*
|
||||
* Returns: -1 for any sort of error.
|
||||
* >0 for number of characters returned (= strlen(result))
|
||||
*
|
||||
* Description: This is currently used for running a user-specified
|
||||
* script to generate a custom speech response.
|
||||
*
|
||||
* Future: There are potential other uses so it should probably
|
||||
* be relocated to a file of other misc. utilities.
|
||||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
int dw_run_cmd (char *cmd, int oneline, char *result, int maxresult)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
strlcpy (result, "", sizeof(result));
|
||||
|
||||
fp = popen (cmd, "r");
|
||||
if (fp != NULL) {
|
||||
int remaining = maxresult;
|
||||
char *pr = result;
|
||||
int err;
|
||||
|
||||
while (remaining > 2 && fgets(pr, remaining, fp) != NULL) {
|
||||
pr = result + strlen(result);
|
||||
remaining = maxresult - strlen(result);
|
||||
}
|
||||
|
||||
if ((err = pclose(fp)) != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR: Unable to run \"%s\"\n", cmd);
|
||||
// On Windows, non-existent file produces "Operation not permitted"
|
||||
// Maybe we should put in a test for whether file exists.
|
||||
dw_printf ("%s\n", strerror(err));
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
// take out any newline characters.
|
||||
|
||||
if (oneline) {
|
||||
for (pr = result; *pr != '\0'; pr++) {
|
||||
if (*pr == '\r' || *pr == '\n' || *pr == '\t') {
|
||||
*pr = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
if (oneline > 1) {
|
||||
pr = result + strlen(result) - 1;
|
||||
while (pr >= result && *pr == ' ') {
|
||||
*pr = '\0';
|
||||
pr--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("%s returns \"%s\"\n", cmd, result);
|
||||
|
||||
return (strlen(result));
|
||||
}
|
||||
|
||||
else {
|
||||
// explain_popen() would be nice but doesn't seem to be commonly available.
|
||||
|
||||
// We get here only if fork or pipe fails.
|
||||
// The command not existing must be caught above.
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR: Unable to run \"%s\"\n", cmd);
|
||||
dw_printf ("%s\n", strerror(errno));
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
|
||||
} /* end dw_run_cmd */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: main
|
||||
|
|
142
aprs_tt.h
142
aprs_tt.h
|
@ -5,6 +5,7 @@
|
|||
#define APRS_TT_H 1
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* For holding location format specifications from config file.
|
||||
* Same thing is also useful for macro definitions.
|
||||
|
@ -13,7 +14,7 @@
|
|||
*/
|
||||
|
||||
struct ttloc_s {
|
||||
enum { TTLOC_POINT, TTLOC_VECTOR, TTLOC_GRID, TTLOC_UTM, TTLOC_MGRS, TTLOC_USNG, TTLOC_MACRO, TTLOC_SATSQ } type;
|
||||
enum { TTLOC_POINT, TTLOC_VECTOR, TTLOC_GRID, TTLOC_UTM, TTLOC_MGRS, TTLOC_USNG, TTLOC_MACRO, TTLOC_MHEAD, TTLOC_SATSQ } type;
|
||||
|
||||
char pattern[20]; /* e.g. B998, B5bbbdddd, B2xxyy, Byyyxxx, BAxxxx */
|
||||
/* For macros, it should be all fixed digits, */
|
||||
|
@ -44,6 +45,7 @@ struct ttloc_s {
|
|||
double x_offset;
|
||||
double y_offset;
|
||||
long lzone; /* UTM zone, should be 1-60 */
|
||||
char latband; /* Latitude band if specified, otherwise space or - */
|
||||
char hemi; /* UTM Hemisphere, should be 'N' or 'S'. */
|
||||
} utm;
|
||||
|
||||
|
@ -51,52 +53,21 @@ struct ttloc_s {
|
|||
char zone[8]; /* Zone and square for USNG/MGRS */
|
||||
} mgrs;
|
||||
|
||||
struct {
|
||||
char prefix[24]; /* should be 10, 6, or 4 digits to be */
|
||||
/* prepended to the received sequence. */
|
||||
} mhead;
|
||||
|
||||
struct {
|
||||
char *definition;
|
||||
} macro;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Configuration options for APRStt.
|
||||
*/
|
||||
|
||||
#define TT_MAX_XMITS 10
|
||||
|
||||
struct tt_config_s {
|
||||
|
||||
int gateway_enabled; /* Send DTMF sequences to APRStt gateway. */
|
||||
|
||||
int obj_recv_chan; /* Channel to listen for tones. */
|
||||
|
||||
int obj_xmit_chan; /* Channel to transmit object report. */
|
||||
|
||||
char obj_xmit_via[AX25_MAX_REPEATERS * (AX25_MAX_ADDR_LEN+1)];
|
||||
/* e.g. empty or "WIDE2-1,WIDE1-1" */
|
||||
|
||||
int retain_time; /* Seconds to keep information about a user. */
|
||||
int num_xmits; /* Number of times to transmit object report. */
|
||||
int xmit_delay[TT_MAX_XMITS]; /* Delay between them. */
|
||||
|
||||
struct ttloc_s *ttloc_ptr; /* Pointer to variable length array of above. */
|
||||
int ttloc_size; /* Number of elements allocated. */
|
||||
int ttloc_len; /* Number of elements actually used. */
|
||||
|
||||
double corral_lat; /* The "corral" for unknown locations. */
|
||||
double corral_lon;
|
||||
double corral_offset;
|
||||
int corral_ambiguity;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
void aprs_tt_init (struct tt_config_s *p_config);
|
||||
|
||||
void aprs_tt_button (int chan, char button);
|
||||
|
||||
/* Error codes for sending responses to user. */
|
||||
|
||||
#define TT_ERROR_OK 0 /* Success. */
|
||||
#define TT_ERROR_D_MSG 1 /* D was first char of field. Not implemented yet. */
|
||||
#define TT_ERROR_INTERNAL 2 /* Internal error. Shouldn't be here. */
|
||||
#define TT_ERROR_MACRO_NOMATCH 3 /* No definition for digit sequence. */
|
||||
|
@ -106,7 +77,97 @@ void aprs_tt_button (int chan, char button);
|
|||
#define TT_ERROR_INVALID_SYMBOL 7 /* Invalid symbol specification. */
|
||||
#define TT_ERROR_INVALID_LOC 8 /* Invalid location. */
|
||||
#define TT_ERROR_NO_CALL 9 /* No call or object name included. */
|
||||
#define TT_ERROR_SATSQ 10 /* Satellite square must be 4 digits. */
|
||||
#define TT_ERROR_INVALID_MHEAD 10 /* Invalid Maidenhead Locator. */
|
||||
#define TT_ERROR_INVALID_SATSQ 11 /* Satellite square must be 4 digits. */
|
||||
|
||||
#define TT_ERROR_MAXP1 12 /* Number of items above. i.e. Last number plus 1. */
|
||||
|
||||
|
||||
#if CONFIG_C /* Is this being included from config.c? */
|
||||
|
||||
/* Must keep in sync with above !!! */
|
||||
|
||||
static const char *tt_msg_id[TT_ERROR_MAXP1] = {
|
||||
"OK",
|
||||
"D_MSG",
|
||||
"INTERNAL",
|
||||
"MACRO_NOMATCH",
|
||||
"BAD_CHECKSUM",
|
||||
"INVALID_CALL",
|
||||
"INVALID_OBJNAME",
|
||||
"INVALID_SYMBOL",
|
||||
"INVALID_LOC",
|
||||
"NO_CALL",
|
||||
"INVALID_MHEAD",
|
||||
"INVALID_SATSQ"
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Configuration options for APRStt.
|
||||
*/
|
||||
|
||||
#define TT_MAX_XMITS 10
|
||||
|
||||
#define TT_MTEXT_LEN 64
|
||||
|
||||
|
||||
struct tt_config_s {
|
||||
|
||||
int gateway_enabled; /* Send DTMF sequences to APRStt gateway. */
|
||||
|
||||
int obj_recv_chan; /* Channel to listen for tones. */
|
||||
|
||||
int obj_xmit_chan; /* Channel to transmit object report. */
|
||||
/* -1 for none. This could happpen if we */
|
||||
/* are only sending to application */
|
||||
/* and/or IGate. */
|
||||
|
||||
int obj_send_to_app; /* send to attached application(s). */
|
||||
|
||||
int obj_send_to_ig; /* send to IGate. */
|
||||
|
||||
char obj_xmit_via[AX25_MAX_REPEATERS * (AX25_MAX_ADDR_LEN+1)];
|
||||
/* e.g. empty or "WIDE2-1,WIDE1-1" */
|
||||
|
||||
int retain_time; /* Seconds to keep information about a user. */
|
||||
|
||||
int num_xmits; /* Number of times to transmit object report. */
|
||||
|
||||
int xmit_delay[TT_MAX_XMITS]; /* Delay between them. */
|
||||
/* e.g. 3 seconds before first transmission then */
|
||||
/* delays of 16, 32, seconds etc. in between repeats. */
|
||||
|
||||
struct ttloc_s *ttloc_ptr; /* Pointer to variable length array of above. */
|
||||
int ttloc_size; /* Number of elements allocated. */
|
||||
int ttloc_len; /* Number of elements actually used. */
|
||||
|
||||
double corral_lat; /* The "corral" for unknown locations. */
|
||||
double corral_lon;
|
||||
double corral_offset;
|
||||
int corral_ambiguity;
|
||||
|
||||
char status[10][TT_MTEXT_LEN]; /* Up to 9 status messages. e.g. "/enroute" */
|
||||
/* Position 0 means none and can't be changed. */
|
||||
|
||||
struct {
|
||||
char method[AX25_MAX_ADDR_LEN]; /* SPEECH or MORSE[-n] */
|
||||
char mtext[TT_MTEXT_LEN]; /* Message text. */
|
||||
} response[TT_ERROR_MAXP1];
|
||||
|
||||
char ttcmd[80]; /* Command to generate custom audible response. */
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
void aprs_tt_init (struct tt_config_s *p_config);
|
||||
|
||||
void aprs_tt_button (int chan, char button);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define APRSTT_LOC_DESC_LEN 32 /* Need at least 26 */
|
||||
|
@ -115,6 +176,9 @@ void aprs_tt_dao_to_desc (char *dao, char *str);
|
|||
|
||||
void aprs_tt_sequence (int chan, char *msg);
|
||||
|
||||
int dw_run_cmd (char *cmd, int oneline, char *result, int maxresult);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/* end aprs_tt.h */
|
2
atest.c
2
atest.c
|
@ -565,7 +565,7 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
|
|||
int info_len;
|
||||
int h;
|
||||
char heard[20];
|
||||
char alevel_text[32];
|
||||
char alevel_text[AX25_ALEVEL_TO_TEXT_SIZE];
|
||||
|
||||
packets_decoded++;
|
||||
|
||||
|
|
126
audio.c
126
audio.c
|
@ -1,7 +1,4 @@
|
|||
|
||||
// Remove next line to eliminate annoying/useful (depending on who you ask) debug messages every 100 seconds.
|
||||
#define STATISTICS 1
|
||||
|
||||
|
||||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
|
@ -95,6 +92,7 @@
|
|||
|
||||
#include "direwolf.h"
|
||||
#include "audio.h"
|
||||
#include "audio_stats.h"
|
||||
#include "textcolor.h"
|
||||
#include "dtime_now.h"
|
||||
#include "demod.h" /* for alevel_t & demod_get_audio_level() */
|
||||
|
@ -290,14 +288,14 @@ int audio_open (struct audio_s *pa)
|
|||
if (strcasecmp(pa->adev[a].adevice_in, "stdin") == 0 || strcmp(pa->adev[a].adevice_in, "-") == 0) {
|
||||
adev[a].g_audio_in_type = AUDIO_IN_TYPE_STDIN;
|
||||
/* Change "-" to stdin for readability. */
|
||||
strcpy (pa->adev[a].adevice_in, "stdin");
|
||||
strlcpy (pa->adev[a].adevice_in, "stdin", sizeof(pa->adev[a].adevice_in));
|
||||
}
|
||||
if (strncasecmp(pa->adev[a].adevice_in, "udp:", 4) == 0) {
|
||||
adev[a].g_audio_in_type = AUDIO_IN_TYPE_SDR_UDP;
|
||||
/* Supply default port if none specified. */
|
||||
if (strcasecmp(pa->adev[a].adevice_in,"udp") == 0 ||
|
||||
strcasecmp(pa->adev[a].adevice_in,"udp:") == 0) {
|
||||
sprintf (pa->adev[a].adevice_in, "udp:%d", DEFAULT_UDP_AUDIO_PORT);
|
||||
snprintf (pa->adev[a].adevice_in, sizeof(pa->adev[a].adevice_in), "udp:%d", DEFAULT_UDP_AUDIO_PORT);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -305,16 +303,16 @@ int audio_open (struct audio_s *pa)
|
|||
|
||||
/* If not specified, the device names should be "default". */
|
||||
|
||||
strcpy (audio_in_name, pa->adev[a].adevice_in);
|
||||
strcpy (audio_out_name, pa->adev[a].adevice_out);
|
||||
strlcpy (audio_in_name, pa->adev[a].adevice_in, sizeof(audio_in_name));
|
||||
strlcpy (audio_out_name, pa->adev[a].adevice_out, sizeof(audio_out_name));
|
||||
|
||||
char ctemp[40];
|
||||
|
||||
if (pa->adev[a].num_channels == 2) {
|
||||
sprintf (ctemp, " (channels %d & %d)", ADEVFIRSTCHAN(a), ADEVFIRSTCHAN(a)+1);
|
||||
snprintf (ctemp, sizeof(ctemp), " (channels %d & %d)", ADEVFIRSTCHAN(a), ADEVFIRSTCHAN(a)+1);
|
||||
}
|
||||
else {
|
||||
sprintf (ctemp, " (channel %d)", ADEVFIRSTCHAN(a));
|
||||
snprintf (ctemp, sizeof(ctemp), " (channel %d)", ADEVFIRSTCHAN(a));
|
||||
}
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
|
@ -358,7 +356,7 @@ int audio_open (struct audio_s *pa)
|
|||
if (oss_audio_device_fd < 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("%s:\n", pa->adev[a].adevice_in);
|
||||
// sprintf (message, "Could not open audio device %s", pa->adev[a].adevice_in);
|
||||
// snprintf (message, sizeof(message), "Could not open audio device %s", pa->adev[a].adevice_in);
|
||||
// perror (message);
|
||||
return (-1);
|
||||
}
|
||||
|
@ -874,75 +872,6 @@ int audio_get (int a)
|
|||
adev[a].inbuf_size_in_bytes / adev[a].bytes_per_frame, n);
|
||||
#endif
|
||||
|
||||
#if STATISTICS
|
||||
|
||||
// TODO1.2: add audio level information to windows version. Common function?
|
||||
// TODO1.2: add quiet option to suppress this.
|
||||
|
||||
/*
|
||||
* Print information about the sample rate as a debugging aid.
|
||||
* I've never seen an issue with Windows or x86 Linux but the Raspberry Pi
|
||||
* has a very troublesome audio input system where many samples got lost.
|
||||
* Occasional lines like this would immediately identify the issue.
|
||||
*
|
||||
* Past 100 seconds, 4409856 audio samples processed, 0 errors.
|
||||
*
|
||||
* That's a little hard to read. Maybe we'd be better off with an average
|
||||
* and fewer digits like this: 44.1 k
|
||||
*
|
||||
* While we are at it we can also print the current audio level(s) providing
|
||||
* more clues if nothing is being decoded.
|
||||
*/
|
||||
|
||||
if (last_time[a] == 0) {
|
||||
last_time[a] = time(NULL);
|
||||
sample_count[a] = 0;
|
||||
error_count[a] = 0;
|
||||
}
|
||||
else {
|
||||
if (n > 0) {
|
||||
sample_count[a] += n;
|
||||
}
|
||||
else {
|
||||
error_count[a]++;
|
||||
}
|
||||
this_time[a] = time(NULL);
|
||||
if (this_time[a] >= last_time[a] + duration) {
|
||||
|
||||
#if 1 /* Try this for version 1.2 and see how people react. */
|
||||
|
||||
float ave_rate = (sample_count[a] / 1000.0) / duration;
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
|
||||
if (save_audio_config_p->adev[a].num_channels > 1) {
|
||||
int ch0 = ADEVFIRSTCHAN(a);
|
||||
alevel_t alevel0 = demod_get_audio_level(a,ch0);
|
||||
int ch1 = ADEVFIRSTCHAN(a) + 1;
|
||||
alevel_t alevel1 = demod_get_audio_level(a,ch1);
|
||||
|
||||
dw_printf ("\nADEVICE%d: Sample rate approx. %.1f k, %d errors, receive audio levels CH%d %d, CH%d %d\n\n",
|
||||
a, ave_rate, error_count[a], ch0, alevel0.rec, ch1, alevel1.rec);
|
||||
}
|
||||
else {
|
||||
int ch0 = ADEVFIRSTCHAN(a);
|
||||
alevel_t alevel0 = demod_get_audio_level(a,ch0);
|
||||
|
||||
dw_printf ("\nADEVICE%d: Sample rate approx. %.1f k, %d errors, receive audio level CH%d %d\n\n",
|
||||
a, ave_rate, error_count[a], ch0, alevel0.rec);
|
||||
}
|
||||
|
||||
#else
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("\nADEVICE%d: Past %d seconds, %d audio samples processed, %d errors.\n\n",
|
||||
a, duration, sample_count[a], error_count[a]);
|
||||
#endif
|
||||
last_time[a] = this_time[a];
|
||||
sample_count[a] = 0;
|
||||
error_count[a] = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (n > 0) {
|
||||
|
||||
|
@ -950,6 +879,12 @@ int audio_get (int a)
|
|||
|
||||
adev[a].inbuf_len = n * adev[a].bytes_per_frame; /* convert to number of bytes */
|
||||
adev[a].inbuf_next = 0;
|
||||
|
||||
audio_stats (a,
|
||||
save_audio_config_p->adev[a].num_channels,
|
||||
n,
|
||||
save_audio_config_p->statistics_interval);
|
||||
|
||||
}
|
||||
else if (n == 0) {
|
||||
|
||||
|
@ -973,6 +908,11 @@ int audio_get (int a)
|
|||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Audio input device %d error: %s\n", a, snd_strerror(n));
|
||||
|
||||
audio_stats (a,
|
||||
save_audio_config_p->adev[a].num_channels,
|
||||
0,
|
||||
save_audio_config_p->statistics_interval);
|
||||
|
||||
/* Try to recover a few times and eventually give up. */
|
||||
if (++retries > 10) {
|
||||
adev[a].inbuf_len = 0;
|
||||
|
@ -1015,10 +955,21 @@ int audio_get (int a)
|
|||
perror("Can't read from audio device");
|
||||
adev[a].inbuf_len = 0;
|
||||
adev[a].inbuf_next = 0;
|
||||
|
||||
audio_stats (a,
|
||||
save_audio_config_p->adev[a].num_channels,
|
||||
0,
|
||||
save_audio_config_p->statistics_interval);
|
||||
|
||||
return (-1);
|
||||
}
|
||||
adev[a].inbuf_len = n;
|
||||
adev[a].inbuf_next = 0;
|
||||
|
||||
audio_stats (a,
|
||||
save_audio_config_p->adev[a].num_channels,
|
||||
n / (save_audio_config_p->adev[a].num_channels * save_audio_config_p->adev[a].bits_per_sample / 8),
|
||||
save_audio_config_p->statistics_interval);
|
||||
}
|
||||
|
||||
#endif /* USE_ALSA */
|
||||
|
@ -1042,11 +993,23 @@ int audio_get (int a)
|
|||
dw_printf ("Can't read from udp socket, res=%d", res);
|
||||
adev[a].inbuf_len = 0;
|
||||
adev[a].inbuf_next = 0;
|
||||
|
||||
audio_stats (a,
|
||||
save_audio_config_p->adev[a].num_channels,
|
||||
0,
|
||||
save_audio_config_p->statistics_interval);
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
adev[a].inbuf_len = res;
|
||||
adev[a].inbuf_next = 0;
|
||||
|
||||
audio_stats (a,
|
||||
save_audio_config_p->adev[a].num_channels,
|
||||
res / (save_audio_config_p->adev[a].num_channels * save_audio_config_p->adev[a].bits_per_sample / 8),
|
||||
save_audio_config_p->statistics_interval);
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1065,6 +1028,11 @@ int audio_get (int a)
|
|||
exit (0);
|
||||
}
|
||||
|
||||
audio_stats (a,
|
||||
save_audio_config_p->adev[a].num_channels,
|
||||
res / (save_audio_config_p->adev[a].num_channels * save_audio_config_p->adev[a].bits_per_sample / 8),
|
||||
save_audio_config_p->statistics_interval);
|
||||
|
||||
adev[a].inbuf_len = res;
|
||||
adev[a].inbuf_next = 0;
|
||||
}
|
||||
|
|
5
audio.h
5
audio.h
|
@ -87,6 +87,9 @@ struct audio_s {
|
|||
|
||||
char tts_script[80]; /* Script for text to speech. */
|
||||
|
||||
int statistics_interval; /* Number of seconds between the audio */
|
||||
/* statistics reports. This is set by */
|
||||
/* the "-a" option. 0 to disable feature. */
|
||||
|
||||
/* Properties for each audio channel, common to receive and transmit. */
|
||||
/* Can be different for each radio channel. */
|
||||
|
@ -227,7 +230,7 @@ struct audio_s {
|
|||
};
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
#if __WIN32__ || __APPLE__
|
||||
#define DEFAULT_ADEVICE "" /* Windows: Empty string = default audio device. */
|
||||
#else
|
||||
#if USE_ALSA
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,178 @@
|
|||
|
||||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2015 John Langner, WB2OSZ
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Module: audio_stats.c
|
||||
*
|
||||
* Purpose: Print statistics for audio input stream.
|
||||
*
|
||||
* A common complaint is that there is no indication of
|
||||
* audio input level until a packet is received correctly.
|
||||
* That's true for the Windows version but the Linux version
|
||||
* prints something like this each 100 seconds:
|
||||
*
|
||||
* ADEVICE0: Sample rate approx. 44.1 k, 0 errors, receive audio level CH0 73
|
||||
*
|
||||
* Some complain about the clutter but it has been a useful
|
||||
* troubleshooting tool. In the earlier RPi days, the sample
|
||||
* rate was quite low due to a device driver issue.
|
||||
* Using a USB hub on the RPi also caused audio problems.
|
||||
* One adapter, that I tried, produces samples at the
|
||||
* right rate but all the samples are 0.
|
||||
*
|
||||
* Here we pull the code out of the Linux version of audio.c
|
||||
* so we have a common function for all the platforms.
|
||||
*
|
||||
* We also add a command line option to adjust the time
|
||||
* between reports or turn them off entirely.
|
||||
*
|
||||
* Revisions: This is new in version 1.3.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "audio_stats.h"
|
||||
#include "textcolor.h"
|
||||
#include "dtime_now.h"
|
||||
#include "demod.h" /* for alevel_t & demod_get_audio_level() */
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: audio_stats
|
||||
*
|
||||
* Purpose: Add sample count from one buffer to the statistics.
|
||||
* Print if specified amount of time has passed.
|
||||
*
|
||||
* Inputs: adev - Audio device number: 0, 1, ..., MAX_ADEVS-1
|
||||
*
|
||||
nchan - Number of channels for this device, 1 or 2.
|
||||
*
|
||||
* nsamp - How many audio samples were read.
|
||||
*
|
||||
* interval - How many seconds between reports.
|
||||
* 0 to turn off.
|
||||
*
|
||||
* Returns: none
|
||||
*
|
||||
* Description: ...
|
||||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
|
||||
void audio_stats (int adev, int nchan, int nsamp, int interval)
|
||||
{
|
||||
|
||||
/* Gather numbers for read from audio device. */
|
||||
|
||||
|
||||
static time_t last_time[MAX_ADEVS] = { 0, 0, 0 };
|
||||
time_t this_time[MAX_ADEVS];
|
||||
static int sample_count[MAX_ADEVS];
|
||||
static int error_count[MAX_ADEVS];
|
||||
static int suppress_first[MAX_ADEVS];
|
||||
|
||||
|
||||
if (interval <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert (adev >= 0 && adev < MAX_ADEVS);
|
||||
|
||||
/*
|
||||
* Print information about the sample rate as a troubleshooting aid.
|
||||
* I've never seen an issue with Windows or x86 Linux but the Raspberry Pi
|
||||
* has a very troublesome audio input system where many samples got lost.
|
||||
*
|
||||
* While we are at it we can also print the current audio level(s) providing
|
||||
* more clues if nothing is being decoded.
|
||||
*/
|
||||
|
||||
if (last_time[adev] == 0) {
|
||||
last_time[adev] = time(NULL);
|
||||
sample_count[adev] = 0;
|
||||
error_count[adev] = 0;
|
||||
suppress_first[adev] = 1;
|
||||
/* suppressing the first one could mean a rather */
|
||||
/* long wait for the first message. We make the */
|
||||
/* first collection interval 3 seconds. */
|
||||
last_time[adev] -= (interval - 3);
|
||||
}
|
||||
else {
|
||||
if (nsamp > 0) {
|
||||
sample_count[adev] += nsamp;
|
||||
}
|
||||
else {
|
||||
error_count[adev]++;
|
||||
}
|
||||
this_time[adev] = time(NULL);
|
||||
if (this_time[adev] >= last_time[adev] + interval) {
|
||||
|
||||
if (suppress_first[adev]) {
|
||||
|
||||
/* The issue we had is that the first time the rate */
|
||||
/* would be off considerably because we didn't start */
|
||||
/* on a second boundary. So we will suppress printing */
|
||||
/* of the first one. */
|
||||
|
||||
suppress_first[adev] = 0;
|
||||
}
|
||||
else {
|
||||
float ave_rate = (sample_count[adev] / 1000.0) / interval;
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
|
||||
if (nchan > 1) {
|
||||
int ch0 = ADEVFIRSTCHAN(adev);
|
||||
alevel_t alevel0 = demod_get_audio_level(ch0,0);
|
||||
int ch1 = ADEVFIRSTCHAN(adev) + 1;
|
||||
alevel_t alevel1 = demod_get_audio_level(ch1,0);
|
||||
|
||||
dw_printf ("\nADEVICE%d: Sample rate approx. %.1f k, %d errors, receive audio levels CH%d %d, CH%d %d\n\n",
|
||||
adev, ave_rate, error_count[adev], ch0, alevel0.rec, ch1, alevel1.rec);
|
||||
}
|
||||
else {
|
||||
int ch0 = ADEVFIRSTCHAN(adev);
|
||||
alevel_t alevel0 = demod_get_audio_level(ch0,0);
|
||||
|
||||
dw_printf ("\nADEVICE%d: Sample rate approx. %.1f k, %d errors, receive audio level CH%d %d\n\n",
|
||||
adev, ave_rate, error_count[adev], ch0, alevel0.rec);
|
||||
}
|
||||
}
|
||||
last_time[adev] = this_time[adev];
|
||||
sample_count[adev] = 0;
|
||||
error_count[adev] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} /* end audio_stats.c */
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
|
||||
/* audio_stats.h */
|
||||
|
||||
|
||||
extern void audio_stats (int adev, int nchan, int nsamp, int interval);
|
||||
|
76
audio_win.c
76
audio_win.c
|
@ -1,13 +1,8 @@
|
|||
|
||||
//#define DEBUGUDP 1
|
||||
//#define DEBUG 1
|
||||
|
||||
#define STATISTICS 1
|
||||
|
||||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2011, 2012, 2013, 2014 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
@ -68,6 +63,7 @@
|
|||
|
||||
#include "direwolf.h"
|
||||
#include "audio.h"
|
||||
#include "audio_stats.h"
|
||||
#include "textcolor.h"
|
||||
#include "ptt.h"
|
||||
#include "demod.h" /* for alevel_t & demod_get_audio_level() */
|
||||
|
@ -290,14 +286,14 @@ int audio_open (struct audio_s *pa)
|
|||
if (strcasecmp(pa->adev[a].adevice_in, "stdin") == 0 || strcmp(pa->adev[a].adevice_in, "-") == 0) {
|
||||
A->g_audio_in_type = AUDIO_IN_TYPE_STDIN;
|
||||
/* Change - to stdin for readability. */
|
||||
strcpy (pa->adev[a].adevice_in, "stdin");
|
||||
strlcpy (pa->adev[a].adevice_in, "stdin", sizeof(pa->adev[a].adevice_in));
|
||||
}
|
||||
else if (strncasecmp(pa->adev[a].adevice_in, "udp:", 4) == 0) {
|
||||
A->g_audio_in_type = AUDIO_IN_TYPE_SDR_UDP;
|
||||
/* Supply default port if none specified. */
|
||||
if (strcasecmp(pa->adev[a].adevice_in,"udp") == 0 ||
|
||||
strcasecmp(pa->adev[a].adevice_in,"udp:") == 0) {
|
||||
sprintf (pa->adev[a].adevice_in, "udp:%d", DEFAULT_UDP_AUDIO_PORT);
|
||||
snprintf (pa->adev[a].adevice_in, sizeof(pa->adev[a].adevice_in), "udp:%d", DEFAULT_UDP_AUDIO_PORT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -754,18 +750,6 @@ int audio_get (int a)
|
|||
|
||||
A = &(adev[a]);
|
||||
|
||||
#if defined(DEBUGUDP) || defined(STATISTICS)
|
||||
|
||||
/* Gather numbers for read from UDP stream. */
|
||||
/* Gather numbers for read from audio device. */
|
||||
|
||||
#define duration 100 /* report every 100 seconds. */
|
||||
static time_t last_time[MAX_ADEVS];
|
||||
time_t this_time[MAX_ADEVS];
|
||||
static int sample_count[MAX_ADEVS];
|
||||
static int error_count[MAX_ADEVS];
|
||||
#endif
|
||||
|
||||
switch (A->g_audio_in_type) {
|
||||
|
||||
/*
|
||||
|
@ -791,6 +775,12 @@ int audio_get (int a)
|
|||
// TODO1.2: Need more details. Can we keep going?
|
||||
|
||||
dw_printf ("Timeout waiting for input from audio device %d.\n", a);
|
||||
|
||||
audio_stats (a,
|
||||
save_audio_config_p->adev[a].num_channels,
|
||||
0,
|
||||
save_audio_config_p->statistics_interval);
|
||||
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
@ -800,6 +790,11 @@ int audio_get (int a)
|
|||
if (p->dwUser == -1) {
|
||||
waveInUnprepareHeader(A->audio_in_handle, p, sizeof(WAVEHDR));
|
||||
p->dwUser = 0; /* Index for next byte. */
|
||||
|
||||
audio_stats (a,
|
||||
save_audio_config_p->adev[a].num_channels,
|
||||
p->dwBytesRecorded / (save_audio_config_p->adev[a].num_channels * save_audio_config_p->adev[a].bits_per_sample / 8),
|
||||
save_audio_config_p->statistics_interval);
|
||||
}
|
||||
|
||||
if (p->dwUser < p->dwBytesRecorded) {
|
||||
|
@ -841,36 +836,20 @@ int audio_get (int a)
|
|||
dw_printf ("Can't read from udp socket, errno %d", WSAGetLastError());
|
||||
A->stream_len = 0;
|
||||
A->stream_next = 0;
|
||||
|
||||
audio_stats (a,
|
||||
save_audio_config_p->adev[a].num_channels,
|
||||
0,
|
||||
save_audio_config_p->statistics_interval);
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
#if DEBUGUDP
|
||||
audio_stats (a,
|
||||
save_audio_config_p->adev[a].num_channels,
|
||||
res / (save_audio_config_p->adev[a].num_channels * save_audio_config_p->adev[a].bits_per_sample / 8),
|
||||
save_audio_config_p->statistics_interval);
|
||||
|
||||
// TODO: We should collect audio statistics like this for all types of input. Move to common function.
|
||||
|
||||
if (last_time[a] == 0) {
|
||||
last_time[a] = time(NULL);
|
||||
sample_count[a] = 0;
|
||||
error_count[a] = 0;
|
||||
}
|
||||
else {
|
||||
if (res > 0) {
|
||||
sample_count[a] += res/2;
|
||||
}
|
||||
else {
|
||||
error_count[a]++;
|
||||
}
|
||||
this_time[a] = time(NULL);
|
||||
if (this_time[a] >= last_time + duration) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("\ADEVICE%d: Past %d seconds, %d UDP audio samples processed, %d errors.\n\n",
|
||||
a, duration, sample_count[a], error_count[a]);
|
||||
last_time[a] = this_time[a];
|
||||
sample_count[a] = 0;
|
||||
error_count[a] = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
A->stream_len = res;
|
||||
A->stream_next = 0;
|
||||
}
|
||||
|
@ -892,6 +871,11 @@ int audio_get (int a)
|
|||
dw_printf ("\nEnd of file on stdin. Exiting.\n");
|
||||
exit (0);
|
||||
}
|
||||
|
||||
audio_stats (a,
|
||||
save_audio_config_p->adev[a].num_channels,
|
||||
res / (save_audio_config_p->adev[a].num_channels * save_audio_config_p->adev[a].bits_per_sample / 8),
|
||||
save_audio_config_p->statistics_interval);
|
||||
|
||||
A->stream_len = res;
|
||||
A->stream_next = 0;
|
||||
|
|
217
ax25_pad.c
217
ax25_pad.c
|
@ -1,4 +1,3 @@
|
|||
|
||||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
|
@ -151,6 +150,7 @@
|
|||
char *strtok_r(char *str, const char *delim, char **saveptr);
|
||||
#endif
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
#include "fcs_calc.h"
|
||||
|
@ -232,10 +232,19 @@ static packet_t ax25_new (void)
|
|||
}
|
||||
|
||||
this_p = calloc(sizeof (struct packet_s), (size_t)1);
|
||||
|
||||
if (this_p == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR - can't allocate memory in ax25_new.\n");
|
||||
}
|
||||
|
||||
assert (this_p != NULL);
|
||||
|
||||
this_p->magic1 = MAGIC;
|
||||
this_p->seq = last_seq_num;
|
||||
this_p->magic2 = MAGIC;
|
||||
this_p->num_addr = (-1);
|
||||
|
||||
return (this_p);
|
||||
}
|
||||
|
||||
|
@ -258,6 +267,13 @@ void ax25_delete (packet_t this_p)
|
|||
dw_printf ("ax25_delete(): before free, new=%d, delete=%d\n", new_count, delete_count);
|
||||
#endif
|
||||
|
||||
if (this_p == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR - NULL pointer passed to ax25_delete.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
delete_count++;
|
||||
|
||||
#if AX25MEMDEBUG
|
||||
|
@ -345,7 +361,7 @@ packet_t ax25_from_text (char *monitor, int strict)
|
|||
/* information field of an AX.25 frame? */
|
||||
/* Yes, but it would be difficult in the from-text case. */
|
||||
|
||||
strcpy (stuff, monitor);
|
||||
strlcpy (stuff, monitor, sizeof(stuff));
|
||||
|
||||
/*
|
||||
* Translate hexadecimal values like <0xff> to non-printing characters.
|
||||
|
@ -379,8 +395,8 @@ packet_t ax25_from_text (char *monitor, int strict)
|
|||
stuff[match[0].rm_so + 5] = '\0';
|
||||
n = strtol (stuff + match[0].rm_so + 3, &p, 16);
|
||||
stuff[match[0].rm_so] = n;
|
||||
strcpy (temp, stuff + match[0].rm_eo);
|
||||
strcpy (stuff + match[0].rm_so + 1, temp);
|
||||
strlcpy (temp, stuff + match[0].rm_eo, sizeof(temp));
|
||||
strlcpy (stuff + match[0].rm_so + 1, temp, sizeof(stuff)-match[0].rm_so-1);
|
||||
}
|
||||
else {
|
||||
keep_going = 0;
|
||||
|
@ -520,10 +536,9 @@ packet_t ax25_from_text (char *monitor, int strict)
|
|||
/*
|
||||
* Append the info part.
|
||||
*/
|
||||
strcpy ((char*)(this_p->frame_data+this_p->frame_len), pinfo);
|
||||
strlcpy ((char*)(this_p->frame_data+this_p->frame_len), pinfo, sizeof(this_p->frame_data)-this_p->frame_len);
|
||||
this_p->frame_len += strlen(pinfo);
|
||||
|
||||
|
||||
return (this_p);
|
||||
}
|
||||
|
||||
|
@ -634,6 +649,8 @@ packet_t ax25_dup (packet_t copy_from)
|
|||
|
||||
|
||||
this_p = ax25_new ();
|
||||
assert (this_p != NULL);
|
||||
|
||||
save_seq = this_p->seq;
|
||||
|
||||
memcpy (this_p, copy_from, sizeof (struct packet_s));
|
||||
|
@ -684,11 +701,11 @@ packet_t ax25_dup (packet_t copy_from)
|
|||
int ax25_parse_addr (char *in_addr, int strict, char *out_addr, int *out_ssid, int *out_heard)
|
||||
{
|
||||
char *p;
|
||||
char sstr[4];
|
||||
char sstr[8]; /* Should be 1 or 2 digits for SSID. */
|
||||
int i, j, k;
|
||||
int maxlen;
|
||||
|
||||
strcpy (out_addr, "");
|
||||
*out_addr = '\0';
|
||||
*out_ssid = 0;
|
||||
*out_heard = 0;
|
||||
|
||||
|
@ -712,8 +729,8 @@ int ax25_parse_addr (char *in_addr, int strict, char *out_addr, int *out_ssid, i
|
|||
}
|
||||
}
|
||||
|
||||
strcpy (sstr, "");
|
||||
j = 0;
|
||||
sstr[j] = '\0';
|
||||
if (*p == '-') {
|
||||
for (p++; isalnum(*p); p++) {
|
||||
if (j >= 2) {
|
||||
|
@ -1081,6 +1098,8 @@ int ax25_get_num_repeaters (packet_t this_p)
|
|||
*
|
||||
* Outputs: station - String representation of the station, including the SSID.
|
||||
* e.g. "WB2OSZ-15"
|
||||
* Usually variables will be AX25_MAX_ADDR_LEN bytes
|
||||
* but 10 would be adequate.
|
||||
*
|
||||
* Bugs: No bounds checking is performed. Be careful.
|
||||
*
|
||||
|
@ -1094,7 +1113,7 @@ int ax25_get_num_repeaters (packet_t this_p)
|
|||
void ax25_get_addr_with_ssid (packet_t this_p, int n, char *station)
|
||||
{
|
||||
int ssid;
|
||||
char sstr[4];
|
||||
char sstr[8]; /* Should be 1 or 2 digits for SSID. */
|
||||
int i;
|
||||
|
||||
assert (this_p->magic1 == MAGIC);
|
||||
|
@ -1105,7 +1124,7 @@ void ax25_get_addr_with_ssid (packet_t this_p, int n, char *station)
|
|||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error detected in ax25_get_addr_with_ssid, %s, line %d.\n", __FILE__, __LINE__);
|
||||
dw_printf ("Address index, %d, is less than zero.\n", n);
|
||||
strcpy (station, "??????");
|
||||
strlcpy (station, "??????", 10);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1113,7 +1132,7 @@ void ax25_get_addr_with_ssid (packet_t this_p, int n, char *station)
|
|||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error detected in ax25_get_addr_with_ssid, %s, line %d.\n", __FILE__, __LINE__);
|
||||
dw_printf ("Address index, %d, is too large for number of addresses, %d.\n", n, this_p->num_addr);
|
||||
strcpy (station, "??????");
|
||||
strlcpy (station, "??????", 10);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1128,12 +1147,72 @@ void ax25_get_addr_with_ssid (packet_t this_p, int n, char *station)
|
|||
|
||||
ssid = ax25_get_ssid (this_p, n);
|
||||
if (ssid != 0) {
|
||||
sprintf (sstr, "-%d", ssid);
|
||||
strcat (station, sstr);
|
||||
snprintf (sstr, sizeof(sstr), "-%d", ssid);
|
||||
strlcat (station, sstr, 10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: ax25_get_addr_no_ssid
|
||||
*
|
||||
* Purpose: Return specified address WITHOUT any SSID.
|
||||
*
|
||||
* Inputs: n - Index of address. Use the symbols
|
||||
* AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc.
|
||||
*
|
||||
* Outputs: station - String representation of the station, WITHOUT the SSID.
|
||||
* e.g. "WB2OSZ"
|
||||
* Usually variables will be AX25_MAX_ADDR_LEN bytes
|
||||
* but 7 would be adequate.
|
||||
*
|
||||
* Bugs: No bounds checking is performed. Be careful.
|
||||
*
|
||||
* Assumption: ax25_from_text or ax25_from_frame was called first.
|
||||
*
|
||||
* Returns: Character string in usual human readable format,
|
||||
*
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
void ax25_get_addr_no_ssid (packet_t this_p, int n, char *station)
|
||||
{
|
||||
int ssid;
|
||||
int i;
|
||||
|
||||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
|
||||
if (n < 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error detected in ax25_get_addr_no_ssid, %s, line %d.\n", __FILE__, __LINE__);
|
||||
dw_printf ("Address index, %d, is less than zero.\n", n);
|
||||
strlcpy (station, "??????", 7);
|
||||
return;
|
||||
}
|
||||
|
||||
if (n >= this_p->num_addr) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error detected in ax25_get_no_with_ssid, %s, line %d.\n", __FILE__, __LINE__);
|
||||
dw_printf ("Address index, %d, is too large for number of addresses, %d.\n", n, this_p->num_addr);
|
||||
strlcpy (station, "??????", 7);
|
||||
return;
|
||||
}
|
||||
|
||||
memset (station, 0, 7);
|
||||
for (i=0; i<6; i++) {
|
||||
unsigned char ch;
|
||||
|
||||
ch = (this_p->frame_data[n*7+i] >> 1) & 0x7f;
|
||||
if (ch <= ' ') break;
|
||||
station[i] = ch;
|
||||
}
|
||||
|
||||
} /* end ax25_get_addr_no_ssid */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: ax25_get_ssid
|
||||
|
@ -1490,6 +1569,8 @@ packet_t ax25_get_nextp (packet_t this_p)
|
|||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
// TODO: max len for result. buffer overflow?
|
||||
|
||||
void ax25_format_addrs (packet_t this_p, char *result)
|
||||
{
|
||||
int i;
|
||||
|
@ -1591,7 +1672,8 @@ int ax25_pack (packet_t this_p, unsigned char result[AX25_MAX_PACKET_LEN])
|
|||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
|
||||
// TODO: need someway to ensure caller allocated enough space.
|
||||
#define DESC_SIZ 16
|
||||
|
||||
ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *desc, int *pf, int *nr, int *ns)
|
||||
{
|
||||
|
@ -1600,14 +1682,14 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *
|
|||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
strcpy (desc, "????");
|
||||
strlcpy (desc, "????", DESC_SIZ);
|
||||
*pf = -1;
|
||||
*nr = -1;
|
||||
*ns = -1;
|
||||
|
||||
c = ax25_get_control(this_p);
|
||||
if (c < 0) {
|
||||
strcpy (desc, "Not AX.25");
|
||||
strlcpy (desc, "Not AX.25", DESC_SIZ);
|
||||
return (frame_not_AX25);
|
||||
}
|
||||
|
||||
|
@ -1626,7 +1708,7 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *
|
|||
*pf = (c >> 4) & 1;
|
||||
*nr = (c >> 5) & 7;
|
||||
}
|
||||
strcpy (desc, "I frame");
|
||||
strlcpy (desc, "I frame", DESC_SIZ);
|
||||
return (frame_type_I);
|
||||
}
|
||||
else if ((c & 2) == 0) {
|
||||
|
@ -1644,10 +1726,10 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *
|
|||
}
|
||||
|
||||
switch ((c >> 2) & 3) {
|
||||
case 0: strcpy (desc, "S frame RR"); return (frame_type_RR); break;
|
||||
case 1: strcpy (desc, "S frame RNR"); return (frame_type_RNR); break;
|
||||
case 2: strcpy (desc, "S frame REJ"); return (frame_type_REJ); break;
|
||||
case 3: strcpy (desc, "S frame SREJ"); return (frame_type_SREJ); break;
|
||||
case 0: strlcpy (desc, "S frame RR", DESC_SIZ); return (frame_type_RR); break;
|
||||
case 1: strlcpy (desc, "S frame RNR", DESC_SIZ); return (frame_type_RNR); break;
|
||||
case 2: strlcpy (desc, "S frame REJ", DESC_SIZ); return (frame_type_REJ); break;
|
||||
case 3: strlcpy (desc, "S frame SREJ", DESC_SIZ); return (frame_type_SREJ); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -1658,16 +1740,16 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *
|
|||
|
||||
switch (c & 0xef) {
|
||||
|
||||
case 0x6f: strcpy (desc, "U frame SABME"); return (frame_type_SABME); break;
|
||||
case 0x2f: strcpy (desc, "U frame SABM"); return (frame_type_SABM); break;
|
||||
case 0x43: strcpy (desc, "U frame DISC"); return (frame_type_DISC); break;
|
||||
case 0x0f: strcpy (desc, "U frame DM"); return (frame_type_DM); break;
|
||||
case 0x63: strcpy (desc, "U frame UA"); return (frame_type_UA); break;
|
||||
case 0x87: strcpy (desc, "U frame FRMR"); return (frame_type_FRMR); break;
|
||||
case 0x03: strcpy (desc, "U frame UI"); return (frame_type_UI); break;
|
||||
case 0xaf: strcpy (desc, "U frame XID"); return (frame_type_XID); break;
|
||||
case 0xe3: strcpy (desc, "U frame TEST"); return (frame_type_TEST); break;
|
||||
default: strcpy (desc, "U frame ???"); return (frame_type_U); break;
|
||||
case 0x6f: strlcpy (desc, "U frame SABME", DESC_SIZ); return (frame_type_SABME); break;
|
||||
case 0x2f: strlcpy (desc, "U frame SABM", DESC_SIZ); return (frame_type_SABM); break;
|
||||
case 0x43: strlcpy (desc, "U frame DISC", DESC_SIZ); return (frame_type_DISC); break;
|
||||
case 0x0f: strlcpy (desc, "U frame DM", DESC_SIZ); return (frame_type_DM); break;
|
||||
case 0x63: strlcpy (desc, "U frame UA", DESC_SIZ); return (frame_type_UA); break;
|
||||
case 0x87: strlcpy (desc, "U frame FRMR", DESC_SIZ); return (frame_type_FRMR); break;
|
||||
case 0x03: strlcpy (desc, "U frame UI", DESC_SIZ); return (frame_type_UI); break;
|
||||
case 0xaf: strlcpy (desc, "U frame XID", DESC_SIZ); return (frame_type_XID); break;
|
||||
case 0xe3: strlcpy (desc, "U frame TEST", DESC_SIZ); return (frame_type_TEST); break;
|
||||
default: strlcpy (desc, "U frame ???", DESC_SIZ); return (frame_type_U); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1740,26 +1822,28 @@ static void ctrl_to_text (int c, char *out)
|
|||
|
||||
/* Text description of protocol id octet. */
|
||||
|
||||
static void pid_to_text (int p, char *out)
|
||||
#define PID_TEXT_SIZE 80
|
||||
|
||||
static void pid_to_text (int p, char out[PID_TEXT_SIZE])
|
||||
{
|
||||
|
||||
if ((p & 0x30) == 0x10) { sprintf (out, "AX.25 layer 3 implemented."); }
|
||||
else if ((p & 0x30) == 0x20) { sprintf (out, "AX.25 layer 3 implemented."); }
|
||||
else if (p == 0x01) { sprintf (out, "ISO 8208/CCITT X.25 PLP"); }
|
||||
else if (p == 0x06) { sprintf (out, "Compressed TCP/IP packet. Van Jacobson (RFC 1144)"); }
|
||||
else if (p == 0x07) { sprintf (out, "Uncompressed TCP/IP packet. Van Jacobson (RFC 1144)"); }
|
||||
else if (p == 0x08) { sprintf (out, "Segmentation fragment"); }
|
||||
else if (p == 0xC3) { sprintf (out, "TEXNET datagram protocol"); }
|
||||
else if (p == 0xC4) { sprintf (out, "Link Quality Protocol"); }
|
||||
else if (p == 0xCA) { sprintf (out, "Appletalk"); }
|
||||
else if (p == 0xCB) { sprintf (out, "Appletalk ARP"); }
|
||||
else if (p == 0xCC) { sprintf (out, "ARPA Internet Protocol"); }
|
||||
else if (p == 0xCD) { sprintf (out, "ARPA Address resolution"); }
|
||||
else if (p == 0xCE) { sprintf (out, "FlexNet"); }
|
||||
else if (p == 0xCF) { sprintf (out, "NET/ROM"); }
|
||||
else if (p == 0xF0) { sprintf (out, "No layer 3 protocol implemented."); }
|
||||
else if (p == 0xFF) { sprintf (out, "Escape character. Next octet contains more Level 3 protocol information."); }
|
||||
else { sprintf (out, "Unknown protocol id = 0x%02x", p); }
|
||||
if ((p & 0x30) == 0x10) { snprintf (out, PID_TEXT_SIZE, "AX.25 layer 3 implemented."); }
|
||||
else if ((p & 0x30) == 0x20) { snprintf (out, PID_TEXT_SIZE, "AX.25 layer 3 implemented."); }
|
||||
else if (p == 0x01) { snprintf (out, PID_TEXT_SIZE, "ISO 8208/CCITT X.25 PLP"); }
|
||||
else if (p == 0x06) { snprintf (out, PID_TEXT_SIZE, "Compressed TCP/IP packet. Van Jacobson (RFC 1144)"); }
|
||||
else if (p == 0x07) { snprintf (out, PID_TEXT_SIZE, "Uncompressed TCP/IP packet. Van Jacobson (RFC 1144)"); }
|
||||
else if (p == 0x08) { snprintf (out, PID_TEXT_SIZE, "Segmentation fragment"); }
|
||||
else if (p == 0xC3) { snprintf (out, PID_TEXT_SIZE, "TEXNET datagram protocol"); }
|
||||
else if (p == 0xC4) { snprintf (out, PID_TEXT_SIZE, "Link Quality Protocol"); }
|
||||
else if (p == 0xCA) { snprintf (out, PID_TEXT_SIZE, "Appletalk"); }
|
||||
else if (p == 0xCB) { snprintf (out, PID_TEXT_SIZE, "Appletalk ARP"); }
|
||||
else if (p == 0xCC) { snprintf (out, PID_TEXT_SIZE, "ARPA Internet Protocol"); }
|
||||
else if (p == 0xCD) { snprintf (out, PID_TEXT_SIZE, "ARPA Address resolution"); }
|
||||
else if (p == 0xCE) { snprintf (out, PID_TEXT_SIZE, "FlexNet"); }
|
||||
else if (p == 0xCF) { snprintf (out, PID_TEXT_SIZE, "NET/ROM"); }
|
||||
else if (p == 0xF0) { snprintf (out, PID_TEXT_SIZE, "No layer 3 protocol implemented."); }
|
||||
else if (p == 0xFF) { snprintf (out, PID_TEXT_SIZE, "Escape character. Next octet contains more Level 3 protocol information."); }
|
||||
else { snprintf (out, PID_TEXT_SIZE, "Unknown protocol id = 0x%02x", p); }
|
||||
}
|
||||
|
||||
|
||||
|
@ -1785,17 +1869,17 @@ void ax25_hex_dump (packet_t this_p)
|
|||
if ( (c & 0x01) == 0 || /* I xxxx xxx0 */
|
||||
c == 0x03 || c == 0x13) { /* UI 000x 0011 */
|
||||
|
||||
char p_text[100];
|
||||
char pid_text[PID_TEXT_SIZE];
|
||||
|
||||
pid_to_text (p, p_text);
|
||||
pid_to_text (p, pid_text);
|
||||
|
||||
strcat (cp_text, ", ");
|
||||
strcat (cp_text, p_text);
|
||||
strlcat (cp_text, ", ", sizeof(cp_text));
|
||||
strlcat (cp_text, pid_text, sizeof(cp_text));
|
||||
|
||||
}
|
||||
|
||||
sprintf (l_text, ", length = %d", flen);
|
||||
strcat (cp_text, l_text);
|
||||
snprintf (l_text, sizeof(l_text), ", length = %d", flen);
|
||||
strlcat (cp_text, l_text, sizeof(cp_text));
|
||||
|
||||
dw_printf ("%s\n", cp_text);
|
||||
}
|
||||
|
@ -2097,7 +2181,7 @@ void ax25_safe_print (char *pstr, int len, int ascii_only)
|
|||
/* UTF-8 does not use fe and ff except in a possible */
|
||||
/* "Byte Order Mark" (BOM) at the beginning. */
|
||||
|
||||
sprintf (safe_str + safe_len, "<0x%02x>", ch);
|
||||
snprintf (safe_str + safe_len, sizeof(safe_str)-safe_len, "<0x%02x>", ch);
|
||||
safe_len += 6;
|
||||
}
|
||||
else {
|
||||
|
@ -2138,6 +2222,8 @@ void ax25_safe_print (char *pstr, int len, int ascii_only)
|
|||
* Comma is to be avoided because one place this
|
||||
* ends up is in a CSV format file.
|
||||
*
|
||||
* size should be AX25_ALEVEL_TO_TEXT_SIZE.
|
||||
*
|
||||
* Returns: True if something to print. (currently if alevel.original >= 0)
|
||||
* False if not.
|
||||
*
|
||||
|
@ -2153,25 +2239,30 @@ void ax25_safe_print (char *pstr, int len, int ascii_only)
|
|||
*------------------------------------------------------------------*/
|
||||
|
||||
|
||||
int ax25_alevel_to_text (alevel_t alevel, char *text)
|
||||
int ax25_alevel_to_text (alevel_t alevel, char text[AX25_ALEVEL_TO_TEXT_SIZE])
|
||||
{
|
||||
if (alevel.rec < 0) {
|
||||
strcpy (text, "");
|
||||
strlcpy (text, "", AX25_ALEVEL_TO_TEXT_SIZE);
|
||||
return (0);
|
||||
}
|
||||
|
||||
// TODO1.2: haven't thought much about non-AFSK cases yet.
|
||||
// What should we do for 9600 baud?
|
||||
// Possibility: low/high tone for DTMF???
|
||||
|
||||
// For DTMF omit the two extra numbers.
|
||||
|
||||
if (alevel.mark >= 0 && alevel.space < 0) { /* baseband */
|
||||
|
||||
sprintf (text, "%d(%+d/%+d)", alevel.rec, alevel.mark, alevel.space);
|
||||
snprintf (text, AX25_ALEVEL_TO_TEXT_SIZE, "%d(%+d/%+d)", alevel.rec, alevel.mark, alevel.space);
|
||||
}
|
||||
else if (alevel.mark == -2 && alevel.space == -2) { /* DTMF */
|
||||
|
||||
snprintf (text, AX25_ALEVEL_TO_TEXT_SIZE, "%d", alevel.rec);
|
||||
}
|
||||
else { /* AFSK */
|
||||
|
||||
//sprintf (text, "%d:%d(%d/%d=%05.3f=)", alevel.original, alevel.rec, alevel.mark, alevel.space, alevel.ms_ratio);
|
||||
sprintf (text, "%d(%d/%d)", alevel.rec, alevel.mark, alevel.space);
|
||||
//snprintf (text, AX25_ALEVEL_TO_TEXT_SIZE, "%d:%d(%d/%d=%05.3f=)", alevel.original, alevel.rec, alevel.mark, alevel.space, alevel.ms_ratio);
|
||||
snprintf (text, AX25_ALEVEL_TO_TEXT_SIZE, "%d(%d/%d)", alevel.rec, alevel.mark, alevel.space);
|
||||
}
|
||||
return (1);
|
||||
|
||||
|
|
|
@ -318,7 +318,8 @@ extern void ax25_remove_addr (packet_t this_p, int n);
|
|||
extern int ax25_get_num_addr (packet_t pp);
|
||||
extern int ax25_get_num_repeaters (packet_t this_p);
|
||||
|
||||
extern void ax25_get_addr_with_ssid (packet_t pp, int n, char *);
|
||||
extern void ax25_get_addr_with_ssid (packet_t pp, int n, char *station);
|
||||
extern void ax25_get_addr_no_ssid (packet_t pp, int n, char *station);
|
||||
|
||||
extern int ax25_get_ssid (packet_t pp, int n);
|
||||
extern void ax25_set_ssid (packet_t this_p, int n, int ssid);
|
||||
|
@ -360,7 +361,8 @@ extern unsigned short ax25_m_m_crc (packet_t pp);
|
|||
|
||||
extern void ax25_safe_print (char *, int, int ascii_only);
|
||||
|
||||
extern int ax25_alevel_to_text (alevel_t alevel, char *text);
|
||||
#define AX25_ALEVEL_TO_TEXT_SIZE 32 // overkill but safe.
|
||||
extern int ax25_alevel_to_text (alevel_t alevel, char text[AX25_ALEVEL_TO_TEXT_SIZE]);
|
||||
|
||||
|
||||
#endif /* AX25_PAD_H */
|
||||
|
|
107
beacon.c
107
beacon.c
|
@ -59,6 +59,7 @@
|
|||
#include "dwgps.h"
|
||||
#include "log.h"
|
||||
#include "dlq.h"
|
||||
#include "aprs_tt.h" // for dw_run_cmd - should relocate someday.
|
||||
|
||||
|
||||
|
||||
|
@ -205,11 +206,11 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct
|
|||
|
||||
case BEACON_CUSTOM:
|
||||
|
||||
/* INFO is required. */
|
||||
/* INFO or INFOCMD is required. */
|
||||
|
||||
if (g_misc_config_p->beacon[j].custom_info == NULL) {
|
||||
if (g_misc_config_p->beacon[j].custom_info == NULL && g_misc_config_p->beacon[j].custom_infocmd == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file, line %d: INFO is required for custom beacon.\n", g_misc_config_p->beacon[j].lineno);
|
||||
dw_printf ("Config file, line %d: INFO or INFOCMD is required for custom beacon.\n", g_misc_config_p->beacon[j].lineno);
|
||||
g_misc_config_p->beacon[j].btype = BEACON_IGNORE;
|
||||
continue;
|
||||
}
|
||||
|
@ -578,6 +579,8 @@ static void * beacon_thread (void *arg)
|
|||
char mycall[AX25_MAX_ADDR_LEN];
|
||||
int alt_ft;
|
||||
|
||||
char super_comment[AX25_MAX_INFO_LEN]; // Fixed part + any dynamic part.
|
||||
|
||||
/*
|
||||
* Obtain source call for the beacon.
|
||||
* This could potentially be different on different channels.
|
||||
|
@ -588,11 +591,11 @@ static void * beacon_thread (void *arg)
|
|||
* Version 1.1 - channel should now be 0 for IGate.
|
||||
* Type of destination is encoded separately.
|
||||
*/
|
||||
strcpy (mycall, "NOCALL");
|
||||
strlcpy (mycall, "NOCALL", sizeof(mycall));
|
||||
|
||||
assert (g_misc_config_p->beacon[j].sendto_chan >= 0);
|
||||
|
||||
strcpy (mycall, g_modem_config_p->achan[g_misc_config_p->beacon[j].sendto_chan].mycall);
|
||||
strlcpy (mycall, g_modem_config_p->achan[g_misc_config_p->beacon[j].sendto_chan].mycall, sizeof(mycall));
|
||||
|
||||
if (strlen(mycall) == 0 || strcmp(mycall, "NOCALL") == 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
|
@ -606,22 +609,52 @@ static void * beacon_thread (void *arg)
|
|||
* src > dest [ , via ]
|
||||
*/
|
||||
|
||||
strcpy (beacon_text, mycall);
|
||||
strcat (beacon_text, ">");
|
||||
strlcpy (beacon_text, mycall, sizeof(beacon_text));
|
||||
strlcat (beacon_text, ">", sizeof(beacon_text));
|
||||
|
||||
if (g_misc_config_p->beacon[j].dest != NULL) {
|
||||
strcat (beacon_text, g_misc_config_p->beacon[j].dest);
|
||||
strlcat (beacon_text, g_misc_config_p->beacon[j].dest, sizeof(beacon_text));
|
||||
}
|
||||
else {
|
||||
sprintf (stemp, "%s%1d%1d", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION);
|
||||
strcat (beacon_text, stemp);
|
||||
snprintf (stemp, sizeof(stemp), "%s%1d%1d", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION);
|
||||
strlcat (beacon_text, stemp, sizeof(beacon_text));
|
||||
}
|
||||
|
||||
if (g_misc_config_p->beacon[j].via != NULL) {
|
||||
strcat (beacon_text, ",");
|
||||
strcat (beacon_text, g_misc_config_p->beacon[j].via);
|
||||
strlcat (beacon_text, ",", sizeof(beacon_text));
|
||||
strlcat (beacon_text, g_misc_config_p->beacon[j].via, sizeof(beacon_text));
|
||||
}
|
||||
strcat (beacon_text, ":");
|
||||
strlcat (beacon_text, ":", sizeof(beacon_text));
|
||||
|
||||
|
||||
/*
|
||||
* If the COMMENTCMD option was specified, run specified command to get variable part.
|
||||
* Result is any fixed part followed by any variable part.
|
||||
*/
|
||||
|
||||
// TODO: test & document.
|
||||
|
||||
strlcpy (super_comment, "", sizeof(super_comment));
|
||||
if (g_misc_config_p->beacon[j].comment != NULL) {
|
||||
strlcpy (super_comment, g_misc_config_p->beacon[j].comment, sizeof(super_comment));
|
||||
}
|
||||
|
||||
if (g_misc_config_p->beacon[j].commentcmd != NULL) {
|
||||
char var_comment[AX25_MAX_INFO_LEN];
|
||||
int k;
|
||||
|
||||
/* Run given command to get variable part of comment. */
|
||||
|
||||
k = dw_run_cmd (g_misc_config_p->beacon[j].commentcmd, 2, var_comment, (int)sizeof(var_comment));
|
||||
if (k > 0) {
|
||||
strlcat (super_comment, var_comment, sizeof(super_comment));
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("xBEACON, config file line %d, COMMENTCMD failure.\n", g_misc_config_p->beacon[j].lineno);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add the info part depending on beacon type.
|
||||
|
@ -638,9 +671,9 @@ static void * beacon_thread (void *arg)
|
|||
g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
|
||||
0, 0, /* course, speed */
|
||||
g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
|
||||
g_misc_config_p->beacon[j].comment,
|
||||
info);
|
||||
strcat (beacon_text, info);
|
||||
super_comment,
|
||||
info, sizeof(info));
|
||||
strlcat (beacon_text, info, sizeof(beacon_text));
|
||||
g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
|
||||
break;
|
||||
|
||||
|
@ -650,9 +683,9 @@ static void * beacon_thread (void *arg)
|
|||
g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol,
|
||||
g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
|
||||
0, 0, /* course, speed */
|
||||
g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset, g_misc_config_p->beacon[j].comment,
|
||||
info);
|
||||
strcat (beacon_text, info);
|
||||
g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset, super_comment,
|
||||
info, sizeof(info));
|
||||
strlcat (beacon_text, info, sizeof(beacon_text));
|
||||
g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
|
||||
break;
|
||||
|
||||
|
@ -673,9 +706,9 @@ static void * beacon_thread (void *arg)
|
|||
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),
|
||||
g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
|
||||
g_misc_config_p->beacon[j].comment,
|
||||
info);
|
||||
strcat (beacon_text, info);
|
||||
super_comment,
|
||||
info, sizeof(info));
|
||||
strlcat (beacon_text, info, sizeof(beacon_text));
|
||||
|
||||
/* Remember most recent tracker beacon. */
|
||||
|
||||
|
@ -707,7 +740,7 @@ static void * beacon_thread (void *arg)
|
|||
A.g_tone = G_UNKNOWN;
|
||||
A.g_dcs = G_UNKNOWN;
|
||||
|
||||
strcpy (A.g_src, mycall);
|
||||
strlcpy (A.g_src, mycall, sizeof(A.g_src));
|
||||
A.g_symbol_table = g_misc_config_p->beacon[j].symtab;
|
||||
A.g_symbol_code = g_misc_config_p->beacon[j].symbol;
|
||||
A.g_lat = my_lat;
|
||||
|
@ -730,12 +763,32 @@ static void * beacon_thread (void *arg)
|
|||
case BEACON_CUSTOM:
|
||||
|
||||
if (g_misc_config_p->beacon[j].custom_info != NULL) {
|
||||
strcat (beacon_text, g_misc_config_p->beacon[j].custom_info);
|
||||
|
||||
/* Fixed handcrafted text. */
|
||||
|
||||
strlcat (beacon_text, g_misc_config_p->beacon[j].custom_info, sizeof(beacon_text));
|
||||
}
|
||||
else if (g_misc_config_p->beacon[j].custom_infocmd != NULL) {
|
||||
char info_part[AX25_MAX_INFO_LEN];
|
||||
char *p;
|
||||
int k;
|
||||
|
||||
/* Run given command to obtain the info part for packet. */
|
||||
|
||||
k = dw_run_cmd (g_misc_config_p->beacon[j].custom_infocmd, 2, info_part, (int)sizeof(info_part));
|
||||
if (k > 0) {
|
||||
strlcat (beacon_text, info_part, sizeof(beacon_text));
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("CBEACON, config file line %d, INFOCMD failure.\n", g_misc_config_p->beacon[j].lineno);
|
||||
strlcpy (beacon_text, "", sizeof(beacon_text)); // abort!
|
||||
}
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error. custom_info is null. %s %d\n", __FILE__, __LINE__);
|
||||
continue;
|
||||
strlcpy (beacon_text, "", sizeof(beacon_text)); // abort!
|
||||
}
|
||||
g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
|
||||
break;
|
||||
|
@ -749,6 +802,10 @@ static void * beacon_thread (void *arg)
|
|||
/*
|
||||
* Parse monitor format into form for transmission.
|
||||
*/
|
||||
if (strlen(beacon_text) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pp = ax25_from_text (beacon_text, strict);
|
||||
|
||||
if (pp != NULL) {
|
||||
|
|
8
config.h
8
config.h
|
@ -32,8 +32,8 @@ enum sendto_type_e { SENDTO_XMIT, SENDTO_IGATE, SENDTO_RECV };
|
|||
|
||||
struct misc_config_s {
|
||||
|
||||
int agwpe_port; /* Port number for the “AGW TCPIP Socket Interface” */
|
||||
int kiss_port; /* Port number for the “KISS” protocol. */
|
||||
int agwpe_port; /* Port number for the "AGW TCPIP Socket Interface" */
|
||||
int kiss_port; /* Port number for the "KISS" protocol. */
|
||||
int enable_kiss_pt; /* Enable pseudo terminal for KISS. */
|
||||
/* Want this to be off by default because it hangs */
|
||||
/* after a while if nothing is reading from other end. */
|
||||
|
@ -97,6 +97,9 @@ struct misc_config_s {
|
|||
char *custom_info; /* Info part for handcrafted custom beacon. */
|
||||
/* Ignore the rest below if this is set. */
|
||||
|
||||
char *custom_infocmd; /* Command to generate info part. */
|
||||
/* Again, other options below are then ignored. */
|
||||
|
||||
int messaging; /* Set messaging attribute for position report. */
|
||||
/* i.e. Data Type Indicator of '=' rather than '!' */
|
||||
|
||||
|
@ -119,6 +122,7 @@ struct misc_config_s {
|
|||
float offset; /* MHz. */
|
||||
|
||||
char *comment; /* Comment or NULL. */
|
||||
char *commentcmd; /* Command to append more to Comment or NULL. */
|
||||
|
||||
|
||||
} beacon[MAX_BEACONS];
|
||||
|
|
855
decode_aprs.c
855
decode_aprs.c
File diff suppressed because it is too large
Load Diff
|
@ -61,6 +61,8 @@ typedef struct decode_aprs_s {
|
|||
char g_name[12]; /* Object or item name. Max. 9 characters. */
|
||||
|
||||
char g_addressee[12]; /* Addressee for a "message." Max. 9 characters. */
|
||||
/* Also for Directed Station Query which is a */
|
||||
/* special case of message. */
|
||||
|
||||
float g_speed; /* Speed in MPH. */
|
||||
|
||||
|
@ -88,7 +90,23 @@ typedef struct decode_aprs_s {
|
|||
|
||||
int g_dcs; /* Digital coded squelch, print as 3 octal digits. */
|
||||
|
||||
int g_offset; /* Transmit offset, KHz */
|
||||
int g_offset; /* Transmit offset, kHz */
|
||||
|
||||
|
||||
char g_query_type[12]; /* General Query: APRS, IGATE, WX, ... */
|
||||
/* Addressee is NOT set. */
|
||||
|
||||
/* Directed Station Query: exactly 5 characters. */
|
||||
/* APRSD, APRST, PING?, ... */
|
||||
/* Addressee is set. */
|
||||
|
||||
double g_footprint_lat; /* A general query may contain a foot print. */
|
||||
double g_footprint_lon; /* Set all to G_UNKNOWN if not used. */
|
||||
float g_footprint_radius; /* Radius in miles. */
|
||||
|
||||
char g_query_callsign[12]; /* Directed query may contain callsign. */
|
||||
/* e.g. tell me all objects from that callsign. */
|
||||
|
||||
|
||||
char g_weather[500]; /* Weather. Can get quite long. Rethink max size. */
|
||||
|
||||
|
|
18
demod.c
18
demod.c
|
@ -191,28 +191,16 @@ int demod_init (struct audio_s *pa)
|
|||
#if __arm__
|
||||
/* We probably don't have a lot of CPU power available. */
|
||||
/* Previously we would use F if possible otherwise fall back to A. */
|
||||
#if 0
|
||||
if (save_audio_config_p->achan[chan].baud == FFF_BAUD &&
|
||||
save_audio_config_p->achan[chan].mark_freq == FFF_MARK_FREQ &&
|
||||
save_audio_config_p->achan[chan].space_freq == FFF_SPACE_FREQ &&
|
||||
save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec == FFF_SAMPLES_PER_SEC) {
|
||||
|
||||
just_letters[0] = FFF_PROFILE;
|
||||
just_letters[1] = '\0';
|
||||
}
|
||||
else {
|
||||
strcpy (just_letters, "A");
|
||||
}
|
||||
#else
|
||||
/* In version 1.2, new default is E+ /3. */
|
||||
strcpy (just_letters, "E"); // version 1.2 now E.
|
||||
if (have_plus != -1) have_plus = 1; // Add as default for version 1.2
|
||||
// If not explicitly turned off.
|
||||
if (save_audio_config_p->achan[chan].decimate == 0) {
|
||||
save_audio_config_p->achan[chan].decimate = 3;
|
||||
if (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec > 40000) {
|
||||
save_audio_config_p->achan[chan].decimate = 3;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
strcpy (just_letters, "E"); // version 1.2 changed C to E.
|
||||
if (have_plus != -1) have_plus = 1; // Add as default for version 1.2
|
||||
|
|
42
digipeater.c
42
digipeater.c
|
@ -134,7 +134,6 @@ void digipeater_init (struct audio_s *p_audio_config, struct digi_config_s *p_di
|
|||
void digipeater (int from_chan, packet_t pp)
|
||||
{
|
||||
int to_chan;
|
||||
packet_t result;
|
||||
|
||||
|
||||
// dw_printf ("digipeater()\n");
|
||||
|
@ -156,6 +155,8 @@ void digipeater (int from_chan, packet_t pp)
|
|||
for (to_chan=0; to_chan<MAX_CHANS; to_chan++) {
|
||||
if (save_digi_config_p->enabled[from_chan][to_chan]) {
|
||||
if (to_chan == from_chan) {
|
||||
packet_t result;
|
||||
|
||||
result = digipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall,
|
||||
save_audio_config_p->achan[to_chan].mycall,
|
||||
&save_digi_config_p->alias[from_chan][to_chan], &save_digi_config_p->wide[from_chan][to_chan],
|
||||
|
@ -179,6 +180,8 @@ void digipeater (int from_chan, packet_t pp)
|
|||
for (to_chan=0; to_chan<MAX_CHANS; to_chan++) {
|
||||
if (save_digi_config_p->enabled[from_chan][to_chan]) {
|
||||
if (to_chan != from_chan) {
|
||||
packet_t result;
|
||||
|
||||
result = digipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall,
|
||||
save_audio_config_p->achan[to_chan].mycall,
|
||||
&save_digi_config_p->alias[from_chan][to_chan], &save_digi_config_p->wide[from_chan][to_chan],
|
||||
|
@ -227,6 +230,9 @@ void digipeater (int from_chan, packet_t pp)
|
|||
* filter_str - Filter expression string or NULL.
|
||||
*
|
||||
* Returns: Packet object for transmission or NULL.
|
||||
* The original packet is not modified. (with one exception, probably obsolete)
|
||||
* We make a copy and return that modified copy!
|
||||
* This is very important because we could digipeat from one channel to many.
|
||||
*
|
||||
* Description: The packet will be digipeated if the next unused digipeater
|
||||
* field matches one of the following:
|
||||
|
@ -262,7 +268,6 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
int ssid;
|
||||
int r;
|
||||
char repeater[AX25_MAX_ADDR_LEN];
|
||||
packet_t result = NULL;
|
||||
int err;
|
||||
char err_msg[100];
|
||||
|
||||
|
@ -276,6 +281,8 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
if (pfilter(from_chan, to_chan, filter_str, pp) != 1) {
|
||||
|
||||
// TODO1.2: take out debug message
|
||||
// Actually it turns out to be useful.
|
||||
// Maybe add a quiet option to suppress it although no one has complained about it yet.
|
||||
//#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Packet was rejected for digipeating from channel %d to %d by filter: %s\n", from_chan, to_chan, filter_str);
|
||||
|
@ -289,8 +296,8 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
*
|
||||
* The SSID in the Destination Address field of all packets is coded to specify
|
||||
* the APRS digipeater path.
|
||||
* If the Destination Address SSID is –0, the packet follows the standard AX.25
|
||||
* digipeater (“VIA”) path contained in the Digipeater Addresses field of the
|
||||
* If the Destination Address SSID is -0, the packet follows the standard AX.25
|
||||
* digipeater ("VIA") path contained in the Digipeater Addresses field of the
|
||||
* AX.25 frame.
|
||||
* If the Destination Address SSID is non-zero, the packet follows one of 15
|
||||
* generic APRS digipeater paths.
|
||||
|
@ -317,7 +324,7 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
r = ax25_get_first_not_repeated(pp);
|
||||
|
||||
if (r < AX25_REPEATER_1) {
|
||||
return NULL;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
ax25_get_addr_with_ssid(pp, r, repeater);
|
||||
|
@ -337,7 +344,11 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
*/
|
||||
|
||||
if (strcmp(repeater, mycall_rec) == 0) {
|
||||
packet_t result;
|
||||
|
||||
result = ax25_dup (pp);
|
||||
assert (result != NULL);
|
||||
|
||||
/* If using multiple radio channels, they */
|
||||
/* could have different calls. */
|
||||
ax25_set_addr (result, r, mycall_xmit);
|
||||
|
@ -369,7 +380,6 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Digipeater: Drop redundant packet to channel %d.\n", to_chan);
|
||||
//#endif
|
||||
assert (result == NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -379,7 +389,11 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
*/
|
||||
err = regexec(alias,repeater,0,NULL,0);
|
||||
if (err == 0) {
|
||||
packet_t result;
|
||||
|
||||
result = ax25_dup (pp);
|
||||
assert (result != NULL);
|
||||
|
||||
ax25_set_addr (result, r, mycall_xmit);
|
||||
ax25_set_h (result, r);
|
||||
return (result);
|
||||
|
@ -408,8 +422,11 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
|
||||
if (strcmp(repeater2, mycall_rec) == 0 ||
|
||||
regexec(alias,repeater2,0,NULL,0) == 0) {
|
||||
packet_t result;
|
||||
|
||||
result = ax25_dup (pp);
|
||||
assert (result != NULL);
|
||||
|
||||
ax25_set_addr (result, r2, mycall_xmit);
|
||||
ax25_set_h (result, r2);
|
||||
|
||||
|
@ -461,14 +478,22 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
*/
|
||||
|
||||
if (ssid == 1) {
|
||||
packet_t result;
|
||||
|
||||
result = ax25_dup (pp);
|
||||
assert (result != NULL);
|
||||
|
||||
ax25_set_addr (result, r, mycall_xmit);
|
||||
ax25_set_h (result, r);
|
||||
return (result);
|
||||
}
|
||||
|
||||
if (ssid >= 2 && ssid <= 7) {
|
||||
packet_t result;
|
||||
|
||||
result = ax25_dup (pp);
|
||||
assert (result != NULL);
|
||||
|
||||
ax25_set_ssid(result, r, ssid-1); // should be at least 1
|
||||
|
||||
if (ax25_get_num_repeaters(pp) < AX25_MAX_REPEATERS) {
|
||||
|
@ -488,8 +513,8 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
/*
|
||||
* Don't repeat it if we get here.
|
||||
*/
|
||||
assert (result == NULL);
|
||||
return NULL;
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
|
@ -597,6 +622,7 @@ static void test (char *in, char *out)
|
|||
ax25_delete (pp);
|
||||
|
||||
pp = ax25_from_frame (frame, frame_len, 50);
|
||||
assert (pp != NULL);
|
||||
ax25_format_addrs (pp, rec);
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
strcat (rec, (char*)pinfo);
|
||||
|
|
100
direwolf.c
100
direwolf.c
|
@ -58,6 +58,7 @@
|
|||
#include <sys/ioctl.h>
|
||||
#ifdef __OpenBSD__
|
||||
#include <soundcard.h>
|
||||
#elif __APPLE__
|
||||
#else
|
||||
#include <sys/soundcard.h>
|
||||
#endif
|
||||
|
@ -100,6 +101,7 @@
|
|||
#include "dwgps.h"
|
||||
#include "log.h"
|
||||
#include "recv.h"
|
||||
#include "morse.h"
|
||||
|
||||
|
||||
//static int idx_decoded = 0;
|
||||
|
@ -112,7 +114,7 @@ static void cleanup_linux (int);
|
|||
|
||||
static void usage (char **argv);
|
||||
|
||||
#if __SSE__
|
||||
#if defined(__SSE__) && !defined(__APPLE__)
|
||||
|
||||
static void __cpuid(int cpuinfo[4], int infotype){
|
||||
__asm__ __volatile__ (
|
||||
|
@ -120,7 +122,7 @@ static void __cpuid(int cpuinfo[4], int infotype){
|
|||
"=a" (cpuinfo[0]),
|
||||
"=b" (cpuinfo[1]),
|
||||
"=c" (cpuinfo[2]),
|
||||
"=d" (cpuinfo[3]) :
|
||||
"=d" (cpuinfo[3]):
|
||||
"a" (infotype)
|
||||
);
|
||||
}
|
||||
|
@ -179,11 +181,12 @@ int main (int argc, char *argv[])
|
|||
int d_n_opt = 0; /* "-d n" option for Network KISS. Can be repeated for more detail. */
|
||||
int d_t_opt = 0; /* "-d t" option for Tracker. Can be repeated for more detail. */
|
||||
int d_o_opt = 0; /* "-d o" option for output control such as PTT and DCD. */
|
||||
int a_opt = 0; /* "-a n" interval, in seconds, for audio statistics report. 0 for none. */
|
||||
|
||||
|
||||
|
||||
strcpy(l_opt, "");
|
||||
strcpy(P_opt, "");
|
||||
strlcpy(l_opt, "", sizeof(l_opt));
|
||||
strlcpy(P_opt, "", sizeof(P_opt));
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
|
@ -226,13 +229,13 @@ int main (int argc, char *argv[])
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: control development/beta/release by versio.h instead of changing here.
|
||||
// TODO: control development/beta/release by version.h instead of changing here.
|
||||
|
||||
text_color_init(t_opt);
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
//dw_printf ("Dire Wolf version %d.%d (%s) Beta Test\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
|
||||
//dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "F", __DATE__);
|
||||
dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
|
||||
dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "F", __DATE__);
|
||||
//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
|
@ -250,10 +253,13 @@ int main (int argc, char *argv[])
|
|||
* Try to warn anyone using a CPU from the previous
|
||||
* century rather than just dying for no apparent reason.
|
||||
*
|
||||
* Apple computers with Intel processors started with P6. Since the
|
||||
* cpu test code was giving Clang compiler grief it has been excluded.
|
||||
*
|
||||
* Now, where can I find a Pentium 2 or earlier to test this?
|
||||
*/
|
||||
|
||||
#if __SSE__
|
||||
#if defined(__SSE__) && !defined(__APPLE__)
|
||||
int cpuinfo[4];
|
||||
__cpuid (cpuinfo, 0);
|
||||
if (cpuinfo[0] >= 1) {
|
||||
|
@ -292,14 +298,14 @@ int main (int argc, char *argv[])
|
|||
* TODO: Automatically search other places.
|
||||
*/
|
||||
|
||||
strcpy (config_file, "direwolf.conf");
|
||||
strlcpy (config_file, "direwolf.conf", sizeof(config_file));
|
||||
|
||||
/*
|
||||
* Look at command line options.
|
||||
* So far, the only one is the configuration file location.
|
||||
*/
|
||||
|
||||
strcpy (input_file, "");
|
||||
strlcpy (input_file, "", sizeof(input_file));
|
||||
while (1) {
|
||||
int this_option_optind = optind ? optind : 1;
|
||||
int option_index = 0;
|
||||
|
@ -314,7 +320,7 @@ int main (int argc, char *argv[])
|
|||
|
||||
/* ':' following option character means arg is required. */
|
||||
|
||||
c = getopt_long(argc, argv, "P:B:D:c:pxr:b:n:d:q:t:Ul:",
|
||||
c = getopt_long(argc, argv, "P:B:D:c:pxr:b:n:d:q:t:Ul:Sa:",
|
||||
long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
@ -330,10 +336,19 @@ int main (int argc, char *argv[])
|
|||
dw_printf("\n");
|
||||
break;
|
||||
|
||||
case 'a': /* -a for audio statistics interval */
|
||||
|
||||
a_opt = atoi(optarg);
|
||||
if (a_opt < 0) a_opt = 0;
|
||||
if (a_opt < 10) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Setting such a small audio statistics interval will produce inaccurate sample rate display.\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'c': /* -c for configuration file name */
|
||||
|
||||
strcpy (config_file, optarg);
|
||||
strlcpy (config_file, optarg, sizeof(config_file));
|
||||
break;
|
||||
|
||||
#if __WIN32__
|
||||
|
@ -360,7 +375,7 @@ int main (int argc, char *argv[])
|
|||
case 'P': /* -P for modem profile. */
|
||||
|
||||
//debug: dw_printf ("Demodulator profile set to \"%s\"\n", optarg);
|
||||
strcpy (P_opt, optarg);
|
||||
strlcpy (P_opt, optarg, sizeof(P_opt));
|
||||
break;
|
||||
|
||||
case 'D': /* -D decrease AFSK demodulator sample rate */
|
||||
|
@ -476,9 +491,16 @@ int main (int argc, char *argv[])
|
|||
|
||||
case 'l': /* -l for log file directory name */
|
||||
|
||||
strncpy (l_opt, optarg, sizeof(l_opt)-1);
|
||||
strlcpy (l_opt, optarg, sizeof(l_opt));
|
||||
break;
|
||||
|
||||
case 'S': /* Print symbol tables and exit. */
|
||||
|
||||
symbols_init ();
|
||||
symbols_list ();
|
||||
exit (0);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
/* Should not be here. */
|
||||
|
@ -497,7 +519,7 @@ int main (int argc, char *argv[])
|
|||
dw_printf ("Warning: File(s) beyond the first are ignored.\n");
|
||||
}
|
||||
|
||||
strcpy (input_file, argv[optind]);
|
||||
strlcpy (input_file, argv[optind], sizeof(input_file));
|
||||
|
||||
}
|
||||
|
||||
|
@ -544,10 +566,12 @@ int main (int argc, char *argv[])
|
|||
}
|
||||
}
|
||||
|
||||
audio_config.statistics_interval = a_opt;
|
||||
|
||||
if (strlen(P_opt) > 0) {
|
||||
/* -P for modem profile. */
|
||||
/* TODO: Not yet documented. Should probably since it is consistent with atest. */
|
||||
strcpy (audio_config.achan[0].profiles, P_opt);
|
||||
strlcpy (audio_config.achan[0].profiles, P_opt, sizeof(audio_config.achan[0].profiles));
|
||||
}
|
||||
|
||||
if (D_opt != 0) {
|
||||
|
@ -556,15 +580,18 @@ int main (int argc, char *argv[])
|
|||
}
|
||||
|
||||
if (strlen(l_opt) > 0) {
|
||||
strncpy (misc_config.logdir, l_opt, sizeof(misc_config.logdir)-1);
|
||||
strlcpy (misc_config.logdir, l_opt, sizeof(misc_config.logdir));
|
||||
}
|
||||
|
||||
misc_config.enable_kiss_pt = enable_pseudo_terminal;
|
||||
|
||||
if (strlen(input_file) > 0) {
|
||||
strcpy (audio_config.adev[0].adevice_in, input_file);
|
||||
|
||||
strlcpy (audio_config.adev[0].adevice_in, input_file, sizeof(audio_config.adev[0].adevice_in));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Open the audio source
|
||||
* - soundcard
|
||||
|
@ -600,6 +627,7 @@ int main (int argc, char *argv[])
|
|||
* It is the range of the digital sound representation.
|
||||
*/
|
||||
gen_tone_init (&audio_config, 100);
|
||||
morse_init (&audio_config, 100);
|
||||
|
||||
assert (audio_config.adev[0].bits_per_sample == 8 || audio_config.adev[0].bits_per_sample == 16);
|
||||
assert (audio_config.adev[0].num_channels == 1 || audio_config.adev[0].num_channels == 2);
|
||||
|
@ -727,9 +755,9 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
|
|||
assert (subchan >= -1 && subchan < MAX_SUBCHANS);
|
||||
assert (pp != NULL); // 1.1J+
|
||||
|
||||
strcpy (display_retries, "");
|
||||
strlcpy (display_retries, "", sizeof(display_retries));
|
||||
if (audio_config.achan[chan].fix_bits != RETRY_NONE || audio_config.achan[chan].passall) {
|
||||
sprintf (display_retries, " [%s] ", retry_text[(int)retries]);
|
||||
snprintf (display_retries, sizeof(display_retries), " [%s] ", retry_text[(int)retries]);
|
||||
}
|
||||
|
||||
ax25_format_addrs (pp, stemp);
|
||||
|
@ -744,7 +772,7 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
|
|||
if (ax25_get_num_addr(pp) == 0) {
|
||||
/* Not AX.25. No station to display below. */
|
||||
h = -1;
|
||||
strcpy (heard, "");
|
||||
strlcpy (heard, "", sizeof(heard));
|
||||
}
|
||||
else {
|
||||
h = ax25_get_heard(pp);
|
||||
|
@ -760,7 +788,7 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
|
|||
dw_printf ("Digipeater ");
|
||||
}
|
||||
|
||||
char alevel_text[32];
|
||||
char alevel_text[AX25_ALEVEL_TO_TEXT_SIZE];
|
||||
|
||||
ax25_alevel_to_text (alevel, alevel_text);
|
||||
|
||||
|
@ -782,6 +810,10 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
|
|||
dw_printf ("%s (probably %s) audio level = %s %s %s\n", heard, probably_really, alevel_text, display_retries, spectrum);
|
||||
|
||||
}
|
||||
else if (strcmp(heard, "DTMF") == 0) {
|
||||
|
||||
dw_printf ("%s audio level = %s tt\n", heard, alevel_text);
|
||||
}
|
||||
else {
|
||||
|
||||
dw_printf ("%s audio level = %s %s %s\n", heard, alevel_text, display_retries, spectrum);
|
||||
|
@ -895,6 +927,7 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
|
|||
|
||||
|
||||
/* Send to another application if connected. */
|
||||
// TODO1.3: Put a wrapper around this so we only call one function to send by all methods.
|
||||
|
||||
int flen;
|
||||
unsigned char fbuf[AX25_MAX_PACKET_LEN];
|
||||
|
@ -924,23 +957,20 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
|
|||
|
||||
if (ax25_is_aprs(pp) && retries == RETRY_NONE) {
|
||||
|
||||
if (digi_config.filter_str[chan][MAX_CHANS] != NULL) {
|
||||
|
||||
// TODO1.2: filtering - maybe it should be ig... so we don't waste time filtering if igate not used.
|
||||
|
||||
}
|
||||
else {
|
||||
igate_send_rec_packet (chan, pp);
|
||||
}
|
||||
igate_send_rec_packet (chan, pp);
|
||||
}
|
||||
|
||||
|
||||
/* Send out a regenerated copy. Applies to all types, not just APRS. */
|
||||
/* This was an experimental feature never documented in the User Guide. */
|
||||
/* Initial feedback was positive but it fell by the wayside. */
|
||||
/* Should follow up with testers and either document this or clean out the clutter. */
|
||||
|
||||
digi_regen (chan, pp);
|
||||
|
||||
|
||||
/*
|
||||
*Note that the digipeater function can modify the packet in place so
|
||||
* Note that the digipeater function can modify the packet in place so
|
||||
* this is the last thing we should do with it.
|
||||
* Again, use only those with correct CRC; We don't want to spread corrupted data!
|
||||
* Single bit change appears to be safe from observations so far but be cautious.
|
||||
|
@ -1000,7 +1030,7 @@ static void usage (char **argv)
|
|||
dw_printf ("\n");
|
||||
dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
|
||||
dw_printf ("\n");
|
||||
dw_printf ("Usage: direwolf [options]\n");
|
||||
dw_printf ("Usage: direwolf [options] [ - | stdin | UDP:nnnn ]\n");
|
||||
dw_printf ("Options:\n");
|
||||
dw_printf (" -c fname Configuration file name.\n");
|
||||
dw_printf (" -l logdir Directory name for log files. Use . for current.\n");
|
||||
|
@ -1024,14 +1054,20 @@ static void usage (char **argv)
|
|||
dw_printf (" h h = Heard line with the audio level.\n");
|
||||
dw_printf (" d d = Decoding of APRS packets.\n");
|
||||
dw_printf (" -t n Text colors. 1=normal, 0=disabled.\n");
|
||||
dw_printf (" -a n Audio statistics interval in seconds. 0 to disable.\n");
|
||||
#if __WIN32__
|
||||
#else
|
||||
dw_printf (" -p Enable pseudo terminal for KISS protocol.\n");
|
||||
#endif
|
||||
dw_printf (" -x Send Xmit level calibration tones.\n");
|
||||
dw_printf (" -U Print UTF-8 test string and exit.\n");
|
||||
dw_printf (" -S Print symbol tables and exit.\n");
|
||||
dw_printf ("\n");
|
||||
|
||||
dw_printf ("After any options, there can be a single command line argument for the source of\n");
|
||||
dw_printf ("received audio. This can overrides the audio input specified in the configuration file.\n");
|
||||
dw_printf ("\n");
|
||||
|
||||
#if __WIN32__
|
||||
#else
|
||||
dw_printf ("Complete documentation can be found in /usr/local/share/doc/direwolf.\n");
|
||||
|
|
48
direwolf.h
48
direwolf.h
|
@ -62,7 +62,10 @@
|
|||
|
||||
#if __WIN32__
|
||||
#define PTW32_STATIC_LIB
|
||||
#include "pthreads/pthread.h"
|
||||
//#include "pthreads/pthread.h"
|
||||
#define gmtime_r( _clock, _result ) \
|
||||
( *(_result) = *gmtime( (_clock) ), \
|
||||
(_result) )
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
@ -153,9 +156,50 @@ typedef pthread_mutex_t dw_mutex_t;
|
|||
} \
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Platform differences for string functions. */
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
char *strsep(char **stringp, const char *delim);
|
||||
#endif
|
||||
|
||||
//#if __WIN32__
|
||||
char *strcasestr(const char *S, const char *FIND);
|
||||
//#endif
|
||||
|
||||
|
||||
#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__APPLE__)
|
||||
|
||||
// strlcpy and strlcat should be in string.h and the C library.
|
||||
|
||||
#else // Use our own copy
|
||||
|
||||
|
||||
#define DEBUG_STRL 1
|
||||
|
||||
#if DEBUG_STRL
|
||||
|
||||
#define strlcpy(dst,src,siz) strlcpy_debug(dst,src,siz,__FILE__,__func__,__LINE__)
|
||||
#define strlcat(dst,src,siz) strlcat_debug(dst,src,siz,__FILE__,__func__,__LINE__)
|
||||
|
||||
size_t strlcpy_debug(char *__restrict__ dst, const char *__restrict__ src, size_t siz, const char *file, const char *func, int line);
|
||||
size_t strlcat_debug(char *__restrict__ dst, const char *__restrict__ src, size_t siz, const char *file, const char *func, int line);
|
||||
|
||||
#else
|
||||
|
||||
#define strlcpy(dst,src,siz) strlcpy_debug(dst,src,siz)
|
||||
#define strlcat(dst,src,siz) strlcat_debug(dst,src,siz)
|
||||
|
||||
size_t strlcpy_debug(char *__restrict__ dst, const char *__restrict__ src, size_t siz);
|
||||
size_t strlcat_debug(char *__restrict__ dst, const char *__restrict__ src, size_t siz);
|
||||
|
||||
#endif /* DEBUG_STRL */
|
||||
|
||||
#endif /* BSD or Apple */
|
||||
|
||||
|
||||
#endif /* ifndef DIREWOLF_H */
|
8
dlq.c
8
dlq.c
|
@ -248,6 +248,14 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
|
|||
dlq_init ();
|
||||
}
|
||||
|
||||
assert (chan >= 0 && chan < MAX_CHANS);
|
||||
|
||||
if (pp == NULL) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("INTERNAL ERROR: dlq_append NULL packet pointer. Please report this!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
#if AX25MEMDEBUG
|
||||
|
||||
if (ax25memdebug_get()) {
|
||||
|
|
|
@ -62,4 +62,10 @@ and a couple things that can be done about it.
|
|||
receiver de-emphasis will
|
||||
cause the amplitudes of the two tones to be different.
|
||||
This makes it more difficult to demodulate them accurately.
|
||||
9600 baud operation is an entirely different animal. ...
|
||||
9600 baud operation is an entirely different animal. ...
|
||||
|
||||
- [WA8LMF TNC Test CD Results a.k.a. Battle of the TNCs](WA8LMF-TNC-Test-CD-Results.pdf)
|
||||
|
||||
How can we compare how well the TNCs perform under real world conditions?
|
||||
The de facto standard of measurement is the number of packets decoded from [WA8LMF’s TNC Test CD](http://wa8lmf.net/TNCtest/index.htm).
|
||||
Many have published the number of packets they have been able to decode from this test. Here they are, all gathered in one place, for your reading pleasure.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
11
dtime_now.c
11
dtime_now.c
|
@ -13,6 +13,10 @@
|
|||
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#if __WIN32__
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
@ -37,7 +41,14 @@ double dtime_now (void)
|
|||
|
||||
struct timespec ts;
|
||||
|
||||
#ifdef __APPLE__
|
||||
struct timeval tp;
|
||||
gettimeofday(&tp, NULL);
|
||||
ts.tv_nsec = tp.tv_usec * 1000;
|
||||
ts.tv_sec = tp.tv_sec;
|
||||
#else
|
||||
clock_gettime (CLOCK_REALTIME, &ts);
|
||||
#endif
|
||||
|
||||
result = ((double)(ts.tv_sec) + (double)(ts.tv_nsec) * 0.000000001);
|
||||
|
||||
|
|
8
dtmf.c
8
dtmf.c
|
@ -5,7 +5,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2013, 2014 John Langner, WB2OSZ
|
||||
// Copyright (C) 2013, 2014, 2015 John Langner, WB2OSZ
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
@ -42,6 +42,7 @@
|
|||
|
||||
#include "direwolf.h"
|
||||
#include "dtmf.h"
|
||||
#include "hdlc_rec.h" // for dcd_change
|
||||
|
||||
|
||||
|
||||
|
@ -269,6 +270,11 @@ char dtmf_sample (int c, float input)
|
|||
|
||||
if (decoded == D->prev_dec) {
|
||||
D->debounced = decoded;
|
||||
|
||||
// Update Data Carrier Detect Indicator.
|
||||
|
||||
dcd_change (c, MAX_SUBCHANS, decoded != ' ');
|
||||
|
||||
/* Reset timeout timer. */
|
||||
if (decoded != ' ') {
|
||||
D->timeout = ((TIMEOUT_SEC) * D->sample_rate) / D->block_size;
|
||||
|
|
|
@ -24,7 +24,7 @@ sleep 30
|
|||
# Nothing to do if it is already running.
|
||||
#
|
||||
|
||||
a=`ps -ef | grep direwolf | grep -v grep`
|
||||
a=`pgrep direwolf`
|
||||
if [ "$a" != "" ]
|
||||
then
|
||||
#date >> /tmp/dw-start.log
|
||||
|
@ -62,13 +62,13 @@ echo "Start up application." >> /tmp/dw-start.log
|
|||
|
||||
if [ -x /usr/bin/lxterminal ]
|
||||
then
|
||||
/usr/bin/lxterminal -t "Dire Wolf" -e "/usr/local/bin/direwolf" &
|
||||
/usr/bin/lxterminal -t "Dire Wolf" -e "/usr/local/bin/direwolf -a 100" &
|
||||
elif [ -x /usr/bin/xterm ]
|
||||
then
|
||||
/usr/bin/xterm -bg white -fg black -e /usr/local/bin/direwolf &
|
||||
/usr/bin/xterm -bg white -fg black -e "/usr/local/bin/direwolf -a 100" &
|
||||
elif [ -x /usr/bin/x-terminal-emulator ]
|
||||
then
|
||||
/usr/bin/x-terminal-emulator -e /usr/local/bin/direwolf &
|
||||
/usr/bin/x-terminal-emulator -e "/usr/local/bin/direwolf -a 100" &
|
||||
else
|
||||
echo "Did not find an X terminal emulator."
|
||||
fi
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
|
@ -342,7 +343,7 @@ static int cse_spd_data_extension (int course, int speed, char *presult)
|
|||
x = course;
|
||||
if (x < 0) x = 0;
|
||||
if (x > 360) x = 360;
|
||||
sprintf (stemp, "%03d", x);
|
||||
snprintf (stemp, sizeof(stemp), "%03d", x);
|
||||
memcpy (r->cse, stemp, 3);
|
||||
|
||||
r->slash = '/';
|
||||
|
@ -350,7 +351,7 @@ static int cse_spd_data_extension (int course, int speed, char *presult)
|
|||
x = speed;
|
||||
if (x < 0) x = 0;
|
||||
if (x > 999) x = 999;
|
||||
sprintf (stemp, "%03d", x);
|
||||
snprintf (stemp, sizeof(stemp), "%03d", x);
|
||||
memcpy (r->spd, stemp, 3);
|
||||
|
||||
return (sizeof(cs_t));
|
||||
|
@ -403,10 +404,11 @@ static int frequency_spec (float freq, float tone, float offset, char *presult)
|
|||
|
||||
if (freq != 0) {
|
||||
freq_t *f = (freq_t*)presult;
|
||||
char stemp[12];
|
||||
char stemp[12]; /* Frequency should be exactly 7 characters: 999.999 */
|
||||
/* Offset shouldbe exactly 4 characters: +999 */
|
||||
|
||||
/* Should use letters for > 999.999. */
|
||||
sprintf (stemp, "%07.3f", freq);
|
||||
/* TODO: Should use letters for > 999.999. */
|
||||
snprintf (stemp, sizeof(stemp), "%07.3f", freq);
|
||||
memcpy (f->f, stemp, 7);
|
||||
memcpy (f->mhz, "MHz", 3);
|
||||
f->space = ' ';
|
||||
|
@ -422,11 +424,11 @@ static int frequency_spec (float freq, float tone, float offset, char *presult)
|
|||
memcpy(to->ttt, "off", 3);
|
||||
}
|
||||
else {
|
||||
sprintf (stemp, "%03d", (int)tone);
|
||||
snprintf (stemp, sizeof(stemp), "%03d", (int)tone);
|
||||
memcpy (to->ttt, stemp, 3);
|
||||
}
|
||||
to->space1 = ' ';
|
||||
sprintf (stemp, "%+04d", (int)round(offset * 100));
|
||||
snprintf (stemp, sizeof(stemp), "%+04d", (int)round(offset * 100));
|
||||
memcpy (to->oooo, stemp, 4);
|
||||
to->space2 = ' ';
|
||||
|
||||
|
@ -466,8 +468,12 @@ static int frequency_spec (float freq, float tone, float offset, char *presult)
|
|||
*
|
||||
* comment - Additional comment text.
|
||||
*
|
||||
* result_size - Ammount of space for result, provideed by
|
||||
* caller, to avoid buffer overflow.
|
||||
*
|
||||
* Outputs: presult - Stored here. Should be at least ??? bytes.
|
||||
* Could get into hundreds of characters
|
||||
* because it includes the comment.
|
||||
*
|
||||
* Returns: Number of characters in result.
|
||||
*
|
||||
|
@ -503,7 +509,7 @@ int encode_position (int messaging, int compressed, double lat, double lon, int
|
|||
int course, int speed,
|
||||
float freq, float tone, float offset,
|
||||
char *comment,
|
||||
char *presult)
|
||||
char *presult, size_t result_size)
|
||||
{
|
||||
int result_len = 0;
|
||||
|
||||
|
@ -551,18 +557,23 @@ int encode_position (int messaging, int compressed, double lat, double lon, int
|
|||
/* 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);
|
||||
snprintf (salt, sizeof(salt), "/A=%06d", alt_ft);
|
||||
strlcat (presult, salt, result_size);
|
||||
result_len += strlen(salt);
|
||||
}
|
||||
|
||||
/* Finally, comment text. */
|
||||
|
||||
if (comment != NULL) {
|
||||
strcat (presult, comment);
|
||||
strlcat (presult, comment, result_size);
|
||||
result_len += strlen(comment);
|
||||
}
|
||||
|
||||
if (result_len >= result_size) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("encode_position result of %d characters won't fit into space provided.\n", result_len);
|
||||
}
|
||||
|
||||
return (result_len);
|
||||
|
||||
} /* end encode_position */
|
||||
|
@ -596,7 +607,14 @@ int encode_position (int messaging, int compressed, double lat, double lon, int
|
|||
*
|
||||
* comment - Additional comment text.
|
||||
*
|
||||
* result_size - Ammount of space for result, provideed by
|
||||
* caller, to avoid buffer overflow.
|
||||
*
|
||||
* Outputs: presult - Stored here. Should be at least ??? bytes.
|
||||
* 36 for fixed part,
|
||||
* 7 for optional extended data,
|
||||
* ~20 for freq, etc.,
|
||||
* comment ...
|
||||
*
|
||||
* Returns: Number of characters in result.
|
||||
*
|
||||
|
@ -622,7 +640,7 @@ int encode_object (char *name, int compressed, time_t thyme, double lat, double
|
|||
int power, int height, int gain, char *dir,
|
||||
int course, int speed,
|
||||
float freq, float tone, float offset, char *comment,
|
||||
char *presult)
|
||||
char *presult, size_t result_size)
|
||||
{
|
||||
aprs_object_t *p = (aprs_object_t *) presult;
|
||||
int result_len = 0;
|
||||
|
@ -651,7 +669,7 @@ int encode_object (char *name, int compressed, time_t thyme, double lat, double
|
|||
|
||||
localtime_r (thyme, &tm);
|
||||
#endif
|
||||
sprintf (p->o.time_stamp, "%02d%02d%02d", tm.tm_mday, tm.tm_hour, tm.tm_min);
|
||||
snprintf (p->o.time_stamp, sizeof(p->o.time_stamp), "%02d%02d%02d", tm.tm_mday, tm.tm_hour, tm.tm_min);
|
||||
#if XMIT_UTC
|
||||
p->o.time_stamp[6] = 'z';
|
||||
#else
|
||||
|
@ -695,10 +713,15 @@ int encode_object (char *name, int compressed, time_t thyme, double lat, double
|
|||
/* Finally, comment text. */
|
||||
|
||||
if (comment != NULL) {
|
||||
strcat (presult, comment);
|
||||
strlcat (presult, comment, result_size);
|
||||
result_len += strlen(comment);
|
||||
}
|
||||
|
||||
if (result_len >= result_size) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("encode_object result of %d characters won't fit into space provided.\n", result_len);
|
||||
}
|
||||
|
||||
return (result_len);
|
||||
|
||||
} /* end encode_object */
|
||||
|
@ -729,42 +752,42 @@ int main (int argc, char *argv[])
|
|||
/*********** Position ***********/
|
||||
|
||||
encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
|
||||
0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result);
|
||||
0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result, sizeof(result));
|
||||
dw_printf ("%s\n", result);
|
||||
if (strcmp(result, "!4234.61ND07126.47W&") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
|
||||
|
||||
/* with PHG. */
|
||||
|
||||
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, sizeof(result));
|
||||
dw_printf ("%s\n", result);
|
||||
if (strcmp(result, "!4234.61ND07126.47W&PHG7368") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
|
||||
|
||||
/* with freq. */
|
||||
|
||||
encode_position (0, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
|
||||
0, 0, 0, NULL, 0, 0, 146.955, 74.4, -0.6, NULL, result);
|
||||
0, 0, 0, NULL, 0, 0, 146.955, 74.4, -0.6, NULL, result, sizeof(result));
|
||||
dw_printf ("%s\n", result);
|
||||
if (strcmp(result, "!4234.61ND07126.47W&146.955MHz T074 -060 ") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
|
||||
|
||||
/* with course/speed, freq, and comment! */
|
||||
|
||||
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, sizeof(result));
|
||||
dw_printf ("%s\n", result);
|
||||
if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz T074 -060 River flooding") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
|
||||
|
||||
/* Course speed, no tone, + offset */
|
||||
|
||||
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, sizeof(result));
|
||||
dw_printf ("%s\n", result);
|
||||
if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz Toff +060 River flooding") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
|
||||
|
||||
/* 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);
|
||||
0, 0, 0, NULL, 180, 55, 146.955, 0, 0.6, "River flooding", result, sizeof(result));
|
||||
dw_printf ("%s\n", result);
|
||||
if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz Toff +060 /A=012345River flooding") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
|
||||
|
||||
|
@ -772,7 +795,7 @@ int main (int argc, char *argv[])
|
|||
/*********** Compressed position. ***********/
|
||||
|
||||
encode_position (1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
|
||||
0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result);
|
||||
0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result, sizeof(result));
|
||||
dw_printf ("%s\n", result);
|
||||
if (strcmp(result, "!D8yKC<Hn[& !") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
|
||||
|
||||
|
@ -780,14 +803,14 @@ int main (int argc, char *argv[])
|
|||
/* with PHG. In this case it is converted to precomputed radio range. TODO: check on this. Is 27.4 correct? */
|
||||
|
||||
encode_position (1, 42+34.61/60, -(71+26.47/60), G_UNKNOWN, 'D', '&',
|
||||
50, 100, 6, "N", 0, 0, 0, 0, 0, NULL, result);
|
||||
50, 100, 6, "N", 0, 0, 0, 0, 0, NULL, result, sizeof(result));
|
||||
dw_printf ("%s\n", result);
|
||||
if (strcmp(result, "!D8yKC<Hn[&{CG") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
|
||||
|
||||
/* with course/speed, freq, and comment! TODO: check on this 55 knots should be 63 MPH. we get 62. */
|
||||
|
||||
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, sizeof(result));
|
||||
dw_printf ("%s\n", result);
|
||||
if (strcmp(result, "!D8yKC<Hn[& !146.955MHz T074 -060 River flooding") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
|
||||
|
||||
|
@ -803,7 +826,7 @@ int main (int argc, char *argv[])
|
|||
/*********** Object. ***********/
|
||||
|
||||
encode_object ("WB1GOF-C", 0, 0, 42+34.61/60, -(71+26.47/60), 'D', '&',
|
||||
0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result);
|
||||
0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, result, sizeof(result));
|
||||
dw_printf ("%s\n", result);
|
||||
if (strcmp(result, ";WB1GOF-C *111111z4234.61ND07126.47W&") != 0) dw_printf ("ERROR! line %d\n", __LINE__);
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@ int encode_position (int messaging, int compressed, double lat, double lon, int
|
|||
int course, int speed,
|
||||
float freq, float tone, float offset,
|
||||
char *comment,
|
||||
char *presult);
|
||||
char *presult, size_t result_size);
|
||||
|
||||
int encode_object (char *name, int compressed, time_t thyme, double lat, double lon,
|
||||
char symtab, char symbol,
|
||||
int power, int height, int gain, char *dir,
|
||||
int course, int speed,
|
||||
float freq, float tone, float offset, char *comment,
|
||||
char *presult);
|
||||
char *presult, size_t result_size);
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
#include "hdlc_send.h"
|
||||
#include "gen_tone.h"
|
||||
#include "textcolor.h"
|
||||
#include "morse.h"
|
||||
|
||||
|
||||
static void usage (char **argv);
|
||||
|
@ -81,6 +82,8 @@ static int audio_file_close (void);
|
|||
|
||||
static int g_add_noise = 0;
|
||||
static float g_noise_level = 0;
|
||||
static int g_morse_wpm = 0; /* Send morse code at this speed. */
|
||||
|
||||
|
||||
static struct audio_s modem;
|
||||
|
||||
|
@ -92,15 +95,22 @@ static void send_packet (char *str)
|
|||
int flen;
|
||||
int c;
|
||||
|
||||
pp = ax25_from_text (str, 1);
|
||||
flen = ax25_pack (pp, fbuf);
|
||||
for (c=0; c<modem.adev[0].num_channels; c++)
|
||||
{
|
||||
hdlc_send_flags (c, 8, 0);
|
||||
hdlc_send_frame (c, fbuf, flen);
|
||||
hdlc_send_flags (c, 2, 1);
|
||||
|
||||
if (g_morse_wpm > 0) {
|
||||
|
||||
morse_send (0, str, g_morse_wpm, 100, 100);
|
||||
}
|
||||
else {
|
||||
pp = ax25_from_text (str, 1);
|
||||
flen = ax25_pack (pp, fbuf);
|
||||
for (c=0; c<modem.adev[0].num_channels; c++)
|
||||
{
|
||||
hdlc_send_flags (c, 8, 0);
|
||||
hdlc_send_frame (c, fbuf, flen);
|
||||
hdlc_send_flags (c, 2, 1);
|
||||
}
|
||||
ax25_delete (pp);
|
||||
}
|
||||
ax25_delete (pp);
|
||||
}
|
||||
|
||||
|
||||
|
@ -164,7 +174,7 @@ int main(int argc, char **argv)
|
|||
|
||||
/* ':' following option character means arg is required. */
|
||||
|
||||
c = getopt_long(argc, argv, "gm:s:a:b:B:r:n:o:z:82",
|
||||
c = getopt_long(argc, argv, "gm:s:a:b:B:r:n:o:z:82M:",
|
||||
long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
@ -328,6 +338,20 @@ int main(int argc, char **argv)
|
|||
dw_printf ("Output file set to %s\n", output_file);
|
||||
break;
|
||||
|
||||
case 'M': /* -M for morse code speed */
|
||||
|
||||
//TODO: document this.
|
||||
|
||||
g_morse_wpm = atoi(optarg);
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Morse code speed set to %d WPM.\n", g_morse_wpm);
|
||||
if (g_morse_wpm < 5 || g_morse_wpm > 50) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Morse code speed must be in range of 5 to 50 WPM.\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
|
||||
case '?':
|
||||
|
||||
/* Unknown option message was already printed. */
|
||||
|
@ -365,12 +389,19 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
gen_tone_init (&modem, amplitude/2);
|
||||
morse_init (&modem, amplitude/2);
|
||||
|
||||
|
||||
assert (modem.adev[0].bits_per_sample == 8 || modem.adev[0].bits_per_sample == 16);
|
||||
assert (modem.adev[0].num_channels == 1 || modem.adev[0].num_channels == 2);
|
||||
assert (modem.adev[0].samples_per_sec >= MIN_SAMPLES_PER_SEC && modem.adev[0].samples_per_sec <= MAX_SAMPLES_PER_SEC);
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Get user packets(s) from file or stdin if specified.
|
||||
* "-n" option is ignored in this case.
|
||||
|
|
|
@ -288,7 +288,6 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp)
|
|||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static void put_sample (int chan, int a, int sam);
|
||||
|
||||
void tone_gen_put_bit (int chan, int dat)
|
||||
{
|
||||
|
@ -319,7 +318,7 @@ void tone_gen_put_bit (int chan, int dat)
|
|||
|
||||
tone_phase[chan] += dat ? f2_change_per_sample[chan] : f1_change_per_sample[chan];
|
||||
sam = sine_table[(tone_phase[chan] >> 24) & 0xff];
|
||||
put_sample (chan, a, sam);
|
||||
gen_tone_put_sample (chan, a, sam);
|
||||
}
|
||||
else {
|
||||
|
||||
|
@ -335,7 +334,7 @@ void tone_gen_put_bit (int chan, int dat)
|
|||
|
||||
sam = (int) convolve (raw[chan], lp_filter[chan], lp_filter_size[chan]);
|
||||
resample[chan] = 0;
|
||||
put_sample (chan, a, sam);
|
||||
gen_tone_put_sample (chan, a, sam);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -349,7 +348,7 @@ void tone_gen_put_bit (int chan, int dat)
|
|||
}
|
||||
|
||||
|
||||
static void put_sample (int chan, int a, int sam) {
|
||||
void gen_tone_put_sample (int chan, int a, int sam) {
|
||||
|
||||
/* Ship out an audio sample. */
|
||||
|
||||
|
|
|
@ -14,3 +14,4 @@ int gen_tone_init (struct audio_s *pp, int amp);
|
|||
|
||||
void tone_gen_put_bit (int chan, int dat);
|
||||
|
||||
void gen_tone_put_sample (int chan, int a, int sam);
|
|
@ -0,0 +1,572 @@
|
|||
C#############################################################
|
||||
C# #
|
||||
C# Configuration file for Dire Wolf #
|
||||
C# #
|
||||
L# Linux version #
|
||||
W# Windows version #
|
||||
M# Macintosh version #
|
||||
C# #
|
||||
C#############################################################
|
||||
R
|
||||
R
|
||||
R The sample config file was getting pretty messy
|
||||
R with the Windows and Linux differences.
|
||||
R It would be a maintenance burden to keep most of
|
||||
R two different versions in sync.
|
||||
R This common source is now used to generate the
|
||||
R two different variations while having only a single
|
||||
R copy of the common parts.
|
||||
R
|
||||
R The first column contains one of the following:
|
||||
R
|
||||
R R remark which is discarded.
|
||||
R C common to both versions.
|
||||
R W Windows version only.
|
||||
R L Linux version only.
|
||||
R M Macintosh version and possibly others (portaudio used).
|
||||
R
|
||||
C#
|
||||
C# Consult the User Guide for more details on configuration options.
|
||||
C#
|
||||
C#
|
||||
C# These are the most likely settings you might change:
|
||||
C#
|
||||
C# (1) MYCALL - call sign and SSID for your station.
|
||||
C#
|
||||
C# Look for lines starting with MYCALL and
|
||||
C# change NOCALL to your own.
|
||||
C#
|
||||
C# (2) PBEACON - enable position beaconing.
|
||||
C#
|
||||
C# Look for lines starting with PBEACON and
|
||||
C# modify for your call, location, etc.
|
||||
C#
|
||||
C# (3) DIGIPEATER - configure digipeating rules.
|
||||
C#
|
||||
C# Look for lines starting with DIGIPEATER.
|
||||
C# Most people will probably use the given example.
|
||||
C# Just remove the "#" from the start of the line
|
||||
C# to enable it.
|
||||
C#
|
||||
C# (4) IGSERVER, IGLOGIN - IGate server and login
|
||||
C#
|
||||
C# Configure an IGate client to relay messages between
|
||||
C# radio and internet servers.
|
||||
C#
|
||||
C#
|
||||
C# The default location is "direwolf.conf" in the current working directory.
|
||||
L# On Linux, the user's home directory will also be searched.
|
||||
C# An alternate configuration file location can be specified with the "-c" command line option.
|
||||
C#
|
||||
C# As you probably guessed by now, # indicates a comment line.
|
||||
C#
|
||||
C# Remove the # at the beginning of a line if you want to use a sample
|
||||
C# configuration that is currently commented out.
|
||||
C#
|
||||
C# Commands are a keyword followed by parameters.
|
||||
C#
|
||||
C# Command key words are case insensitive. i.e. upper and lower case are equivalent.
|
||||
C#
|
||||
C# Command parameters are generally case sensitive. i.e. upper and lower case are different.
|
||||
C#
|
||||
C
|
||||
C
|
||||
C#############################################################
|
||||
C# #
|
||||
C# FIRST AUDIO DEVICE PROPERTIES #
|
||||
C# (Channel 0 + 1 if in stereo) #
|
||||
C# #
|
||||
C#############################################################
|
||||
C
|
||||
C#
|
||||
C# Many people will simply use the default sound device.
|
||||
C# Some might want to use an alternative device by chosing it here.
|
||||
C#
|
||||
W# When the Windows version starts up, it displays something like
|
||||
W# this with the available sound devices and capabilities:
|
||||
W#
|
||||
W# Available audio input devices for receive (*=selected):
|
||||
W# * 0: Microphone (C-Media USB Headpho (channel 2)
|
||||
W# 1: Microphone (Bluetooth SCO Audio
|
||||
W# 2: Microphone (Bluetooth AV Audio)
|
||||
W# * 3: Microphone (Realtek High Defini (channels 0 & 1)
|
||||
W# Available audio output devices for transmit (*=selected):
|
||||
W# * 0: Speakers (C-Media USB Headphone (channel 2)
|
||||
W# 1: Speakers (Bluetooth SCO Audio)
|
||||
W# 2: Realtek Digital Output(Optical)
|
||||
W# 3: Speakers (Bluetooth AV Audio)
|
||||
W# * 4: Speakers (Realtek High Definiti (channels 0 & 1)
|
||||
W# 5: Realtek Digital Output (Realtek
|
||||
W#
|
||||
W# Example: To use the microphone and speaker connections on the
|
||||
W# system board, either of these forms can be used:
|
||||
W
|
||||
W#ADEVICE High
|
||||
W#ADEVICE 3 4
|
||||
W
|
||||
W
|
||||
W# Example: To use the USB Audio, use a command like this with
|
||||
W# the input and output device numbers. (Remove the # comment character.)
|
||||
W#ADEVICE USB
|
||||
W
|
||||
W# The position in the list can change when devices (e.g. USB) are added and removed.
|
||||
W# You can also specify devices by using part of the name.
|
||||
W# Here is an example of specifying the USB Audio device.
|
||||
W# This is case-sensitive. Upper and lower case are not treated the same.
|
||||
W
|
||||
W#ADEVICE USB
|
||||
W
|
||||
W
|
||||
L# Linux ALSA is complicated. See User Guide for discussion.
|
||||
L# To use something other than the default, generally use plughw
|
||||
L# and a card number reported by "arecord -l" command. Example:
|
||||
L
|
||||
L# ADEVICE plughw:1,0
|
||||
L
|
||||
L# Starting with version 1.0, you can also use "-" or "stdin" to
|
||||
L# pipe stdout from some other application such as a software defined
|
||||
L# radio. You can also specify "UDP:" and an optional port for input.
|
||||
L# Something different must be specified for output.
|
||||
L
|
||||
M# Macintosh Operating System uses portaudio driver for audio
|
||||
M# input/output. Default device selection not available. User/OP
|
||||
M# must configure the sound input/output option. Note that
|
||||
M# the device names can contain spaces. In this case, the names
|
||||
M# must be enclosed by quotes.
|
||||
M#
|
||||
M# Examples:
|
||||
M#
|
||||
M# ADEVICE "USB Audio Codec:6" "USB Audio Codec:5"
|
||||
M#
|
||||
M#
|
||||
W# ADEVICE - 0
|
||||
W# ADEVICE UDP:7355 0
|
||||
L# ADEVICE - plughw:1,0
|
||||
L# ADEVICE UDP:7355 default
|
||||
M# ADEVICE UDP:7355 default
|
||||
M#
|
||||
L
|
||||
L
|
||||
C
|
||||
C#
|
||||
C# Number of audio channels for this souncard: 1 or 2.
|
||||
C#
|
||||
C
|
||||
CACHANNELS 1
|
||||
C#ACHANNELS 2
|
||||
C
|
||||
C
|
||||
C#############################################################
|
||||
C# #
|
||||
C# SECOND AUDIO DEVICE PROPERTIES #
|
||||
C# (Channel 2 + 3 if in stereo) #
|
||||
C# #
|
||||
C#############################################################
|
||||
C
|
||||
C#ADEVICE1 ...
|
||||
C
|
||||
C
|
||||
C#############################################################
|
||||
C# #
|
||||
C# THIRD AUDIO DEVICE PROPERTIES #
|
||||
C# (Channel 4 + 5 if in stereo) #
|
||||
C# #
|
||||
C#############################################################
|
||||
C
|
||||
C#ADEVICE2 ...
|
||||
C
|
||||
C
|
||||
C#############################################################
|
||||
C# #
|
||||
C# CHANNEL 0 PROPERTIES #
|
||||
C# #
|
||||
C#############################################################
|
||||
C
|
||||
CCHANNEL 0
|
||||
C
|
||||
C#
|
||||
C# The following MYCALL, MODEM, PTT, etc. configuration items
|
||||
C# apply to the most recent CHANNEL.
|
||||
C#
|
||||
C
|
||||
C#
|
||||
C# Station identifier for this channel.
|
||||
C# Multiple channels can have the same or different names.
|
||||
C#
|
||||
C# It can be up to 6 letters and digits with an optional ssid.
|
||||
C# The APRS specification requires that it be upper case.
|
||||
C#
|
||||
C# Example (don't use this unless you are me): MYCALL WB2OSZ-5
|
||||
C#
|
||||
C
|
||||
CMYCALL N0CALL
|
||||
C
|
||||
C#
|
||||
C# Pick a suitable modem speed based on your situation.
|
||||
C# 1200 Most common for VHF/UHF. Default if not specified.
|
||||
C# 300 Low speed for HF SSB.
|
||||
C# 9600 High speed - Can't use Microphone and Speaker connections.
|
||||
C#
|
||||
C# In the simplest form, just specify the speed.
|
||||
C#
|
||||
C
|
||||
CMODEM 1200
|
||||
C#MODEM 300
|
||||
C#MODEM 9600
|
||||
C
|
||||
C#
|
||||
C# These are the defaults should be fine for most cases. In special situations,
|
||||
C# you might want to specify different AFSK tones or the baseband mode which does
|
||||
C# not use AFSK.
|
||||
C#
|
||||
C#MODEM 1200 1200:2200
|
||||
C#MODEM 300 1600:1800
|
||||
C#MODEM 9600 0:0
|
||||
C#
|
||||
C#
|
||||
C# On HF SSB, you might want to use multiple demodulators on slightly different
|
||||
C# frequencies to compensate for stations off frequency. Here we have 7 different
|
||||
C# demodulators at 30 Hz intervals. This takes a lot of CPU power so you will
|
||||
C# probably need to reduce the audio sampling rate with the /n option.
|
||||
C
|
||||
C#MODEM 300 1600:1800 7@30 /4
|
||||
C
|
||||
C
|
||||
C#
|
||||
C# Uncomment line below to enable the DTMF decoder for this channel.
|
||||
C#
|
||||
C
|
||||
C#DTMF
|
||||
C
|
||||
C#
|
||||
C# If not using a VOX circuit, the transmitter Push to Talk (PTT)
|
||||
C# control is usually wired to a serial port with a suitable interface circuit.
|
||||
C# DON'T connect it directly!
|
||||
C#
|
||||
C# For the PTT command, specify the device and either RTS or DTR.
|
||||
C# RTS or DTR may be preceded by "-" to invert the signal.
|
||||
C# Both can be used for interfaces that want them driven with opposite polarity.
|
||||
C#
|
||||
L# COM1 can be used instead of /dev/ttyS0, COM2 for /dev/ttyS1, and so on.
|
||||
L#
|
||||
C
|
||||
C#PTT COM1 RTS
|
||||
C#PTT COM1 RTS -DTR
|
||||
L#PTT /dev/ttyUSB0 RTS
|
||||
C
|
||||
L#
|
||||
L# On Linux, you can also use general purpose I/O pins if
|
||||
L# your system is configured for user access to them.
|
||||
L# This would apply mostly to microprocessor boards, not a regular PC.
|
||||
L# See separate Raspberry Pi document for more details.
|
||||
L# The number may be preceded by "-" to invert the signal.
|
||||
L#
|
||||
L
|
||||
L#PTT GPIO 25
|
||||
L
|
||||
C# The Data Carrier Detect (DCD) signal can be sent to the same places
|
||||
C# as the PTT signal. This could be used to light up an LED like a normal TNC.
|
||||
C
|
||||
C#DCD COM1 -DTR
|
||||
L#DCD GPIO 24
|
||||
C
|
||||
C
|
||||
C#############################################################
|
||||
C# #
|
||||
C# CHANNEL 1 PROPERTIES #
|
||||
C# #
|
||||
C#############################################################
|
||||
C
|
||||
C#CHANNEL 1
|
||||
C
|
||||
C#
|
||||
C# Specify MYCALL, MODEM, PTT, etc. configuration items for
|
||||
C# CHANNEL 1. Repeat for any other channels.
|
||||
C
|
||||
C
|
||||
C#############################################################
|
||||
C# #
|
||||
C# TEXT TO SPEECH COMMAND FILE #
|
||||
C# #
|
||||
C#############################################################
|
||||
C
|
||||
W#SPEECH dwespeak.bat
|
||||
L#SPEECH dwespeak.sh
|
||||
C
|
||||
C
|
||||
C#############################################################
|
||||
C# #
|
||||
C# VIRTUAL TNC SERVER PROPERTIES #
|
||||
C# #
|
||||
C#############################################################
|
||||
C
|
||||
C#
|
||||
C# Dire Wolf acts as a virtual TNC and can communicate with
|
||||
C# client applications by different protocols:
|
||||
C#
|
||||
C# - the "AGW TCPIP Socket Interface" - default port 8000
|
||||
C# - KISS protocol over TCP socket - default port 8001
|
||||
W# - KISS TNC via serial port
|
||||
L# - KISS TNC via pseudo terminal (-p command line option)
|
||||
C#
|
||||
C
|
||||
CAGWPORT 8000
|
||||
CKISSPORT 8001
|
||||
C
|
||||
W#
|
||||
W# Some applications are designed to operate with only a physical
|
||||
W# TNC attached to a serial port. For these, we provide a virtual serial
|
||||
W# port that appears to be connected to a TNC.
|
||||
W#
|
||||
W# Take a look at the User Guide for instructions to set up
|
||||
W# two virtual serial ports named COM3 and COM4 connected by
|
||||
W# a null modem.
|
||||
W#
|
||||
W# Using the configuration described, Dire Wolf will connect to
|
||||
W# COM3 and the client application will use COM4.
|
||||
W#
|
||||
W# Uncomment following line to use this feature.
|
||||
W
|
||||
W#NULLMODEM COM3
|
||||
W
|
||||
W
|
||||
C#
|
||||
C# It is sometimes possible to recover frames with a bad FCS.
|
||||
C# This applies to all channels.
|
||||
C#
|
||||
C# 0 [NONE] - Don't try to repair.
|
||||
C# 1 [SINGLE] - Attempt to fix single bit error. (default)
|
||||
C# 2 [DOUBLE] - Also attempt to fix two adjacent bits.
|
||||
C# ... see User Guide for more values and in-depth discussion.
|
||||
C#
|
||||
C
|
||||
C#FIX_BITS 0
|
||||
C
|
||||
C#
|
||||
C#############################################################
|
||||
C# #
|
||||
C# BEACONING PROPERTIES #
|
||||
C# #
|
||||
C#############################################################
|
||||
C
|
||||
C
|
||||
C#
|
||||
C# Beaconing is configured with these two commands:
|
||||
C#
|
||||
C# PBEACON - for a position report (usually yourself)
|
||||
C# OBEACON - for an object report (usually some other entity)
|
||||
C#
|
||||
C# Each has a series of keywords and values for options.
|
||||
C# See User Guide for details.
|
||||
C#
|
||||
C# Example:
|
||||
C#
|
||||
C# This results in a broadcast once every 10 minutes.
|
||||
C# Every half hour, it can travel via two digipeater hops.
|
||||
C# The others are kept local.
|
||||
C#
|
||||
C
|
||||
C#PBEACON delay=1 every=30 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W power=50 height=20 gain=4 comment="Chelmsford MA" via=WIDE1-1,WIDE2-1
|
||||
C#PBEACON delay=11 every=30 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W power=50 height=20 gain=4 comment="Chelmsford MA"
|
||||
C#PBEACON delay=21 every=30 overlay=S symbol="digi" lat=42^37.14N long=071^20.83W power=50 height=20 gain=4 comment="Chelmsford MA"
|
||||
C
|
||||
C
|
||||
C# With UTM coordinates instead of latitude and longitude.
|
||||
C
|
||||
C#PBEACON delay=1 every=10 overlay=S symbol="digi" zone=19T easting=307477 northing=4720178
|
||||
C
|
||||
C
|
||||
C#
|
||||
C# When the destination field is set to "SPEECH" the information part is
|
||||
C# converted to speech rather than transmitted as a data frame.
|
||||
C#
|
||||
C
|
||||
C#CBEACON dest="SPEECH" info="Club meeting tonight at 7 pm."
|
||||
C
|
||||
C# Similar for Morse code. If SSID is specified, it is multiplied
|
||||
C# by 2 to get speed in words per minute (WPM).
|
||||
C
|
||||
C#CBEACON dest="MORSE-6" info="de MYCALL"
|
||||
C
|
||||
C
|
||||
C#
|
||||
C# Modify for your particular situation before removing
|
||||
C# the # comment character from the beginning of appropriate lines above.
|
||||
C#
|
||||
C
|
||||
C
|
||||
C#############################################################
|
||||
C# #
|
||||
C# DIGIPEATER PROPERTIES #
|
||||
C# #
|
||||
C#############################################################
|
||||
C
|
||||
C#
|
||||
C# For most common situations, use something like this by removing
|
||||
C# the "#" from the beginning of the line below.
|
||||
C#
|
||||
C
|
||||
C#DIGIPEAT 0 0 ^WIDE[3-7]-[1-7]$|^TEST$ ^WIDE[12]-[12]$ TRACE
|
||||
C
|
||||
C# See User Guide for more explanation of what this means and how
|
||||
C# it can be customized for your particular needs.
|
||||
C
|
||||
C# Filtering can be used to limit was is digipeated.
|
||||
C# For example, only weather weather reports, received on channel 0,
|
||||
C# will be retransmitted on channel 1.
|
||||
C#
|
||||
C
|
||||
C#FILTER 0 1 t/wn
|
||||
C
|
||||
C
|
||||
C#############################################################
|
||||
C# #
|
||||
C# INTERNET GATEWAY #
|
||||
C# #
|
||||
C#############################################################
|
||||
C
|
||||
C# First you need to specify the name of a Tier 2 server.
|
||||
C# The current preferred way is to use one of these regional rotate addresses:
|
||||
C
|
||||
C# noam.aprs2.net - for North America
|
||||
C# soam.aprs2.net - for South America
|
||||
C# euro.aprs2.net - for Europe and Africa
|
||||
C# asia.aprs2.net - for Asia
|
||||
C# aunz.aprs2.net - for Oceania
|
||||
C
|
||||
C#IGSERVER noam.aprs2.net
|
||||
C
|
||||
C# You also need to specify your login name and passcode.
|
||||
C# Contact the author if you can't figure out how to generate the passcode.
|
||||
C
|
||||
C#IGLOGIN WB2OSZ-5 123456
|
||||
C
|
||||
C# That's all you need for a receive only IGate which relays
|
||||
C# messages from the local radio channel to the global servers.
|
||||
C
|
||||
C# Some might want to send an IGate client position directly to a server
|
||||
C# without sending it over the air and relying on someone else to
|
||||
C# forward it to an IGate server. This is done by using sendto=IG rather
|
||||
C# than a radio channel number. Overlay R for receive only, T for two way.
|
||||
C
|
||||
C#PBEACON sendto=IG delay=0:30 every=60:00 symbol="igate" overlay=R lat=42^37.14N long=071^20.83W
|
||||
C#PBEACON sendto=IG delay=0:30 every=60:00 symbol="igate" overlay=T lat=42^37.14N long=071^20.83W
|
||||
C
|
||||
C
|
||||
C# To relay messages from the Internet to radio, you need to add
|
||||
C# one more option with the transmit channel number and a VIA path.
|
||||
C
|
||||
C#IGTXVIA 0 WIDE1-1
|
||||
C
|
||||
C# You might want to apply a filter for what packets will be obtained from the server.
|
||||
C# Read about filters here: http://www.aprs-is.net/javaprsfilter.aspx
|
||||
C# Example, positions and objects within 50 km of my location:
|
||||
C
|
||||
C#IGFILTER m/50
|
||||
C
|
||||
C# That is known as a server-side filter. It is processed by the IGate server.
|
||||
C# You can also apply local filtering to limit what will be transmitted on the
|
||||
C# RF side. For example, transmit only "messages" on channel 0 and weather
|
||||
C# reports on channel 1.
|
||||
C
|
||||
C#FILTER IG 0 t/m
|
||||
C#FILTER IG 1 t/wn
|
||||
C
|
||||
C# Finally, we don't want to flood the radio channel.
|
||||
C# The IGate function will limit the number of packets transmitted
|
||||
C# during 1 minute and 5 minute intervals. If a limit would
|
||||
C# be exceeded, the packet is dropped and message is displayed in red.
|
||||
C
|
||||
CIGTXLIMIT 6 10
|
||||
C
|
||||
C
|
||||
C#############################################################
|
||||
C# #
|
||||
C# APRStt GATEWAY #
|
||||
C# #
|
||||
C#############################################################
|
||||
C
|
||||
C#
|
||||
C# Dire Wolf can receive DTMF (commonly known as Touch Tone)
|
||||
C# messages and convert them to packet objects.
|
||||
C#
|
||||
C# See separate "APRStt-Implementation-Notes" document for details.
|
||||
C#
|
||||
C
|
||||
C#
|
||||
C# Sample gateway configuration based on:
|
||||
C#
|
||||
C# http://www.aprs.org/aprstt/aprstt-coding24.txt
|
||||
C# http://www.aprs.org/aprs-jamboree-2013.html
|
||||
C#
|
||||
C
|
||||
C# Define specific points.
|
||||
C
|
||||
CTTPOINT B01 37^55.37N 81^7.86W
|
||||
CTTPOINT B7495088 42.605237 -71.34456
|
||||
CTTPOINT B934 42.605237 -71.34456
|
||||
C
|
||||
CTTPOINT B901 42.661279 -71.364452
|
||||
CTTPOINT B902 42.660411 -71.364419
|
||||
CTTPOINT B903 42.659046 -71.364452
|
||||
CTTPOINT B904 42.657578 -71.364602
|
||||
C
|
||||
C
|
||||
C# For location at given bearing and distance from starting point.
|
||||
C
|
||||
CTTVECTOR B5bbbddd 37^55.37N 81^7.86W 0.01 mi
|
||||
C
|
||||
C# For location specified by x, y coordinates.
|
||||
C
|
||||
CTTGRID Byyyxxx 37^50.00N 81^00.00W 37^59.99N 81^09.99W
|
||||
C
|
||||
C# UTM location for Lowell-Dracut-Tyngsborough State Forest.
|
||||
C
|
||||
CTTUTM B6xxxyyy 19T 10 300000 4720000
|
||||
C
|
||||
C
|
||||
C
|
||||
C# Location for the corral.
|
||||
C
|
||||
CTTCORRAL 37^55.50N 81^7.00W 0^0.02N
|
||||
C
|
||||
C# Compact messages - Fixed locations xx and object yyy where
|
||||
C# Object numbers 100 - 199 = bicycle
|
||||
C# Object numbers 200 - 299 = fire truck
|
||||
C# Others = dog
|
||||
C
|
||||
CTTMACRO xx1yy B9xx*AB166*AA2B4C5B3B0A1yy
|
||||
CTTMACRO xx2yy B9xx*AB170*AA3C4C7C3B0A2yy
|
||||
CTTMACRO xxyyy B9xx*AB180*AA3A6C4A0Ayyy
|
||||
C
|
||||
CTTMACRO z Cz
|
||||
C
|
||||
C# Receive on channel 0, Transmit object reports on channel 1 with optional via path.
|
||||
C# You probably want to put in a transmit delay on the APRStt channel so it
|
||||
C# it doesn't start sending a response before the user releases PTT.
|
||||
C# This is in 10 ms units so 100 means 1000 ms = 1 second.
|
||||
C
|
||||
C#TTOBJ 0 1 WIDE1-1
|
||||
C#CHANNEL 0
|
||||
C#DWAIT 100
|
||||
C
|
||||
C# Advertise gateway position with beacon.
|
||||
C
|
||||
C# OBEACON DELAY=0:15 EVERY=10:00 VIA=WIDE1-1 OBJNAME=WB2OSZ-tt SYMBOL=APRStt LAT=42^37.14N LONG=71^20.83W COMMENT="APRStt Gateway"
|
||||
C
|
||||
C
|
||||
C# Sample speech responses.
|
||||
C# Default is Morse code "R" for received OK and "?" for all errors.
|
||||
C
|
||||
C#TTERR OK SPEECH Message Received.
|
||||
C#TTERR D_MSG SPEECH D not implemented.
|
||||
C#TTERR INTERNAL SPEECH Internal error.
|
||||
C#TTERR MACRO_NOMATCH SPEECH No definition for digit sequence.
|
||||
C#TTERR BAD_CHECKSUM SPEECH Bad checksum on call.
|
||||
C#TTERR INVALID_CALL SPEECH Invalid callsign.
|
||||
C#TTERR INVALID_OBJNAME SPEECH Invalid object name.
|
||||
C#TTERR INVALID_SYMBOL SPEECH Invalid symbol.
|
||||
C#TTERR INVALID_LOC SPEECH Invalid location.
|
||||
C#TTERR NO_CALL SPEECH No call or object name.
|
||||
C#TTERR SATSQ SPEECH Satellite square must be 4 digits.
|
||||
C
|
|
@ -87,7 +87,7 @@ 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_fhs_facility = 181, /* U Fishing Hot Spots(tm) Facility */
|
||||
sym_bot_cond = 182, /* Bottom Conditions */
|
||||
sym_tide_pred_stn = 183, /* Tide/Current Prediction Station */
|
||||
sym_anchor_prohib = 184, /* U anchor prohibited symbol */
|
||||
|
|
13
hdlc_rec.c
13
hdlc_rec.c
|
@ -112,7 +112,6 @@ static int num_subchan[MAX_CHANS]; //TODO1.2 use ptr rather than copy.
|
|||
|
||||
static int composite_dcd[MAX_CHANS];
|
||||
|
||||
static void dcd_change (int chan, int subchan, int state);
|
||||
|
||||
|
||||
/***********************************************************************************
|
||||
|
@ -558,7 +557,10 @@ int hdlc_rec_gathering (int chan, int subchan)
|
|||
* state for the channel.
|
||||
*
|
||||
* Inputs: chan
|
||||
* subchan
|
||||
*
|
||||
* subchan 0 to MAX_SUBCHANS-1 for HDLC.
|
||||
* MAX_SUBCHANS for DTMF decoder.
|
||||
*
|
||||
* state 1 for active, 0 for not.
|
||||
*
|
||||
* Returns: None. Use ??? to retrieve result.
|
||||
|
@ -566,17 +568,18 @@ int hdlc_rec_gathering (int chan, int subchan)
|
|||
* Description: DCD for the channel is active if ANY of the subchannels
|
||||
* is active. Update the DCD indicator.
|
||||
*
|
||||
* Future: Roll DTMF into the final result.
|
||||
* version 1.3: Add DTMF detection into the final result.
|
||||
* This is now called from dtmf.c too.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static void dcd_change (int chan, int subchan, int state)
|
||||
void dcd_change (int chan, int subchan, int state)
|
||||
{
|
||||
int old, new;
|
||||
|
||||
assert (chan >= 0 && chan < MAX_CHANS);
|
||||
assert (subchan >= 0 && subchan < MAX_SUBCHANS);
|
||||
assert (subchan >= 0 && subchan <= MAX_SUBCHANS);
|
||||
assert (state == 0 || state == 1);
|
||||
|
||||
#if DEBUG3
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
#include "audio.h"
|
||||
|
||||
//#include "rrbb.h"
|
||||
|
||||
|
||||
void hdlc_rec_init (struct audio_s *pa);
|
||||
|
||||
|
@ -11,7 +9,6 @@ void hdlc_rec_init (struct audio_s *pa);
|
|||
void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int descram_state);
|
||||
|
||||
|
||||
|
||||
/* Provided elsewhere to process a complete frame. */
|
||||
|
||||
//void process_rec_frame (int chan, unsigned char *fbuf, int flen, int level);
|
||||
|
@ -26,5 +23,6 @@ int hdlc_rec_gathering (int chan, int subchan);
|
|||
|
||||
/* Transmit needs to know when someone else is transmitting. */
|
||||
|
||||
void dcd_change (int chan, int subchan, int state);
|
||||
|
||||
int hdlc_rec_data_detect_any (int chan);
|
||||
|
|
126
igate.c
126
igate.c
|
@ -85,9 +85,8 @@
|
|||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "ax25_pad.h"
|
||||
|
@ -153,7 +152,7 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S
|
|||
case AF_INET:
|
||||
sa4 = (struct sockaddr_in *)pAddr;
|
||||
#if __WIN32__
|
||||
sprintf (pStringBuf, "%d.%d.%d.%d", sa4->sin_addr.S_un.S_un_b.s_b1,
|
||||
snprintf (pStringBuf, StringBufSize, "%d.%d.%d.%d", sa4->sin_addr.S_un.S_un_b.s_b1,
|
||||
sa4->sin_addr.S_un.S_un_b.s_b2,
|
||||
sa4->sin_addr.S_un.S_un_b.s_b3,
|
||||
sa4->sin_addr.S_un.S_un_b.s_b4);
|
||||
|
@ -164,7 +163,7 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S
|
|||
case AF_INET6:
|
||||
sa6 = (struct sockaddr_in6 *)pAddr;
|
||||
#if __WIN32__
|
||||
sprintf (pStringBuf, "%x:%x:%x:%x:%x:%x:%x:%x",
|
||||
snprintf (pStringBuf, StringBufSize, "%x:%x:%x:%x:%x:%x:%x:%x",
|
||||
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[0]),
|
||||
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[1]),
|
||||
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[2]),
|
||||
|
@ -178,9 +177,9 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S
|
|||
#endif
|
||||
break;
|
||||
default:
|
||||
sprintf (pStringBuf, "Invalid address family!");
|
||||
snprintf (pStringBuf, StringBufSize, "Invalid address family!");
|
||||
}
|
||||
assert (strlen(pStringBuf) < StringBufSize);
|
||||
//assert (strlen(pStringBuf) < StringBufSize);
|
||||
return pStringBuf;
|
||||
}
|
||||
|
||||
|
@ -198,19 +197,19 @@ int main (int argc, char *argv[])
|
|||
|
||||
memset (&audio_config, 0, sizeof(audio_config));
|
||||
audio_config.adev[0].num_chans = 2;
|
||||
strcpy (audio_config.achan[0].mycall, "WB2OSZ-1");
|
||||
strcpy (audio_config.achan[0].mycall, "WB2OSZ-2");
|
||||
strlcpy (audio_config.achan[0].mycall, "WB2OSZ-1", sizeof(audio_config.achan[0].mycall));
|
||||
strlcpy (audio_config.achan[1].mycall, "WB2OSZ-2", sizeof(audio_config.achan[0].mycall));
|
||||
|
||||
memset (&igate_config, 0, sizeof(igate_config));
|
||||
|
||||
strcpy (igate_config.t2_server_name, "localhost");
|
||||
strlcpy (igate_config.t2_server_name, "localhost", sizeof(igate_config.t2_server_name));
|
||||
igate_config.t2_server_port = 14580;
|
||||
strcpy (igate_config.t2_login, "WB2OSZ-JL");
|
||||
strcpy (igate_config.t2_passcode, "-1");
|
||||
strlcpy (igate_config.t2_login, "WB2OSZ-JL", sizeof(igate_config.t2_login));
|
||||
strlcpy (igate_config.t2_passcode, "-1", sizeof(igate_config.t2_passcode));
|
||||
igate_config.t2_filter = strdup ("r/1/2/3");
|
||||
|
||||
igate_config.tx_chan = 0;
|
||||
strcpy (igate_config.tx_via, ",WIDE2-1");
|
||||
strlcpy (igate_config.tx_via, ",WIDE2-1", sizeof(igate_config.tx_via));
|
||||
igate_config.tx_limit_1 = 3;
|
||||
igate_config.tx_limit_5 = 5;
|
||||
|
||||
|
@ -314,6 +313,35 @@ static int stats_tx_igate_packets; /* Number of packets from IGate server. */
|
|||
static int stats_rf_xmit_packets; /* Number of packets passed along to radio */
|
||||
/* after rate limiting or other restrictions. */
|
||||
|
||||
/* We have some statistics. What do we do with them?
|
||||
|
||||
|
||||
IGate stations often send packets like this:
|
||||
|
||||
<IGATE MSG_CNT=1238 LOC_CNT=0 FILL_CNT=0
|
||||
<IGATE,MSG_CNT=1,LOC_CNT=25
|
||||
<IGATE,MSG_CNT=0,LOC_CNT=46,DIR_CNT=13,RF_CNT=49,RFPORT_ID=0
|
||||
|
||||
What does it all mean?
|
||||
Why do some have spaces instead of commas between the capabilities?
|
||||
|
||||
The APRS Protocol Reference ( http://www.aprs.org/doc/APRS101.PDF ),
|
||||
section 15, briefly discusses station capabilities and gives the example
|
||||
IGATE,MSG_CNT=n,LOC_CNT=n
|
||||
|
||||
IGate Design ( http://www.aprs-is.net/IGating.aspx ) barely mentions
|
||||
<IGATE,MSG_CNT=n,LOC_CNT=n
|
||||
|
||||
This leaves many questions. Does "number of messages transmitted" mean only
|
||||
the APRS "Message" (data type indicator ":") or does it mean any type of
|
||||
APRS packet? What are "local" stations? Those we hear directly without
|
||||
going thru a digipeater?
|
||||
|
||||
What are DIR_CNT, RF_CNT, and so on?
|
||||
|
||||
Are the counts since the system started up or are they for some interval?
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
|
@ -504,7 +532,7 @@ static void * connnect_thread (void *arg)
|
|||
WSADATA wsadata;
|
||||
#endif
|
||||
|
||||
sprintf (server_port_str, "%d", save_igate_config_p->t2_server_port);
|
||||
snprintf (server_port_str, sizeof(server_port_str), "%d", save_igate_config_p->t2_server_port);
|
||||
#if DEBUGx
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("DEBUG: igate connect_thread start, port = %d = '%s'\n", save_igate_config_p->t2_server_port, server_port_str);
|
||||
|
@ -704,14 +732,14 @@ static void * connnect_thread (void *arg)
|
|||
*/
|
||||
|
||||
SLEEP_SEC(3);
|
||||
sprintf (stemp, "user %s pass %s vers Dire-Wolf %d.%d",
|
||||
snprintf (stemp, sizeof(stemp), "user %s pass %s vers Dire-Wolf %d.%d",
|
||||
save_igate_config_p->t2_login, save_igate_config_p->t2_passcode,
|
||||
MAJOR_VERSION, MINOR_VERSION);
|
||||
if (save_igate_config_p->t2_filter != NULL) {
|
||||
strcat (stemp, " filter ");
|
||||
strcat (stemp, save_igate_config_p->t2_filter);
|
||||
strlcat (stemp, " filter ", sizeof(stemp));
|
||||
strlcat (stemp, save_igate_config_p->t2_filter, sizeof(stemp));
|
||||
}
|
||||
strcat (stemp, "\r\n");
|
||||
strlcat (stemp, "\r\n", sizeof(stemp));
|
||||
send_msg_to_server (stemp);
|
||||
|
||||
/* Delay until it is ok to start sending packets. */
|
||||
|
@ -739,7 +767,7 @@ static void * connnect_thread (void *arg)
|
|||
|
||||
char heartbeat[10];
|
||||
|
||||
strcpy (heartbeat, "#\r\n");
|
||||
strlcpy (heartbeat, "#\r\n", sizeof(heartbeat));
|
||||
|
||||
/* This will close the socket if any error. */
|
||||
send_msg_to_server (heartbeat);
|
||||
|
@ -809,7 +837,8 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
|
|||
}
|
||||
|
||||
|
||||
/* Count only while connected. */
|
||||
/* Gather statistics. */
|
||||
|
||||
stats_rf_recv_packets++;
|
||||
|
||||
/*
|
||||
|
@ -817,6 +846,7 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
|
|||
*/
|
||||
|
||||
pp = ax25_dup (recv_pp);
|
||||
assert (pp != NULL);
|
||||
|
||||
/*
|
||||
* Third party frames require special handling to unwrap payload.
|
||||
|
@ -915,7 +945,7 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
|
|||
/*
|
||||
* Someone around here occasionally sends a packet with no information part.
|
||||
*/
|
||||
if (strlen(pinfo) == 0) {
|
||||
if (strlen((char*)pinfo) == 0) {
|
||||
|
||||
#if DEBUGx
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
|
@ -946,11 +976,11 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
|
|||
|
||||
ax25_format_addrs (pp, msg);
|
||||
msg[strlen(msg)-1] = '\0'; /* Remove trailing ":" */
|
||||
strcat (msg, ",qAR,");
|
||||
strcat (msg, save_audio_config_p->achan[chan].mycall);
|
||||
strcat (msg, ":");
|
||||
strcat (msg, (char*)pinfo);
|
||||
strcat (msg, "\r\n");
|
||||
strlcat (msg, ",qAR,", sizeof(msg));
|
||||
strlcat (msg, save_audio_config_p->achan[chan].mycall, sizeof(msg));
|
||||
strlcat (msg, ":", sizeof(msg));
|
||||
strlcat (msg, (char*)pinfo, sizeof(msg));
|
||||
strlcat (msg, "\r\n", sizeof(msg));
|
||||
|
||||
send_msg_to_server (msg);
|
||||
stats_rx_igate_packets++;
|
||||
|
@ -1205,10 +1235,11 @@ static void * igate_recv_thread (void *arg)
|
|||
static void xmit_packet (char *message)
|
||||
{
|
||||
packet_t pp3;
|
||||
char payload[500]; /* what is max len? */
|
||||
char payload[AX25_MAX_PACKET_LEN]; /* what is max len? */
|
||||
char *pinfo = NULL;
|
||||
int info_len;
|
||||
int to_chan = save_igate_config_p->tx_chan; /* which could be -1 if not configured for xmit!!! */
|
||||
int to_chan = save_igate_config_p->tx_chan; /* Should be -1 if not configured for xmit!!! */
|
||||
/* Future: Array of boolean to allow multiple xmit channels? */
|
||||
|
||||
/*
|
||||
* Is IGate to Radio direction enabled?
|
||||
|
@ -1219,12 +1250,12 @@ static void xmit_packet (char *message)
|
|||
|
||||
stats_tx_igate_packets++;
|
||||
|
||||
assert (save_igate_config_p->tx_chan >= 0 && save_igate_config_p->tx_chan < MAX_CHANS);
|
||||
assert (to_chan >= 0 && to_chan < MAX_CHANS);
|
||||
|
||||
/*
|
||||
* Try to parse it into a packet object.
|
||||
* Bug: Up to 8 digipeaters are allowed in radio format.
|
||||
* There is a potential of finding more here.
|
||||
* There is a potential of finding a larger number here.
|
||||
*/
|
||||
pp3 = ax25_from_text(message, 0);
|
||||
if (pp3 == NULL) {
|
||||
|
@ -1239,8 +1270,6 @@ static void xmit_packet (char *message)
|
|||
* Apply our own packet filtering if configured.
|
||||
*/
|
||||
|
||||
//TODO1.2: Should we allow IGating to RF with more than one channel?
|
||||
|
||||
assert (to_chan >= 0 && to_chan < MAX_CHANS);
|
||||
|
||||
if (save_digi_config_p->filter_str[MAX_CHANS][to_chan] != NULL) {
|
||||
|
@ -1281,9 +1310,11 @@ static void xmit_packet (char *message)
|
|||
/*
|
||||
* Convert to text representation.
|
||||
*/
|
||||
memset (payload, 0, sizeof(payload));
|
||||
|
||||
ax25_format_addrs (pp3, payload);
|
||||
info_len = ax25_get_info (pp3, (unsigned char **)(&pinfo));
|
||||
strcat (payload, pinfo);
|
||||
strlcat (payload, pinfo, sizeof(payload));
|
||||
#if DEBUGx
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Tx IGate: payload=%s\n", payload);
|
||||
|
@ -1296,23 +1327,38 @@ static void xmit_packet (char *message)
|
|||
char radio [500];
|
||||
packet_t pradio;
|
||||
|
||||
sprintf (radio, "%s>%s%d%d%s:}%s",
|
||||
snprintf (radio, sizeof(radio), "%s>%s%d%d%s:}%s",
|
||||
save_audio_config_p->achan[save_igate_config_p->tx_chan].mycall,
|
||||
APP_TOCALL, MAJOR_VERSION, MINOR_VERSION,
|
||||
save_igate_config_p->tx_via,
|
||||
payload);
|
||||
|
||||
pradio = ax25_from_text (radio, 1);
|
||||
|
||||
/* Oops. Didn't have a check for NULL here. */
|
||||
/* Could this be the cause of rare and elusive crashes in 1.2? */
|
||||
|
||||
if (pradio != NULL) {
|
||||
|
||||
#if ITEST
|
||||
text_color_set(DW_COLOR_XMIT);
|
||||
dw_printf ("Xmit: %s\n", radio);
|
||||
ax25_delete (pradio);
|
||||
text_color_set(DW_COLOR_XMIT);
|
||||
dw_printf ("Xmit: %s\n", radio);
|
||||
ax25_delete (pradio);
|
||||
#else
|
||||
/* This consumes packet so don't reference it again! */
|
||||
tq_append (save_igate_config_p->tx_chan, TQ_PRIO_1_LO, pradio);
|
||||
/* This consumes packet so don't reference it again! */
|
||||
tq_append (save_igate_config_p->tx_chan, TQ_PRIO_1_LO, pradio);
|
||||
#endif
|
||||
stats_rf_xmit_packets++;
|
||||
ig_to_tx_remember (pp3);
|
||||
stats_rf_xmit_packets++;
|
||||
ig_to_tx_remember (pp3);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Received invalid packet from IGate.\n");
|
||||
dw_printf ("%s\n", payload);
|
||||
dw_printf ("Will not attempt to transmit third party packet.\n");
|
||||
dw_printf ("%s\n", radio);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ax25_delete (pp3);
|
||||
|
|
4
kiss.c
4
kiss.c
|
@ -84,7 +84,7 @@
|
|||
* might be limited to these four choices.
|
||||
*
|
||||
* The documentation instucts the user to install the com0com
|
||||
* “Null-modem emulator” from http://sourceforge.net/projects/com0com/
|
||||
* "Null-modem emulator" from http://sourceforge.net/projects/com0com/
|
||||
* and configure it for COM3 & COM4.
|
||||
*
|
||||
* By default Dire Wolf will use COM3 (/dev/ttyS2 or /dev/com3 - lower case!)
|
||||
|
@ -955,7 +955,7 @@ static THREAD_F kiss_listen_thread (void *arg)
|
|||
#if __WIN32__
|
||||
return(0);
|
||||
#else
|
||||
return; /* Unreachable but avoids compiler warning. */
|
||||
return (THREAD_F) 0; /* Unreachable but avoids compiler warning. */
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -131,6 +131,12 @@ void kiss_frame_init (struct audio_s *pa)
|
|||
* Inputs: in - Address of input block.
|
||||
* First byte is the "type indicator" with type and
|
||||
* channel but we don't care about that here.
|
||||
*
|
||||
* This seems cumbersome and confusing to have this
|
||||
* one byte offset when encapsulating an AX.25 frame.
|
||||
* Maybe the type/channel byte should be passed in
|
||||
* as a separate argument.
|
||||
*
|
||||
* Note that this is "binary" data and can contain
|
||||
* nul (0x00) values. Don't treat it like a text string!
|
||||
*
|
||||
|
@ -176,6 +182,7 @@ int kiss_encapsulate (unsigned char *in, int ilen, unsigned char *out)
|
|||
} /* end kiss_encapsulate */
|
||||
|
||||
|
||||
#ifndef WALK96
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
|
@ -659,4 +666,6 @@ main ()
|
|||
|
||||
#endif
|
||||
|
||||
#endif /* WALK96 */
|
||||
|
||||
/* end kiss_frame.c */
|
||||
|
|
|
@ -382,6 +382,7 @@ static void * connect_listen_thread (void *arg)
|
|||
socklen_t sockaddr_size = sizeof(struct sockaddr_in);
|
||||
int kiss_port = (int)(long)arg;
|
||||
int listen_sock;
|
||||
int bcopt = 1;
|
||||
|
||||
listen_sock= socket(AF_INET,SOCK_STREAM,0);
|
||||
if (listen_sock == -1) {
|
||||
|
@ -390,6 +391,14 @@ static void * connect_listen_thread (void *arg)
|
|||
return (NULL);
|
||||
}
|
||||
|
||||
/* Version 1.3 - as suggested by G8BPQ. */
|
||||
/* Without this, if you kill the application then try to run it */
|
||||
/* again quickly the port number is unavailable for a while. */
|
||||
/* Don't try doing the same thing On Windows; It has a different meaning. */
|
||||
/* http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t */
|
||||
|
||||
setsockopt (listen_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&bcopt, 4);
|
||||
|
||||
sockaddr.sin_addr.s_addr = INADDR_ANY;
|
||||
sockaddr.sin_port = htons(kiss_port);
|
||||
sockaddr.sin_family = AF_INET;
|
||||
|
|
84
latlong.c
84
latlong.c
|
@ -559,7 +559,7 @@ double ll_distance_km (double lat1, double lon1, double lat2, double lon2)
|
|||
*
|
||||
* Purpose: Convert Maidenhead locator to latitude and longitude.
|
||||
*
|
||||
* Inputs: maidenhead - 4 or 6 character grid square.
|
||||
* Inputs: maidenhead - 2, 4, 6, or 8 character grid square locator.
|
||||
*
|
||||
* Outputs: dlat, dlon - Latitude and longitude.
|
||||
* Original values unchanged if error.
|
||||
|
@ -591,9 +591,9 @@ int ll_from_grid_square (char *maidenhead, double *dlat, double *dlon)
|
|||
char mh[16];
|
||||
|
||||
|
||||
if (strlen(maidenhead) != 4 && strlen(maidenhead) != 6) {
|
||||
if (strlen(maidenhead) != 2 && strlen(maidenhead) != 4 && strlen(maidenhead) != 6 && strlen(maidenhead) != 8) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Maidenhead locator \"%s\" must 4 or 6 characters in this context.\n", maidenhead);
|
||||
dw_printf("Maidenhead locator \"%s\" must 2, 4, 6, or 8 characters.\n", maidenhead);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -607,11 +607,6 @@ int ll_from_grid_square (char *maidenhead, double *dlat, double *dlon)
|
|||
return (0);
|
||||
}
|
||||
|
||||
if ( ! isdigit(mh[2]) || ! isdigit(mh[3]) ) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("The second pair of characters in Maidenhead locator \"%s\" must be digits.\n", maidenhead);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Lon: 360 deg / 18 squares = 20 deg / square */
|
||||
/* Lat: 180 deg / 18 squares = 10 deg / square */
|
||||
|
@ -619,55 +614,68 @@ int ll_from_grid_square (char *maidenhead, double *dlat, double *dlon)
|
|||
lon = (mh[0] - 'A') * 20 - 180;
|
||||
lat = (mh[1] - 'A') * 10 - 90;
|
||||
|
||||
/* Lon: 20 deg / 10 squares = 2 deg / square */
|
||||
/* Lat: 10 deg / 10 squares = 1 deg / square */
|
||||
if (strlen(mh) >= 4) {
|
||||
|
||||
lon += (mh[2] - '0') * 2;
|
||||
lat += (mh[3] - '0');
|
||||
|
||||
|
||||
if (strlen(mh) >=6) {
|
||||
|
||||
if (islower(mh[4])) mh[4] = toupper(mh[4]);
|
||||
if (islower(mh[5])) mh[5] = toupper(mh[5]);
|
||||
|
||||
if (mh[4] < 'A' || mh[4] > 'X' || mh[5] < 'A' || mh[5] > 'X') {
|
||||
if ( ! isdigit(mh[2]) || ! isdigit(mh[3]) ) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("The third pair of characters in Maidenhead locator \"%s\" must be in range of A thru X.\n", maidenhead);
|
||||
dw_printf("The second pair of characters in Maidenhead locator \"%s\" must be digits.\n", maidenhead);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Lon: 2 deg / 24 squares = 5 minutes / square */
|
||||
/* Lat: 1 deg / 24 squares = 2.5 minutes / square */
|
||||
/* Lon: 20 deg / 10 squares = 2 deg / square */
|
||||
/* Lat: 10 deg / 10 squares = 1 deg / square */
|
||||
|
||||
lon += (mh[4] - 'A') * 5.0 / 60.0;
|
||||
lat += (mh[5] - 'A') * 2.5 / 60.0;
|
||||
lon += (mh[2] - '0') * 2;
|
||||
lat += (mh[3] - '0');
|
||||
|
||||
if (strlen(mh) >= 8) {
|
||||
|
||||
if ( ! isdigit(mh[6]) || ! isdigit(mh[7]) ) {
|
||||
if (strlen(mh) >=6) {
|
||||
|
||||
if (islower(mh[4])) mh[4] = toupper(mh[4]);
|
||||
if (islower(mh[5])) mh[5] = toupper(mh[5]);
|
||||
|
||||
if (mh[4] < 'A' || mh[4] > 'X' || mh[5] < 'A' || mh[5] > 'X') {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("The fourth pair of characters in Maidenhead locator \"%s\" must be digits.\n", maidenhead);
|
||||
dw_printf("The third pair of characters in Maidenhead locator \"%s\" must be in range of A thru X.\n", maidenhead);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Lon: 5 min / 10 squares = 0.5 minutes / square */
|
||||
/* Lat: 2.5 min / 10 squares = 0.25 minutes / square */
|
||||
/* Lon: 2 deg / 24 squares = 5 minutes / square */
|
||||
/* Lat: 1 deg / 24 squares = 2.5 minutes / square */
|
||||
|
||||
lon += (mh[6] - '0') * 0.50 / 60.0;
|
||||
lat += (mh[7] - '0') * 0.25 / 60.0;
|
||||
lon += (mh[4] - 'A') * 5.0 / 60.0;
|
||||
lat += (mh[5] - 'A') * 2.5 / 60.0;
|
||||
|
||||
lon += 0.250 / 60.0; /* Move from corner to center of square */
|
||||
lat += 0.125 / 60.0;
|
||||
if (strlen(mh) >= 8) {
|
||||
|
||||
if ( ! isdigit(mh[6]) || ! isdigit(mh[7]) ) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("The fourth pair of characters in Maidenhead locator \"%s\" must be digits.\n", maidenhead);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Lon: 5 min / 10 squares = 0.5 minutes / square */
|
||||
/* Lat: 2.5 min / 10 squares = 0.25 minutes / square */
|
||||
|
||||
lon += (mh[6] - '0') * 0.50 / 60.0;
|
||||
lat += (mh[7] - '0') * 0.25 / 60.0;
|
||||
|
||||
lon += 0.250 / 60.0; /* Move from corner to center of square */
|
||||
lat += 0.125 / 60.0;
|
||||
}
|
||||
else {
|
||||
lon += 2.5 / 60.0; /* Move from corner to center of square */
|
||||
lat += 1.25 / 60.0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
lon += 2.5 / 60.0; /* Move from corner to center of square */
|
||||
lat += 1.25 / 60.0;
|
||||
lon += 1.0; /* Move from corner to center of square */
|
||||
lat += 0.5;
|
||||
}
|
||||
}
|
||||
else {
|
||||
lon += 1.0; /* Move from corner to center of square */
|
||||
lat += 0.5;
|
||||
lon += 10; /* Move from corner to center of square */
|
||||
lat += 5;
|
||||
}
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
|
|
|
@ -113,6 +113,14 @@ Send Xmit level calibration tones.
|
|||
.B "-U "
|
||||
Print UTF-8 test string and exit.
|
||||
|
||||
.TP
|
||||
.B "-S "
|
||||
Print Symbol tables and exit.
|
||||
|
||||
.TP
|
||||
.BI "-a " "n"
|
||||
Report audio device statistics each n seconds.
|
||||
|
||||
|
||||
.SH EXAMPLES
|
||||
gqrx (2.3 and later) has the ability to send streaming audio through a UDP socket to another application for further processing.
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*
|
||||
* Waypoint icon codes for use in the $PMGNWPL sentence.
|
||||
*
|
||||
* Derived from Data Transmission Protocol For Magellan Products – version 2.11
|
||||
* Derived from Data Transmission Protocol For Magellan Products - version 2.11
|
||||
*/
|
||||
|
||||
#define MGN_crossed_square "a"
|
||||
|
|
|
@ -1,9 +1,34 @@
|
|||
This is NOT used for the Linux version.
|
||||
These are part of the standard C library for Linux and Cygwin.
|
||||
|
||||
Files in this directory fill in the gaps missing for some operating systems.
|
||||
|
||||
|
||||
--------------------------------------
|
||||
|
||||
These are part of the standard C library for Linux and similar operating systems.
|
||||
For the Windows version we need to include our own copy.
|
||||
|
||||
They were copied from Cygwin source:
|
||||
They were copied from Cygwin source.
|
||||
/usr/src/cygwin-1.7.10-1/newlib/libc/string/...
|
||||
|
||||
/usr/src/cygwin-1.7.10-1/newlib/libc/string/strsep.c
|
||||
/usr/src/cygwin-1.7.10-1/newlib/libc/string/strtok_r.c
|
||||
strsep.c
|
||||
strtok_r.c
|
||||
|
||||
--------------------------------------
|
||||
|
||||
This was also missing on Windows but available everywhere else.
|
||||
|
||||
strcasestr.c
|
||||
|
||||
--------------------------------------
|
||||
|
||||
|
||||
The are used for the Linux and Windows versions.
|
||||
They should be part of the standard C library for OpenBSD, FreeBSD, Mac OS X.
|
||||
These are from OpenBSD.
|
||||
http://ftp.netbsd.org/pub/pkgsrc/current/pkgsrc/net/tnftp/files/libnetbsd/strlcpy.c
|
||||
http://ftp.netbsd.org/pub/pkgsrc/current/pkgsrc/net/tnftp/files/libnetbsd/strlcat.c
|
||||
|
||||
|
||||
strlcpy.c
|
||||
strlcat.c
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Module: strlcat.c
|
||||
*
|
||||
* Purpose: Safe string functions to guard against buffer overflow.
|
||||
*
|
||||
* Description: The size of character strings, especially when coming from the
|
||||
* outside, can sometimes exceed a fixed size storage area.
|
||||
*
|
||||
* There was one case where a MIC-E format packet had an enormous
|
||||
* comment that exceeded an internal buffer of 256 characters,
|
||||
* resulting in a crash.
|
||||
*
|
||||
* We are not always meticulous about checking sizes to avoid overflow.
|
||||
* Use of these functions, instead of strcpy and strcat, should
|
||||
* help avoid issues.
|
||||
*
|
||||
* Orgin: From OpenBSD as the copyright notice indicates.
|
||||
* The GNU folks didn't think it was appropriate for inclusion
|
||||
* in glibc. https://lwn.net/Articles/507319/
|
||||
*
|
||||
* Modifications: Added extra debug output when strings are truncated.
|
||||
* Not sure if I will leave this in the release version
|
||||
* or just let it happen silently.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "direwolf.h"
|
||||
#include "textcolor.h"
|
||||
|
||||
|
||||
/* $NetBSD: strlcat.c,v 1.5 2014/10/31 18:59:32 spz Exp $ */
|
||||
/* from NetBSD: strlcat.c,v 1.16 2003/10/27 00:12:42 lukem Exp */
|
||||
/* from OpenBSD: strlcat.c,v 1.10 2003/04/12 21:56:39 millert Exp */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
|
||||
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
|
||||
* FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Appends src to string dst of size siz (unlike strncat, siz is the
|
||||
* full size of dst, not space left). At most siz-1 characters
|
||||
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
|
||||
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
|
||||
* If retval >= siz, truncation occurred.
|
||||
*/
|
||||
|
||||
#if DEBUG_STRL
|
||||
size_t strlcat_debug(char *__restrict__ dst, const char *__restrict__ src, size_t siz, const char *file, const char *func, int line)
|
||||
#else
|
||||
size_t strlcat_debug(char *__restrict__ dst, const char *__restrict__ src, size_t siz)
|
||||
#endif
|
||||
{
|
||||
char *d = dst;
|
||||
const char *s = src;
|
||||
size_t n = siz;
|
||||
size_t dlen;
|
||||
size_t retval;
|
||||
|
||||
#if DEBUG_STRL
|
||||
if (dst == NULL) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR: strlcat dst is NULL. (%s %s %d)\n", file, func, line);
|
||||
return (0);
|
||||
}
|
||||
if (src == NULL) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR: strlcat src is NULL. (%s %s %d)\n", file, func, line);
|
||||
return (0);
|
||||
}
|
||||
if (siz == 1 || siz == 4) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Suspicious strlcat siz. Is it using sizeof a pointer variable? (%s %s %d)\n", file, func, line);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Find the end of dst and adjust bytes left but don't go past end */
|
||||
while (n-- != 0 && *d != '\0')
|
||||
d++;
|
||||
dlen = d - dst;
|
||||
n = siz - dlen;
|
||||
|
||||
if (n == 0) {
|
||||
retval = dlen + strlen(s);
|
||||
goto the_end;
|
||||
}
|
||||
while (*s != '\0') {
|
||||
if (n != 1) {
|
||||
*d++ = *s;
|
||||
n--;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
*d = '\0';
|
||||
|
||||
retval = dlen + (s - src); /* count does not include NUL */
|
||||
the_end:
|
||||
|
||||
#if DEBUG_STRL
|
||||
if (retval >= siz) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("WARNING: strlcat result length %d exceeds maximum length %d. (%s %s %d)\n",
|
||||
(int)retval, (int)(siz-1), file, func, line);
|
||||
}
|
||||
#endif
|
||||
return (retval);
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Module: strlcpy.c
|
||||
*
|
||||
* Purpose: Safe string functions to guard against buffer overflow.
|
||||
*
|
||||
* Description: The size of character strings, especially when coming from the
|
||||
* outside, can sometimes exceed a fixed size storage area.
|
||||
*
|
||||
* There was one case where a MIC-E format packet had an enormous
|
||||
* comment that exceeded an internal buffer of 256 characters,
|
||||
* resulting in a crash.
|
||||
*
|
||||
* We are not always meticulous about checking sizes to avoid overflow.
|
||||
* Use of these functions, instead of strcpy and strcat, should
|
||||
* help avoid issues.
|
||||
*
|
||||
* Orgin: From OpenBSD as the copyright notice indicates.
|
||||
* The GNU folks didn't think it was appropriate for inclusion
|
||||
* in glibc. https://lwn.net/Articles/507319/
|
||||
*
|
||||
* Modifications: Added extra debug output when strings are truncated.
|
||||
* Not sure if I will leave this in the release version
|
||||
* or just let it happen silently.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
||||
/* $NetBSD: strlcpy.c,v 1.5 2014/10/31 18:59:32 spz Exp $ */
|
||||
/* from NetBSD: strlcpy.c,v 1.14 2003/10/27 00:12:42 lukem Exp */
|
||||
/* from OpenBSD: strlcpy.c,v 1.7 2003/04/12 21:56:39 millert Exp */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
|
||||
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
|
||||
* FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "direwolf.h"
|
||||
#include "textcolor.h"
|
||||
|
||||
/*
|
||||
* Copy src to string dst of size siz. At most siz-1 characters
|
||||
* will be copied. Always NUL terminates (unless siz == 0).
|
||||
* Returns strlen(src); if retval >= siz, truncation occurred.
|
||||
*/
|
||||
|
||||
#if DEBUG_STRL
|
||||
size_t strlcpy_debug(char *__restrict__ dst, const char *__restrict__ src, size_t siz, const char *file, const char *func, int line)
|
||||
#else
|
||||
size_t strlcpy_debug(char *__restrict__ dst, const char *__restrict__ src, size_t siz)
|
||||
#endif
|
||||
{
|
||||
char *d = dst;
|
||||
const char *s = src;
|
||||
size_t n = siz;
|
||||
size_t retval;
|
||||
|
||||
#if DEBUG_STRL
|
||||
if (dst == NULL) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR: strlcpy dst is NULL. (%s %s %d)\n", file, func, line);
|
||||
return (0);
|
||||
}
|
||||
if (src == NULL) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR: strlcpy src is NULL. (%s %s %d)\n", file, func, line);
|
||||
return (0);
|
||||
}
|
||||
if (siz == 1 || siz == 4) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Suspicious strlcpy siz. Is it using sizeof a pointer variable? (%s %s %d)\n", file, func, line);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Copy as many bytes as will fit */
|
||||
if (n != 0 && --n != 0) {
|
||||
do {
|
||||
if ((*d++ = *s++) == 0)
|
||||
break;
|
||||
} while (--n != 0);
|
||||
}
|
||||
|
||||
/* Not enough room in dst, add NUL and traverse rest of src */
|
||||
if (n == 0) {
|
||||
if (siz != 0)
|
||||
*d = '\0'; /* NUL-terminate dst */
|
||||
while (*s++)
|
||||
;
|
||||
}
|
||||
|
||||
retval = s - src - 1; /* count does not include NUL */
|
||||
|
||||
#if DEBUG_STRL
|
||||
if (retval >= siz) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("WARNING: strlcpy result length %d exceeds maximum length %d. (%s %s %d)\n",
|
||||
(int)retval, (int)(siz-1), file, func, line);
|
||||
}
|
||||
#endif
|
||||
return (retval);
|
||||
}
|
||||
|
252
morse.c
252
morse.c
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2013 John Langner, WB2OSZ
|
||||
// Copyright (C) 2013, 2015 John Langner, WB2OSZ
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
@ -35,47 +35,32 @@
|
|||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/time.h>
|
||||
#include <math.h>
|
||||
|
||||
#if __WIN32__
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "textcolor.h"
|
||||
#include "audio.h"
|
||||
#include "ptt.h"
|
||||
|
||||
|
||||
#define WPM 10
|
||||
#define TIME_UNITS_TO_MS(tu,wpm) (((tu)*1200)/(wpm))
|
||||
|
||||
|
||||
// TODO : should be in .h file.
|
||||
#include "gen_tone.h" /* for gen_tone_put_sample */
|
||||
#include "morse.h"
|
||||
|
||||
/*
|
||||
* Delay from PTT on to start of first character.
|
||||
* Currently the only anticipated use for this is
|
||||
* APRStt responses. In this case, we want an adequate
|
||||
* delay for someone to press the # button, release
|
||||
* the PTT button, and start listening for a response.
|
||||
* Might get ambitious and make this adjustable some day.
|
||||
* Good enough for now.
|
||||
*/
|
||||
#define MORSE_TXDELAY_MS 1500
|
||||
|
||||
/*
|
||||
* Delay from end of last character to PTT off.
|
||||
* Avoid chopping off the last element.
|
||||
*/
|
||||
#define MORSE_TXTAIL_MS 200
|
||||
#define MORSE_TONE 800
|
||||
|
||||
#define TIME_UNITS_TO_MS(tu,wpm) (((tu)*1200.0)/(wpm))
|
||||
|
||||
|
||||
|
||||
static const struct morse_s {
|
||||
char ch;
|
||||
char enc[7];
|
||||
char enc[8]; /* $ has 7 elements */
|
||||
} morse[] = {
|
||||
{ 'A', ".-" },
|
||||
{ 'B', "-..." },
|
||||
|
@ -117,19 +102,103 @@ static const struct morse_s {
|
|||
{ '.', ".-.-.-" },
|
||||
{ ',', "--..--" },
|
||||
{ '?', "..--.." },
|
||||
{ '/', "-..-." }
|
||||
{ '/', "-..-." },
|
||||
|
||||
{ '=', "-...-" }, /* from ARRL */
|
||||
{ '-', "-....-" },
|
||||
{ ')', "-.--.-" }, /* does not distinguish open/close */
|
||||
{ ':', "---..." },
|
||||
{ ';', "-.-.-." },
|
||||
{ '"', ".-..-." },
|
||||
{ '\'', ".----." },
|
||||
{ '$', "...-..-" },
|
||||
|
||||
{ '!', "-.-.--" }, /* more from wikipedia */
|
||||
{ '(', "-.--." },
|
||||
{ '&', ".-..." },
|
||||
{ '+', ".-.-." },
|
||||
{ '_', "..--.-" },
|
||||
{ '@', ".--.-." },
|
||||
|
||||
};
|
||||
|
||||
#define NUM_MORSE (sizeof(morse) / sizeof(struct morse_s))
|
||||
|
||||
static void morse_tone (int tu);
|
||||
static void morse_quiet (int tu);
|
||||
static void morse_tone (int chan, int tu, int wpm);
|
||||
static void morse_quiet (int chan, int tu, int wpm);
|
||||
static void morse_quiet_ms (int chan, int ms);
|
||||
static int morse_lookup (int ch);
|
||||
static int morse_units_ch (int ch);
|
||||
static int morse_units_str (char *str);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Properties of the digitized sound stream.
|
||||
*/
|
||||
|
||||
static struct audio_s *save_audio_config_p;
|
||||
|
||||
|
||||
/* Constants after initialization. */
|
||||
|
||||
#define TICKS_PER_CYCLE ( 256.0 * 256.0 * 256.0 * 256.0 )
|
||||
|
||||
static short sine_table[256];
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: morse_init
|
||||
*
|
||||
* Purpose: Initialize for tone generation.
|
||||
*
|
||||
* Inputs: audio_config_p - Pointer to audio configuration structure.
|
||||
*
|
||||
* The fields we care about are:
|
||||
*
|
||||
* samples_per_sec
|
||||
*
|
||||
* amp - Signal amplitude on scale of 0 .. 100.
|
||||
*
|
||||
* 100 will produce maximum amplitude of +-32k samples.
|
||||
*
|
||||
* Returns: 0 for success.
|
||||
* -1 for failure.
|
||||
*
|
||||
* Description: Precompute a sine wave table.
|
||||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
|
||||
int morse_init (struct audio_s *audio_config_p, int amp)
|
||||
{
|
||||
int j;
|
||||
|
||||
/*
|
||||
* Save away modem parameters for later use.
|
||||
*/
|
||||
|
||||
save_audio_config_p = audio_config_p;
|
||||
|
||||
for (j=0; j<256; j++) {
|
||||
double a;
|
||||
int s;
|
||||
|
||||
a = ((double)(j) / 256.0) * (2 * M_PI);
|
||||
s = (int) (sin(a) * 32767.0 * amp / 100.0);
|
||||
|
||||
/* 16 bit sound sample is in range of -32768 .. +32767. */
|
||||
assert (s >= -32768 && s <= 32767);
|
||||
sine_table[j] = s;
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
||||
} /* end morse_init */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: morse_send
|
||||
|
@ -159,7 +228,11 @@ int morse_send (int chan, char *str, int wpm, int txdelay, int txtail)
|
|||
int time_units;
|
||||
char *p;
|
||||
|
||||
|
||||
time_units = 0;
|
||||
|
||||
morse_quiet_ms (chan, txdelay);
|
||||
|
||||
for (p = str; *p != '\0'; p++) {
|
||||
int i;
|
||||
|
||||
|
@ -169,37 +242,38 @@ int morse_send (int chan, char *str, int wpm, int txdelay, int txtail)
|
|||
|
||||
for (e = morse[i].enc; *e != '\0'; e++) {
|
||||
if (*e == '.') {
|
||||
morse_tone (1);
|
||||
morse_tone (chan,1,wpm);
|
||||
time_units++;
|
||||
}
|
||||
else {
|
||||
morse_tone (3);
|
||||
morse_tone (chan,3,wpm);
|
||||
time_units += 3;
|
||||
}
|
||||
if (e[1] != '\0') {
|
||||
morse_quiet (1);
|
||||
morse_quiet (chan,1,wpm);
|
||||
time_units++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
morse_quiet (1);
|
||||
morse_quiet (chan,1,wpm);
|
||||
time_units++;
|
||||
}
|
||||
if (p[1] != '\0') {
|
||||
morse_quiet (3);
|
||||
morse_quiet (chan,3,wpm);
|
||||
time_units += 3;
|
||||
}
|
||||
}
|
||||
|
||||
morse_quiet_ms (chan, txtail);
|
||||
|
||||
if (time_units != morse_units_str(str)) {
|
||||
dw_printf ("morse: Internal error. Inconsistent length, %d vs. %d calculated.\n",
|
||||
time_units, morse_units_str(str));
|
||||
}
|
||||
|
||||
|
||||
return (txdelay +
|
||||
TIME_UNITS_TO_MS(time_units, wpm) +
|
||||
(int) (TIME_UNITS_TO_MS(time_units, wpm) + 0.5) +
|
||||
txtail);
|
||||
|
||||
} /* end morse_send */
|
||||
|
@ -212,18 +286,47 @@ int morse_send (int chan, char *str, int wpm, int txdelay, int txtail)
|
|||
*
|
||||
* Purpose: Generate tone for specified number of time units.
|
||||
*
|
||||
* Inputs: tu - Number of time units.
|
||||
* Inputs: chan - Radio channel.
|
||||
* tu - Number of time units. Should be 1 or 3.
|
||||
* wpm - Speed in WPM.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static void morse_tone (int tu) {
|
||||
int num_cycles;
|
||||
int n;
|
||||
static void morse_tone (int chan, int tu, int wpm) {
|
||||
|
||||
#if MTEST1
|
||||
int n;
|
||||
for (n=0; n<tu; n++) {
|
||||
dw_printf ("#");
|
||||
}
|
||||
#else
|
||||
|
||||
int a = ACHAN2ADEV(chan); /* device for channel. */
|
||||
int sam;
|
||||
int nsamples;
|
||||
int j;
|
||||
unsigned int tone_phase; // Phase accumulator for tone generation.
|
||||
// Upper bits are used as index into sine table.
|
||||
|
||||
int f1_change_per_sample; // How much to advance phase for each audio sample.
|
||||
|
||||
assert (save_audio_config_p->achan[chan].valid);
|
||||
|
||||
tone_phase = 0;
|
||||
|
||||
f1_change_per_sample = (int) (((double)MORSE_TONE * TICKS_PER_CYCLE / (double)save_audio_config_p->adev[a].samples_per_sec ) + 0.5);
|
||||
|
||||
nsamples = (int) ((TIME_UNITS_TO_MS(tu,wpm) * (float)save_audio_config_p->adev[a].samples_per_sec / 1000.) + 0.5);
|
||||
|
||||
for (j=0; j<nsamples; j++) {
|
||||
|
||||
tone_phase += f1_change_per_sample;
|
||||
sam = sine_table[(tone_phase >> 24) & 0xff];
|
||||
gen_tone_put_sample (chan, a, sam);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
} /* end morse_tone */
|
||||
|
@ -235,20 +338,75 @@ static void morse_tone (int tu) {
|
|||
*
|
||||
* Purpose: Generate silence for specified number of time units.
|
||||
*
|
||||
* Inputs: tu - Number of time units.
|
||||
* Inputs: chan - Radio channel.
|
||||
* tu - Number of time units.
|
||||
* wpm - Speed in WPM.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static void morse_quiet (int tu) {
|
||||
int n;
|
||||
static void morse_quiet (int chan, int tu, int wpm) {
|
||||
|
||||
|
||||
#if MTEST1
|
||||
int n;
|
||||
for (n=0; n<tu; n++) {
|
||||
dw_printf (".");
|
||||
}
|
||||
#else
|
||||
int a = ACHAN2ADEV(chan); /* device for channel. */
|
||||
int sam = 0;
|
||||
int nsamples;
|
||||
int j;
|
||||
|
||||
assert (save_audio_config_p->achan[chan].valid);
|
||||
|
||||
nsamples = (int) ((TIME_UNITS_TO_MS(tu,wpm) * (float)save_audio_config_p->adev[a].samples_per_sec / 1000.) + 0.5);
|
||||
|
||||
for (j=0; j<nsamples; j++) {
|
||||
|
||||
gen_tone_put_sample (chan, a, sam);
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
} /* end morse_quiet */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: morse_quiet
|
||||
*
|
||||
* Purpose: Generate silence for specified number of milliseconds.
|
||||
* This is used for the txdelay and txtail times.
|
||||
*
|
||||
* Inputs: chan - Radio channel.
|
||||
* tu - Number of time units.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static void morse_quiet_ms (int chan, int ms) {
|
||||
|
||||
#if MTEST1
|
||||
#else
|
||||
int a = ACHAN2ADEV(chan); /* device for channel. */
|
||||
int sam = 0;
|
||||
int nsamples;
|
||||
int j;
|
||||
|
||||
assert (save_audio_config_p->achan[chan].valid);
|
||||
|
||||
nsamples = (int) ((ms * (float)save_audio_config_p->adev[a].samples_per_sec / 1000.) + 0.5);
|
||||
|
||||
for (j=0; j<nsamples; j++) {
|
||||
|
||||
gen_tone_put_sample (chan, a, sam);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} /* end morse_quiet_ms */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
|
@ -346,7 +504,7 @@ static int morse_units_ch (int ch)
|
|||
|
||||
static int morse_units_str (char *str)
|
||||
{
|
||||
int i;
|
||||
//int i;
|
||||
int len;
|
||||
int k;
|
||||
int units;
|
||||
|
@ -362,6 +520,8 @@ static int morse_units_str (char *str)
|
|||
}
|
||||
|
||||
|
||||
#if MTEST1
|
||||
|
||||
int main (int argc, char *argv[]) {
|
||||
|
||||
dw_printf ("CQ DX\n");
|
||||
|
@ -374,6 +534,8 @@ int main (int argc, char *argv[]) {
|
|||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* end morse.c */
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
/* morse.h */
|
||||
|
||||
int morse_init (struct audio_s *audio_config_p, int amp) ;
|
||||
|
||||
int morse_send (int chan, char *str, int wpm, int txdelay, int txtail);
|
||||
|
||||
#define MORSE_DEFAULT_WPM 10
|
||||
|
342
nmea.c
342
nmea.c
|
@ -64,15 +64,7 @@ char *strsep(char **stringp, const char *delim);
|
|||
#include "nmea.h"
|
||||
#include "grm_sym.h" /* Garmin symbols */
|
||||
#include "mgn_icon.h" /* Magellan icons */
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
typedef HANDLE MYFDTYPE;
|
||||
#define MYFDERROR INVALID_HANDLE_VALUE
|
||||
#else
|
||||
typedef int MYFDTYPE;
|
||||
#define MYFDERROR (-1)
|
||||
#endif
|
||||
#include "serial_port.h"
|
||||
|
||||
|
||||
// TODO: receive buffer... static kiss_frame_t kf; /* Accumulated KISS frame and state of decoder. */
|
||||
|
@ -103,7 +95,7 @@ void nmea_set_debug (int n)
|
|||
* Name: nmea_init
|
||||
*
|
||||
* Purpose: Initialization for NMEA communication port.
|
||||
&
|
||||
*
|
||||
* Inputs: mc->nmea_port - name of serial port.
|
||||
*
|
||||
* Global output: nmea_port_fd
|
||||
|
@ -112,15 +104,9 @@ void nmea_set_debug (int n)
|
|||
* Description: (1) Open serial port device.
|
||||
* (2) Start a new thread to listen for GPS receiver.
|
||||
*
|
||||
* Reference: http://www.robbayer.com/files/serial-win.pdf
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
||||
|
||||
static MYFDTYPE nmea_open_port (char *device);
|
||||
|
||||
void nmea_init (struct misc_config_s *mc)
|
||||
{
|
||||
|
||||
|
@ -132,24 +118,12 @@ void nmea_init (struct misc_config_s *mc)
|
|||
|
||||
/*
|
||||
* Open serial port connection.
|
||||
* 4800 baud is standard for GPS.
|
||||
* Should add an option to allow changing someday.
|
||||
*/
|
||||
if (strlen(mc->nmea_port) > 0) {
|
||||
|
||||
#if ! __WIN32__
|
||||
|
||||
/* Translate Windows device name into Linux name. */
|
||||
/* COM1 -> /dev/ttyS0, etc. */
|
||||
|
||||
if (strncasecmp(mc->nmea_port, "COM", 3) == 0) {
|
||||
int n = atoi (mc->nmea_port + 3);
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Converted NMEA device '%s'", mc->nmea_port);
|
||||
if (n < 1) n = 1;
|
||||
sprintf (mc->nmea_port, "/dev/ttyS%d", n-1);
|
||||
dw_printf (" to Linux equivalent '%s'\n", mc->nmea_port);
|
||||
}
|
||||
#endif
|
||||
nmea_port_fd = nmea_open_port (mc->nmea_port);
|
||||
nmea_port_fd = serial_port_open (mc->nmea_port, 4800);
|
||||
|
||||
if (nmea_port_fd != MYFDERROR) {
|
||||
#if __WIN32__
|
||||
|
@ -180,138 +154,6 @@ void nmea_init (struct misc_config_s *mc)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns fd for serial port or MYFDERROR for error.
|
||||
*/
|
||||
|
||||
|
||||
static MYFDTYPE nmea_open_port (char *devicename)
|
||||
{
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
MYFDTYPE fd;
|
||||
DCB dcb;
|
||||
int ok;
|
||||
char bettername[50];
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("nmea_open_port ( '%s' )\n", devicename);
|
||||
#endif
|
||||
|
||||
|
||||
// Need to use FILE_FLAG_OVERLAPPED for full duplex operation.
|
||||
// Without it, write blocks when waiting on read.
|
||||
|
||||
// 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
|
||||
|
||||
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);
|
||||
|
||||
if (fd == MYFDERROR) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR - Could not open NMEA port %s.\n", devicename);
|
||||
return (MYFDERROR);
|
||||
}
|
||||
|
||||
/* Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/aa363201(v=vs.85).aspx */
|
||||
|
||||
memset (&dcb, 0, sizeof(dcb));
|
||||
dcb.DCBlength = sizeof(DCB);
|
||||
|
||||
ok = GetCommState (fd, &dcb);
|
||||
if (! ok) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("nmea_open_port: GetCommState failed.\n");
|
||||
}
|
||||
|
||||
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx */
|
||||
|
||||
dcb.DCBlength = sizeof(DCB);
|
||||
dcb.BaudRate = CBR_4800;
|
||||
dcb.fBinary = 1;
|
||||
dcb.fParity = 0;
|
||||
dcb.fOutxCtsFlow = 0;
|
||||
dcb.fOutxDsrFlow = 0;
|
||||
dcb.fDtrControl = DTR_CONTROL_DISABLE;
|
||||
dcb.fDsrSensitivity = 0;
|
||||
dcb.fOutX = 0;
|
||||
dcb.fInX = 0;
|
||||
dcb.fErrorChar = 0;
|
||||
dcb.fNull = 0; /* Don't drop nul characters! */
|
||||
dcb.fRtsControl = 0;
|
||||
dcb.ByteSize = 8;
|
||||
dcb.Parity = NOPARITY;
|
||||
dcb.StopBits = ONESTOPBIT;
|
||||
|
||||
ok = SetCommState (fd, &dcb);
|
||||
if (! ok) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("nmea_open_port: SetCommState failed.\n");
|
||||
}
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf("NMEA communication started on %s.\n", devicename);
|
||||
|
||||
#else
|
||||
|
||||
/* Linux version. */
|
||||
|
||||
int fd;
|
||||
struct termios ts;
|
||||
int e;
|
||||
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("nmea_open_port ( '%s' )\n", devicename);
|
||||
#endif
|
||||
|
||||
fd = open (devicename, O_RDWR);
|
||||
|
||||
if (fd == MYFDERROR) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR - Could not open NMEA port %s.\n", devicename);
|
||||
return (MYFDERROR);
|
||||
}
|
||||
|
||||
e = tcgetattr (fd, &ts);
|
||||
if (e != 0) { perror ("nm tcgetattr"); }
|
||||
|
||||
cfmakeraw (&ts);
|
||||
|
||||
ts.c_cc[VMIN] = 1; /* wait for at least one character */
|
||||
ts.c_cc[VTIME] = 0; /* no fancy timing. */
|
||||
|
||||
cfsetispeed (&ts, B4800);
|
||||
cfsetospeed (&ts, B4800);
|
||||
|
||||
e = tcsetattr (fd, TCSANOW, &ts);
|
||||
if (e != 0) { perror ("nmea tcsetattr"); }
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf("NMEA communication started on %s.\n", devicename);
|
||||
|
||||
#endif
|
||||
|
||||
return (fd);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
|
@ -575,7 +417,7 @@ https://freepository.com:444/50lItuLQ7fW6s-web/browser/Tracker2/trunk/sources/wa
|
|||
|
||||
|
||||
|
||||
Data Transmission Protocol For Magellan Products – version 2.11
|
||||
Data Transmission Protocol For Magellan Products - version 2.11
|
||||
|
||||
|
||||
|
||||
|
@ -637,45 +479,7 @@ static void nmea_send_sentence (char *sent)
|
|||
|
||||
// TODO: need to append CR LF.
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
DWORD nwritten;
|
||||
|
||||
/* Without this, write blocks while we are waiting on a read. */
|
||||
static OVERLAPPED ov_wr;
|
||||
memset (&ov_wr, 0, sizeof(ov_wr));
|
||||
|
||||
if ( ! WriteFile (nmea_port_fd, sent, len, &nwritten, &ov_wr))
|
||||
{
|
||||
err = GetLastError();
|
||||
if (err != ERROR_IO_PENDING)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nError sending NMEA sentence. Error %d.\n\n", (int)GetLastError());
|
||||
//CloseHandle (nmea_port_fd);
|
||||
//nmea_port_fd = MYFDERROR;
|
||||
}
|
||||
}
|
||||
else if (nwritten != len)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nError sending NMEA sentence. Only %d of %d written.\n\n", (int)nwritten, len);
|
||||
//CloseHandle (nmea_port_fd);
|
||||
//nmea_port_fd = MYFDERROR;
|
||||
}
|
||||
|
||||
#else
|
||||
err = write (nmea_port_fd, sent, (size_t)len);
|
||||
if (err != len)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nError sending NMEA sentence. err=%d\n\n", err);
|
||||
//close (nmea_port_fd);
|
||||
//nmea_port_fd = MYFDERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
serial_port_write (nmea_port_fd, sent, len);
|
||||
|
||||
} /* nmea_send_sentence */
|
||||
|
||||
|
@ -695,109 +499,6 @@ static void nmea_send_sentence (char *sent)
|
|||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
//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. */
|
||||
|
||||
|
||||
static int nmea_get (MYFDTYPE fd)
|
||||
{
|
||||
unsigned char ch;
|
||||
|
||||
#if __WIN32__ /* Native Windows version. */
|
||||
|
||||
DWORD n;
|
||||
static OVERLAPPED ov_rd;
|
||||
|
||||
memset (&ov_rd, 0, sizeof(ov_rd));
|
||||
ov_rd.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
|
||||
|
||||
|
||||
/* Overlapped I/O makes reading rather complicated. */
|
||||
/* See: http://msdn.microsoft.com/en-us/library/ms810467.aspx */
|
||||
|
||||
/* It seems that the read completes OK with a count */
|
||||
/* of 0 every time we send a message to the serial port. */
|
||||
|
||||
n = 0; /* Number of characters read. */
|
||||
|
||||
while (n == 0) {
|
||||
|
||||
if ( ! ReadFile (fd, &ch, 1, &n, &ov_rd))
|
||||
{
|
||||
int err1 = GetLastError();
|
||||
|
||||
if (err1 == ERROR_IO_PENDING)
|
||||
{
|
||||
/* Wait for completion. */
|
||||
|
||||
if (WaitForSingleObject (ov_rd.hEvent, INFINITE) == WAIT_OBJECT_0)
|
||||
{
|
||||
if ( ! GetOverlappedResult (fd, &ov_rd, &n, 1))
|
||||
{
|
||||
int err3 = GetLastError();
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nKISS GetOverlappedResult error %d.\n\n", err3);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Success! n should be 1 */
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nKISS ReadFile error %d. Closing connection.\n\n", err1);
|
||||
//CloseHandle (fd);
|
||||
//fd = MYFDERROR;
|
||||
//pthread_exit (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
} /* end while n==0 */
|
||||
|
||||
CloseHandle(ov_rd.hEvent);
|
||||
|
||||
if (n != 1) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nNMEA failed to get one byte. n=%d.\n\n", (int)n);
|
||||
|
||||
#if DEBUG9
|
||||
fprintf (log_fp, "n=%d\n", n);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#else /* Linux version */
|
||||
|
||||
int n;
|
||||
|
||||
n = read(fd, &ch, (size_t)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);
|
||||
|
||||
fd = MYFDERROR;
|
||||
pthread_exit (NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if DEBUGx
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("nmea_get(%d) returns 0x%02x\n", fd, ch);
|
||||
#endif
|
||||
|
||||
return (ch);
|
||||
}
|
||||
|
||||
|
||||
// Maximum length of message from GPS receiver.
|
||||
// 82 according to some people. Larger to be safe.
|
||||
|
@ -823,9 +524,25 @@ static void * nmea_listen_thread (void *arg)
|
|||
|
||||
|
||||
while (1) {
|
||||
unsigned char ch;
|
||||
int ch;
|
||||
|
||||
ch = serial_port_get1(fd);
|
||||
|
||||
|
||||
// TODO: if ch < 0, terminate thread ...
|
||||
|
||||
//CloseHandle (fd);
|
||||
//fd = MYFDERROR;
|
||||
//pthread_exit (NULL);
|
||||
|
||||
//text_color_set(DW_COLOR_ERROR);
|
||||
//dw_printf ("\nError trying to read from GPS receiver. Closing connection %d.\n\n", fd);
|
||||
|
||||
//close (fd); -> serial_port_close(fd)
|
||||
|
||||
//fd = MYFDERROR;
|
||||
//pthread_exit (NULL);
|
||||
|
||||
ch = nmea_get(fd);
|
||||
|
||||
if (ch == '$') {
|
||||
// Start of new sentence.
|
||||
|
@ -982,7 +699,14 @@ static void nmea_parse_gps (char *sentence)
|
|||
|
||||
text_color_set (DW_COLOR_INFO);
|
||||
dw_printf("%d %.6f %.6f %.1f %.0f %.1f\n", fix, g_lat, g_lon, g_speed, g_course, g_alt);
|
||||
|
||||
|
||||
#if WALK96
|
||||
// TODO: Need to design a proper interface.
|
||||
|
||||
extern void walk96 (int fix, double lat, double lon, float knots, float course, float alt);
|
||||
|
||||
walk96 (fix, g_lat, g_lon, g_speed, g_course, g_alt);
|
||||
#endif
|
||||
}
|
||||
|
||||
// $GPGGA has altitude.
|
||||
|
|
75
pfilter.c
75
pfilter.c
|
@ -133,7 +133,7 @@ static int filt_s (pfstate_t *pf);
|
|||
* Both are 0 .. MAX_CHANS-1 or MAX_CHANS for IGate.
|
||||
* For debug/error messages only.
|
||||
*
|
||||
* filter - Filter specs and logical operators to combine them.
|
||||
* filter - String of filter specs and logical operators to combine them.
|
||||
*
|
||||
* pp - Packet object handle.
|
||||
*
|
||||
|
@ -152,12 +152,28 @@ int pfilter (int from_chan, int to_chan, char *filter, packet_t pp)
|
|||
char *p;
|
||||
int result;
|
||||
|
||||
assert (from_chan >= 0 && from_chan <= MAX_CHANS);
|
||||
assert (to_chan >= 0 && to_chan <= MAX_CHANS);
|
||||
|
||||
if (pp == NULL) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("INTERNAL ERROR in pfilter: NULL packet pointer. Please report this!\n");
|
||||
return (-1);
|
||||
}
|
||||
if (filter == NULL) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("INTERNAL ERROR in pfilter: NULL filter string pointer. Please report this!\n");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
pfstate.from_chan = from_chan;
|
||||
pfstate.to_chan = to_chan;
|
||||
|
||||
/* Copy filter string, removing any control characters. */
|
||||
|
||||
memset (pfstate.filter_str, 0, sizeof(pfstate.filter_str));
|
||||
strncpy (pfstate.filter_str, filter, MAX_FILTER_LEN-1);
|
||||
pfstate.filter_str[MAX_FILTER_LEN-1] = '\0';
|
||||
|
||||
pfstate.nexti = 0;
|
||||
for (p = pfstate.filter_str; *p != '\0'; p++) {
|
||||
if (iscntrl(*p)) {
|
||||
|
@ -206,11 +222,11 @@ int pfilter (int from_chan, int to_chan, char *filter, packet_t pp)
|
|||
* Note that a filter-spec must be followed by space or
|
||||
* end of line. This is so the magic characters can appear in one.
|
||||
*
|
||||
* Future: Allow words like 'OR' as alternatives to symbols like '|'.
|
||||
* Future: Maybe allow words like 'OR' as alternatives to symbols like '|'.
|
||||
*
|
||||
* Unresolved Issue:
|
||||
*
|
||||
* Adding the special operattors adds a new complication.
|
||||
* Adding the special operators adds a new complication.
|
||||
* How do we handle the case where we want those characters in
|
||||
* a filter specification? For example how do we know if the
|
||||
* last character of /#& means HF gateway or AND the next part
|
||||
|
@ -242,32 +258,32 @@ static void next_token (pfstate_t *pf)
|
|||
|
||||
if (pf->filter_str[pf->nexti] == '\0') {
|
||||
pf->token_type = TOKEN_EOL;
|
||||
strcpy (pf->token_str, "end-of-line");
|
||||
strlcpy (pf->token_str, "end-of-line", sizeof(pf->token_str));
|
||||
}
|
||||
else if (pf->filter_str[pf->nexti] == '&') {
|
||||
pf->nexti++;
|
||||
pf->token_type = TOKEN_AND;
|
||||
strcpy (pf->token_str, "\"&\"");
|
||||
strlcpy (pf->token_str, "\"&\"", sizeof(pf->token_str));
|
||||
}
|
||||
else if (pf->filter_str[pf->nexti] == '|') {
|
||||
pf->nexti++;
|
||||
pf->token_type = TOKEN_OR;
|
||||
strcpy (pf->token_str, "\"|\"");
|
||||
strlcpy (pf->token_str, "\"|\"", sizeof(pf->token_str));
|
||||
}
|
||||
else if (pf->filter_str[pf->nexti] == '!') {
|
||||
pf->nexti++;
|
||||
pf->token_type = TOKEN_NOT;
|
||||
strcpy (pf->token_str, "\"!\"");
|
||||
strlcpy (pf->token_str, "\"!\"", sizeof(pf->token_str));
|
||||
}
|
||||
else if (pf->filter_str[pf->nexti] == '(') {
|
||||
pf->nexti++;
|
||||
pf->token_type = TOKEN_LPAREN;
|
||||
strcpy (pf->token_str, "\"(\"");
|
||||
strlcpy (pf->token_str, "\"(\"", sizeof(pf->token_str));
|
||||
}
|
||||
else if (pf->filter_str[pf->nexti] == ')') {
|
||||
pf->nexti++;
|
||||
pf->token_type = TOKEN_RPAREN;
|
||||
strcpy (pf->token_str, "\")\"");
|
||||
strlcpy (pf->token_str, "\")\"", sizeof(pf->token_str));
|
||||
}
|
||||
else {
|
||||
char *p = pf->token_str;
|
||||
|
@ -488,7 +504,7 @@ static int parse_filter_spec (pfstate_t *pf)
|
|||
|
||||
else {
|
||||
char stemp[80];
|
||||
sprintf (stemp, "Unrecognized filter type '%c'", pf->token_str[0]);
|
||||
snprintf (stemp, sizeof(stemp), "Unrecognized filter type '%c'", pf->token_str[0]);
|
||||
print_error (pf, stemp);
|
||||
result = -1;
|
||||
}
|
||||
|
@ -535,7 +551,7 @@ static int filt_bodgu (pfstate_t *pf, char *arg)
|
|||
char *v;
|
||||
int result = 0;
|
||||
|
||||
strcpy (str, pf->token_str);
|
||||
strlcpy (str, pf->token_str, sizeof(str));
|
||||
sep[0] = str[1];
|
||||
sep[1] = '\0';
|
||||
cp = str + 2;
|
||||
|
@ -590,13 +606,16 @@ static int filt_bodgu (pfstate_t *pf, char *arg)
|
|||
|
||||
static int filt_t (pfstate_t *pf)
|
||||
{
|
||||
char src[MAX_TOKEN_LEN];
|
||||
char *infop;
|
||||
char src[AX25_MAX_ADDR_LEN];
|
||||
char *infop = NULL;
|
||||
char *f;
|
||||
|
||||
memset (src, 0, sizeof(src));
|
||||
ax25_get_addr_with_ssid (pf->pp, AX25_SOURCE, src);
|
||||
(void) ax25_get_info (pf->pp, (unsigned char **)(&infop));
|
||||
|
||||
assert (infop != NULL);
|
||||
|
||||
for (f = pf->token_str + 2; *f != '\0'; f++) {
|
||||
switch (*f) {
|
||||
|
||||
|
@ -678,6 +697,12 @@ static int filt_t (pfstate_t *pf)
|
|||
infop[2] == src[1] &&
|
||||
infop[3] == src[2]) return (1);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
print_error (pf, "Invalid letter in t/ filter.\n");
|
||||
return (-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (0); /* Didn't match anything. Reject */
|
||||
|
@ -690,7 +715,7 @@ static int filt_t (pfstate_t *pf)
|
|||
*
|
||||
* Name: filt_r
|
||||
*
|
||||
* Purpose: Is it in range of given location.
|
||||
* Purpose: Is it in range (kilometers) of given location.
|
||||
*
|
||||
* Inputs: pf - Pointer to current state information.
|
||||
* token_str should contain something of format:
|
||||
|
@ -718,7 +743,7 @@ static int filt_r (pfstate_t *pf)
|
|||
double dlat, dlon, ddist, km;
|
||||
|
||||
|
||||
strcpy (str, pf->token_str);
|
||||
strlcpy (str, pf->token_str, sizeof(str));
|
||||
sep[0] = str[1];
|
||||
sep[1] = '\0';
|
||||
cp = str + 2;
|
||||
|
@ -781,9 +806,9 @@ static int filt_r (pfstate_t *pf)
|
|||
*
|
||||
* Description:
|
||||
*
|
||||
* “pri” is zero or more symbols from the primary symbol set.
|
||||
* “alt” is one or more symbols from the alternate symbol set.
|
||||
* “over” is overlay characters. Overlays apply only to the alternate symbol set.
|
||||
* "pri" is zero or more symbols from the primary symbol set.
|
||||
* "alt" is one or more symbols from the alternate symbol set.
|
||||
* "over" is overlay characters. Overlays apply only to the alternate symbol set.
|
||||
*
|
||||
* Examples:
|
||||
* s/-> Allow house and car from primary symbol table.
|
||||
|
@ -801,7 +826,7 @@ static int filt_s (pfstate_t *pf)
|
|||
char *pri, *alt, *over;
|
||||
|
||||
|
||||
strcpy (str, pf->token_str);
|
||||
strlcpy (str, pf->token_str, sizeof(str));
|
||||
sep[0] = str[1];
|
||||
sep[1] = '\0';
|
||||
cp = str + 2;
|
||||
|
@ -869,19 +894,19 @@ static void print_error (pfstate_t *pf, char *msg)
|
|||
if (pf->from_chan == MAX_CHANS) {
|
||||
|
||||
if (pf->to_chan == MAX_CHANS) {
|
||||
sprintf (intro, "filter[IG,IG]: ");
|
||||
snprintf (intro, sizeof(intro), "filter[IG,IG]: ");
|
||||
}
|
||||
else {
|
||||
sprintf (intro, "filter[IG,%d]: ", pf->to_chan);
|
||||
snprintf (intro, sizeof(intro), "filter[IG,%d]: ", pf->to_chan);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
if (pf->to_chan == MAX_CHANS) {
|
||||
sprintf (intro, "filter[%d,IG]: ", pf->from_chan);
|
||||
snprintf (intro, sizeof(intro), "filter[%d,IG]: ", pf->from_chan);
|
||||
}
|
||||
else {
|
||||
sprintf (intro, "filter[%d,%d]: ", pf->from_chan, pf->to_chan);
|
||||
snprintf (intro, sizeof(intro), "filter[%d,%d]: ", pf->from_chan, pf->to_chan);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -903,7 +928,7 @@ static void print_error (pfstate_t *pf, char *msg)
|
|||
*
|
||||
* Purpose: Unit test for packet filtering.
|
||||
*
|
||||
* Usage: gcc -Wall -o pftest -DTEST pfilter.c ax25_pad.o textcolor.o fcs_calc.o decode_aprs.o latlong.o symbols.o telemetry.o misc.a regex.a && ./pftest
|
||||
* Usage: gcc -Wall -o pftest -DTEST pfilter.c ax25_pad.o textcolor.o fcs_calc.o decode_aprs.o latlong.o symbols.o telemetry.o tt_text.c misc.a regex.a && ./pftest
|
||||
*
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
|
6
ptt.c
6
ptt.c
|
@ -35,6 +35,12 @@
|
|||
*
|
||||
* Version 1.1: Add parallel printer port for x86 Linux only.
|
||||
*
|
||||
* This is hardcoded to use the primary motherboard parallel
|
||||
* printer port at I/O address 0x378. This might work with
|
||||
* a PCI card configured to use the same address if the
|
||||
* motherboard does not have a built in parallel port.
|
||||
* It won't work with a USB-to-parallel-printer-port adapter.
|
||||
*
|
||||
* Version 1.2: More than two radio channels.
|
||||
* Generalize for additional signals besides PTT.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,451 @@
|
|||
|
||||
// TODO: Needs more clean up and testing of error conditions.
|
||||
|
||||
// TODO: use this in place of other similar code.
|
||||
|
||||
|
||||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2014, 2015 John Langner, WB2OSZ
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// 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/>.
|
||||
//
|
||||
|
||||
|
||||
//#define DEBUG 1
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Module: serial.c
|
||||
*
|
||||
* Purpose: Interface to serial port, hiding operating system differences.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
|
||||
#else
|
||||
|
||||
#define __USE_XOPEN2KXSI 1
|
||||
#define __USE_XOPEN 1
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/errno.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "textcolor.h"
|
||||
#include "serial_port.h"
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: serial_port_open
|
||||
*
|
||||
* Purpose: Open serial port.
|
||||
*
|
||||
* Inputs: devicename - For Windows, usually like COM5.
|
||||
* For Linux, usually /dev/tty...
|
||||
* "COMn" also allowed and converted to /dev/ttyS(n-1)
|
||||
*
|
||||
* baud - Speed. 4800, 9600, etc.
|
||||
*
|
||||
* Returns Handle for serial port or MYFDERROR for error.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
|
||||
MYFDTYPE serial_port_open (char *devicename, int baud)
|
||||
{
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
MYFDTYPE fd;
|
||||
DCB dcb;
|
||||
int ok;
|
||||
char bettername[50];
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("serial_port_open ( '%s', %d )\n", devicename, baud);
|
||||
#endif
|
||||
|
||||
|
||||
// Reference: http://www.robbayer.com/files/serial-win.pdf
|
||||
|
||||
// Need to use FILE_FLAG_OVERLAPPED for full duplex operation.
|
||||
// Without it, write blocks when waiting on read.
|
||||
|
||||
// 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
|
||||
|
||||
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);
|
||||
|
||||
if (fd == MYFDERROR) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR - Could not open serial port %s.\n", devicename);
|
||||
return (MYFDERROR);
|
||||
}
|
||||
|
||||
/* Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/aa363201(v=vs.85).aspx */
|
||||
|
||||
memset (&dcb, 0, sizeof(dcb));
|
||||
dcb.DCBlength = sizeof(DCB);
|
||||
|
||||
ok = GetCommState (fd, &dcb);
|
||||
if (! ok) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("serial_port_open: GetCommState failed.\n");
|
||||
}
|
||||
|
||||
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx */
|
||||
|
||||
dcb.DCBlength = sizeof(DCB);
|
||||
|
||||
switch (baud) {
|
||||
|
||||
case 1200: dcb.BaudRate = CBR_1200; break;
|
||||
case 2400: dcb.BaudRate = CBR_2400; break;
|
||||
case 4800: dcb.BaudRate = CBR_4800; break;
|
||||
case 9600: dcb.BaudRate = CBR_9600; break;
|
||||
case 19200: dcb.BaudRate = CBR_19200; break;
|
||||
case 38400: dcb.BaudRate = CBR_38400; break;
|
||||
case 57600: dcb.BaudRate = CBR_57600; break;
|
||||
case 115200: dcb.BaudRate = CBR_115200; break;
|
||||
|
||||
default: text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("serial_port_open: Unsupported speed %d. Using 4800.\n", baud);
|
||||
dcb.BaudRate = CBR_4800;
|
||||
break;
|
||||
}
|
||||
|
||||
dcb.fBinary = 1;
|
||||
dcb.fParity = 0;
|
||||
dcb.fOutxCtsFlow = 0;
|
||||
dcb.fOutxDsrFlow = 0;
|
||||
dcb.fDtrControl = DTR_CONTROL_DISABLE;
|
||||
dcb.fDsrSensitivity = 0;
|
||||
dcb.fOutX = 0;
|
||||
dcb.fInX = 0;
|
||||
dcb.fErrorChar = 0;
|
||||
dcb.fNull = 0; /* Don't drop nul characters! */
|
||||
dcb.fRtsControl = 0;
|
||||
dcb.ByteSize = 8;
|
||||
dcb.Parity = NOPARITY;
|
||||
dcb.StopBits = ONESTOPBIT;
|
||||
|
||||
ok = SetCommState (fd, &dcb);
|
||||
if (! ok) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("serial_port_open: SetCommState failed.\n");
|
||||
}
|
||||
|
||||
//text_color_set(DW_COLOR_INFO);
|
||||
//dw_printf("Successful serial port open on %s.\n", devicename);
|
||||
|
||||
#else
|
||||
|
||||
/* Linux version. */
|
||||
|
||||
int fd;
|
||||
struct termios ts;
|
||||
int e;
|
||||
char linuxname[50];
|
||||
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("serial_port_open ( '%s' )\n", devicename);
|
||||
#endif
|
||||
|
||||
/* Translate Windows device name into Linux name. */
|
||||
/* COM1 -> /dev/ttyS0, etc. */
|
||||
|
||||
strcpy (linuxname, devicename);
|
||||
|
||||
if (strncasecmp(devicename, "COM", 3) == 0) {
|
||||
int n = atoi (devicename + 3);
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Converted serial port name '%s'", devicename);
|
||||
if (n < 1) n = 1;
|
||||
sprintf (linuxname, "/dev/ttyS%d", n-1);
|
||||
dw_printf (" to Linux equivalent '%s'\n", linuxname);
|
||||
}
|
||||
|
||||
fd = open (linuxname, O_RDWR);
|
||||
|
||||
if (fd == MYFDERROR) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR - Could not open serial port %s.\n", linuxname);
|
||||
return (MYFDERROR);
|
||||
}
|
||||
|
||||
e = tcgetattr (fd, &ts);
|
||||
if (e != 0) { perror ("tcgetattr"); }
|
||||
|
||||
cfmakeraw (&ts);
|
||||
|
||||
ts.c_cc[VMIN] = 1; /* wait for at least one character */
|
||||
ts.c_cc[VTIME] = 0; /* no fancy timing. */
|
||||
|
||||
switch (baud) {
|
||||
|
||||
case 1200: cfsetispeed (&ts, B1200); cfsetospeed (&ts, B1200); break;
|
||||
case 2400: cfsetispeed (&ts, B2400); cfsetospeed (&ts, B2400); break;
|
||||
case 4800: cfsetispeed (&ts, B4800); cfsetospeed (&ts, B4800); break;
|
||||
case 9600: cfsetispeed (&ts, B9600); cfsetospeed (&ts, B9600); break;
|
||||
case 19200: cfsetispeed (&ts, B19200); cfsetospeed (&ts, B19200); break;
|
||||
case 38400: cfsetispeed (&ts, B38400); cfsetospeed (&ts, B38400); break;
|
||||
case 57600: cfsetispeed (&ts, B57600); cfsetospeed (&ts, B57600); break;
|
||||
case 115200: cfsetispeed (&ts, B115200); cfsetospeed (&ts, B115200); break;
|
||||
|
||||
default: text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("serial_port_open: Unsupported speed %d. Using 4800.\n", baud);
|
||||
cfsetispeed (&ts, B4800); cfsetospeed (&ts, B4800);
|
||||
break;
|
||||
}
|
||||
|
||||
e = tcsetattr (fd, TCSANOW, &ts);
|
||||
if (e != 0) { perror ("tcsetattr"); }
|
||||
|
||||
//text_color_set(DW_COLOR_INFO);
|
||||
//dw_printf("Successfully opened serial port %s.\n", devicename);
|
||||
|
||||
#endif
|
||||
|
||||
return (fd);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: serial_port_write
|
||||
*
|
||||
* Purpose: Send characters to serial port.
|
||||
*
|
||||
* Inputs: fd - Handle from open.
|
||||
* str - Pointer to array of bytes.
|
||||
* len - Number of bytes to write.
|
||||
*
|
||||
* Returns Number of bytes written. Should be the same as len.
|
||||
* -1 if error.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
|
||||
int serial_port_write (MYFDTYPE fd, char *str, int len)
|
||||
{
|
||||
|
||||
if (fd == MYFDERROR) {
|
||||
return (-1);
|
||||
}
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
DWORD nwritten;
|
||||
|
||||
/* Without this, write blocks while we are waiting on a read. */
|
||||
static OVERLAPPED ov_wr;
|
||||
memset (&ov_wr, 0, sizeof(ov_wr));
|
||||
|
||||
if ( ! WriteFile (fd, str, len, &nwritten, &ov_wr))
|
||||
{
|
||||
int err = GetLastError();
|
||||
|
||||
if (err != ERROR_IO_PENDING)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Error writing to serial port. Error %d.\n\n", err);
|
||||
}
|
||||
}
|
||||
else if (nwritten != len)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Error writing to serial port. Only %d of %d written.\n\n", (int)nwritten, len);
|
||||
}
|
||||
|
||||
return (nwritten);
|
||||
|
||||
#else
|
||||
int written;
|
||||
|
||||
written = write (fd, str, (size_t)len);
|
||||
if (written != len)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Error writing to serial port. err=%d\n\n", written);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (written);
|
||||
#endif
|
||||
|
||||
|
||||
} /* serial_port_write */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: serial_port_get1
|
||||
*
|
||||
* Purpose: Get one byte from the serial port. Wait if not ready.
|
||||
*
|
||||
* Inputs: fd - Handle from open.
|
||||
*
|
||||
* Returns: Value of byte in range of 0 to 255.
|
||||
* -1 if error.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
int serial_port_get1 (MYFDTYPE fd)
|
||||
{
|
||||
unsigned char ch;
|
||||
|
||||
#if __WIN32__ /* Native Windows version. */
|
||||
|
||||
DWORD n;
|
||||
static OVERLAPPED ov_rd;
|
||||
|
||||
memset (&ov_rd, 0, sizeof(ov_rd));
|
||||
ov_rd.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
|
||||
|
||||
|
||||
/* Overlapped I/O makes reading rather complicated. */
|
||||
/* See: http://msdn.microsoft.com/en-us/library/ms810467.aspx */
|
||||
|
||||
/* It seems that the read completes OK with a count */
|
||||
/* of 0 every time we send a message to the serial port. */
|
||||
|
||||
n = 0; /* Number of characters read. */
|
||||
|
||||
while (n == 0) {
|
||||
|
||||
if ( ! ReadFile (fd, &ch, 1, &n, &ov_rd))
|
||||
{
|
||||
int err1 = GetLastError();
|
||||
|
||||
if (err1 == ERROR_IO_PENDING)
|
||||
{
|
||||
/* Wait for completion. */
|
||||
|
||||
if (WaitForSingleObject (ov_rd.hEvent, INFINITE) == WAIT_OBJECT_0)
|
||||
{
|
||||
if ( ! GetOverlappedResult (fd, &ov_rd, &n, 1))
|
||||
{
|
||||
int err3 = GetLastError();
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Serial Port GetOverlappedResult error %d.\n\n", err3);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Success! n should be 1 */
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Serial port read error %d.\n", err1);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
} /* end while n==0 */
|
||||
|
||||
CloseHandle(ov_rd.hEvent);
|
||||
|
||||
if (n != 1) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Serial port failed to get one byte. n=%d.\n\n", (int)n);
|
||||
}
|
||||
|
||||
|
||||
#else /* Linux version */
|
||||
|
||||
int n;
|
||||
|
||||
n = read(fd, &ch, (size_t)1);
|
||||
|
||||
if (n != 1) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("serial_port_get1(%d) returns -1 for error.\n", fd);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if DEBUGx
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
if (isprint(ch)) {
|
||||
dw_printf ("serial_port_get1(%d) returns 0x%02x = '%c'\n", fd, ch, ch);
|
||||
}
|
||||
else {
|
||||
dw_printf ("serial_port_get1(%d) returns 0x%02x\n", fd, ch);
|
||||
}
|
||||
#endif
|
||||
|
||||
return (ch);
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: serial_port_close
|
||||
*
|
||||
* Purpose: Close the device.
|
||||
*
|
||||
* Inputs: fd - Handle from open.
|
||||
*
|
||||
* Returns: None.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
// TODO:
|
||||
|
||||
|
||||
/* end serial_port.c */
|
|
@ -0,0 +1,27 @@
|
|||
/* serial_port.h */
|
||||
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
|
||||
typedef HANDLE MYFDTYPE;
|
||||
#define MYFDERROR INVALID_HANDLE_VALUE
|
||||
|
||||
#else
|
||||
|
||||
typedef int MYFDTYPE;
|
||||
#define MYFDERROR (-1)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
extern MYFDTYPE serial_port_open (char *devicename, int baud);
|
||||
|
||||
extern int serial_port_write (MYFDTYPE fd, char *str, int len);
|
||||
|
||||
extern int serial_port_get1 (MYFDTYPE fd);
|
||||
|
||||
extern void serial_port_close (MYFDTYPE fd);
|
122
server.c
122
server.c
|
@ -256,52 +256,52 @@ static void debug_print (fromto_t fromto, int client, struct agwpe_s *pmsg, int
|
|||
switch (fromto) {
|
||||
|
||||
case FROM_CLIENT:
|
||||
strcpy (direction, "from"); /* from the client application */
|
||||
strlcpy (direction, "from", sizeof(direction)); /* from the client application */
|
||||
|
||||
switch (pmsg->kind_lo) {
|
||||
case 'P': strcpy (datakind, "Application Login"); break;
|
||||
case 'X': strcpy (datakind, "Register CallSign"); break;
|
||||
case 'x': strcpy (datakind, "Unregister CallSign"); break;
|
||||
case 'G': strcpy (datakind, "Ask Port Information"); break;
|
||||
case 'm': strcpy (datakind, "Enable Reception of Monitoring Frames"); break;
|
||||
case 'R': strcpy (datakind, "AGWPE Version Info"); break;
|
||||
case 'g': strcpy (datakind, "Ask Port Capabilities"); break;
|
||||
case 'H': strcpy (datakind, "Callsign Heard on a Port"); break;
|
||||
case 'y': strcpy (datakind, "Ask Outstanding frames waiting on a Port"); break;
|
||||
case 'Y': strcpy (datakind, "Ask Outstanding frames waiting for a connection"); break;
|
||||
case 'M': strcpy (datakind, "Send UNPROTO Information"); break;
|
||||
case 'C': strcpy (datakind, "Connect, Start an AX.25 Connection"); break;
|
||||
case 'D': strcpy (datakind, "Send Connected Data"); break;
|
||||
case 'd': strcpy (datakind, "Disconnect, Terminate an AX.25 Connection"); break;
|
||||
case 'v': strcpy (datakind, "Connect VIA, Start an AX.25 circuit thru digipeaters"); break;
|
||||
case 'V': strcpy (datakind, "Send UNPROTO VIA"); break;
|
||||
case 'c': strcpy (datakind, "Non-Standard Connections, Connection with PID"); break;
|
||||
case 'K': strcpy (datakind, "Send data in raw AX.25 format"); break;
|
||||
case 'k': strcpy (datakind, "Activate reception of Frames in raw format"); break;
|
||||
default: strcpy (datakind, "**INVALID**"); break;
|
||||
case 'P': strlcpy (datakind, "Application Login", sizeof(datakind)); break;
|
||||
case 'X': strlcpy (datakind, "Register CallSign", sizeof(datakind)); break;
|
||||
case 'x': strlcpy (datakind, "Unregister CallSign", sizeof(datakind)); break;
|
||||
case 'G': strlcpy (datakind, "Ask Port Information", sizeof(datakind)); break;
|
||||
case 'm': strlcpy (datakind, "Enable Reception of Monitoring Frames", sizeof(datakind)); break;
|
||||
case 'R': strlcpy (datakind, "AGWPE Version Info", sizeof(datakind)); break;
|
||||
case 'g': strlcpy (datakind, "Ask Port Capabilities", sizeof(datakind)); break;
|
||||
case 'H': strlcpy (datakind, "Callsign Heard on a Port", sizeof(datakind)); break;
|
||||
case 'y': strlcpy (datakind, "Ask Outstanding frames waiting on a Port", sizeof(datakind)); break;
|
||||
case 'Y': strlcpy (datakind, "Ask Outstanding frames waiting for a connection", sizeof(datakind)); break;
|
||||
case 'M': strlcpy (datakind, "Send UNPROTO Information", sizeof(datakind)); break;
|
||||
case 'C': strlcpy (datakind, "Connect, Start an AX.25 Connection", sizeof(datakind)); break;
|
||||
case 'D': strlcpy (datakind, "Send Connected Data", sizeof(datakind)); break;
|
||||
case 'd': strlcpy (datakind, "Disconnect, Terminate an AX.25 Connection", sizeof(datakind)); break;
|
||||
case 'v': strlcpy (datakind, "Connect VIA, Start an AX.25 circuit thru digipeaters", sizeof(datakind)); break;
|
||||
case 'V': strlcpy (datakind, "Send UNPROTO VIA", sizeof(datakind)); break;
|
||||
case 'c': strlcpy (datakind, "Non-Standard Connections, Connection with PID", sizeof(datakind)); break;
|
||||
case 'K': strlcpy (datakind, "Send data in raw AX.25 format", sizeof(datakind)); break;
|
||||
case 'k': strlcpy (datakind, "Activate reception of Frames in raw format", sizeof(datakind)); break;
|
||||
default: strlcpy (datakind, "**INVALID**", sizeof(datakind)); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case TO_CLIENT:
|
||||
default:
|
||||
strcpy (direction, "to"); /* sent to the client application. */
|
||||
strlcpy (direction, "to", sizeof(direction)); /* sent to the client application. */
|
||||
|
||||
switch (pmsg->kind_lo) {
|
||||
case 'R': strcpy (datakind, "Version Number"); break;
|
||||
case 'X': strcpy (datakind, "Callsign Registration"); break;
|
||||
case 'G': strcpy (datakind, "Port Information"); break;
|
||||
case 'g': strcpy (datakind, "Capabilities of a Port"); break;
|
||||
case 'y': strcpy (datakind, "Frames Outstanding on a Port"); break;
|
||||
case 'Y': strcpy (datakind, "Frames Outstanding on a Connection"); break;
|
||||
case 'H': strcpy (datakind, "Heard Stations on a Port"); break;
|
||||
case 'C': strcpy (datakind, "AX.25 Connection Received"); break;
|
||||
case 'D': strcpy (datakind, "Connected AX.25 Data"); break;
|
||||
case 'M': strcpy (datakind, "Monitored Connected Information"); break;
|
||||
case 'S': strcpy (datakind, "Monitored Supervisory Information"); break;
|
||||
case 'U': strcpy (datakind, "Monitored Unproto Information"); break;
|
||||
case 'T': strcpy (datakind, "Monitoring Own Information"); break;
|
||||
case 'K': strcpy (datakind, "Monitored Information in Raw Format"); break;
|
||||
default: strcpy (datakind, "**INVALID**"); break;
|
||||
case 'R': strlcpy (datakind, "Version Number", sizeof(datakind)); break;
|
||||
case 'X': strlcpy (datakind, "Callsign Registration", sizeof(datakind)); break;
|
||||
case 'G': strlcpy (datakind, "Port Information", sizeof(datakind)); break;
|
||||
case 'g': strlcpy (datakind, "Capabilities of a Port", sizeof(datakind)); break;
|
||||
case 'y': strlcpy (datakind, "Frames Outstanding on a Port", sizeof(datakind)); break;
|
||||
case 'Y': strlcpy (datakind, "Frames Outstanding on a Connection", sizeof(datakind)); break;
|
||||
case 'H': strlcpy (datakind, "Heard Stations on a Port", sizeof(datakind)); break;
|
||||
case 'C': strlcpy (datakind, "AX.25 Connection Received", sizeof(datakind)); break;
|
||||
case 'D': strlcpy (datakind, "Connected AX.25 Data", sizeof(datakind)); break;
|
||||
case 'M': strlcpy (datakind, "Monitored Connected Information", sizeof(datakind)); break;
|
||||
case 'S': strlcpy (datakind, "Monitored Supervisory Information", sizeof(datakind)); break;
|
||||
case 'U': strlcpy (datakind, "Monitored Unproto Information", sizeof(datakind)); break;
|
||||
case 'T': strlcpy (datakind, "Monitoring Own Information", sizeof(datakind)); break;
|
||||
case 'K': strlcpy (datakind, "Monitored Information in Raw Format", sizeof(datakind)); break;
|
||||
default: strlcpy (datakind, "**INVALID**", sizeof(datakind)); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -466,7 +466,7 @@ static THREAD_F connect_listen_thread (void *arg)
|
|||
SOCKET listen_sock;
|
||||
WSADATA wsadata;
|
||||
|
||||
sprintf (server_port_str, "%d", (int)(long)arg);
|
||||
snprintf (server_port_str, sizeof(server_port_str), "%d", (int)(long)arg);
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("DEBUG: serverport = %d = '%s'\n", (int)(long)arg, server_port_str);
|
||||
|
@ -591,6 +591,7 @@ static THREAD_F connect_listen_thread (void *arg)
|
|||
socklen_t sockaddr_size = sizeof(struct sockaddr_in);
|
||||
int server_port = (int)(long)arg;
|
||||
int listen_sock;
|
||||
int bcopt = 1;
|
||||
|
||||
listen_sock= socket(AF_INET,SOCK_STREAM,0);
|
||||
if (listen_sock == -1) {
|
||||
|
@ -599,6 +600,15 @@ static THREAD_F connect_listen_thread (void *arg)
|
|||
return (NULL);
|
||||
}
|
||||
|
||||
/* Version 1.3 - as suggested by G8BPQ. */
|
||||
/* Without this, if you kill the application then try to run it */
|
||||
/* again quickly the port number is unavailable for a while. */
|
||||
/* Don't try doing the same thing On Windows; It has a different meaning. */
|
||||
/* http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t */
|
||||
|
||||
setsockopt (listen_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&bcopt, 4);
|
||||
|
||||
|
||||
sockaddr.sin_addr.s_addr = INADDR_ANY;
|
||||
sockaddr.sin_port = htons(server_port);
|
||||
sockaddr.sin_family = AF_INET;
|
||||
|
@ -791,7 +801,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
|
|||
/* The documentation example includes these 3 extra in the Len= value */
|
||||
/* but actual observed data uses only the packet info length. */
|
||||
|
||||
sprintf (agwpe_msg.data, " %d:Fm %s To %s <UI pid=%02X Len=%d >[%02d:%02d:%02d]\r%s\r\r",
|
||||
snprintf (agwpe_msg.data, sizeof(agwpe_msg.data), " %d:Fm %s To %s <UI pid=%02X Len=%d >[%02d:%02d:%02d]\r%s\r\r",
|
||||
chan+1, agwpe_msg.hdr.call_from, agwpe_msg.hdr.call_to,
|
||||
ax25_get_pid(pp), info_len,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec,
|
||||
|
@ -1103,7 +1113,7 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
count++;
|
||||
}
|
||||
}
|
||||
sprintf (reply.info, "%d;", count);
|
||||
snprintf (reply.info, sizeof(reply.info), "%d;", count);
|
||||
|
||||
for (j=0; j<MAX_CHANS; j++) {
|
||||
if (save_audio_config_p->achan[j].valid) {
|
||||
|
@ -1113,22 +1123,22 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
static const char *names[8] = { "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth" };
|
||||
|
||||
if (save_audio_config_p->adev[a].num_channels == 1) {
|
||||
sprintf (stemp, "Port%d %s soundcard mono;", j+1, names[a]);
|
||||
strcat (reply.info, stemp);
|
||||
snprintf (stemp, sizeof(stemp), "Port%d %s soundcard mono;", j+1, names[a]);
|
||||
strlcat (reply.info, stemp, sizeof(reply.info));
|
||||
}
|
||||
else {
|
||||
sprintf (stemp, "Port%d %s soundcard %s;", j+1, names[a], j&1 ? "right" : "left");
|
||||
strcat (reply.info, stemp);
|
||||
snprintf (stemp, sizeof(stemp), "Port%d %s soundcard %s;", j+1, names[a], j&1 ? "right" : "left");
|
||||
strlcat (reply.info, stemp, sizeof(reply.info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
if (num_channels == 1) {
|
||||
sprintf (reply.info, "1;Port1 Single channel;");
|
||||
snprintf (reply.info, sizeof(reply.info), "1;Port1 Single channel;");
|
||||
}
|
||||
else {
|
||||
sprintf (reply.info, "2;Port1 Left channel;Port2 Right Channel;");
|
||||
snprintf (reply.info, sizeof(reply.info), "2;Port1 Left channel;Port2 Right Channel;");
|
||||
}
|
||||
#endif
|
||||
reply.hdr.data_len = strlen(reply.info) + 1;
|
||||
|
@ -1200,9 +1210,9 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
|
||||
reply.hdr.portx = cmd.hdr.portx
|
||||
|
||||
strcpy (reply.hdr.call_from, "WB2OSZ-15");
|
||||
strlcpy (reply.hdr.call_from, "WB2OSZ-15", sizeof(reply.hdr.call_from));
|
||||
|
||||
strcpy (agwpe_msg.data, ...);
|
||||
strlcpy (agwpe_msg.data, ..., sizeof(agwpe_msg.data));
|
||||
|
||||
reply.hdr.data_len = strlen(reply.info);
|
||||
|
||||
|
@ -1247,21 +1257,21 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
|
||||
// We have already assured these do not exceed 9 characters.
|
||||
|
||||
strcpy (stemp, cmd.hdr.call_from);
|
||||
strcat (stemp, ">");
|
||||
strcat (stemp, cmd.hdr.call_to);
|
||||
strlcpy (stemp, cmd.hdr.call_from, sizeof(stemp));
|
||||
strlcat (stemp, ">", sizeof(stemp));
|
||||
strlcat (stemp, cmd.hdr.call_to, sizeof(stemp));
|
||||
|
||||
cmd.data[cmd.hdr.data_len] = '\0';
|
||||
ndigi = cmd.data[0];
|
||||
p = cmd.data + 1;
|
||||
|
||||
for (k=0; k<ndigi; k++) {
|
||||
strcat (stemp, ",");
|
||||
strcat (stemp, p);
|
||||
strlcat (stemp, ",", sizeof(stemp));
|
||||
strlcat (stemp, p, sizeof(stemp));
|
||||
p += 10;
|
||||
}
|
||||
strcat (stemp, ":");
|
||||
strcat (stemp, p);
|
||||
strlcat (stemp, ":", sizeof(stemp));
|
||||
strlcat (stemp, p, sizeof(stemp));
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("Transmit '%s'\n", stemp);
|
||||
|
@ -1306,7 +1316,7 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
//
|
||||
// The first byte of data is described as:
|
||||
//
|
||||
// the “TNC” to use
|
||||
// the "TNC" to use
|
||||
// 00=Port 1
|
||||
// 16=Port 2
|
||||
//
|
||||
|
|
184
symbols.c
184
symbols.c
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2011,2012,2013,2014 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
@ -31,6 +31,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "textcolor.h"
|
||||
#include "symbols.h"
|
||||
|
||||
|
@ -259,6 +261,17 @@ static const struct {
|
|||
/* ~ 94 */ { "Q4", "TNC Stream Switch" } };
|
||||
|
||||
|
||||
// Make sure the array is null terminated.
|
||||
static const char *search_locations[] = {
|
||||
(const char *) "symbols-new.txt",
|
||||
#ifndef __WIN32__
|
||||
(const char *) "/usr/share/direwolf/symbols-new.txt",
|
||||
(const char *) "/usr/local/share/direwolf/symbols-new.txt",
|
||||
#endif
|
||||
(const char *) NULL
|
||||
};
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: symbols_init
|
||||
|
@ -306,7 +319,7 @@ static int new_sym_len = 0; /* Number of elements used. */
|
|||
|
||||
void symbols_init (void)
|
||||
{
|
||||
FILE *fp;
|
||||
FILE *fp = NULL;
|
||||
|
||||
/*
|
||||
* We only care about lines with this format:
|
||||
|
@ -340,13 +353,13 @@ void symbols_init (void)
|
|||
|
||||
// If search strategy changes, be sure to keep decode_tocall in sync.
|
||||
|
||||
fp = NULL;
|
||||
j = 0;
|
||||
do {
|
||||
if (search_locations[j] == NULL) break;
|
||||
fp = fopen(search_locations[j++], "r");
|
||||
} while (fp == NULL);
|
||||
|
||||
fp = fopen("symbols-new.txt", "r");
|
||||
#ifndef __WIN32__
|
||||
if (fp == NULL) {
|
||||
fp = fopen("/usr/share/direwolf/symbols-new.txt", "r");
|
||||
}
|
||||
#endif
|
||||
if (fp == NULL) {
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
|
@ -401,6 +414,83 @@ void symbols_init (void)
|
|||
} /* end symbols_init */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: symbols_list
|
||||
*
|
||||
* Purpose: Print a list of all the symbols.
|
||||
*
|
||||
* Inputs: none
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
void symbols_list (void)
|
||||
{
|
||||
int n;
|
||||
|
||||
dw_printf ("\n");
|
||||
|
||||
dw_printf ("\tPRIMARY SYMBOL TABLE\n");
|
||||
dw_printf ("\n");
|
||||
dw_printf ("sym GPSxy GPSCnn APRStt Icon\n");
|
||||
dw_printf ("--- ----- ------ ------ ----\n");
|
||||
for (n = 1; n < SYMTAB_SIZE; n++) {
|
||||
dw_printf (" /%c %s %02d AB1%02d %s\n", n + ' ', primary_symtab[n].xy, n, n, primary_symtab[n].description);
|
||||
}
|
||||
|
||||
dw_printf ("\n");
|
||||
dw_printf ("\tALTERNATE SYMBOL TABLE\n");
|
||||
dw_printf ("\n");
|
||||
dw_printf ("sym GPSxy GPSEnn APRStt Icon\n");
|
||||
dw_printf ("--- ----- ------ ------ ----\n");
|
||||
for (n = 1; n < SYMTAB_SIZE; n++) {
|
||||
dw_printf (" \\%c %s %02d AB2%02d %s\n", n + ' ', alternate_symtab[n].xy, n, n, alternate_symtab[n].description);
|
||||
}
|
||||
|
||||
dw_printf ("\n");
|
||||
dw_printf ("\tNEW SYMBOLS from symbols-new.txt\n");
|
||||
dw_printf ("\n");
|
||||
dw_printf ("sym GPSxyz GPSxnn APRStt Icon\n");
|
||||
dw_printf ("--- ------ ------ ------ ----\n");
|
||||
|
||||
|
||||
for (n = 0; n < new_sym_len; n++) {
|
||||
|
||||
int overlay = new_sym_ptr[n].overlay;
|
||||
int symbol = new_sym_ptr[n].symbol;
|
||||
char tones[12];
|
||||
|
||||
symbols_to_tones (overlay, symbol, tones);
|
||||
|
||||
if (overlay == '/') {
|
||||
|
||||
dw_printf (" %c%c %s%c C%02d %-7s %s\n", overlay, symbol,
|
||||
primary_symtab[symbol - ' '].xy, ' ',
|
||||
symbol - ' ', tones,
|
||||
new_sym_ptr[n].description);
|
||||
}
|
||||
else if (isupper(overlay) || isdigit(overlay)) {
|
||||
|
||||
dw_printf (" %c%c %s%c %-7s %s\n", overlay, symbol,
|
||||
alternate_symtab[symbol - ' '].xy, overlay,
|
||||
tones,
|
||||
new_sym_ptr[n].description);
|
||||
}
|
||||
else {
|
||||
|
||||
dw_printf (" %c%c %s%c E%02d %-7s %s\n", overlay, symbol,
|
||||
alternate_symtab[symbol - ' '].xy, ' ',
|
||||
symbol - ' ', tones,
|
||||
new_sym_ptr[n].description);
|
||||
}
|
||||
}
|
||||
dw_printf ("\n");
|
||||
|
||||
|
||||
} /* end symbols_list */
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: symbols_from_dest_or_src
|
||||
|
@ -602,19 +692,19 @@ int symbols_into_dest (char symtab, char symbol, char *dest)
|
|||
if (symbol >= '!' && symbol <= '~' && symtab == '/') {
|
||||
|
||||
/* Primary Symbol table. */
|
||||
sprintf (dest, "GPSC%02d", symbol - ' ');
|
||||
snprintf (dest, 7, "GPSC%02d", symbol - ' ');
|
||||
return (0);
|
||||
}
|
||||
else if (symbol >= '!' && symbol <= '~' && symtab == '\\') {
|
||||
|
||||
/* Alternate Symbol table. */
|
||||
sprintf (dest, "GPSE%02d", symbol - ' ');
|
||||
snprintf (dest, 7, "GPSE%02d", symbol - ' ');
|
||||
return (0);
|
||||
}
|
||||
else if (symbol >= '!' && symbol <= '~' && (isupper(symtab) || isdigit(symtab))) {
|
||||
|
||||
/* Alternate Symbol table with overlay. */
|
||||
sprintf (dest, "GPS%s%c", alternate_symtab[symbol - ' '].xy, symtab);
|
||||
snprintf (dest, 7, "GPS%s%c", alternate_symtab[symbol - ' '].xy, symtab);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -623,7 +713,7 @@ int symbols_into_dest (char symtab, char symbol, char *dest)
|
|||
dw_printf ("Could not convert symbol \"%c%c\" to GPSxyz destination format.\n",
|
||||
symtab, symbol);
|
||||
|
||||
strcpy (dest, "GPS???"); /* Error. */
|
||||
strlcpy (dest, "GPS???", sizeof(dest)); /* Error. */
|
||||
return (1);
|
||||
}
|
||||
|
||||
|
@ -637,16 +727,19 @@ int symbols_into_dest (char symtab, char symbol, char *dest)
|
|||
* Inputs: symtab /, \, 0-9, A-Z
|
||||
* symbol any printable character ! to ~
|
||||
*
|
||||
* desc_size Size of description provided by caller
|
||||
* so we can avoid buffer overflow.
|
||||
*
|
||||
* Outputs: description Text description.
|
||||
* "--no-symbol--" if error.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Description: This is used for the monitoring and the
|
||||
* decode_aprs utility.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
void symbols_get_description (char symtab, char symbol, char *description)
|
||||
void symbols_get_description (char symtab, char symbol, char *description, size_t desc_size)
|
||||
{
|
||||
char tmp2[2];
|
||||
int j;
|
||||
|
@ -672,7 +765,7 @@ void symbols_get_description (char symtab, char symbol, char *description)
|
|||
/* We do the latter. */
|
||||
|
||||
symbol = ' ';
|
||||
strcpy (description, primary_symtab[symbol-' '].description);
|
||||
strlcpy (description, primary_symtab[symbol-' '].description, desc_size);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -688,7 +781,7 @@ void symbols_get_description (char symtab, char symbol, char *description)
|
|||
|
||||
for (j=0; j<new_sym_len; j++) {
|
||||
if (symtab == new_sym_ptr[j].overlay && symbol == new_sym_ptr[j].symbol) {
|
||||
strcpy (description, new_sym_ptr[j].description);
|
||||
strlcpy (description, new_sym_ptr[j].description, desc_size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -696,15 +789,15 @@ void symbols_get_description (char symtab, char symbol, char *description)
|
|||
// Otherwise use the original symbol tables.
|
||||
|
||||
if (symtab == '/') {
|
||||
strcpy (description, primary_symtab[symbol-' '].description);
|
||||
strlcpy (description, primary_symtab[symbol-' '].description, desc_size);
|
||||
}
|
||||
else {
|
||||
strcpy (description, alternate_symtab[symbol-' '].description);
|
||||
strlcpy (description, alternate_symtab[symbol-' '].description, desc_size);
|
||||
if (symtab != '\\') {
|
||||
strcat (description, " w/overlay ");
|
||||
strlcat (description, " w/overlay ", desc_size);
|
||||
tmp2[0] = symtab;
|
||||
tmp2[1] = '\0';
|
||||
strcat (description, tmp2);
|
||||
strlcat (description, tmp2, desc_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -798,6 +891,57 @@ int symbols_code_from_description (char overlay, char *description, char *symtab
|
|||
} /* end symbols_code_from_description */
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: symbols_to_tones
|
||||
*
|
||||
* Purpose: Convert symbol to APRStt tone sequence.
|
||||
*
|
||||
* Inputs: symtab/overlay
|
||||
* symbol
|
||||
*
|
||||
* Output: tones - string of AB...
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Primary: AB1nn nn = same number as GPSCnn
|
||||
* Alternate: AB2nn nn = same number as GPSEnn
|
||||
* with overlay: AB0nntt nn = same as with alternate
|
||||
* tt = one or two tones from two key method.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
void symbols_to_tones (char symtab, char symbol, char *tones)
|
||||
{
|
||||
|
||||
if (symtab == '/') {
|
||||
|
||||
// TODO: potential buffer overflow.
|
||||
sprintf (tones, "AB1%02d", symbol - ' ');
|
||||
}
|
||||
else if (isupper(symtab) || isdigit(symtab)) {
|
||||
|
||||
char text[2];
|
||||
char tt[8];
|
||||
|
||||
text[0] = symtab;
|
||||
text[1] = '\0';
|
||||
|
||||
tt_text_to_two_key (text, 0, tt);
|
||||
|
||||
sprintf (tones, "AB0%02d%s", symbol - ' ', tt);
|
||||
}
|
||||
else {
|
||||
|
||||
sprintf (tones, "AB2%02d", symbol - ' ');
|
||||
}
|
||||
|
||||
} /* end symbols_to_tones */
|
||||
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
/* Quick, incomplete, unit test. */
|
||||
|
|
|
@ -3,12 +3,17 @@
|
|||
|
||||
void symbols_init (void);
|
||||
|
||||
void symbols_list (void);
|
||||
|
||||
void symbols_from_dest_or_src (char dti, char *src, char *dest, char *symtab, char *symbol);
|
||||
|
||||
int symbols_into_dest (char symtab, char symbol, char *dest);
|
||||
|
||||
void symbols_get_description (char symtab, char symbol, char *description);
|
||||
void symbols_get_description (char symtab, char symbol, char *description, size_t desc_size);
|
||||
|
||||
int symbols_code_from_description (char overlay, char *description, char *symtab, char *symbol);
|
||||
|
||||
void symbols_to_tones (char symtab, char symbol, char *tones);
|
||||
|
||||
|
||||
/* end symbols.h */
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
# Sample configuration for demonstration of sending telemetry.
|
||||
# Here we try to replicate actual data heard for a balloon.
|
||||
|
||||
CHANNEL 0
|
||||
MYCALL M0XER-3
|
||||
|
||||
# These will send the beacons to the transmitter (which you disconnected, right?)
|
||||
|
||||
# First the metadata.
|
||||
|
||||
# Channel 1: Battery voltage, Volts, scaled up by 100
|
||||
# Channel 2: Solar voltage, Volts, scaled up by 100
|
||||
# Channel 3: Temperature, degrees C, sent as Kelvin x 10
|
||||
# Channel 4: Number of satellites, no units
|
||||
|
||||
# Note: When using Strawberry perl, as specified in the example, Windows knows
|
||||
# that the .pl file type is associated with it. When using a different implementation
|
||||
# of perl, which doesn't make this association of file type to application, it might
|
||||
# be necessary to use something like this instead:
|
||||
#
|
||||
# ... infocmd="c:\strawberry\perl\bin\perl.exe telem-parm.pl M0XER-3 Vbat Vsolar Temp Sat"
|
||||
|
||||
# Here we use the generic scripts to generate the messages with metadata.
|
||||
# The "infocmd=..." option means use the result for the info part of the packet.
|
||||
|
||||
CBEACON delay=0:10 every=1:00 infocmd="telem-parm.pl M0XER-3 Vbat Vsolar Temp Sat"
|
||||
CBEACON delay=0:12 every=1:00 infocmd="telem-unit.pl M0XER-3 V V C """" m"
|
||||
CBEACON delay=0:14 every=1:00 infocmd="telem-eqns.pl M0XER-3 0 0.001 0 0 0.001 0 0 0.1 -273.2 0 1 0 0 1 0"
|
||||
CBEACON delay=0:16 every=1:00 infocmd="telem-bits.pl M0XER-3 11111111 ""10mW research balloon"""
|
||||
|
||||
# Now the telemetry data.
|
||||
# In a real situation, the location and telemetry data would come from sensors.
|
||||
# Here we have just hardcoded 3 sets of historical data as a demonstration.
|
||||
|
||||
# telem-balloon.pl accumulates the data then invokes telem-data91.pl to convert
|
||||
# it to the compressed format. This is inserted into the position comment with "commentcmd=..."
|
||||
|
||||
PBEACON compress=1 delay=0:20 every=1:00 via=WIDE2-1 symbol=Balloon lat=61^34.2876N lon=155^40.0931W alt=12953 commentcmd="telem-balloon.pl 3307 4.383 0.436 -34.6 12"
|
||||
PBEACON compress=1 delay=0:22 every=1:00 via=WIDE2-1 symbol=Balloon lat=51^07.4402N lon=124^14.4472W alt=12563 commentcmd="telem-balloon.pl 6524 4.515 0.653 -1.3 7"
|
||||
PBEACON compress=1 delay=0:24 every=1:00 via=WIDE2-1 symbol=Balloon lat=55^58.5558N lon=122^28.5933W alt=12680 commentcmd="telem-balloon.pl 7458 4.521 0.587 -8.3 7"
|
||||
|
||||
|
||||
# Now we do the same thing again.
|
||||
|
||||
# This time, add the SENDTO=R0 option to simulate reception.
|
||||
# These will be sent to any attached applications so you can see how they process the data.
|
||||
|
||||
CBEACON SENDTO=R0 delay=0:30 every=1:00 infocmd="telem-parm.pl M0XER-3 Vbat Vsolar Temp Sat"
|
||||
CBEACON SENDTO=R0 delay=0:32 every=1:00 infocmd="telem-unit.pl M0XER-3 V V C """" m"
|
||||
CBEACON SENDTO=R0 delay=0:34 every=1:00 infocmd="telem-eqns.pl M0XER-3 0 0.001 0 0 0.001 0 0 0.1 -273.2 0 1 0 0 1 0"
|
||||
CBEACON SENDTO=R0 delay=0:36 every=1:00 infocmd="telem-bits.pl M0XER-3 11111111 ""10mW research balloon"""
|
||||
|
||||
PBEACON SENDTO=R0 compress=1 delay=0:40 every=1:00 via=WIDE2-1 symbol=Balloon lat=61^34.2876N lon=155^40.0931W alt=12953 commentcmd="telem-balloon.pl 3307 4.383 0.436 -34.6 12"
|
||||
PBEACON SENDTO=R0 compress=1 delay=0:42 every=1:00 via=WIDE2-1 symbol=Balloon lat=51^07.4402N lon=124^14.4472W alt=12563 commentcmd="telem-balloon.pl 6524 4.515 0.653 -1.3 7"
|
||||
PBEACON SENDTO=R0 compress=1 delay=0:44 every=1:00 via=WIDE2-1 symbol=Balloon lat=55^58.5558N lon=122^28.5933W alt=12680 commentcmd="telem-balloon.pl 7458 4.521 0.587 -8.3 7"
|
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# Part of Dire Wolf APRS Telemetry Toolkit, WB2OSZ, 2015
|
||||
|
||||
# In a real situation this would obtain data from sensors.
|
||||
# For demonstration purposes we use historical data supplied on the command line.
|
||||
|
||||
if ($#ARGV+1 != 5) {
|
||||
print STDERR "5 command line arguments must be provided.\n";
|
||||
usage();
|
||||
}
|
||||
|
||||
($seq,$vbat,$vsolar,$temp,$sat) = @ARGV;
|
||||
|
||||
# Scale to integer in range of 0 to 8280.
|
||||
# This must be the inverse of the mapping in the EQNS message.
|
||||
|
||||
$vbat = int(($vbat * 1000) + 0.5);
|
||||
$vsolar = int(($vsolar * 1000) + 0.5);
|
||||
$temp = int((($temp + 273.2) * 10) + 0.5);
|
||||
|
||||
exit system("telem-data91.pl $seq $vbat $vsolar $temp $sat");
|
||||
|
||||
|
||||
sub usage ()
|
||||
{
|
||||
print STDERR "\n";
|
||||
print STDERR "balloon.pl - Format data into Compressed telemetry format.\n";
|
||||
print STDERR "\n";
|
||||
print STDERR "In a real situation this would obtain data from sensors.\n";
|
||||
print STDERR "For demonstration purposes we use historical data supplied on the command line.\n";
|
||||
print STDERR "\n";
|
||||
print STDERR "Usage: balloon.pl seq vbat vsolar temp sat\n";
|
||||
print STDERR "\n";
|
||||
print STDERR "Where,\n";
|
||||
print STDERR " seq is a sequence number.\n";
|
||||
print STDERR " vbat is battery voltage.\n";
|
||||
print STDERR " vsolar is solar cell voltage.\n";
|
||||
print STDERR " temp is temperature, degrees C.\n";
|
||||
print STDERR " sat is number of GPS satellites visible.\n";
|
||||
|
||||
exit 1;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# Part of Dire Wolf APRS Telemetry Toolkit, WB2OSZ, 2015
|
||||
|
||||
if ($#ARGV+1 < 2 || $#ARGV+1 > 3) {
|
||||
print STDERR "A callsign, bit sense string, and optional project title must be provided.\n";
|
||||
usage();
|
||||
}
|
||||
|
||||
# Separate out call and pad to 9 characters.
|
||||
$call = shift @ARGV;
|
||||
$call = substr($call . " ", 0, 9);
|
||||
|
||||
if ( ! ($ARGV[0] =~ m/^[01]{8}$/)) {
|
||||
print STDERR "The bit-sense value must be 8 binary digits.\n";
|
||||
usage();
|
||||
}
|
||||
|
||||
print ":$call:BITS." . join (',', @ARGV) . "\n";
|
||||
exit 0;
|
||||
|
||||
sub usage ()
|
||||
{
|
||||
print STDERR "\n";
|
||||
print STDERR "telem-bits.pl - Generate BITS message with bit polarity and optional project title.\n";
|
||||
print STDERR "\n";
|
||||
print STDERR "Usage: telem-bits.pl call bit-sense [ project-title ]\n";
|
||||
print STDERR "\n";
|
||||
print STDERR "Bit-sense is string of 8 binary digits.\n";
|
||||
print STDERR "If project title contains any spaces, enclose it in quotes.\n";
|
||||
|
||||
exit 1;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# Part of Dire Wolf APRS Telemetry Toolkit, WB2OSZ, 2015
|
||||
|
||||
if ($#ARGV+1 < 2 || $#ARGV+1 > 7) {
|
||||
print STDERR "2 to 7 command line arguments must be provided.\n";
|
||||
usage();
|
||||
}
|
||||
|
||||
if ($#ARGV+1 == 7) {
|
||||
if ( ! ($ARGV[6] =~ m/^[01]{8}$/)) {
|
||||
print STDERR "The sixth value must be 8 binary digits.\n";
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
print "T#" . join (',', @ARGV) . "\n";
|
||||
exit 0;
|
||||
|
||||
sub usage ()
|
||||
{
|
||||
print STDERR "\n";
|
||||
print STDERR "telem-data.pl - Format data into Telemetry Report format.\n";
|
||||
print STDERR "\n";
|
||||
print STDERR "Usage: telem-data.pl sequence value1 [ value2 ... ]\n";
|
||||
print STDERR "\n";
|
||||
print STDERR "A sequence number and up to 5 analog values can be specified.\n";
|
||||
print STDERR "Any sixth value must be 8 binary digits.\n";
|
||||
|
||||
exit 1;
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# Part of Dire Wolf APRS Telemetry Toolkit, WB2OSZ, 2015
|
||||
|
||||
# For explanation of encoding see:
|
||||
# http://he.fi/doc/aprs-base91-comment-telemetry.txt
|
||||
|
||||
|
||||
if ($#ARGV+1 < 2 || $#ARGV+1 > 7) {
|
||||
print STDERR "2 to 7 command line arguments must be provided.\n";
|
||||
usage();
|
||||
}
|
||||
|
||||
|
||||
if ($#ARGV+1 == 7) {
|
||||
if ( ! ($ARGV[6] =~ m/^[01]{8}$/)) {
|
||||
print STDERR "The sixth value must be 8 binary digits.\n";
|
||||
usage();
|
||||
}
|
||||
# Convert binary digits to value.
|
||||
$ARGV[6] = oct("0b" . reverse($ARGV[6]));
|
||||
}
|
||||
|
||||
$result = "|";
|
||||
|
||||
for ($n = 0 ; $n <= $#ARGV; $n++) {
|
||||
#print $n . " = " . $ARGV[$n] . "\n";
|
||||
$v = $ARGV[$n];
|
||||
if ($v != int($v) || $v < 0 || $v > 8280) {
|
||||
print STDERR "$v is not an integer in range of 0 to 8280.\n";
|
||||
usage();
|
||||
}
|
||||
|
||||
$result .= base91($v);
|
||||
}
|
||||
|
||||
$result .= "|";
|
||||
print "$result\n";
|
||||
exit 0;
|
||||
|
||||
|
||||
sub base91 ()
|
||||
{
|
||||
my $x = @_[0];
|
||||
|
||||
my $d1 = int ($x / 91);
|
||||
my $d2 = $x % 91;
|
||||
|
||||
return chr($d1+33) . chr($d2+33);
|
||||
}
|
||||
|
||||
|
||||
sub usage ()
|
||||
{
|
||||
print STDERR "\n";
|
||||
print STDERR "telem-data91.pl - Format data into compressed base 91 telemetry.\n";
|
||||
print STDERR "\n";
|
||||
print STDERR "Usage: telem-data91.pl sequence value1 [ value2 ... ]\n";
|
||||
print STDERR "\n";
|
||||
print STDERR "A sequence number and up to 5 analog values can be specified.\n";
|
||||
print STDERR "Any sixth value must be 8 binary digits.\n";
|
||||
print STDERR "Values must be integers in range of 0 to 8280.\n";
|
||||
|
||||
exit 1;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# Part of Dire Wolf APRS Telemetry Toolkit, WB2OSZ, 2015
|
||||
|
||||
if ($#ARGV+1 != 4 && $#ARGV+1 != 7 && $#ARGV+1 != 10 && $#ARGV+1 != 13 && $#ARGV+1 != 16) {
|
||||
print STDERR "A callsign and 1 to 5 sets of 3 coefficients must be provided.\n";
|
||||
usage();
|
||||
}
|
||||
|
||||
# Separate out call and pad to 9 characters.
|
||||
$call = shift @ARGV;
|
||||
$call = substr($call . " ", 0, 9);
|
||||
|
||||
print ":$call:EQNS." . join (',', @ARGV) . "\n";
|
||||
exit 0;
|
||||
|
||||
sub usage ()
|
||||
{
|
||||
print STDERR "\n";
|
||||
print STDERR "telem-eqns.pl - Generate EQNS message with scaling coefficients\n";
|
||||
print STDERR "\n";
|
||||
print STDERR "Usage: telem-eqns.pl call a1 b1 c1 [ a2 b2 c2 ... ]\n";
|
||||
print STDERR "\n";
|
||||
print STDERR "Specify a callsign and 1 to 5 sets of 3 coefficients.\n";
|
||||
print STDERR "See APRS protocol reference for their meaning.\n";
|
||||
|
||||
exit 1;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
2E0TOY>APRS::M0XER-3 :BITS.11111111,10mW research balloon
|
||||
2E0TOY>APRS::M0XER-3 :PARM.Vbat,Vsolar,Temp,Sat
|
||||
2E0TOY>APRS::M0XER-3 :EQNS.0,0.001,0,0,0.001,0,0,0.1,-273.2,0,1,0,0,1,0
|
||||
2E0TOY>APRS::M0XER-3 :UNIT.V,V,C,,m
|
||||
M0XER-3>APRS63,WIDE2-1:!//Bap'.ZGO JHAE/A=042496|E@Q0%i;5!-|
|
||||
M0XER-3>APRS63,WIDE2-1:!/4\;u/)K$O J]YD/A=041216|h`RY(1>q!(|
|
||||
M0XER-3>APRS63,WIDE2-1:!/23*f/R$UO Jf'x/A=041600|rxR_'J>+!(|
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# Part of Dire Wolf APRS Telemetry Toolkit, WB2OSZ, 2015
|
||||
|
||||
if ($#ARGV+1 < 2 || $#ARGV+1 > 14) {
|
||||
print STDERR "A callsign and 1 to 13 channel names must be provided.\n";
|
||||
usage();
|
||||
}
|
||||
|
||||
# Separate out call and pad to 9 characters.
|
||||
$call = shift @ARGV;
|
||||
$call = substr($call . " ", 0, 9);
|
||||
|
||||
print ":$call:PARM." . join (',', @ARGV) . "\n";
|
||||
exit 0;
|
||||
|
||||
sub usage ()
|
||||
{
|
||||
print STDERR "\n";
|
||||
print STDERR "telem-parm.pl - Generate PARM message with channel names.\n";
|
||||
print STDERR "\n";
|
||||
print STDERR "Usage: telem-parm.pl call aname1 ... aname5 dname1 .,, dname8\n";
|
||||
print STDERR "\n";
|
||||
print STDERR "Specify a callsign and up to 13 names for the analog & digital channels.\n";
|
||||
|
||||
exit 1;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# Part of Dire Wolf APRS Telemetry Toolkit, WB2OSZ, 2015
|
||||
|
||||
if ($#ARGV+1 < 2 || $#ARGV+1 > 14) {
|
||||
print STDERR "A callsign and 1 to 13 units/labels must be provided.\n";
|
||||
usage();
|
||||
}
|
||||
|
||||
# Separate out call and pad to 9 characters.
|
||||
$call = shift @ARGV;
|
||||
$call = substr($call . " ", 0, 9);
|
||||
|
||||
print ":$call:UNIT." . join (',', @ARGV) . "\n";
|
||||
exit 0;
|
||||
|
||||
sub usage ()
|
||||
{
|
||||
print STDERR "\n";
|
||||
print STDERR "telem-unit.pl - Generate UNIT message with channel units/labels.\n";
|
||||
print STDERR "\n";
|
||||
print STDERR "Usage: telem-unit.pl call unit1 ... unit5 label1 .,, label8\n";
|
||||
print STDERR "\n";
|
||||
print STDERR "Specify a callsign and up to 13 names for the units/labels.\n";
|
||||
|
||||
exit 1;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
# Sample configuration for demonstration of sending telemetry.
|
||||
# Here we take a voltage from an analog to digital converter (ADC).
|
||||
|
||||
ADEVICE plughw:1,0
|
||||
|
||||
MYCALL MYCALL-9
|
||||
|
||||
# For demonstration purposes, the metadata will be sent each minute and
|
||||
# voltage data every 10 seconds. In actual practice, it would be much less frequent.
|
||||
|
||||
# First the metadata.
|
||||
|
||||
# The "infocmd=..." option means use the result for the info part of the packet.
|
||||
|
||||
CBEACON delay=0:10 every=1:00 via=WIDE2-1 infocmd="telem-parm.pl MYCALL-9 Supply"
|
||||
CBEACON delay=0:11 every=1:00 via=WIDE2-1 infocmd="telem-unit.pl MYCALL-9 Volts"
|
||||
|
||||
# Now the telemetry data.
|
||||
|
||||
# First we use telem-volts.py to read a volage from the A/D converter.
|
||||
# This is supplied to telem-data.pl as a command line argument.
|
||||
# The result is used as the info part of a custom beacon.
|
||||
|
||||
CBEACON delay=0:15 every=0:10 via=WIDE2-1 infocmd="telem-data.pl 0 `PYTHONPATH=~/Adafruit-Raspberry-Pi-Python-Code/Adafruit_ADS1x15 telem-volts.py`"
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
# Part of Dire Wolf APRS Telemetry Toolkit, WB2OSZ, 2015
|
||||
|
||||
# Derived from
|
||||
# https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code/blob/master/Adafruit_ADS1x15/ads1x15_ex_singleended.py
|
||||
|
||||
import time, signal, sys
|
||||
from Adafruit_ADS1x15 import ADS1x15
|
||||
|
||||
ADS1015 = 0x00 # 12-bit ADC
|
||||
ADS1115 = 0x01 # 16-bit ADC
|
||||
|
||||
# Set ADC full scale to 2048 mV.
|
||||
# Can't use original 4096 with 3.3V supply.
|
||||
gain = 2048
|
||||
|
||||
# Select the sample time, 1/sps second.
|
||||
# Longer is better to average out noise.
|
||||
sps = 64
|
||||
|
||||
# Set this to ADS1015 or ADS1115 depending on the ADC you are using!
|
||||
adc = ADS1x15(ic=ADS1115)
|
||||
|
||||
# Values for voltage divider on ADC input.
|
||||
r1 = 1000000.
|
||||
r2 = 100000.
|
||||
|
||||
# Read channel 0 in single-ended mode using the settings above
|
||||
volts = adc.readADCSingleEnded(0, gain, sps) * 0.001 * (r1+r2) / r2
|
||||
|
||||
# Calibration correction specific to my hardware.
|
||||
# (multiply by expected value, divide by uncalibrated result.)
|
||||
#volts = volts * 4.98 / 4.889
|
||||
|
||||
print "%.3f" % (volts)
|
122
telemetry.c
122
telemetry.c
|
@ -129,7 +129,7 @@ static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_A
|
|||
*
|
||||
* Inputs: station - Station name with optional SSID.
|
||||
*
|
||||
* REturns: Pointer to metadata.
|
||||
* Returns: Pointer to metadata.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
@ -145,6 +145,10 @@ static struct t_metadata_s * t_get_metadata (char *station)
|
|||
|
||||
for (p = md_list_head; p != NULL; p = p->pnext) {
|
||||
if (strcmp(station, p->station) == 0) {
|
||||
|
||||
assert (p->magic1 == MAGIC1);
|
||||
assert (p->magic2 == MAGIC2);
|
||||
|
||||
return (p);
|
||||
}
|
||||
}
|
||||
|
@ -153,6 +157,7 @@ static struct t_metadata_s * t_get_metadata (char *station)
|
|||
memset (p, 0, sizeof (struct t_metadata_s));
|
||||
|
||||
p->magic1 = MAGIC1;
|
||||
|
||||
strncpy (p->station, station, sizeof(p->station)-1);
|
||||
|
||||
for (n = 0; n < T_NUM_ANALOG; n++) {
|
||||
|
@ -232,6 +237,7 @@ static int t_ndp (char *str)
|
|||
* quiet - suppress error messages.
|
||||
*
|
||||
* Outputs: output - Decoded telemetry in human readable format.
|
||||
* TODO: How big does it need to be? (buffer overflow?)
|
||||
* comment - Any comment after the data.
|
||||
*
|
||||
* Description: The first character, after the "T" data type indicator, must be "#"
|
||||
|
@ -249,7 +255,7 @@ static int t_ndp (char *str)
|
|||
* KB1GKN-10>APRX27,UNCAN,WIDE1*:T#491,4.9,0.3,25.0,0.0,1.0,00000000
|
||||
*
|
||||
* Not integers. Not fixed width fields.
|
||||
* We will accept these but issue a warning that others might not.
|
||||
* We will accept these but issue a warning that others might not.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
@ -275,6 +281,10 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output
|
|||
#endif
|
||||
|
||||
pm = t_get_metadata(station);
|
||||
|
||||
assert (pm->magic1 == MAGIC1);
|
||||
assert (pm->magic2 == MAGIC2);
|
||||
|
||||
seq = 0;
|
||||
for (n = 0; n < T_NUM_ANALOG; n++) {
|
||||
araw[n] = G_UNKNOWN;
|
||||
|
@ -294,10 +304,15 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output
|
|||
|
||||
/*
|
||||
* Make a copy of the input string because this will alter it.
|
||||
* Remove any trailing CR/LF.
|
||||
*/
|
||||
|
||||
strncpy (stemp, info+2, sizeof(stemp));
|
||||
stemp[sizeof(stemp)-1] = '\0';
|
||||
memset (stemp, 0, sizeof(stemp));
|
||||
strncpy (stemp, info+2, sizeof(stemp)-1);
|
||||
|
||||
for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) {
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
next = stemp;
|
||||
p = strsep(&next,",");
|
||||
|
@ -309,13 +324,16 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output
|
|||
araw[n] = atof(p);
|
||||
ndp[n] = t_ndp(p);
|
||||
}
|
||||
if (strlen(p) != 3 || araw[n] < 0 || araw[n] > 255 || araw[n] != (int)(araw[n])) {
|
||||
if ( ! quiet) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Telemetry analog values should be 3 digit integer values in range of 000 to 255.\n");
|
||||
dw_printf("Some applications might not interpret \"%s\" properly.\n", p);
|
||||
}
|
||||
}
|
||||
// Version 1.3: Suppress this message.
|
||||
// No one pays attention to the original 000 to 255 range.
|
||||
// BTW, this doesn't trap values like 0.0 or 1.0
|
||||
//if (strlen(p) != 3 || araw[n] < 0 || araw[n] > 255 || araw[n] != (int)(araw[n])) {
|
||||
// if ( ! quiet) {
|
||||
// text_color_set(DW_COLOR_ERROR);
|
||||
// dw_printf("Telemetry analog values should be 3 digit integer values in range of 000 to 255.\n");
|
||||
// dw_printf("Some applications might not interpret \"%s\" properly.\n", p);
|
||||
// }
|
||||
//}
|
||||
n++;
|
||||
}
|
||||
|
||||
|
@ -331,8 +349,10 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output
|
|||
dw_printf("Expected to find 8 binary digits after \"%s\" for the digital values.\n", p);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test this!
|
||||
if (strlen(next) > 8) {
|
||||
strcpy (comment, next+8);
|
||||
strlcpy (comment, next+8, sizeof(comment));
|
||||
next[8] = '\0';
|
||||
}
|
||||
for (k = 0; k < strlen(next); k++) {
|
||||
|
@ -393,7 +413,7 @@ void telemetry_data_original (char *station, char *info, int quiet, char *output
|
|||
* Description: We are expecting from 2 to 7 pairs of base 91 digits.
|
||||
* The first pair is the sequence number.
|
||||
* Next we have 1 to 5 analog values.
|
||||
* If digital values are present, all 5 analog values must be present.
|
||||
* If digital values are present, all 5 analog values must be present.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
@ -447,6 +467,9 @@ void telemetry_data_base91 (char *station, char *cdata, char *output)
|
|||
|
||||
pm = t_get_metadata(station);
|
||||
|
||||
assert (pm->magic1 == MAGIC1);
|
||||
assert (pm->magic2 == MAGIC2);
|
||||
|
||||
seq = 0;
|
||||
for (n = 0; n < T_NUM_ANALOG; n++) {
|
||||
araw[n] = G_UNKNOWN;
|
||||
|
@ -513,7 +536,7 @@ void telemetry_data_base91 (char *station, char *cdata, char *output)
|
|||
* Outputs: Stored for future use when data values are received.
|
||||
*
|
||||
* Description: The first 5 characters of the message are "PARM." and the
|
||||
* rest is a variable length list of comma separated names.
|
||||
* rest is a variable length list of comma separated names.
|
||||
*
|
||||
* The original spec has different maximum lengths for different
|
||||
* fields which we will ignore.
|
||||
|
@ -540,12 +563,19 @@ void telemetry_name_message (char *station, char *msg)
|
|||
|
||||
/*
|
||||
* Make a copy of the input string because this will alter it.
|
||||
* Remove any trailing CR LF.
|
||||
*/
|
||||
|
||||
strncpy (stemp, msg, sizeof(stemp));
|
||||
stemp[sizeof(stemp)-1] = '\0';
|
||||
memset (stemp, 0, sizeof(stemp));
|
||||
strncpy (stemp, msg, sizeof(stemp)-1);
|
||||
|
||||
for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) {
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
pm = t_get_metadata(station);
|
||||
assert (pm->magic1 == MAGIC1);
|
||||
assert (pm->magic2 == MAGIC2);
|
||||
|
||||
next = stemp;
|
||||
|
||||
|
@ -553,6 +583,7 @@ void telemetry_name_message (char *station, char *msg)
|
|||
while ((p = strsep(&next,",")) != NULL) {
|
||||
if (n < T_NUM_ANALOG + T_NUM_DIGITAL) {
|
||||
if (strlen(p) > 0 && strcmp(p,"-") != 0) {
|
||||
memset (pm->name[n], 0, T_STR_LEN);
|
||||
strncpy (pm->name[n], p, T_STR_LEN-1);
|
||||
}
|
||||
n++;
|
||||
|
@ -586,7 +617,7 @@ void telemetry_name_message (char *station, char *msg)
|
|||
* Outputs: Stored for future use when data values are received.
|
||||
*
|
||||
* Description: The first 5 characters of the message are "UNIT." and the
|
||||
* rest is a variable length list of comma separated units/labels.
|
||||
* rest is a variable length list of comma separated units/labels.
|
||||
*
|
||||
* The original spec has different maximum lengths for different
|
||||
* fields which we will ignore.
|
||||
|
@ -610,12 +641,19 @@ void telemetry_unit_label_message (char *station, char *msg)
|
|||
|
||||
/*
|
||||
* Make a copy of the input string because this will alter it.
|
||||
* Remove any trailing CR LF.
|
||||
*/
|
||||
|
||||
memset (stemp, 0, sizeof(stemp));
|
||||
strncpy (stemp, msg, sizeof(stemp)-1);
|
||||
|
||||
strncpy (stemp, msg, sizeof(stemp));
|
||||
stemp[sizeof(stemp)-1] = '\0';
|
||||
for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) {
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
pm = t_get_metadata(station);
|
||||
assert (pm->magic1 == MAGIC1);
|
||||
assert (pm->magic2 == MAGIC2);
|
||||
|
||||
next = stemp;
|
||||
|
||||
|
@ -623,6 +661,7 @@ void telemetry_unit_label_message (char *station, char *msg)
|
|||
while ((p = strsep(&next,",")) != NULL) {
|
||||
if (n < T_NUM_ANALOG + T_NUM_DIGITAL) {
|
||||
if (strlen(p) > 0) {
|
||||
memset (pm->unit[n], 0, T_STR_LEN);
|
||||
strncpy (pm->unit[n], p, T_STR_LEN-1);
|
||||
}
|
||||
n++;
|
||||
|
@ -657,7 +696,7 @@ void telemetry_unit_label_message (char *station, char *msg)
|
|||
* Outputs: Stored for future use when data values are received.
|
||||
*
|
||||
* Description: The first 5 characters of the message are "EQNS." and the
|
||||
* rest is a comma separated list of 15 floating point values.
|
||||
* rest is a comma separated list of 15 floating point values.
|
||||
*
|
||||
* The spec appears to require all 15 so we will issue an
|
||||
* error if fewer found.
|
||||
|
@ -681,12 +720,19 @@ void telemetry_coefficents_message (char *station, char *msg, int quiet)
|
|||
|
||||
/*
|
||||
* Make a copy of the input string because this will alter it.
|
||||
* Remove any trailing CR LF.
|
||||
*/
|
||||
|
||||
strncpy (stemp, msg, sizeof(stemp));
|
||||
stemp[sizeof(stemp)-1] = '\0';
|
||||
memset (stemp, 0, sizeof(stemp));
|
||||
strncpy (stemp, msg, sizeof(stemp)-1);
|
||||
|
||||
for (p = stemp + strlen(stemp) - 1; p >= stemp && (*p == '\r' || *p == '\n') ; p--) {
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
pm = t_get_metadata(station);
|
||||
assert (pm->magic1 == MAGIC1);
|
||||
assert (pm->magic2 == MAGIC2);
|
||||
|
||||
next = stemp;
|
||||
|
||||
|
@ -765,6 +811,8 @@ void telemetry_bit_sense_message (char *station, char *msg, int quiet)
|
|||
#endif
|
||||
|
||||
pm = t_get_metadata(station);
|
||||
assert (pm->magic1 == MAGIC1);
|
||||
assert (pm->magic2 == MAGIC2);
|
||||
|
||||
if (strlen(msg) < 8) {
|
||||
if ( ! quiet) {
|
||||
|
@ -831,34 +879,36 @@ void telemetry_bit_sense_message (char *station, char *msg, int quiet)
|
|||
* Outputs: output - Decoded telemetry in human readable format.
|
||||
*
|
||||
* Description: Process raw data according to any metadata available
|
||||
* and put into human readable form.
|
||||
* and put into human readable form.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static void fval_to_str (float x, int ndp, char *str)
|
||||
#define VAL_STR_SIZE 64
|
||||
|
||||
static void fval_to_str (float x, int ndp, char str[VAL_STR_SIZE])
|
||||
{
|
||||
if (x == G_UNKNOWN) {
|
||||
strcpy (str, "?");
|
||||
strlcpy (str, "?", VAL_STR_SIZE);
|
||||
}
|
||||
else {
|
||||
sprintf (str, "%.*f", ndp, x);
|
||||
snprintf (str, VAL_STR_SIZE, "%.*f", ndp, x);
|
||||
}
|
||||
}
|
||||
|
||||
static void ival_to_str (int x, char *str)
|
||||
static void ival_to_str (int x, char str[VAL_STR_SIZE])
|
||||
{
|
||||
if (x == G_UNKNOWN) {
|
||||
strcpy (str, "?");
|
||||
strlcpy (str, "?", VAL_STR_SIZE);
|
||||
}
|
||||
else {
|
||||
sprintf (str, "%d", x);
|
||||
snprintf (str, VAL_STR_SIZE, "%d", x);
|
||||
}
|
||||
}
|
||||
|
||||
static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_ANALOG], int ndp[T_NUM_ANALOG], int draw[T_NUM_DIGITAL], char *output)
|
||||
{
|
||||
int n;
|
||||
char stemp[50];
|
||||
char val_str[VAL_STR_SIZE];
|
||||
|
||||
|
||||
assert (pm != NULL);
|
||||
|
@ -872,9 +922,9 @@ static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_A
|
|||
strcat (output, ": ");
|
||||
}
|
||||
|
||||
ival_to_str (seq, stemp);
|
||||
ival_to_str (seq, val_str);
|
||||
strcat (output, "Seq=");
|
||||
strcat (output, stemp);
|
||||
strcat (output, val_str);
|
||||
|
||||
for (n = 0; n < T_NUM_ANALOG; n++) {
|
||||
|
||||
|
@ -905,8 +955,8 @@ static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_A
|
|||
z = pm->coeff_ndp[n][C_A] == 0 ? 0 : pm->coeff_ndp[n][C_A] + ndp[n] + ndp[n];
|
||||
fndp = MAX (z, MAX(pm->coeff_ndp[n][C_B] + ndp[n], pm->coeff_ndp[n][C_C]));
|
||||
}
|
||||
fval_to_str (fval, fndp, stemp);
|
||||
strcat (output, stemp);
|
||||
fval_to_str (fval, fndp, val_str);
|
||||
strcat (output, val_str);
|
||||
if (strlen(pm->unit[n]) > 0) {
|
||||
strcat (output, " ");
|
||||
strcat (output, pm->unit[n]);
|
||||
|
@ -936,13 +986,13 @@ static void t_data_process (struct t_metadata_s *pm, int seq, float araw[T_NUM_A
|
|||
dval = draw[n] ^ ! pm->sense[n];
|
||||
}
|
||||
|
||||
ival_to_str (dval , stemp);
|
||||
ival_to_str (dval, val_str);
|
||||
|
||||
if (strlen(pm->unit[T_NUM_ANALOG+n]) > 0) {
|
||||
strcat (output, " ");
|
||||
strcat (output, pm->unit[T_NUM_ANALOG+n]);
|
||||
}
|
||||
strcat (output, stemp);
|
||||
strcat (output, val_str);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
|
@ -38,6 +39,12 @@
|
|||
* http://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
* http://academic.evergreen.edu/projects/biophysics/technotes/program/ansi_esc.htm
|
||||
*
|
||||
*
|
||||
|
||||
>>>> READ THIS PART!!! <<<<
|
||||
|
||||
*
|
||||
*
|
||||
* Problem: The ANSI escape sequences, used on Linux, allow 8 basic colors.
|
||||
* Unfortunately, white is not one of them. We only have dark
|
||||
* white, also known as light gray. To get brighter colors,
|
||||
|
@ -111,6 +118,8 @@ static const char clear_eos[] = "\e[0J";
|
|||
/* expected bright/bold (1) to get bright white background. */
|
||||
/* Makes no sense but I stumbled across that somewhere. */
|
||||
|
||||
/* If you do get blinking, remove all references to "\e[5;47m" */
|
||||
|
||||
static const char background_white[] = "\e[5;47m";
|
||||
|
||||
/* Whenever a dark color is used, the */
|
||||
|
|
12
tq.c
12
tq.c
|
@ -226,6 +226,12 @@ void tq_append (int chan, int prio, packet_t pp)
|
|||
assert (chan >= 0 && chan < MAX_CHANS);
|
||||
assert (prio >= 0 && prio < TQ_NUM_PRIO);
|
||||
|
||||
if (pp == NULL) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("INTERNAL ERROR: tq_append NULL packet pointer. Please report this!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
#if AX25MEMDEBUG
|
||||
|
||||
if (ax25memdebug_get()) {
|
||||
|
@ -245,16 +251,16 @@ void tq_append (int chan, int prio, packet_t pp)
|
|||
* Is transmit queue out of control?
|
||||
*
|
||||
* There is no technical reason to limit the transmit packet queue length, it just seemed like a good
|
||||
* warning that something wasn’t right.
|
||||
* warning that something wasn't right.
|
||||
* When this was written, I was mostly concerned about APRS where packets would only be sent
|
||||
* occasionally and they can be discarded if they can’t be sent out in a reasonable amount of time.
|
||||
* occasionally and they can be discarded if they can't be sent out in a reasonable amount of time.
|
||||
*
|
||||
* If a large file is being sent, with TCP/IP, it is perfectly reasonable to have a large number
|
||||
* of packets waiting for transmission.
|
||||
*
|
||||
* Ideally, the application should be able to throttle the transmissions so the queue doesn't get too long.
|
||||
* If using the KISS interface, there is no way to get this information from the TNC back to the client app.
|
||||
* The AGW network interface does have a command ‘y’ to query about the number of frames waiting for transmission.
|
||||
* The AGW network interface does have a command 'y' to query about the number of frames waiting for transmission.
|
||||
* This was implemented in version 1.2.
|
||||
*
|
||||
* I'd rather not take out the queue length check because it is a useful sanity check for something going wrong.
|
||||
|
|
403
tt_text.c
403
tt_text.c
|
@ -139,7 +139,7 @@ static const char call10encoding[10][4] = {
|
|||
|
||||
|
||||
/*
|
||||
* 4 digit gridsquares to cover "99.99% of the world's population."
|
||||
* Special satellite 4 digit gridsquares to cover "99.99% of the world's population."
|
||||
*/
|
||||
|
||||
static const char grid[10][10][3] =
|
||||
|
@ -368,6 +368,77 @@ int tt_text_to_two_key (char *text, int quiet, char *buttons)
|
|||
} /* end tt_text_to_two_key */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: tt_letter_to_two_digits
|
||||
*
|
||||
* Purpose: Convert one letter to 2 digit representation.
|
||||
*
|
||||
* Inputs: c - One letter.
|
||||
*
|
||||
* quiet - True to suppress error messages.
|
||||
*
|
||||
* Outputs: buttons - Sequence of two buttons to press.
|
||||
* "00" for error because this is probably
|
||||
* being used to build up a fixed length
|
||||
* string where positions are signficant.
|
||||
*
|
||||
* Returns: Number of errors detected.
|
||||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
|
||||
// TODO: need to test this.
|
||||
|
||||
int tt_letter_to_two_digits (char c, int quiet, char *buttons)
|
||||
{
|
||||
char *b = buttons;
|
||||
int row, col;
|
||||
int errors = 0;
|
||||
int found;
|
||||
|
||||
*b = '\0';
|
||||
|
||||
if (islower(c)) {
|
||||
c = toupper(c);
|
||||
}
|
||||
|
||||
if ( ! isupper(c)) {
|
||||
errors++;
|
||||
if (! quiet) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Letter to two digits: \"%c\" found where a letter is required.\n", c);
|
||||
}
|
||||
strcpy (buttons, "00");
|
||||
return (errors);
|
||||
}
|
||||
|
||||
/* Search in the translation table. */
|
||||
|
||||
found = 0;
|
||||
|
||||
for (row=0; row<10 && ! found; row++) {
|
||||
for (col=0; col<4 && ! found; col++) {
|
||||
if (c == translate[row][col]) {
|
||||
*b++ = '0' + row;
|
||||
*b++ = '1' + col;
|
||||
*b = '\0';
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! found) {
|
||||
errors++;
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Letter to two digits: INTERNAL ERROR. Should not be here.\n");
|
||||
strcpy (buttons, "00");
|
||||
}
|
||||
|
||||
return (errors);
|
||||
|
||||
} /* end tt_letter_to_two_digits */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: tt_text_to_call10
|
||||
|
@ -478,9 +549,9 @@ int tt_text_to_call10 (char *text, int quiet, char *buttons)
|
|||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: tt_text_to_gridsquare
|
||||
* Name: tt_text_to_satsq
|
||||
*
|
||||
* Purpose: Convert Gridsquare to 4 digit DTMF representation.
|
||||
* Purpose: Convert Special Satellite Gridsquare to 4 digit DTMF representation.
|
||||
*
|
||||
* Inputs: text - Input string.
|
||||
* Should be two letters (A thru R) and two digits.
|
||||
|
@ -497,7 +568,7 @@ int tt_text_to_call10 (char *text, int quiet, char *buttons)
|
|||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
int tt_text_to_gridsquare (char *text, int quiet, char *buttons)
|
||||
int tt_text_to_satsq (char *text, int quiet, char *buttons)
|
||||
{
|
||||
|
||||
int row, col;
|
||||
|
@ -514,7 +585,7 @@ int tt_text_to_gridsquare (char *text, int quiet, char *buttons)
|
|||
|
||||
if (! quiet) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Gridsquare to DTMF: Gridsquare \"%s\" must be 4 characters.\n", text);
|
||||
dw_printf ("Satellite Gridsquare to DTMF: Gridsquare \"%s\" must be 4 characters.\n", text);
|
||||
}
|
||||
errors++;
|
||||
return (errors);
|
||||
|
@ -530,7 +601,7 @@ int tt_text_to_gridsquare (char *text, int quiet, char *buttons)
|
|||
|
||||
if (! quiet) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Gridsquare to DTMF: First two characters \"%s\" must be letters in range of A to R.\n", text);
|
||||
dw_printf ("Satellite Gridsquare to DTMF: First two characters \"%s\" must be letters in range of A to R.\n", text);
|
||||
}
|
||||
errors++;
|
||||
return (errors);
|
||||
|
@ -540,7 +611,7 @@ int tt_text_to_gridsquare (char *text, int quiet, char *buttons)
|
|||
|
||||
if (! quiet) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Gridsquare to DTMF: Last two characters \"%s\" must digits.\n", text);
|
||||
dw_printf ("Satellite Gridsquare to DTMF: Last two characters \"%s\" must digits.\n", text);
|
||||
}
|
||||
errors++;
|
||||
return (errors);
|
||||
|
@ -569,13 +640,13 @@ int tt_text_to_gridsquare (char *text, int quiet, char *buttons)
|
|||
errors++;
|
||||
if (! quiet) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Gridsquare to DTMF: Sorry, your location can't be converted to DTMF.\n");
|
||||
dw_printf ("Satellite Gridsquare to DTMF: Sorry, your location can't be converted to DTMF.\n");
|
||||
}
|
||||
}
|
||||
|
||||
return (errors);
|
||||
|
||||
} /* end tt_text_to_gridsquare */
|
||||
} /* end tt_text_to_satsq */
|
||||
|
||||
|
||||
|
||||
|
@ -765,6 +836,79 @@ int tt_two_key_to_text (char *buttons, int quiet, char *text)
|
|||
} /* end tt_two_key_to_text */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: tt_two_digits_to_letter
|
||||
*
|
||||
* Purpose: Convert the two digit representation to one letter.
|
||||
*
|
||||
* Inputs: buttons - Input string.
|
||||
* Should contain exactly two digits.
|
||||
*
|
||||
* quiet - True to suppress error messages.
|
||||
*
|
||||
* Outputs: text - Converted to string which should contain one upper case letter.
|
||||
* If error, use 'x' as a placeholder because we are probably
|
||||
* dealing with fixed length strings where position matters.
|
||||
*
|
||||
* Returns: Number of errors detected.
|
||||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
// TODO: need to test
|
||||
|
||||
int tt_two_digits_to_letter (char *buttons, int quiet, char *text)
|
||||
{
|
||||
char c1 = buttons[0];
|
||||
char c2 = buttons[1];
|
||||
int row, col;
|
||||
int errors = 0;
|
||||
|
||||
strcpy (text, "x");
|
||||
|
||||
if (c1 >= '2' && c1 <= '9') {
|
||||
|
||||
if (c2 >= '1' && c2 <= '4') {
|
||||
|
||||
row = c1 - '0';
|
||||
col = c2 - '1';
|
||||
|
||||
if (translate[row][col] != 0) {
|
||||
text[0] = translate[row][col];
|
||||
text[1] = '\0';
|
||||
}
|
||||
else {
|
||||
errors++;
|
||||
strcpy (text, "x");
|
||||
if (! quiet) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Two digits to letter: Invalid combination \"%c%c\".\n", c1, c2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
errors++;
|
||||
strcpy (text, "x");
|
||||
if (! quiet) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Two digits to letter: Second character \"%c\" must be in range of 1 through 4.\n", c2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
errors++;
|
||||
strcpy (text, "x");
|
||||
if (! quiet) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Two digits to letter: First character \"%c\" must be in range of 2 through 9.\n", c1);
|
||||
}
|
||||
}
|
||||
|
||||
return (errors);
|
||||
|
||||
} /* end tt_two_digits_to_letter */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: tt_call10_to_text
|
||||
|
@ -864,9 +1008,218 @@ int tt_call10_to_text (char *buttons, int quiet, char *text)
|
|||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: tt_gridsquare_to_text
|
||||
* Name: tt_mhead_to_text
|
||||
*
|
||||
* Purpose: Convert the 4 digit DTMF gridsquare to normal 2 letters and 2 digits.
|
||||
* Purpose: Convert the 4, 6, 10, or 12 digit DTMF representation of Maidenhead
|
||||
* Grid Square Locator to normal text representation.
|
||||
*
|
||||
* Inputs: buttons - Input string.
|
||||
* Must contain 4, 6, 10, or 12 digits.
|
||||
*
|
||||
* quiet - True to suppress error messages.
|
||||
*
|
||||
* Outputs: text - Converted to gridsquare with upper case letters and digits.
|
||||
* Length should be 2, 4, 6, or 8 with alternating letter or digit pairs.
|
||||
*
|
||||
* Returns: Number of errors detected.
|
||||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
|
||||
int tt_mhead_to_text (char *buttons, int quiet, char *text)
|
||||
{
|
||||
char *b;
|
||||
char *t;
|
||||
int errors = 0;
|
||||
|
||||
strcpy (text, "");
|
||||
|
||||
/* Validity check. */
|
||||
|
||||
if (strlen(buttons) != 4 && strlen(buttons) != 6 && strlen(buttons) != 10 && strlen(buttons) != 12) {
|
||||
|
||||
if (! quiet) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("DTMF to Maidenhead Gridsquare Locator: Input \"%s\" must be exactly 4, 6, 10, or 12 digits.\n", buttons);
|
||||
}
|
||||
errors++;
|
||||
return (errors);
|
||||
}
|
||||
|
||||
for (b = buttons; *b != '\0'; b++) {
|
||||
|
||||
if (! isdigit(*b)) {
|
||||
if (! quiet) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("DTMF to Maidenhead Gridsquare Locator: Input \"%s\" can contain only digits.\n", buttons);
|
||||
}
|
||||
errors++;
|
||||
return (errors);
|
||||
}
|
||||
}
|
||||
|
||||
b = buttons;
|
||||
t = text;
|
||||
|
||||
errors += tt_two_digits_to_letter (b, quiet, t);
|
||||
b += 2;
|
||||
t++;
|
||||
|
||||
errors += tt_two_digits_to_letter (b, quiet, t);
|
||||
b += 2;
|
||||
t++;
|
||||
|
||||
if (strlen(buttons) > 4) {
|
||||
|
||||
*t++ = *b++;
|
||||
*t++ = *b++;
|
||||
*t = '\0';
|
||||
|
||||
if (strlen(buttons) > 6) {
|
||||
|
||||
errors += tt_two_digits_to_letter (b, quiet, t);
|
||||
b += 2;
|
||||
t++;
|
||||
|
||||
errors += tt_two_digits_to_letter (b, quiet, t);
|
||||
b += 2;
|
||||
t++;
|
||||
|
||||
if (strlen(buttons) > 10) {
|
||||
|
||||
*t++ = *b++;
|
||||
*t++ = *b++;
|
||||
*t = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (errors);
|
||||
|
||||
} /* end tt_mhead_to_text */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: tt_text_to_mhead
|
||||
*
|
||||
* Purpose: Convert the 2, 4, 6, or 8 character Maidenhead
|
||||
* Grid Square Locator to DTMF representation.
|
||||
*
|
||||
* Outputs: text - Maidenhead Grid Square locator in usual format.
|
||||
* Length should be 2, 4, 6, or 8 with alternating letter or digit pairs.
|
||||
*
|
||||
* Inputs: buttons - Result with 4, 6, 10, or 12 digits.
|
||||
* Each letter is replaced by two digits.
|
||||
*
|
||||
* quiet - True to suppress error messages.
|
||||
*
|
||||
* Returns: Number of errors detected.
|
||||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
|
||||
int tt_text_to_mhead (char *text, int quiet, char *buttons)
|
||||
{
|
||||
char *b;
|
||||
char *t;
|
||||
int errors = 0;
|
||||
|
||||
strcpy (buttons, "");
|
||||
|
||||
|
||||
if (strlen(text) != 2 && strlen(text) != 4 && strlen(text) != 6 && strlen(text) != 8) {
|
||||
|
||||
if (! quiet) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Maidenhead Gridsquare Locator to DTMF: Input \"%s\" must be exactly 2, 4, 6, or 8 characters.\n", text);
|
||||
}
|
||||
errors++;
|
||||
return (errors);
|
||||
}
|
||||
|
||||
t = text;
|
||||
b = buttons;
|
||||
|
||||
if (toupper(t[0]) < 'A' || toupper(t[0]) > 'R' || toupper(t[1]) < 'A' || toupper(t[1]) > 'R') {
|
||||
if (! quiet) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("The first pair of characters in Maidenhead locator \"%s\" must be in range of A thru R.\n", text);
|
||||
}
|
||||
errors++;
|
||||
return(errors);
|
||||
}
|
||||
|
||||
errors += tt_letter_to_two_digits (*t, quiet, b);
|
||||
t++;
|
||||
b += 2;
|
||||
|
||||
errors += tt_letter_to_two_digits (*t, quiet, b);
|
||||
t++;
|
||||
b += 2;
|
||||
|
||||
if (strlen(text) > 2) {
|
||||
|
||||
if ( ! isdigit(t[0]) || ! isdigit(t[1])) {
|
||||
if (! quiet) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("The second pair of characters in Maidenhead locator \"%s\" must digits 0 thru 9.\n", text);
|
||||
}
|
||||
errors++;
|
||||
return(errors);
|
||||
}
|
||||
|
||||
*b++ = *t++;
|
||||
*b++ = *t++;
|
||||
*b = '\0';
|
||||
|
||||
if (strlen(text) > 4) {
|
||||
|
||||
if (toupper(t[0]) < 'A' || toupper(t[0]) > 'X' || toupper(t[1]) < 'A' || toupper(t[1]) > 'X') {
|
||||
if (! quiet) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("The third pair of characters in Maidenhead locator \"%s\" must be in range of A thru X.\n", text);
|
||||
}
|
||||
errors++;
|
||||
return(errors);
|
||||
}
|
||||
|
||||
errors += tt_letter_to_two_digits (*t, quiet, b);
|
||||
t++;
|
||||
b += 2;
|
||||
|
||||
errors += tt_letter_to_two_digits (*t, quiet, b);
|
||||
t++;
|
||||
b += 2;
|
||||
|
||||
if (strlen(text) > 6) {
|
||||
|
||||
if ( ! isdigit(t[0]) || ! isdigit(t[1])) {
|
||||
if (! quiet) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("The fourth pair of characters in Maidenhead locator \"%s\" must digits 0 thru 9.\n", text);
|
||||
}
|
||||
errors++;
|
||||
return(errors);
|
||||
}
|
||||
|
||||
*b++ = *t++;
|
||||
*b++ = *t++;
|
||||
*b = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (errors);
|
||||
|
||||
} /* tt_text_to_mhead */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: tt_satsq_to_text
|
||||
*
|
||||
* Purpose: Convert the 4 digit DTMF special Satellite gridsquare to normal 2 letters and 2 digits.
|
||||
*
|
||||
* Inputs: buttons - Input string.
|
||||
* Should contain 4 digits.
|
||||
|
@ -879,7 +1232,7 @@ int tt_call10_to_text (char *buttons, int quiet, char *text)
|
|||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
int tt_gridsquare_to_text (char *buttons, int quiet, char *text)
|
||||
int tt_satsq_to_text (char *buttons, int quiet, char *text)
|
||||
{
|
||||
char *b;
|
||||
int row, col;
|
||||
|
@ -893,7 +1246,7 @@ int tt_gridsquare_to_text (char *buttons, int quiet, char *text)
|
|||
|
||||
if (! quiet) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("DTMF to Gridsquare: Input \"%s\" must be exactly 4 digits.\n", buttons);
|
||||
dw_printf ("DTMF to Satellite Gridsquare: Input \"%s\" must be exactly 4 digits.\n", buttons);
|
||||
}
|
||||
errors++;
|
||||
return (errors);
|
||||
|
@ -904,7 +1257,7 @@ int tt_gridsquare_to_text (char *buttons, int quiet, char *text)
|
|||
if (! isdigit(*b)) {
|
||||
if (! quiet) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("DTMF to Gridsquare: Input \"%s\" can contain only digits.\n", buttons);
|
||||
dw_printf ("DTMF to Satellite Gridsquare: Input \"%s\" can contain only digits.\n", buttons);
|
||||
}
|
||||
errors++;
|
||||
return (errors);
|
||||
|
@ -919,7 +1272,7 @@ int tt_gridsquare_to_text (char *buttons, int quiet, char *text)
|
|||
|
||||
return (errors);
|
||||
|
||||
} /* end tt_gridsquare_to_text */
|
||||
} /* end tt_satsq_to_text */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
|
@ -1042,9 +1395,15 @@ int main (int argc, char *argv[])
|
|||
dw_printf ("\"%s\"\n", buttons);
|
||||
}
|
||||
|
||||
n = tt_text_to_gridsquare (text, 1, buttons);
|
||||
n = tt_text_to_mhead (text, 1, buttons);
|
||||
if (n == 0) {
|
||||
dw_printf ("Push buttons for gridsquare:\n");
|
||||
dw_printf ("Push buttons for Maidenhead Grid Square Locator:\n");
|
||||
dw_printf ("\"%s\"\n", buttons);
|
||||
}
|
||||
|
||||
n = tt_text_to_satsq (text, 1, buttons);
|
||||
if (n == 0) {
|
||||
dw_printf ("Push buttons for satellite gridsquare:\n");
|
||||
dw_printf ("\"%s\"\n", buttons);
|
||||
}
|
||||
|
||||
|
@ -1112,9 +1471,15 @@ int main (int argc, char *argv[])
|
|||
dw_printf ("\"%s\"\n", text);
|
||||
}
|
||||
|
||||
n = tt_gridsquare_to_text (buttons, 1, text);
|
||||
n = tt_mhead_to_text (buttons, 1, text);
|
||||
if (n == 0) {
|
||||
dw_printf ("Decoded gridsquare from 4 DTMF digits:\n");
|
||||
dw_printf ("Decoded Maidenhead Locator from DTMF digits:\n");
|
||||
dw_printf ("\"%s\"\n", text);
|
||||
}
|
||||
|
||||
n = tt_satsq_to_text (buttons, 1, text);
|
||||
if (n == 0) {
|
||||
dw_printf ("Decoded satellite gridsquare from 4 DTMF digits:\n");
|
||||
dw_printf ("\"%s\"\n", text);
|
||||
}
|
||||
|
||||
|
|
10
tt_text.h
10
tt_text.h
|
@ -2,7 +2,7 @@
|
|||
/* tt_text.h */
|
||||
|
||||
|
||||
/* Encode to DTMF representation. */
|
||||
/* Encode normal human readable to DTMF representation. */
|
||||
|
||||
int tt_text_to_multipress (char *text, int quiet, char *buttons);
|
||||
|
||||
|
@ -10,7 +10,9 @@ int tt_text_to_two_key (char *text, int quiet, char *buttons);
|
|||
|
||||
int tt_text_to_call10 (char *text, int quiet, char *buttons) ;
|
||||
|
||||
int tt_text_to_gridsquare (char *text, int quiet, char *buttons) ;
|
||||
int tt_text_to_mhead (char *text, int quiet, char *buttons) ;
|
||||
|
||||
int tt_text_to_satsq (char *text, int quiet, char *buttons) ;
|
||||
|
||||
|
||||
/* Decode DTMF to normal human readable form. */
|
||||
|
@ -21,7 +23,9 @@ int tt_two_key_to_text (char *buttons, int quiet, char *text);
|
|||
|
||||
int tt_call10_to_text (char *buttons, int quiet, char *text);
|
||||
|
||||
int tt_gridsquare_to_text (char *buttons, int quiet, char *text);
|
||||
int tt_mhead_to_text (char *buttons, int quiet, char *text);
|
||||
|
||||
int tt_satsq_to_text (char *buttons, int quiet, char *text);
|
||||
|
||||
|
||||
/* end tt_text.h */
|
421
tt_user.c
421
tt_user.c
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2013, 2014 John Langner, WB2OSZ
|
||||
// Copyright (C) 2013, 2014, 2015 John Langner, WB2OSZ
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
@ -55,6 +55,11 @@
|
|||
#include "encode_aprs.h"
|
||||
#include "latlong.h"
|
||||
|
||||
#include "server.h"
|
||||
#include "kiss.h"
|
||||
#include "kissnet.h"
|
||||
|
||||
|
||||
/*
|
||||
* Information kept about local APRStt users.
|
||||
*
|
||||
|
@ -86,6 +91,10 @@ static struct tt_user_s {
|
|||
/* Possibly other tactical call / object label. */
|
||||
/* Null string indicates table position is not used. */
|
||||
|
||||
int count; /* Number of times we received information for this object. */
|
||||
/* Value 1 means first time and could be used to send */
|
||||
/* a welcome greeting. */
|
||||
|
||||
int ssid; /* SSID to add. */
|
||||
/* Default of 12 but not always. */
|
||||
|
||||
|
@ -108,6 +117,7 @@ static struct tt_user_s {
|
|||
/* 3 within 30 seconds to improve chances of */
|
||||
/* being heard while using digipeater duplicate */
|
||||
/* removal. */
|
||||
// TODO: I think implementation is different.
|
||||
|
||||
time_t next_xmit; /* Time for next transmit. Meaningful only */
|
||||
/* if xmits > 0. */
|
||||
|
@ -116,23 +126,53 @@ static struct tt_user_s {
|
|||
/* Otherwise, this is a display offset position */
|
||||
/* from the gateway. */
|
||||
|
||||
char loc_text[24]; /* Text representation of location when a single */
|
||||
/* lat/lon point would be deceptive. e.g. */
|
||||
/* 32TPP8049 */
|
||||
/* 32TPP8179549363 */
|
||||
/* 32T 681795 4849363 */
|
||||
/* EM29QE78 */
|
||||
|
||||
double latitude, longitude; /* Location either from user or generated */
|
||||
/* position in the corral. */
|
||||
|
||||
char freq[12]; /* Frequency in format 999.999MHz */
|
||||
|
||||
char comment[MAX_COMMENT_LEN+1]; /* Free form comment. */
|
||||
char comment[MAX_COMMENT_LEN+1]; /* Free form comment from user. */
|
||||
/* Comment sent in final object report includes */
|
||||
/* other information besides this. */
|
||||
|
||||
char mic_e; /* Position status. */
|
||||
/* Should be a character in range of '1' to '9' for */
|
||||
/* the predefined status strings or '0' for none. */
|
||||
|
||||
char dao[8]; /* Enhanced position information. */
|
||||
|
||||
|
||||
} tt_user[MAX_TT_USERS];
|
||||
|
||||
|
||||
static void clear_user(int i);
|
||||
|
||||
static void xmit_object_report (int i, double c_lat, double c_long, int ambiguity, double c_offs);
|
||||
static void xmit_object_report (int i, int first_time);
|
||||
|
||||
static void tt_setenv (int i);
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
// setenv is missing on Windows!
|
||||
|
||||
int setenv(const char *name, const char *value, int overwrite)
|
||||
{
|
||||
char etemp[1000];
|
||||
|
||||
snprintf (etemp, sizeof(etemp), "%s=%s", name, value);
|
||||
putenv (etemp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
|
@ -244,7 +284,7 @@ static void clear_user(int i)
|
|||
{
|
||||
assert (i >= 0 && i < MAX_TT_USERS);
|
||||
|
||||
memset (&tt_user[i], 0, sizeof (struct tt_user_s));
|
||||
memset (&(tt_user[i]), 0, sizeof (struct tt_user_s));
|
||||
|
||||
} /* end clear_user */
|
||||
|
||||
|
@ -341,7 +381,7 @@ static void digit_suffix (char *callsign, char *suffix)
|
|||
char *t;
|
||||
|
||||
|
||||
strcpy (suffix, "000");
|
||||
strlcpy (suffix, "000", sizeof(suffix));
|
||||
tt_text_to_two_key (callsign, 0, two_key);
|
||||
for (t = two_key; *t != '\0'; t++) {
|
||||
if (isdigit(*t)) {
|
||||
|
@ -365,6 +405,7 @@ static void digit_suffix (char *callsign, char *suffix)
|
|||
* ssid
|
||||
* overlay - or symbol table identifier
|
||||
* symbol
|
||||
* loc_text - Original text for non lat/lon location
|
||||
* latitude
|
||||
* longitude
|
||||
* freq
|
||||
|
@ -380,11 +421,15 @@ static void digit_suffix (char *callsign, char *suffix)
|
|||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double latitude,
|
||||
int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, char *loc_text, double latitude,
|
||||
double longitude, char *freq, char *comment, char mic_e, char *dao)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("tt_user_heard (%s, %d, %c, %c, %s, ...)\n", callsign, ssid, overlay, symbol, loc_text);
|
||||
|
||||
/*
|
||||
* At this time all messages are expected to contain a callsign.
|
||||
* Other types of messages, not related to a particular person/object
|
||||
|
@ -392,7 +437,7 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double l
|
|||
*/
|
||||
if (callsign[0] == '\0') {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
printf ("APRStt message did not include callsign.\n");
|
||||
printf ("APRStt tone sequence did not include callsign / object name.\n");
|
||||
return (TT_ERROR_NO_CALL);
|
||||
}
|
||||
|
||||
|
@ -410,10 +455,13 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double l
|
|||
assert (i >= 0 && i < MAX_TT_USERS);
|
||||
strncpy (tt_user[i].callsign, callsign, MAX_CALLSIGN_LEN);
|
||||
tt_user[i].callsign[MAX_CALLSIGN_LEN] = '\0';
|
||||
tt_user[i].count = 1;
|
||||
tt_user[i].ssid = ssid;
|
||||
tt_user[i].overlay = overlay;
|
||||
tt_user[i].symbol = symbol;
|
||||
digit_suffix(tt_user[i].callsign, tt_user[i].digit_suffix);
|
||||
strlcpy (tt_user[i].loc_text, loc_text, sizeof(tt_user[i].loc_text));
|
||||
|
||||
if (latitude != G_UNKNOWN && longitude != G_UNKNOWN) {
|
||||
/* We have specific location. */
|
||||
tt_user[i].corral_slot = 0;
|
||||
|
@ -425,7 +473,7 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double l
|
|||
tt_user[i].corral_slot = corral_slot();
|
||||
}
|
||||
|
||||
strcpy (tt_user[i].freq, freq);
|
||||
strlcpy (tt_user[i].freq, freq, sizeof(tt_user[i].freq));
|
||||
strncpy (tt_user[i].comment, comment, MAX_COMMENT_LEN);
|
||||
tt_user[i].comment[MAX_COMMENT_LEN] = '\0';
|
||||
tt_user[i].mic_e = mic_e;
|
||||
|
@ -437,8 +485,14 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double l
|
|||
*/
|
||||
assert (i >= 0 && i < MAX_TT_USERS);
|
||||
|
||||
tt_user[i].count++;
|
||||
|
||||
/* Any reason to look at ssid here? */
|
||||
|
||||
if (strlen(loc_text) > 0) {
|
||||
strlcpy (tt_user[i].loc_text, loc_text, sizeof(tt_user[i].loc_text));
|
||||
}
|
||||
|
||||
if (latitude != G_UNKNOWN && longitude != G_UNKNOWN) {
|
||||
/* We have specific location. */
|
||||
tt_user[i].corral_slot = 0;
|
||||
|
@ -447,7 +501,7 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double l
|
|||
}
|
||||
|
||||
if (freq[0] != '\0') {
|
||||
strcpy (tt_user[i].freq, freq);
|
||||
strlcpy (tt_user[i].freq, freq, sizeof(tt_user[i].freq));
|
||||
}
|
||||
|
||||
if (comment[0] != '\0') {
|
||||
|
@ -471,6 +525,19 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double l
|
|||
tt_user[i].xmits = 0;
|
||||
tt_user[i].next_xmit = tt_user[i].last_heard + save_tt_config_p->xmit_delay[0];
|
||||
|
||||
/*
|
||||
* Send to applications and IGate immediately.
|
||||
*/
|
||||
|
||||
xmit_object_report (i, 1);
|
||||
|
||||
/*
|
||||
* Put properties into environment variables in preparation
|
||||
* for calling a user-specified script.
|
||||
*/
|
||||
|
||||
tt_setenv (i);
|
||||
|
||||
return (0); /* Success! */
|
||||
|
||||
} /* end tt_user_heard */
|
||||
|
@ -497,12 +564,23 @@ void tt_user_background (void)
|
|||
time_t now = time(NULL);
|
||||
int i;
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("tt_user_background() now = %d\n", (int)now);
|
||||
|
||||
|
||||
for (i=0; i<MAX_TT_USERS; i++) {
|
||||
|
||||
assert (i >= 0 && i < MAX_TT_USERS);
|
||||
|
||||
if (tt_user[i].callsign[0] != '\0') {
|
||||
if (tt_user[i].xmits < save_tt_config_p->num_xmits && tt_user[i].next_xmit <= now) {
|
||||
|
||||
xmit_object_report (i, save_tt_config_p->corral_lat, save_tt_config_p->corral_lon,
|
||||
save_tt_config_p->corral_ambiguity, save_tt_config_p->corral_offset);
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("tt_user_background() now = %d\n", (int)now);
|
||||
//tt_user_dump ();
|
||||
|
||||
xmit_object_report (i, 0);
|
||||
|
||||
/* Increase count of number times this one was sent. */
|
||||
tt_user[i].xmits++;
|
||||
|
@ -510,6 +588,8 @@ void tt_user_background (void)
|
|||
/* Schedule next one. */
|
||||
tt_user[i].next_xmit += save_tt_config_p->xmit_delay[tt_user[i].xmits];
|
||||
}
|
||||
|
||||
//tt_user_dump ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -521,7 +601,7 @@ void tt_user_background (void)
|
|||
if (tt_user[i].callsign[0] != '\0') {
|
||||
if (tt_user[i].last_heard + save_tt_config_p->retain_time < now) {
|
||||
|
||||
// debug - dw_printf ("debug: purging expired user %d\n", i);
|
||||
//dw_printf ("debug: purging expired user %d\n", i);
|
||||
|
||||
clear_user (i);
|
||||
}
|
||||
|
@ -536,11 +616,13 @@ void tt_user_background (void)
|
|||
*
|
||||
* Purpose: Create object report packet and put into transmit queue.
|
||||
*
|
||||
* Inputs: i - Index into user table.
|
||||
* c_lat - Corral latitude.
|
||||
* c_long - Corral longitude.
|
||||
* ambiguity - Number of amibiguity digits: 0, 1, 2, or 3.
|
||||
* c_offs - Corral (latitude) offset.
|
||||
* Inputs: i - Index into user table.
|
||||
*
|
||||
* first_time - Is this being called immediately after the tone sequence
|
||||
* was received or after some delay?
|
||||
* For the former, we send to any attached applications
|
||||
* and the IGate.
|
||||
* For the latter, we transmit over radio.
|
||||
*
|
||||
* Outputs: Append to transmit queue.
|
||||
*
|
||||
|
@ -561,30 +643,20 @@ void tt_user_background (void)
|
|||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
static const char *mic_e_position_comment[10] = {
|
||||
"",
|
||||
"/off duty ",
|
||||
"/enroute ",
|
||||
"/in service",
|
||||
"/returning ",
|
||||
"/committed ",
|
||||
"/special ",
|
||||
"/priority ",
|
||||
"/emergency ",
|
||||
"/custom 1 " };
|
||||
|
||||
static void xmit_object_report (int i, double c_lat, double c_long, int ambiguity, double c_offs)
|
||||
static void xmit_object_report (int i, int first_time)
|
||||
{
|
||||
char object_name[20];
|
||||
char object_info[50];
|
||||
char info_comment[100];
|
||||
char object_name[20]; // xxxxxxxxx or xxxxxx-nn
|
||||
char info_comment[200]; // usercomment [locationtext] /status !DAO!
|
||||
char object_info[250]; // info part of Object Report packet
|
||||
char stemp[300]; // src>dest,path:object_info
|
||||
|
||||
double olat, olong;
|
||||
char stemp[200];
|
||||
packet_t pp;
|
||||
unsigned char fbuf[AX25_MAX_PACKET_LEN];
|
||||
int flen;
|
||||
char c4[4];
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//printf ("xmit_object_report (index = %d, first_time = %d) rx = %d, tx = %d\n", i, first_time,
|
||||
// save_tt_config_p->obj_recv_chan, save_tt_config_p->obj_xmit_chan);
|
||||
|
||||
assert (i >= 0 && i < MAX_TT_USERS);
|
||||
|
||||
|
@ -592,12 +664,12 @@ static void xmit_object_report (int i, double c_lat, double c_long, int ambiguit
|
|||
* Prepare the object name.
|
||||
* Tack on "-12" if it is a callsign.
|
||||
*/
|
||||
strcpy (object_name, tt_user[i].callsign);
|
||||
strlcpy (object_name, tt_user[i].callsign, sizeof(object_name));
|
||||
|
||||
if (strlen(object_name) <= 6 && tt_user[i].ssid != 0) {
|
||||
char stemp8[8];
|
||||
sprintf (stemp8, "-%d", tt_user[i].ssid);
|
||||
strcat (object_name, stemp8);
|
||||
snprintf (stemp8, sizeof(stemp8), "-%d", tt_user[i].ssid);
|
||||
strlcat (object_name, stemp8, sizeof(object_name));
|
||||
}
|
||||
|
||||
if (tt_user[i].corral_slot == 0) {
|
||||
|
@ -611,61 +683,93 @@ static void xmit_object_report (int i, double c_lat, double c_long, int ambiguit
|
|||
/*
|
||||
* Use made up position in the corral.
|
||||
*/
|
||||
double c_lat = save_tt_config_p->corral_lat; // Corral latitude.
|
||||
double c_long = save_tt_config_p->corral_lon; // Corral longitude.
|
||||
double c_offs = save_tt_config_p->corral_offset; // Corral (latitude) offset.
|
||||
|
||||
olat = c_lat - (tt_user[i].corral_slot - 1) * c_offs;
|
||||
olong = c_long;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build comment field from various information.
|
||||
*
|
||||
* usercomment [locationtext] /status !DAO!
|
||||
*
|
||||
* Any frequency is inserted at beginning later.
|
||||
*/
|
||||
strcpy (info_comment, "");
|
||||
strlcpy (info_comment, "", sizeof(info_comment));
|
||||
|
||||
if (strlen(tt_user[i].comment) != 0) {
|
||||
strcat (info_comment, tt_user[i].comment);
|
||||
strlcat (info_comment, tt_user[i].comment, sizeof(info_comment));
|
||||
}
|
||||
|
||||
if (strlen(tt_user[i].loc_text) > 0) {
|
||||
if (strlen(info_comment) > 0) {
|
||||
strlcat (info_comment, " ", sizeof(info_comment));
|
||||
}
|
||||
strlcat (info_comment, "[", sizeof(info_comment));
|
||||
strlcat (info_comment, tt_user[i].loc_text, sizeof(info_comment));
|
||||
strlcat (info_comment, "]", sizeof(info_comment));
|
||||
}
|
||||
|
||||
if (tt_user[i].mic_e >= '1' && tt_user[i].mic_e <= '9') {
|
||||
strcat (info_comment, mic_e_position_comment[tt_user[i].mic_e - '0']);
|
||||
|
||||
if (strlen(info_comment) > 0) {
|
||||
strlcat (info_comment, " ", sizeof(info_comment));
|
||||
}
|
||||
|
||||
// Insert "/" if status does not already begin with it.
|
||||
if (save_tt_config_p->status[tt_user[i].mic_e - '0'][0] != '/') {
|
||||
strlcat (info_comment, "/", sizeof(info_comment));
|
||||
}
|
||||
strlcat (info_comment, save_tt_config_p->status[tt_user[i].mic_e - '0'], sizeof(info_comment));
|
||||
}
|
||||
|
||||
if (strlen(tt_user[i].dao) > 0) {
|
||||
strcat (info_comment, tt_user[i].dao);
|
||||
if (strlen(info_comment) > 0) {
|
||||
strlcat (info_comment, " ", sizeof(info_comment));
|
||||
}
|
||||
strlcat (info_comment, tt_user[i].dao, sizeof(info_comment));
|
||||
}
|
||||
|
||||
/* Official limit is 43 characters. */
|
||||
info_comment[MAX_COMMENT_LEN] = '\0';
|
||||
//info_comment[MAX_COMMENT_LEN] = '\0';
|
||||
|
||||
/*
|
||||
* Packet header is built from mycall (of transmit channel) and software version.
|
||||
*/
|
||||
|
||||
strcpy (stemp, save_audio_config_p->achan[save_tt_config_p->obj_xmit_chan].mycall);
|
||||
strcat (stemp, ">");
|
||||
strcat (stemp, APP_TOCALL);
|
||||
if (save_tt_config_p->obj_xmit_chan >= 0) {
|
||||
strlcpy (stemp, save_audio_config_p->achan[save_tt_config_p->obj_xmit_chan].mycall, sizeof(stemp));
|
||||
}
|
||||
else {
|
||||
strlcpy (stemp, save_audio_config_p->achan[save_tt_config_p->obj_recv_chan].mycall, sizeof(stemp));
|
||||
}
|
||||
strlcat (stemp, ">", sizeof(stemp));
|
||||
strlcat (stemp, APP_TOCALL, sizeof(stemp));
|
||||
c4[0] = '0' + MAJOR_VERSION;
|
||||
c4[1] = '0' + MINOR_VERSION;
|
||||
c4[2] = '\0';
|
||||
strcat (stemp, c4);
|
||||
strlcat (stemp, c4, sizeof(stemp));
|
||||
|
||||
/*
|
||||
* Append via path if specified.
|
||||
* Append via path, for transmission, if specified.
|
||||
*/
|
||||
|
||||
if (save_tt_config_p->obj_xmit_via[0] != '\0') {
|
||||
strcat (stemp, ",");
|
||||
strcat (stemp, save_tt_config_p->obj_xmit_via);
|
||||
if ( ! first_time && save_tt_config_p->obj_xmit_via[0] != '\0') {
|
||||
strlcat (stemp, ",", sizeof(stemp));
|
||||
strlcat (stemp, save_tt_config_p->obj_xmit_via, sizeof(stemp));
|
||||
}
|
||||
|
||||
strcat (stemp, ":");
|
||||
strlcat (stemp, ":", sizeof(stemp));
|
||||
|
||||
encode_object (object_name, 0, tt_user[i].last_heard, olat, olong,
|
||||
tt_user[i].overlay, tt_user[i].symbol,
|
||||
0,0,0,NULL, 0,0, /* PHGD, C/S */
|
||||
atof(tt_user[i].freq), 0, 0, info_comment, object_info);
|
||||
|
||||
strcat (stemp, object_info);
|
||||
|
||||
//text_color_set(DW_COLOR_ERROR);
|
||||
//printf ("\nDEBUG: %s\n\n", stemp);
|
||||
atof(tt_user[i].freq), 0, 0, info_comment, object_info, sizeof(object_info));
|
||||
|
||||
strlcat (stemp, object_info, sizeof(stemp));
|
||||
|
||||
#if TT_MAIN
|
||||
|
||||
|
@ -673,32 +777,201 @@ static void xmit_object_report (int i, double c_lat, double c_long, int ambiguit
|
|||
|
||||
#else
|
||||
|
||||
if (first_time) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("[APRStt] %s\n", stemp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert to packet and append to transmit queue.
|
||||
* Convert text to packet.
|
||||
*/
|
||||
pp = ax25_from_text (stemp, 1);
|
||||
|
||||
flen = ax25_pack (pp, fbuf);
|
||||
if (pp == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\"%s\"\n", stemp);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process as if we heard ourself.
|
||||
|
||||
/*
|
||||
* Send to one or more of the following depending on configuration:
|
||||
* Transmit queue.
|
||||
* Any attached application(s).
|
||||
* IGate.
|
||||
*
|
||||
* When transmitting over the radio, it gets sent multipe times, to help
|
||||
* probablity of being heard, with increasing delays between.
|
||||
*
|
||||
* The other methods are reliable so we only want to send it once.
|
||||
*/
|
||||
// TODO: We need radio channel where this came from.
|
||||
// It would make a difference if running two radios
|
||||
// and they have different station identifiers.
|
||||
|
||||
int chan = 0;
|
||||
igate_send_rec_packet (chan, pp);
|
||||
|
||||
/* Remember it so we don't digipeat our own. */
|
||||
if (first_time && save_tt_config_p->obj_send_to_app) {
|
||||
unsigned char fbuf[AX25_MAX_PACKET_LEN];
|
||||
int flen;
|
||||
|
||||
dedupe_remember (pp, save_tt_config_p->obj_xmit_chan);
|
||||
// TODO1.3: Put a wrapper around this so we only call one function to send by all methods.
|
||||
|
||||
flen = ax25_pack(pp, fbuf);
|
||||
|
||||
server_send_rec_packet (save_tt_config_p->obj_recv_chan, pp, fbuf, flen);
|
||||
kissnet_send_rec_packet (save_tt_config_p->obj_recv_chan, fbuf, flen);
|
||||
kiss_send_rec_packet (save_tt_config_p->obj_recv_chan, fbuf, flen);
|
||||
}
|
||||
|
||||
if (first_time && save_tt_config_p->obj_send_to_ig) {
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("xmit_object_report (): send to IGate\n");
|
||||
|
||||
igate_send_rec_packet (save_tt_config_p->obj_recv_chan, pp);
|
||||
|
||||
}
|
||||
|
||||
if ( ! first_time && save_tt_config_p->obj_xmit_chan >= 0) {
|
||||
|
||||
/* Remember it so we don't digipeat our own. */
|
||||
|
||||
dedupe_remember (pp, save_tt_config_p->obj_xmit_chan);
|
||||
|
||||
tq_append (save_tt_config_p->obj_xmit_chan, TQ_PRIO_1_LO, pp);
|
||||
}
|
||||
else {
|
||||
ax25_delete (pp);
|
||||
}
|
||||
|
||||
tq_append (save_tt_config_p->obj_xmit_chan, TQ_PRIO_1_LO, pp);
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
static const char *letters[26] = {
|
||||
"Alpha",
|
||||
"Bravo",
|
||||
"Charlie",
|
||||
"Delta",
|
||||
"Echo",
|
||||
"Foxtrot",
|
||||
"Golf",
|
||||
"Hotel",
|
||||
"India",
|
||||
"Juliet",
|
||||
"Kilo",
|
||||
"Lima",
|
||||
"Mike",
|
||||
"November",
|
||||
"Oscar",
|
||||
"Papa",
|
||||
"Quebec",
|
||||
"Romeo",
|
||||
"Sierra",
|
||||
"Tango",
|
||||
"Uniform",
|
||||
"Victor",
|
||||
"Whiskey",
|
||||
"X-ray",
|
||||
"Yankee",
|
||||
"Zulu"
|
||||
};
|
||||
|
||||
static const char *digits[10] = {
|
||||
"Zero",
|
||||
"One",
|
||||
"Two",
|
||||
"Three",
|
||||
"Four",
|
||||
"Five",
|
||||
"Six",
|
||||
"Seven",
|
||||
"Eight",
|
||||
"Nine"
|
||||
};
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: tt_setenv
|
||||
*
|
||||
* Purpose: Put information in environment variables in preparation
|
||||
* for calling a user-supplied script for custom processing.
|
||||
*
|
||||
* Inputs: i - Index into tt_user table.
|
||||
*
|
||||
* Description: Timestamps displayed relative to current time.
|
||||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
|
||||
static void tt_setenv (int i)
|
||||
{
|
||||
char stemp[256];
|
||||
char t2[2];
|
||||
char *p;
|
||||
|
||||
assert (i >= 0 && i < MAX_TT_USERS);
|
||||
|
||||
setenv ("TTCALL", tt_user[i].callsign, 1);
|
||||
|
||||
strlcpy (stemp, "", sizeof(stemp));
|
||||
t2[1] = '\0';
|
||||
for (p = tt_user[i].callsign; *p != '\0'; p++) {
|
||||
t2[0] = *p;
|
||||
strlcat (stemp, t2, sizeof(stemp));
|
||||
if (p[1] != '\0') strlcat (stemp, " ", sizeof(stemp));
|
||||
}
|
||||
setenv ("TTCALLSP", stemp, 1);
|
||||
|
||||
strlcpy (stemp, "", sizeof(stemp));
|
||||
for (p = tt_user[i].callsign; *p != '\0'; p++) {
|
||||
if (isupper(*p)) {
|
||||
strlcat (stemp, letters[*p - 'A'], sizeof(stemp));
|
||||
}
|
||||
else if (islower(*p)) {
|
||||
strlcat (stemp, letters[*p - 'a'], sizeof(stemp));
|
||||
}
|
||||
else if (isdigit(*p)) {
|
||||
strlcat (stemp, digits[*p - '0'], sizeof(stemp));
|
||||
}
|
||||
else {
|
||||
t2[0] = *p;
|
||||
strlcat (stemp, t2, sizeof(stemp));
|
||||
}
|
||||
if (p[1] != '\0') strlcat (stemp, " ", sizeof(stemp));
|
||||
}
|
||||
setenv ("TTCALLPH", stemp, 1);
|
||||
|
||||
snprintf (stemp, sizeof(stemp), "%d", tt_user[i].ssid);
|
||||
setenv ("TTSSID",stemp , 1);
|
||||
|
||||
snprintf (stemp, sizeof(stemp), "%d", tt_user[i].count);
|
||||
setenv ("TTCOUNT",stemp , 1);
|
||||
|
||||
snprintf (stemp, sizeof(stemp), "%c%c", tt_user[i].overlay, tt_user[i].symbol);
|
||||
setenv ("TTSYMBOL",stemp , 1);
|
||||
|
||||
snprintf (stemp, sizeof(stemp), "%.6f", tt_user[i].latitude);
|
||||
setenv ("TTLAT",stemp , 1);
|
||||
|
||||
snprintf (stemp, sizeof(stemp), "%.6f", tt_user[i].longitude);
|
||||
setenv ("TTLON",stemp , 1);
|
||||
|
||||
setenv ("TTFREQ", tt_user[i].freq, 1);
|
||||
|
||||
setenv ("TTCOMMENT", tt_user[i].comment, 1);
|
||||
|
||||
setenv ("TTLOC", tt_user[i].loc_text, 1);
|
||||
|
||||
if (tt_user[i].mic_e >= '1' && tt_user[i].mic_e <= '9') {
|
||||
setenv ("TTSTATUS", save_tt_config_p->status[tt_user[i].mic_e - '0'], 1);
|
||||
}
|
||||
else {
|
||||
setenv ("TTSTATUS", "", 1);
|
||||
}
|
||||
|
||||
setenv ("TTDAO", tt_user[i].dao, 1);
|
||||
|
||||
} /* end tt_setenv */
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
|
@ -770,7 +1043,7 @@ int main (int argc, char *argv[])
|
|||
|
||||
memset (&my_audio_config, 0, sizeof(my_audio_config));
|
||||
|
||||
strcpy (my_audio_config.achan[0].mycall, "WB2OSZ-15");
|
||||
strlcpy (my_audio_config.achan[0].mycall, "WB2OSZ-15", sizeof(my_audio_config.achan[0].mycall));
|
||||
|
||||
/* Fake TT gateway config. */
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
|
||||
void tt_user_init (struct audio_s *p_audio_config, struct tt_config_s *p);
|
||||
|
||||
int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, double latitude,
|
||||
int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, char *loc_text, double latitude,
|
||||
double longitude, char *freq, char *comment, char mic_e, char *dao);
|
||||
|
||||
void tt_user_background (void);
|
||||
void tt_user_background (void);
|
||||
void tt_user_dump (void);
|
4
utm2ll.c
4
utm2ll.c
|
@ -7,7 +7,7 @@
|
|||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "utm.h"
|
||||
#include "mgrs.h"
|
||||
#include "usng.h"
|
||||
|
@ -40,7 +40,7 @@ int main (int argc, char *argv[])
|
|||
|
||||
// 3 command line arguments for UTM
|
||||
|
||||
strcpy (szone, argv[1]);
|
||||
strlcpy (szone, argv[1], sizeof(szone));
|
||||
lzone = strtoul(szone, &zlet, 10);
|
||||
|
||||
if (*zlet == '\0') {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
/* Dire Wolf version 1.2 */
|
||||
/* Dire Wolf version 1.3 */
|
||||
|
||||
#define APP_TOCALL "APDW"
|
||||
|
||||
#define MAJOR_VERSION 1
|
||||
#define MINOR_VERSION 2
|
||||
#define EXTRA_VERSION "Beta Test"
|
||||
#define MINOR_VERSION 3
|
||||
//#define EXTRA_VERSION "Beta Test"
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2015 John Langner, WB2OSZ
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// 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/>.
|
||||
//
|
||||
|
||||
|
||||
//#define DEBUG 1
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Module: walk96.c
|
||||
*
|
||||
* Purpose: Quick hack to read GPS location and send very frequent
|
||||
* position reports frames to a KISS TNC.
|
||||
*
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if __WIN32__
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#define __USE_XOPEN2KXSI 1
|
||||
#define __USE_XOPEN 1
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/errno.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "config.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
#include "latlong.h"
|
||||
#include "nmea.h"
|
||||
#include "encode_aprs.h"
|
||||
#include "serial_port.h"
|
||||
|
||||
|
||||
#define MYCALL "WB2OSZ" /************ Change this if you use it!!! ***************/
|
||||
|
||||
static MYFDTYPE tnc;
|
||||
|
||||
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
struct misc_config_s config;
|
||||
char cmd[100];
|
||||
|
||||
|
||||
// Look for Silicon Labs CP210x
|
||||
// Just happens to be same on desktop & laptop.
|
||||
|
||||
tnc = serial_port_open ("COM5", 9600);
|
||||
if (tnc == MYFDERROR) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Can't open serial port to KISS TNC.\n");
|
||||
exit (EXIT_FAILURE); // defined in stdlib.h
|
||||
}
|
||||
|
||||
strcpy (cmd, "\r\rhbaud 9600\rkiss on\rrestart\r");
|
||||
|
||||
serial_port_write (tnc, cmd, strlen(cmd));
|
||||
SLEEP_MS(500);
|
||||
|
||||
memset (&config, 0, sizeof(config));
|
||||
strcpy (config.nmea_port, "COM1");
|
||||
nmea_init (&config);
|
||||
|
||||
SLEEP_SEC(20);
|
||||
|
||||
// Exit out of KISS mode.
|
||||
|
||||
serial_port_write (tnc, "\xc0\xff\c0", 3);
|
||||
|
||||
SLEEP_MS(100);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Should be called once per second. */
|
||||
|
||||
void walk96 (int fix, double lat, double lon, float knots, float course, float alt)
|
||||
{
|
||||
static int sequence = 0;
|
||||
char comment[50];
|
||||
|
||||
sequence++;
|
||||
sprintf (comment, "Sequence number %04d", sequence);
|
||||
|
||||
|
||||
/*
|
||||
* Construct the packet in normal monitoring format.
|
||||
*/
|
||||
|
||||
int messaging = 0;
|
||||
int compressed = 0;
|
||||
|
||||
char info[AX25_MAX_INFO_LEN];
|
||||
int info_len;
|
||||
|
||||
char position_report[AX25_MAX_PACKET_LEN];
|
||||
|
||||
info_len = encode_position (messaging, compressed,
|
||||
lat, lon, (int)(DW_METERS_TO_FEET(alt)),
|
||||
'/', '?', // TODO: look up code for person.
|
||||
G_UNKNOWN, G_UNKNOWN, G_UNKNOWN, "", // PHG
|
||||
(int)course, (int)knots,
|
||||
445.925, 0, 0,
|
||||
comment,
|
||||
info, sizeof(info));
|
||||
|
||||
sprintf (position_report, "%s>WALK96:%s", MYCALL, info);
|
||||
|
||||
text_color_set (DW_COLOR_XMIT);
|
||||
dw_printf ("%s\n", position_report);
|
||||
|
||||
/*
|
||||
* Convert it into AX.25 frame.
|
||||
*/
|
||||
packet_t pp;
|
||||
unsigned char ax25_frame[AX25_MAX_PACKET_LEN];
|
||||
int frame_len;
|
||||
|
||||
pp = ax25_from_text (position_report, 1);
|
||||
|
||||
if (pp == NULL) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Unexpected error in ax25_from_text. Quitting.\n");
|
||||
exit (EXIT_FAILURE); // defined in stdlib.h
|
||||
}
|
||||
|
||||
ax25_frame[0] = 0; // Insert channel before KISS encapsulation.
|
||||
|
||||
frame_len = ax25_pack (pp, ax25_frame+1);
|
||||
ax25_delete (pp);
|
||||
|
||||
/*
|
||||
* Encapsulate as KISS and send to TNC.
|
||||
*/
|
||||
|
||||
unsigned char kiss_frame[AX25_MAX_PACKET_LEN*2];
|
||||
int kiss_len;
|
||||
|
||||
kiss_len = kiss_encapsulate (ax25_frame, frame_len+1, (unsigned char *)kiss_frame);
|
||||
|
||||
//text_color_set (DW_COLOR_DEBUG);
|
||||
//dw_printf ("AX.25 frame length = %d, KISS frame length = %d\n", frame_len, kiss_len);
|
||||
|
||||
//kiss_debug_print (1, NULL, kiss_frame, kiss_len);
|
||||
|
||||
serial_port_write (tnc, kiss_frame, kiss_len);
|
||||
|
||||
}
|
||||
|
||||
/* end walk96.c */
|
82
xmit.c
82
xmit.c
|
@ -76,6 +76,7 @@
|
|||
#include "hdlc_rec.h"
|
||||
#include "ptt.h"
|
||||
#include "dtime_now.h"
|
||||
#include "morse.h"
|
||||
|
||||
|
||||
|
||||
|
@ -98,7 +99,7 @@ static int xmit_persist[MAX_CHANS]; /* Sets probability for transmitting after e
|
|||
static int xmit_txdelay[MAX_CHANS]; /* After turning on the transmitter, */
|
||||
/* send "flags" for txdelay * 10 mS. */
|
||||
|
||||
static int xmit_txtail[MAX_CHANS]; /* Amount of time to keep transmitting after we */
|
||||
static int xmit_txtail[MAX_CHANS]; /* Amount of time to keep transmitting after we */
|
||||
/* are done sending the data. This is to avoid */
|
||||
/* dropping PTT too soon and chopping off the end */
|
||||
/* of the frame. Again 10 mS units. */
|
||||
|
@ -137,6 +138,7 @@ static dw_mutex_t audio_out_dev_mutex[MAX_ADEVS];
|
|||
static int wait_for_clear_channel (int channel, int nowait, int slotttime, int persist);
|
||||
static void xmit_ax25_frames (int c, int p, packet_t pp);
|
||||
static void xmit_speech (int c, packet_t pp);
|
||||
static void xmit_morse (int c, packet_t pp, int wpm);
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
|
@ -433,11 +435,18 @@ static void * xmit_thread (void *arg)
|
|||
if (ok) {
|
||||
/*
|
||||
* Channel is clear and we have lock on output device.
|
||||
*
|
||||
* If destination is "SPEECH" send info part to speech synthesizer.
|
||||
* If destination is "MORSE" send as morse code.
|
||||
*/
|
||||
char dest[AX25_MAX_ADDR_LEN];
|
||||
int ssid = 0;
|
||||
|
||||
|
||||
if (ax25_is_aprs (pp)) {
|
||||
ax25_get_addr_with_ssid(pp, AX25_DESTINATION, dest);
|
||||
|
||||
ax25_get_addr_no_ssid(pp, AX25_DESTINATION, dest);
|
||||
ssid = ax25_get_ssid(pp, AX25_DESTINATION);
|
||||
}
|
||||
else {
|
||||
strcpy (dest, "");
|
||||
|
@ -446,6 +455,24 @@ static void * xmit_thread (void *arg)
|
|||
if (strcmp(dest, "SPEECH") == 0) {
|
||||
xmit_speech (c, pp);
|
||||
}
|
||||
else if (strcmp(dest, "MORSE") == 0) {
|
||||
|
||||
int wpm = ssid * 2;
|
||||
if (wpm == 0) wpm = MORSE_DEFAULT_WPM;
|
||||
|
||||
// This is a bit of a hack so we don't respond too quickly for APRStt.
|
||||
// It will be sent in high priority queue while a beacon wouldn't.
|
||||
// Add a little delay so user has time release PTT after sending #.
|
||||
// This and default txdelay would give us a second.
|
||||
|
||||
if (p == TQ_PRIO_0_HI) {
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("APRStt morse xmit delay hack...\n");
|
||||
SLEEP_MS (700);
|
||||
}
|
||||
|
||||
xmit_morse (c, pp, wpm);
|
||||
}
|
||||
else {
|
||||
xmit_ax25_frames (c, p, pp);
|
||||
}
|
||||
|
@ -748,12 +775,13 @@ static void xmit_ax25_frames (int c, int p, packet_t pp)
|
|||
} /* end xmit_ax25_frames */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: xmit_ax25_frames
|
||||
* Name: xmit_speech
|
||||
*
|
||||
* Purpose: After we have a clear channel, and possibly waited a random time,
|
||||
* we transmit one or more frames.
|
||||
* we transmit information part of frame as speech.
|
||||
*
|
||||
* Inputs: c - Channel number.
|
||||
*
|
||||
|
@ -765,7 +793,6 @@ static void xmit_ax25_frames (int c, int p, packet_t pp)
|
|||
* Invoke the text-to-speech script.
|
||||
* Turn off transmitter.
|
||||
*
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
@ -861,6 +888,51 @@ int xmit_speak_it (char *script, int c, char *orig_msg)
|
|||
}
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: xmit_morse
|
||||
*
|
||||
* Purpose: After we have a clear channel, and possibly waited a random time,
|
||||
* we transmit information part of frame as Morse code.
|
||||
*
|
||||
* Inputs: c - Channel number.
|
||||
*
|
||||
* pp - Packet object pointer.
|
||||
* It will be deleted so caller should not try
|
||||
* to reference it after this.
|
||||
*
|
||||
* wpm - Speed in words per minute.
|
||||
*
|
||||
* Description: Turn on transmitter.
|
||||
* Send text as Morse code.
|
||||
* Turn off transmitter.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static void xmit_morse (int c, packet_t pp, int wpm)
|
||||
{
|
||||
|
||||
|
||||
int info_len;
|
||||
unsigned char *pinfo;
|
||||
|
||||
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
text_color_set(DW_COLOR_XMIT);
|
||||
dw_printf ("[%d.morse] \"%s\"\n", c, pinfo);
|
||||
|
||||
ptt_set (OCTYPE_PTT, c, 1);
|
||||
|
||||
morse_send (c, (char*)pinfo, wpm, xmit_txdelay[c] * 10, xmit_txtail[c] * 10);
|
||||
|
||||
ptt_set (OCTYPE_PTT, c, 0);
|
||||
ax25_delete (pp);
|
||||
|
||||
} /* end xmit_morse */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: wait_for_clear_channel
|
||||
|
|
Loading…
Reference in New Issue