mirror of https://github.com/wb2osz/direwolf.git
Merge remote-tracking branch 'origin/dev'
Put version 1.4 into master branch.
This commit is contained in:
commit
612c2dc928
79
CHANGES.md
79
CHANGES.md
|
@ -1,12 +1,82 @@
|
|||
|
||||
# Revision History #
|
||||
|
||||
|
||||
## Version 1.4 -- April 2017 ##
|
||||
|
||||
|
||||
### New Features: ###
|
||||
|
||||
- AX.25 v2.2 connected mode. See chapter 10 of User Guide for details.
|
||||
|
||||
- New client side packet filter to select "messages" only to stations that have been heard nearby recently. This is now the default if no IS to RF filter is specified.
|
||||
|
||||
- New beacon type, IBEACON, for sending IGate statistics.
|
||||
|
||||
- Expanded debug options so you can understand what is going on with packet filtering.
|
||||
|
||||
- Added new document ***Successful-APRS-IGate-Operation.pdf*** with IGate background, configuration, and troubleshooting tips.
|
||||
- 2400 & 4800 bps PSK modems. See ***2400-4800-PSK-for-APRS-Packet-Radio.pdf*** in the doc directory for discussion.
|
||||
|
||||
- The top speed of 9600 bps has been increased to 38400. You will need a sound card capable of 96k or 192k samples per second for the higher rates. Radios must also have adequate bandwidth. See ***Going-beyond-9600-baud.pdf*** in the doc directory for more details.
|
||||
|
||||
- Better decoder performance for 9600 and higher especially for low audio sample rate to baud ratios.
|
||||
|
||||
- Generate waypoint sentences for use by AvMap G5 / G6 or other mapping devices or applications. Formats include
|
||||
- $GPWPL - NMEA generic with only location and name.
|
||||
- $PGRMW - Garmin, adds altitude, symbol, and comment to previously named waypoint.
|
||||
- $PMGNWPL - Magellan, more complete for stationary objects.
|
||||
- $PKWDWPL - Kenwood with APRS style symbol but missing comment.
|
||||
|
||||
|
||||
- DTMF tones can be sent by putting "DTMF" in the destination address, similar to the way that Morse Code is sent.
|
||||
|
||||
- Take advantage of new 'gpio' group and new /sys/class/gpio ownership in Raspbian Jessie.
|
||||
|
||||
- Handle more complicated gpio naming for CubieBoard, etc.
|
||||
|
||||
- More flexible dw-start.sh start up script for both GUI and CLI environments.
|
||||
|
||||
|
||||
|
||||
### Bugs Fixed: ###
|
||||
|
||||
- The transmitter (PTT control) was being turned off too soon when sending Morse Code.
|
||||
|
||||
- The -qd (quiet decode) command line option now suppresses errors about improperly formed Telemetry packets.
|
||||
|
||||
- Longer tocall.txt files can now be handled.
|
||||
|
||||
- Sometimes kissattach would have an issue with the Dire Wolf pseudo terminal. This showed up most often on Raspbian but sometimes occurred with other versions of Linux.
|
||||
|
||||
*kissattach: Error setting line discipline: TIOCSETD: Device or resource busy
|
||||
Are you sure you have enabled MKISS support in the kernel
|
||||
or, if you made it a module, that the module is loaded?*
|
||||
|
||||
|
||||
- Sometimes writes to a pseudo terminal would block causing the received
|
||||
frame processing thread to hang. The first thing you will notice is that
|
||||
received frames are not being printed. After a while this message will appear:
|
||||
|
||||
*Received frame queue is out of control. Length=... Reader thread is probably
|
||||
frozen. This can be caused by using a pseudo terminal (direwolf -p) where
|
||||
another application is not reading the frames from the other side.*
|
||||
|
||||
- -p command line option caused segmentation fault with glibc >= 2.24.
|
||||
|
||||
|
||||
- The Windows version 1.3 would crash when starting to transmit on Windows XP. There have also been some other reports of erratic behavior on Windows. The crashing problem was fixed in in the 1.3.1 patch release. Linux version was not affected.
|
||||
|
||||
- IGate did not retain nul characters in the information part of a packet. This should never happen with a valid APRS packet but there are a couple cases where it has. If we encounter these malformed packets, pass them along as-is, rather than truncating.
|
||||
|
||||
- Don't digipeat packets when the source is my call.
|
||||
|
||||
|
||||
|
||||
----------
|
||||
|
||||
## Version 1.3 -- May 2016 ##
|
||||
|
||||
This is the same as the 1.3 beta test version with a few minor documentation updates. If you are already using 1.3 beta test, there is no need to install this.
|
||||
|
||||
### New Features: ###
|
||||
|
||||
- Support for Mac OS X.
|
||||
|
@ -63,7 +133,7 @@ such as PowerPC or MIPS.
|
|||
|
||||
- Improved decoder performance.
|
||||
Over 1000 error-free frames decoded from WA8LMF TNC Test CD.
|
||||
See "A-Better-APRS-Packet-Demodulator.pdf" for details.
|
||||
See ***A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf*** for details.
|
||||
|
||||
- Up to 3 soundcards and 6 radio channels can be handled at the same time.
|
||||
|
||||
|
@ -274,8 +344,7 @@ to rebuild it from source.
|
|||
|
||||
### New Features: ###
|
||||
|
||||
- Added APRStt gateway capability. For details, see:
|
||||
**APRStt-Implementation-Notes.pdf**
|
||||
- Added APRStt gateway capability. For details, see ***APRStt-Implementation-Notes.pdf***
|
||||
|
||||
|
||||
-----------
|
||||
|
|
208
Makefile.linux
208
Makefile.linux
|
@ -12,9 +12,32 @@ all : $(APPS) direwolf.desktop direwolf.conf
|
|||
@echo " "
|
||||
|
||||
CC := gcc
|
||||
CFLAGS := -O3 -pthread -Igeotranz
|
||||
|
||||
LDFLAGS := -lm -lpthread -lrt
|
||||
# Just for fun, let's see how clang compares to gcc. First install like this:
|
||||
# sudo apt-get update
|
||||
# apt-cache search clang
|
||||
# sudo apt-get install clang-3.5
|
||||
#
|
||||
# CC := clang-3.5
|
||||
|
||||
# _XOPEN_SOURCE=600 and _DEFAULT_SOURCE=1 are needed for glibc >= 2.24.
|
||||
# Explanation here: https://github.com/wb2osz/direwolf/issues/62
|
||||
|
||||
# There are a few source files where it had been necessary to define __USE_XOPEN2KXSI,
|
||||
# __USE_XOPEN, or _POSIX_C_SOURCE. Doesn't seem to be needed after adding this.
|
||||
|
||||
# The first assignment to CFLAGS and LDFLAGS uses +=, rather than :=, so
|
||||
# we will inherit options already set in build environment.
|
||||
# Explanation - https://github.com/wb2osz/direwolf/pull/38
|
||||
|
||||
CFLAGS += -O3 -pthread -Igeotranz -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE=1 -Wall
|
||||
|
||||
# That was fine for a recent Ubuntu and Raspbian Jessie.
|
||||
# However, Raspbian wheezy was then missing declaration for strsep and definition of fd_set.
|
||||
|
||||
CFLAGS += -D_BSD_SOURCE
|
||||
|
||||
LDFLAGS += -lm -lpthread -lrt
|
||||
|
||||
|
||||
|
||||
|
@ -106,7 +129,26 @@ endif
|
|||
# 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.
|
||||
# ---------- How does clang compare? - Ubuntu x86_64 ----------
|
||||
#
|
||||
# I keep hearing a lot about "clang." Let's see how it compares with gcc.
|
||||
# Simply use different compiler and keep all options the same.
|
||||
#
|
||||
# test case: atest 02_Track_2.wav
|
||||
#
|
||||
# gcc 4.8.4: 988 packets decoded in 40.503 seconds. 38.3 x realtime
|
||||
# 988 packets decoded in 40.403 seconds. 38.4 x realtime
|
||||
#
|
||||
# clang 3.8.0-2: 988 packets decoded in 77.454 seconds. 20.0 x realtime
|
||||
# 988 packets decoded in 77.232 seconds. 20.1 x realtime
|
||||
#
|
||||
# I'm not impressed. Almost twice as long. Maybe we need to try some other compiler options.
|
||||
# -march=native did not help.
|
||||
# Makefile.macosx, which uses clang, has these:
|
||||
# -fvectorize -fslp-vectorize -fslp-vectorize-aggressive
|
||||
# Those did not help.
|
||||
#
|
||||
|
||||
|
||||
useffast := $(shell gcc --help -v 2>/dev/null | grep ffast-math)
|
||||
ifneq ($(useffast),)
|
||||
|
@ -193,10 +235,27 @@ endif
|
|||
# cause compatibility issues for those with older computers.
|
||||
#
|
||||
|
||||
# ---------- How does clang compare? - ARM - Raspberry Pi 2 ----------
|
||||
#
|
||||
# I keep hearing a lot about "clang." Let's see how it compares with gcc.
|
||||
# Simply use different compiler and keep all options the same.
|
||||
#
|
||||
# test case: atest 02_Track_2.wav
|
||||
#
|
||||
# gcc 4.9.2-10: 988 packets decoded in 353.025 seconds. 4.4 x realtime
|
||||
# 988 packets decoded in 352.752 seconds. 4.4 x realtime
|
||||
#
|
||||
# clang 3.5.0-10: 988 packets decoded in 825.879 seconds. 1.9 x realtime
|
||||
# 988 packets decoded in 831.469 seconds. 1.9 x realtime
|
||||
#
|
||||
# There might be a different set of options which produce faster code
|
||||
# but the initial test doesn't look good. About 2.3 times as slow.
|
||||
|
||||
# If you want to use OSS (for FreeBSD, OpenBSD) instead of
|
||||
# ALSA (for Linux), comment out (or remove) the two lines below.
|
||||
|
||||
# TODO: Can we automate this somehow?
|
||||
|
||||
CFLAGS += -DUSE_ALSA
|
||||
LDFLAGS += -lasound
|
||||
|
||||
|
@ -213,6 +272,8 @@ endif
|
|||
|
||||
|
||||
# Uncomment following lines to enable hamlib support.
|
||||
# TODO: automate this too. See if hamlib has been installed.
|
||||
|
||||
#CFLAGS += -DUSE_HAMLIB
|
||||
#LDFLAGS += -lhamlib
|
||||
|
||||
|
@ -228,14 +289,14 @@ z := $(notdir ${CURDIR})
|
|||
|
||||
|
||||
|
||||
direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_9600.o hdlc_rec.o \
|
||||
hdlc_rec2.o multi_modem.o redecode.o rdq.o rrbb.o dlq.o \
|
||||
fcs_calc.o ax25_pad.o \
|
||||
direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_psk.o demod_9600.o hdlc_rec.o \
|
||||
hdlc_rec2.o multi_modem.o rdq.o rrbb.o dlq.o \
|
||||
fcs_calc.o ax25_pad.o ax25_pad2.o xid.o \
|
||||
decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
|
||||
gen_tone.o audio.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o morse.o \
|
||||
gen_tone.o audio.o audio_stats.o digipeater.o cdigipeater.o pfilter.o dedupe.o tq.o xmit.o morse.o \
|
||||
ptt.o beacon.o encode_aprs.o latlong.o encode_aprs.o latlong.o textcolor.o \
|
||||
dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o serial_port.o log.o telemetry.o \
|
||||
dwgps.o dwgpsnmea.o dwgpsd.o dtime_now.o \
|
||||
dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o waypoint.o serial_port.o log.o telemetry.o \
|
||||
dwgps.o dwgpsnmea.o dwgpsd.o dtime_now.o mheard.o ax25_link.o \
|
||||
misc.a geotranz.a
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
ifneq ($(enable_gpsd),)
|
||||
|
@ -253,10 +314,13 @@ 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 $(LDFLAGS)
|
||||
fsk_fast_filter.h : gen_fff
|
||||
./gen_fff > fsk_fast_filter.h
|
||||
|
||||
gen_fff : demod_afsk.c dsp.c textcolor.c
|
||||
echo " " > tune.h
|
||||
$(CC) $(CFLAGS) -DGEN_FFF -o $@ $^ $(LDFLAGS)
|
||||
|
||||
|
||||
#
|
||||
# The destination field is often used to identify the manufacturer/model.
|
||||
|
@ -322,15 +386,15 @@ log2gpx : log2gpx.c textcolor.o misc.a
|
|||
|
||||
# Test application to generate sound.
|
||||
|
||||
gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c morse.c textcolor.c dsp.c misc.a
|
||||
gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c morse.c dtmf.c textcolor.c dsp.c misc.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
# Unit test for AFSK demodulator
|
||||
|
||||
atest : atest.c demod.o demod_afsk.o demod_9600.o \
|
||||
atest : atest.c demod.o demod_afsk.o demod_psk.o demod_9600.o \
|
||||
dsp.o hdlc_rec.o hdlc_rec2.o multi_modem.o rrbb.o \
|
||||
fcs_calc.o ax25_pad.o decode_aprs.o dwgpsnmea.o \
|
||||
dwgps.o dwgpsd.o serial_port.o telemetry.o latlong.o symbols.o tt_text.o textcolor.o \
|
||||
dwgps.o dwgpsd.o serial_port.o telemetry.o dtime_now.o latlong.o symbols.o tt_text.o textcolor.o \
|
||||
misc.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
|
@ -522,7 +586,7 @@ install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon
|
|||
# the home directory or other desired location.
|
||||
#
|
||||
$(INSTALL) -D --mode=644 direwolf.conf $(INSTALLDIR)/share/doc/direwolf/examples/direwolf.conf
|
||||
$(INSTALL) -D --mode=644 dw-start.sh $(INSTALLDIR)/share/doc/direwolf/examples/dw-start.sh
|
||||
$(INSTALL) -D --mode=755 dw-start.sh $(INSTALLDIR)/share/doc/direwolf/examples/dw-start.sh
|
||||
$(INSTALL) -D --mode=644 sdr.conf $(INSTALLDIR)/share/doc/direwolf/examples/sdr.conf
|
||||
$(INSTALL) -D --mode=644 telemetry-toolkit/telem-m0xer-3.txt $(INSTALLDIR)/share/doc/direwolf/examples/telem-m0xer-3.txt
|
||||
$(INSTALL) -D --mode=644 telemetry-toolkit/telem-balloon.conf $(INSTALLDIR)/share/doc/direwolf/examples/telem-balloon.conf
|
||||
|
@ -549,19 +613,22 @@ install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon
|
|||
@echo " "
|
||||
|
||||
|
||||
# Put sample configuration files in home directory.
|
||||
# These would be done as ordinary user.
|
||||
|
||||
# The Raspberry Pi has ~/Desktop but Ubuntu does not.
|
||||
|
||||
# TODO: Handle Linux variations correctly.
|
||||
|
||||
# Version 1.4 - Add "-n" option to avoid clobbering existing, probably customized, config files.
|
||||
|
||||
|
||||
.PHONY: install-conf
|
||||
install-conf : direwolf.conf
|
||||
cp direwolf.conf ~
|
||||
cp sdr.conf ~
|
||||
cp telemetry-toolkit/telem-m0xer-3.txt ~
|
||||
cp telemetry-toolkit/telem-*.conf ~
|
||||
cp -n direwolf.conf ~
|
||||
cp -n sdr.conf ~
|
||||
cp -n telemetry-toolkit/telem-m0xer-3.txt ~
|
||||
cp -n telemetry-toolkit/telem-*.conf ~
|
||||
ifneq ($(wildcard $(HOME)/Desktop),)
|
||||
@echo " "
|
||||
@echo "This will add a desktop icon on some systems:"
|
||||
|
@ -571,9 +638,13 @@ ifneq ($(wildcard $(HOME)/Desktop),)
|
|||
endif
|
||||
|
||||
|
||||
# dw-start.sh is greatly improved in version 1.4.
|
||||
# It should probably be part of install-conf because it is not just for the RPi.
|
||||
|
||||
.PHONY: install-rpi
|
||||
install-rpi : dw-start.sh
|
||||
cp dw-start.sh ~
|
||||
chmod +x dw-start.sh
|
||||
cp -n dw-start.sh ~
|
||||
ln -f -s /usr/share/applications/direwolf.desktop ~/Desktop/direwolf.desktop
|
||||
|
||||
|
||||
|
@ -585,15 +656,15 @@ install-rpi : dw-start.sh
|
|||
# Combine some unit tests into a single regression sanity check.
|
||||
|
||||
|
||||
check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest check-modem1200 check-modem300 check-modem9600
|
||||
check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest pad2test xidtest dtmftest check-modem1200 check-modem300 check-modem9600 check-modem19200 check-modem2400 check-modem4800
|
||||
|
||||
# Can we encode and decode at popular data rates?
|
||||
|
||||
check-modem1200 : gen_packets atest
|
||||
./gen_packets -n 100 -o /tmp/test1.wav
|
||||
./atest -F0 -PE -L70 -G71 /tmp/test1.wav
|
||||
./atest -F1 -PE -L73 -G75 /tmp/test1.wav
|
||||
#rm /tmp/test1.wav
|
||||
./gen_packets -n 100 -o /tmp/test12.wav
|
||||
./atest -F0 -PE -L63 -G71 /tmp/test12.wav
|
||||
./atest -F1 -PE -L70 -G75 /tmp/test12.wav
|
||||
rm /tmp/test12.wav
|
||||
|
||||
check-modem300 : gen_packets atest
|
||||
./gen_packets -B300 -n 100 -o /tmp/test3.wav
|
||||
|
@ -602,18 +673,35 @@ check-modem300 : gen_packets atest
|
|||
rm /tmp/test3.wav
|
||||
|
||||
check-modem9600 : gen_packets atest
|
||||
./gen_packets -B9600 -n 100 -o /tmp/test9.wav
|
||||
./atest -B9600 -F0 -L57 -G59 /tmp/test9.wav
|
||||
./atest -B9600 -F1 -L66 -G67 /tmp/test9.wav
|
||||
rm /tmp/test9.wav
|
||||
./gen_packets -B9600 -n 100 -o /tmp/test96.wav
|
||||
./atest -B9600 -F0 -L50 -G54 /tmp/test96.wav
|
||||
./atest -B9600 -F1 -L55 -G59 /tmp/test96.wav
|
||||
rm /tmp/test96.wav
|
||||
|
||||
check-modem19200 : gen_packets atest
|
||||
./gen_packets -r 96000 -B19200 -n 100 -o /tmp/test19.wav
|
||||
./atest -B19200 -F0 -L55 -G59 /tmp/test19.wav
|
||||
./atest -B19200 -F1 -L60 -G64 /tmp/test19.wav
|
||||
rm /tmp/test19.wav
|
||||
|
||||
check-modem2400 : gen_packets atest
|
||||
./gen_packets -B2400 -n 100 -o /tmp/test24.wav
|
||||
./atest -B2400 -F0 -L70 -G78 /tmp/test24.wav
|
||||
./atest -B2400 -F1 -L80 -G87 /tmp/test24.wav
|
||||
rm /tmp/test24.wav
|
||||
|
||||
check-modem4800 : gen_packets atest
|
||||
./gen_packets -B2400 -n 100 -o /tmp/test48.wav
|
||||
./atest -B2400 -F0 -L70 -G79 /tmp/test48.wav
|
||||
./atest -B2400 -F1 -L80 -G90 /tmp/test48.wav
|
||||
rm /tmp/test48.wav
|
||||
|
||||
|
||||
# Unit test for inner digipeater algorithm
|
||||
|
||||
.PHONY : dtest
|
||||
dtest : digipeater.c dedupe.c \
|
||||
pfilter.o ax25_pad.o fcs_calc.o tq.o textcolor.o \
|
||||
dtest : digipeater.c dedupe.c pfilter.c \
|
||||
ax25_pad.o fcs_calc.o tq.o textcolor.o \
|
||||
decode_aprs.o dwgpsnmea.o dwgps.o dwgpsd.o serial_port.o latlong.o telemetry.o symbols.o tt_text.o misc.a
|
||||
$(CC) $(CFLAGS) -DDIGITEST -o $@ $^ $(LDFLAGS)
|
||||
./dtest
|
||||
|
@ -679,6 +767,32 @@ kisstest : kiss_frame.c
|
|||
./kisstest
|
||||
rm kisstest
|
||||
|
||||
# Unit test for constructing frames besides UI.
|
||||
|
||||
.PHONY: pad2test
|
||||
pad2test : ax25_pad2.c ax25_pad.c fcs_calc.o textcolor.o misc.a
|
||||
$(CC) $(CFLAGS) -DPAD2TEST -o $@ $^ $(LDFLAGS)
|
||||
./pad2test
|
||||
rm pad2test
|
||||
|
||||
|
||||
# Unit Test for XID frame encode/decode.
|
||||
|
||||
.PHONY: xidtest
|
||||
xidtest : xid.c textcolor.o misc.a
|
||||
$(CC) $(CFLAGS) -DXIDTEST -o $@ $^ $(LDFLAGS)
|
||||
./xidtest
|
||||
rm xidtest
|
||||
|
||||
|
||||
# Unit Test for DTMF encode/decode.
|
||||
|
||||
.PHONY: dtmftest
|
||||
dtmftest : dtmf.c textcolor.o
|
||||
$(CC) $(CFLAGS) -DDTMF_TEST -o $@ $^ $(LDFLAGS)
|
||||
./dtmftest
|
||||
rm dtmftest
|
||||
|
||||
|
||||
|
||||
# ----------------------------- Manual tests and experiments ---------------------------
|
||||
|
@ -694,7 +808,7 @@ itest : igate.c textcolor.c ax25_pad.c fcs_calc.c textcolor.o misc.a
|
|||
# Unit test for UDP reception with AFSK demodulator.
|
||||
# Temporary during development. Might not be useful anymore.
|
||||
|
||||
udptest : udp_test.c demod.o dsp.o demod_afsk.o demod_9600.o hdlc_rec.o hdlc_rec2.o multi_modem.o rrbb.o \
|
||||
udptest : udp_test.c demod.o dsp.o demod_afsk.o demod_psk.o demod_9600.o hdlc_rec.o hdlc_rec2.o multi_modem.o rrbb.o \
|
||||
fcs_calc.o ax25_pad.o decode_aprs.o symbols.o textcolor.o misc.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
./udptest
|
||||
|
@ -703,15 +817,38 @@ udptest : udp_test.c demod.o dsp.o demod_afsk.o demod_9600.o hdlc_rec.o hdlc_rec
|
|||
# Dependencies of demod*.c, rather than .o, are intentional.
|
||||
|
||||
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.o hdlc_rec2.o multi_modem.o rrbb.o \
|
||||
fcs_calc.o ax25_pad.o decode_aprs.o telemetry.o latlong.o symbols.o tune.h textcolor.o misc.a
|
||||
demod_psk.o : tune.h
|
||||
|
||||
tune.h :
|
||||
echo " " > tune.h
|
||||
|
||||
|
||||
testagc : atest.c demod.c dsp.c demod_afsk.c demod_psk.c demod_9600.c hdlc_rec.o hdlc_rec2.o multi_modem.o rrbb.o \
|
||||
fcs_calc.o ax25_pad.o decode_aprs.o telemetry.o dtime_now.o latlong.o symbols.o tune.h textcolor.o misc.a
|
||||
$(CC) $(CFLAGS) -o atest $^ $(LDFLAGS)
|
||||
./atest 02_Track_2.wav | grep "packets decoded in" > atest.out
|
||||
|
||||
|
||||
testagc96 : atest.c fsk_fast_filter.h tune.h demod.c demod_afsk.c demod_psk.c demod_9600.c \
|
||||
dsp.o hdlc_rec.o hdlc_rec2.o multi_modem.o \
|
||||
rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o \
|
||||
dwgpsnmea.o dwgps.o dwgpsd.o serial_port.o latlong.o \
|
||||
symbols.o tt_text.o textcolor.o telemetry.o dtime_now.o \
|
||||
misc.a
|
||||
rm -f atest96
|
||||
$(CC) $(CFLAGS) -o atest96 $^ $(LDFLAGS)
|
||||
./atest96 -B 9600 ../walkabout9600c.wav | grep "packets decoded in" >atest.out
|
||||
#./atest96 -B 9600 noisy96.wav | grep "packets decoded in" >atest.out
|
||||
#./atest96 -B 9600 19990303_0225_9600_8bis_22kHz.wav | grep "packets decoded in" >atest.out
|
||||
#./atest96 -B 9600 19990303_0225_9600_16bit_22kHz.wav | grep "packets decoded in" >atest.out
|
||||
#./atest96 -B 9600 -P + z8-22k.wav| grep "packets decoded in" >atest.out
|
||||
#./atest96 -B 9600 test9600.wav | grep "packets decoded in" >atest.out
|
||||
echo " " > tune.h
|
||||
|
||||
|
||||
|
||||
|
@ -757,8 +894,7 @@ dist-src : README.md CHANGES.md
|
|||
|
||||
.PHONY: clean
|
||||
clean :
|
||||
rm -f $(APPS) fsk_fast_filter.h *.o *.a direwolf.desktop
|
||||
echo " " > tune.h
|
||||
rm -f $(APPS) gen_fff tune.h fsk_fast_filter.h *.o *.a direwolf.desktop
|
||||
|
||||
|
||||
depend : $(wildcard *.c)
|
||||
|
|
|
@ -24,8 +24,9 @@
|
|||
# 3. Removed fsk_fast_filter.h from atest receipe, clang compiler was having
|
||||
# a hissy fit. Not check with GCC.
|
||||
|
||||
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc direwolf.conf
|
||||
@echo " "
|
||||
APPS := direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc
|
||||
|
||||
all : $(APPS) direwolf.desktop direwolf.conf @echo " "
|
||||
@echo "Next step install with: "
|
||||
@echo " "
|
||||
@echo " sudo make install"
|
||||
|
@ -70,7 +71,18 @@ endif
|
|||
|
||||
#CC := $(DARWIN_CC) -m64 $(SYS_LIBS) $(SYS_MIN)
|
||||
CC := $(DARWIN_CC) -m32 $(SYS_LIBS) $(SYS_MIN)
|
||||
CFLAGS := -Os -pthread -Igeotranz $(EXTRA_CFLAGS)
|
||||
|
||||
# _XOPEN_SOURCE=600 and _DEFAULT_SOURCE=1 are needed for glibc >= 2.24.
|
||||
# Explanation here: https://github.com/wb2osz/direwolf/issues/62
|
||||
|
||||
CFLAGS := -Os -pthread -Igeotranz -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE=1 $(EXTRA_CFLAGS)
|
||||
|
||||
# That was fine for a recent Ubuntu and Raspbian Jessie.
|
||||
# However, Raspbian wheezy was then missing declaration for strsep and definition of fd_set.
|
||||
|
||||
CFLAGS += -D_BSD_SOURCE
|
||||
|
||||
|
||||
# $(info $$CC is [${CC}])
|
||||
|
||||
#
|
||||
|
@ -220,15 +232,15 @@ 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 \
|
||||
direwolf : direwolf.o aprs_tt.o audio_portaudio.o audio_stats.o ax25_link.o ax25_pad.o ax25_pad2.o beacon.o \
|
||||
config.o decode_aprs.o dedupe.o demod_9600.o demod_afsk.o demod_psk.o \
|
||||
demod.o digipeater.o cdigipeater.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 \
|
||||
dwgps.o dwgpsnmea.o
|
||||
waypoint.o serial_port.o pfilter.o ptt.o rdq.o recv.o rrbb.o server.o \
|
||||
symbols.o telemetry.o textcolor.o tq.o tt_text.o tt_user.o xid.o xmit.o \
|
||||
dwgps.o dwgpsnmea.o mheard.o
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lpthread $(LDLIBS) -lm
|
||||
|
||||
|
||||
|
@ -239,10 +251,15 @@ 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
|
||||
fsk_fast_filter.h : gen_fff
|
||||
./gen_fff > fsk_fast_filter.h
|
||||
|
||||
gen_fff : demod_afsk.c dsp.c textcolor.c
|
||||
echo " " > tune.h
|
||||
$(CC) $(CFLAGS) -DGEN_FFF -o $@ $^ $(LDFLAGS)
|
||||
|
||||
|
||||
|
||||
# UTM, USNG, MGRS conversions.
|
||||
|
||||
geotranz.a : error_string.o mgrs.o polarst.o tranmerc.o ups.o usng.o utm.o
|
||||
|
@ -392,10 +409,6 @@ install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon
|
|||
|
||||
# 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
|
||||
|
@ -406,7 +419,7 @@ install-conf : direwolf.conf
|
|||
|
||||
# Separate application to decode raw data.
|
||||
|
||||
decode_aprs : decode_aprs.c dwgpsnmea.o dwgps.o serial_port.o symbols.o ax25_pad.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o
|
||||
decode_aprs : decode_aprs.c dwgpsnmea.o dwgps.o dwgpsd.o serial_port.o symbols.o ax25_pad.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o
|
||||
$(CC) $(CFLAGS) -DDECAMAIN -o $@ $^ -lm
|
||||
|
||||
# Convert between text and touch tone representation.
|
||||
|
@ -435,25 +448,33 @@ log2gpx : log2gpx.c
|
|||
|
||||
# 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
|
||||
gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c morse.c dtmf.c textcolor.c dsp.c
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -lm
|
||||
|
||||
demod.o : tune.h
|
||||
|
||||
demod_afsk.o : tune.h
|
||||
|
||||
demod_9600.o : tune.h
|
||||
|
||||
demod_psk.o : tune.h
|
||||
|
||||
tune.h :
|
||||
echo " " > 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 dtime_now.o 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
|
||||
# Unit test for demodulators
|
||||
|
||||
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 dwgpsnmea.o dwgps.o serial_port.o telemetry.c latlong.c symbols.c textcolor.c tt_text.c
|
||||
atest : atest.c demod.c dsp.c demod_afsk.c demod_psk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \
|
||||
fcs_calc.c ax25_pad.c decode_aprs.c dwgpsnmea.o dwgps.o serial_port.o telemetry.c dtest_now.o latlong.c symbols.c textcolor.c tt_text.c
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lm
|
||||
#atest : atest.c fsk_fast_filter.h demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \
|
||||
#atest : atest.c fsk_fast_filter.h demod.c dsp.c demod_afsk.c demod_psk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \
|
||||
# fcs_calc.c ax25_pad.c decode_aprs.c dwgpsnmea.o dwgps.o serial_port.o telemetry.c latlong.c symbols.c textcolor.c tt_text.c
|
||||
# $(CC) $(CFLAGS) -o $@ $^ -lm
|
||||
|
||||
|
@ -513,7 +534,7 @@ depend : $(wildcard *.c)
|
|||
|
||||
.PHONY: clean
|
||||
clean :
|
||||
rm -f direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc \
|
||||
rm -f $(APPS) gen_fff \
|
||||
fsk_fast_filter.h *.o *.a use_this_sdk
|
||||
echo " " > tune.h
|
||||
|
||||
|
|
292
Makefile.win
292
Makefile.win
|
@ -16,18 +16,41 @@
|
|||
#
|
||||
|
||||
|
||||
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets atest ttcalc
|
||||
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets atest ttcalc tnctest
|
||||
|
||||
|
||||
# People say we need -mthreads option for threads to work properly.
|
||||
# They also say it creates a dependency on mingwm10.dll but I'm not seeing that.
|
||||
# Maybe that is for pthreads. We are using the Windows threads.
|
||||
|
||||
# -Ofast was added in gcc 4.6 which was the MinGW version back in 2012.
|
||||
|
||||
CC := gcc
|
||||
CFLAGS := -Wall -Ofast -march=pentium3 -msse -Iregex -Iutm -Igeotranz -mthreads -DUSE_REGEX_STATIC
|
||||
CFLAGS := -Ofast -march=pentium3 -msse -Iregex -Iutm -Igeotranz -mthreads -DUSE_REGEX_STATIC -Wall -Wlogical-op
|
||||
AR := ar
|
||||
|
||||
CFLAGS += -g
|
||||
|
||||
# For version 1.4, we upgrade from gcc 4.6.2 to 4.9.3.
|
||||
|
||||
# gcc 4.8 adds these. Try them just for fun.
|
||||
# No, it needs libasan which is not on Windows.
|
||||
#CFLAGS += -fsanitize=address -fno-omit-frame-pointer
|
||||
|
||||
# Helpful for the demodulators. Overkill for non-hot spots.
|
||||
#CFLAGS += -Wdouble-promotion
|
||||
|
||||
# Don't have the patience for this right now.
|
||||
#CFLAGS += -Wextra
|
||||
|
||||
# Continue working on these.
|
||||
CFLAGS += -Wsign-compare
|
||||
CFLAGS += -Wuninitialized
|
||||
CFLAGS += -Wold-style-declaration
|
||||
# CFLAGS += -fdelete-null-pointer-checks -Wnull-dereference ---not recognized
|
||||
#CFLAGS += -Wold-style-definition
|
||||
#-Wmissing-prototypes
|
||||
|
||||
#
|
||||
# Let's see impact of various optimization levels.
|
||||
# Benchmark results with MinGW gcc version 4.6.2.
|
||||
|
@ -61,19 +84,25 @@ CFLAGS += -g
|
|||
|
||||
# -------------------------------------- Main application --------------------------------
|
||||
|
||||
# Not sure why this is here.
|
||||
|
||||
demod.o : fsk_demod_state.h
|
||||
|
||||
demod_9600.o : fsk_demod_state.h
|
||||
|
||||
demod_afsk.o : fsk_demod_state.h
|
||||
|
||||
demod_psk.o : fsk_demod_state.h
|
||||
|
||||
direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_9600.o hdlc_rec.o \
|
||||
hdlc_rec2.o multi_modem.o redecode.o rdq.o rrbb.o dlq.o \
|
||||
fcs_calc.o ax25_pad.o \
|
||||
|
||||
direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_psk.o demod_9600.o hdlc_rec.o \
|
||||
hdlc_rec2.o multi_modem.o rdq.o rrbb.o dlq.o \
|
||||
fcs_calc.o ax25_pad.o ax25_pad2.o xid.o \
|
||||
decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
|
||||
gen_tone.o morse.o audio_win.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o \
|
||||
gen_tone.o morse.o audio_win.o audio_stats.o digipeater.o cdigipeater.o pfilter.o dedupe.o tq.o xmit.o \
|
||||
ptt.o beacon.o dwgps.o encode_aprs.o latlong.o textcolor.o \
|
||||
dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o serial_port.o log.o telemetry.o \
|
||||
dwgps.o dwgpsnmea.o dtime_now.o \
|
||||
dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o waypoint.o serial_port.o log.o telemetry.o \
|
||||
dwgps.o dwgpsnmea.o dtime_now.o mheard.o ax25_link.o \
|
||||
dw-icon.o regex.a misc.a geotranz.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
|
||||
|
||||
|
@ -88,10 +117,13 @@ 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
|
||||
fsk_fast_filter.h : gen_fff
|
||||
./gen_fff > fsk_fast_filter.h
|
||||
|
||||
gen_fff : demod_afsk.c dsp.c textcolor.c
|
||||
echo " " > tune.h
|
||||
$(CC) $(CFLAGS) -DGEN_FFF -o $@ $^
|
||||
|
||||
|
||||
#
|
||||
# The destination field is often used to identify the manufacturer/model.
|
||||
|
@ -157,7 +189,7 @@ log2gpx : log2gpx.c textcolor.o misc.a
|
|||
|
||||
# Test application to generate sound.
|
||||
|
||||
gen_packets : gen_packets.o ax25_pad.o hdlc_send.o fcs_calc.o gen_tone.o morse.o textcolor.o dsp.o misc.a regex.a
|
||||
gen_packets : gen_packets.o ax25_pad.o hdlc_send.o fcs_calc.o gen_tone.o morse.o dtmf.o textcolor.o dsp.o misc.a regex.a
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
|
||||
|
@ -198,7 +230,7 @@ utm.o : geotranz/utm.c
|
|||
# functions supplied by the gnu C library.
|
||||
# For the native WIN32 version, we need to use our own copy.
|
||||
# These were copied from http://gnuwin32.sourceforge.net/packages/regex.htm
|
||||
#
|
||||
# Consider upgrading from https://www.gnu.org/software/libc/sources.html
|
||||
|
||||
regex.a : regex.o
|
||||
ar -cr $@ $^
|
||||
|
@ -207,7 +239,8 @@ regex.o : regex/regex.c
|
|||
$(CC) $(CFLAGS) -Dbool=int -Dtrue=1 -Dfalse=0 -c -o $@ $^
|
||||
|
||||
|
||||
# There are several string functios found in Linux
|
||||
|
||||
# There are several string functions found in Linux
|
||||
# but not on Windows. Need to provide our own copy.
|
||||
|
||||
misc.a : strsep.o strtok_r.o strcasestr.o strlcpy.o strlcat.o
|
||||
|
@ -235,36 +268,78 @@ strlcat.o : misc/strlcat.c
|
|||
# Combine some unit tests into a single regression sanity check.
|
||||
|
||||
|
||||
check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest check-modem1200 check-modem300 check-modem9600
|
||||
check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest pad2test xidtest dtmftest check-modem1200 check-modem300 check-modem9600 check-modem19200 check-modem2400 check-modem4800
|
||||
|
||||
# Can we encode and decode at popular data rates?
|
||||
# Verify that single bit fixup increases the count.
|
||||
|
||||
check-modem1200 : gen_packets atest
|
||||
gen_packets -n 100 -o test1.wav
|
||||
atest -F0 -PE -L70 -G71 test1.wav
|
||||
atest -F1 -PE -L73 -G75 test1.wav
|
||||
#rm test1.wav
|
||||
gen_packets -n 100 -o test12.wav
|
||||
atest -F0 -PE -L64 -G72 test12.wav
|
||||
atest -F1 -PE -L70 -G75 test12.wav
|
||||
rm test12.wav
|
||||
|
||||
check-modem300 : gen_packets atest
|
||||
gen_packets -B300 -n 100 -o test3.wav
|
||||
atest -B300 -F0 -L68 -G69 test3.wav
|
||||
atest -B300 -F1 -L73 -G75 test3.wav
|
||||
atest -B300 -F1 -L71 -G75 test3.wav
|
||||
rm test3.wav
|
||||
|
||||
#FIXME: test full amplitude.
|
||||
|
||||
check-modem9600 : gen_packets atest
|
||||
gen_packets -B9600 -n 100 -o test9.wav
|
||||
atest -B9600 -F0 -L57 -G59 test9.wav
|
||||
atest -B9600 -F1 -L66 -G67 test9.wav
|
||||
rm test9.wav
|
||||
gen_packets -B9600 -a 170 -o test96.wav
|
||||
sleep 1
|
||||
atest -B9600 -F0 -L4 -G4 test96.wav
|
||||
sleep 1
|
||||
rm test96.wav
|
||||
sleep 1
|
||||
gen_packets -B9600 -n 100 -o test96.wav
|
||||
sleep 1
|
||||
atest -B9600 -F0 -L50 -G54 test96.wav
|
||||
atest -B9600 -F1 -L55 -G59 test96.wav
|
||||
sleep 1
|
||||
rm test96.wav
|
||||
|
||||
# Unit test for AFSK demodulator
|
||||
check-modem19200 : gen_packets atest
|
||||
gen_packets -r 96000 -B19200 -a 170 -o test19.wav
|
||||
sleep 1
|
||||
atest -B19200 -F0 -L4 test19.wav
|
||||
sleep 1
|
||||
rm test19.wav
|
||||
sleep 1
|
||||
gen_packets -r 96000 -B19200 -n 100 -o test19.wav
|
||||
sleep 1
|
||||
atest -B19200 -F0 -L55 -G59 test19.wav
|
||||
atest -B19200 -F1 -L60 -G64 test19.wav
|
||||
sleep 1
|
||||
rm test19.wav
|
||||
|
||||
atest : atest.c fsk_fast_filter.h demod.c demod_afsk.c demod_9600.c \
|
||||
check-modem2400 : gen_packets atest
|
||||
gen_packets -B2400 -n 100 -o test24.wav
|
||||
sleep 1
|
||||
atest -B2400 -F0 -L70 -G78 test24.wav
|
||||
atest -B2400 -F1 -L80 -G87 test24.wav
|
||||
sleep 1
|
||||
rm test24.wav
|
||||
|
||||
check-modem4800 : gen_packets atest
|
||||
gen_packets -B4800 -n 100 -o test48.wav
|
||||
sleep 1
|
||||
atest -B4800 -F0 -L70 -G74 test48.wav
|
||||
atest -B4800 -F1 -L79 -G84 test48.wav
|
||||
sleep 1
|
||||
rm test48.wav
|
||||
|
||||
|
||||
# Unit test for demodulators
|
||||
|
||||
atest : atest.c fsk_fast_filter.h demod.c demod_afsk.c demod_psk.c demod_9600.c \
|
||||
dsp.o hdlc_rec.o hdlc_rec2.o multi_modem.o \
|
||||
rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o \
|
||||
dwgpsnmea.o dwgps.o serial_port.o latlong.c \
|
||||
symbols.c tt_text.c textcolor.c telemetry.c \
|
||||
symbols.c tt_text.c textcolor.c telemetry.c dtime_now.o \
|
||||
decode_aprs.o log.o \
|
||||
misc.a regex.a
|
||||
echo " " > tune.h
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
@ -272,8 +347,8 @@ atest : atest.c fsk_fast_filter.h demod.c demod_afsk.c demod_9600.c \
|
|||
#atest -B 9600 z9.wav
|
||||
#atest za100.wav
|
||||
|
||||
atest9 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \
|
||||
rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c latlong.c symbols.c textcolor.c telemetry.c misc.a regex.a \
|
||||
atest9 : atest.c demod.c dsp.c demod_afsk.c demod_psk.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 dtime_now.o misc.a regex.a \
|
||||
fsk_fast_filter.h
|
||||
echo " " > tune.h
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
@ -284,8 +359,8 @@ atest9 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c
|
|||
# Unit test for inner digipeater algorithm
|
||||
|
||||
.PHONY: dtest
|
||||
dtest : digipeater.c dedupe.c \
|
||||
pfilter.o ax25_pad.o fcs_calc.o tq.o textcolor.o \
|
||||
dtest : digipeater.c dedupe.c pfilter.c \
|
||||
ax25_pad.o fcs_calc.o tq.o textcolor.o \
|
||||
decode_aprs.o dwgpsnmea.o dwgps.o serial_port.o latlong.o telemetry.o symbols.o tt_text.o misc.a regex.a
|
||||
$(CC) $(CFLAGS) -DDIGITEST -o $@ $^
|
||||
./dtest
|
||||
|
@ -352,20 +427,49 @@ kisstest : kiss_frame.c
|
|||
rm kisstest.exe
|
||||
|
||||
|
||||
# Unit test for constructing frames besides UI.
|
||||
|
||||
.PHONY: pad2test
|
||||
pad2test : ax25_pad2.c ax25_pad.c fcs_calc.o textcolor.o regex.a misc.a
|
||||
$(CC) $(CFLAGS) -DPAD2TEST -o $@ $^
|
||||
./pad2test
|
||||
rm pad2test.exe
|
||||
|
||||
# Unit Test for XID frame encode/decode.
|
||||
|
||||
.PHONY: xidtest
|
||||
xidtest : xid.c textcolor.o misc.a
|
||||
$(CC) $(CFLAGS) -DXIDTEST -o $@ $^
|
||||
./xidtest
|
||||
rm xidtest.exe
|
||||
|
||||
# Unit Test for DTMF encode/decode.
|
||||
|
||||
.PHONY: dtmftest
|
||||
dtmftest : dtmf.c textcolor.o
|
||||
$(CC) $(CFLAGS) -DDTMF_TEST -o $@ $^
|
||||
./dtmftest
|
||||
rm dtmftest.exe
|
||||
|
||||
|
||||
# ------------------------------ Other manual testing & experimenting -------------------------------
|
||||
|
||||
|
||||
tnctest : tnctest.c textcolor.o dtime_now.o serial_port.o misc.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
|
||||
|
||||
|
||||
# For tweaking the demodulator.
|
||||
|
||||
demod.o : tune.h
|
||||
demod_9600.o : tune.h
|
||||
demod_afsk.o : tune.h
|
||||
demod_psk.o : tune.h
|
||||
|
||||
|
||||
testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.o fsk_demod_agc.h \
|
||||
testagc : atest.c demod.c dsp.c demod_afsk.c demod_psk.c demod_9600.o fsk_demod_agc.h \
|
||||
hdlc_rec.o hdlc_rec2.o multi_modem.o \
|
||||
rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o latlong.o symbols.o textcolor.o telemetry.o \
|
||||
dwgpsnmea.o dwgps.o serial_port.o tt_text.o regex.a misc.a
|
||||
dwgpsnmea.o dwgps.o serial_port.o tt_text.o dtime_now.o regex.a misc.a
|
||||
rm -f atest.exe
|
||||
$(CC) $(CFLAGS) -o atest $^
|
||||
./atest -P GGG- -F 0 ../02_Track_2.wav | grep "packets decoded in" >atest.out
|
||||
|
@ -375,27 +479,60 @@ testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.o fsk_demod_agc.h \
|
|||
noisy3.wav : gen_packets
|
||||
./gen_packets -B 300 -n 100 -o noisy3.wav
|
||||
|
||||
testagc3 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \
|
||||
rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c latlong.c symbols.c textcolor.c telemetry.c regex.a misc.a \
|
||||
testagc3 : atest.c demod.c dsp.c demod_afsk.c demod_psk.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 dtime_now.o regex.a misc.a \
|
||||
tune.h
|
||||
rm -f atest.exe
|
||||
$(CC) $(CFLAGS) -o atest $^
|
||||
./atest -B 300 -P D -D 3 noisy3.wav | grep "packets decoded in" >atest.out
|
||||
rm -f atest3.exe
|
||||
$(CC) $(CFLAGS) -o atest3 $^
|
||||
./atest3 -B 300 -P D -D 3 noisy3.wav | grep "packets decoded in" >atest.out
|
||||
echo " " > tune.h
|
||||
|
||||
|
||||
noisy96.wav : gen_packets
|
||||
./gen_packets -B 9600 -n 100 -o noisy96.wav
|
||||
|
||||
testagc9 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \
|
||||
rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c latlong.c symbols.c textcolor.c telemetry.c regex.a misc.a \
|
||||
tune.h
|
||||
rm -f atest.exe
|
||||
$(CC) $(CFLAGS) -o atest $^
|
||||
./atest -B 9600 ../walkabout9600.wav | grep "packets decoded in" >atest.out
|
||||
#./atest -B 9600 noisy96.wav | grep "packets decoded in" >atest.out
|
||||
testagc96 : atest.c fsk_fast_filter.h tune.h demod.c demod_afsk.c demod_psk.c demod_9600.c \
|
||||
dsp.o hdlc_rec.o hdlc_rec2.o multi_modem.o \
|
||||
rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o \
|
||||
dwgpsnmea.o dwgps.o serial_port.o latlong.o \
|
||||
symbols.o tt_text.o textcolor.o telemetry.o dtime_now.o \
|
||||
misc.a regex.a
|
||||
rm -f atest96.exe
|
||||
$(CC) $(CFLAGS) -o atest96 $^
|
||||
./atest96 -B 9600 ../walkabout9600c.wav | grep "packets decoded in" >atest.out
|
||||
#./atest96 -B 9600 noisy96.wav | grep "packets decoded in" >atest.out
|
||||
#./atest96 -B 9600 19990303_0225_9600_8bis_22kHz.wav | grep "packets decoded in" >atest.out
|
||||
#./atest96 -B 9600 19990303_0225_9600_16bit_22kHz.wav | grep "packets decoded in" >atest.out
|
||||
#./atest96 -B 9600 -P + z8-22k.wav| grep "packets decoded in" >atest.out
|
||||
#./atest96 -B 9600 test9600.wav | grep "packets decoded in" >atest.out
|
||||
echo " " > tune.h
|
||||
|
||||
testagc24 : atest.c fsk_fast_filter.h tune.h demod.c demod_afsk.c demod_psk.c demod_9600.c \
|
||||
dsp.o hdlc_rec.o hdlc_rec2.o multi_modem.o \
|
||||
rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o \
|
||||
dwgpsnmea.o dwgps.o serial_port.o latlong.o \
|
||||
symbols.o tt_text.o textcolor.o telemetry.o dtime_now.o \
|
||||
misc.a regex.a
|
||||
rm -f atest24.exe
|
||||
sleep 1
|
||||
$(CC) $(CFLAGS) -o atest24 $^
|
||||
./atest24 -B 2400 test2400.wav | grep "packets decoded in" >atest.out
|
||||
echo " " > tune.h
|
||||
|
||||
testagc48 : atest.c fsk_fast_filter.h tune.h demod.c demod_afsk.c demod_psk.c demod_9600.c \
|
||||
dsp.o hdlc_rec.o hdlc_rec2.o multi_modem.o \
|
||||
rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o \
|
||||
dwgpsnmea.o dwgps.o serial_port.o latlong.o \
|
||||
symbols.o tt_text.o textcolor.o telemetry.o dtime_now.o \
|
||||
misc.a regex.a
|
||||
rm -f atest48.exe
|
||||
sleep 1
|
||||
$(CC) $(CFLAGS) -o atest48 $^
|
||||
./atest48 -B 4800 test4800.wav | grep "packets decoded in" >atest.out
|
||||
#./atest48 -B 4800 test4800.wav
|
||||
echo " " > tune.h
|
||||
|
||||
|
||||
# Unit test for IGate
|
||||
|
||||
itest : igate.c textcolor.c ax25_pad.c fcs_calc.c misc.a regex.a
|
||||
|
@ -424,8 +561,8 @@ walk96 : walk96.c dwgps.o dwgpsnmea.o kiss_frame.o \
|
|||
ax25_pad.o fcs_calc.o \
|
||||
xmit.o hdlc_send.o gen_tone.o ptt.o tq.o \
|
||||
hdlc_rec.o hdlc_rec2.o rrbb.o dsp.o audio_win.o \
|
||||
multi_modem.o demod.o demod_afsk.o demod_9600.o rdq.o \
|
||||
server.o morse.o audio_stats.o dtime_now.o dlq.o \
|
||||
multi_modem.o demod.o demod_afsk.o demod_psk.c demod_9600.o rdq.o \
|
||||
server.o morse.o dtmf.o audio_stats.o dtime_now.o dlq.o \
|
||||
regex.a misc.a
|
||||
$(CC) $(CFLAGS) -DWALK96 -o $@ $^ -lwinmm -lws2_32
|
||||
|
||||
|
@ -494,67 +631,6 @@ dist-win : direwolf.exe decode_aprs.exe text2tt.exe tt2text.exe ll2utm.exe utm2l
|
|||
dwespeak.bat \
|
||||
telemetry-toolkit/*
|
||||
|
||||
.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 \
|
||||
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
|
||||
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/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/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/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/* )
|
||||
unix2dos Makefile
|
||||
unix2dos Makefile.linux
|
||||
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
|
||||
|
||||
|
||||
# Reminders if pdf files are not up to date.
|
||||
|
||||
|
|
67
README.md
67
README.md
|
@ -3,24 +3,24 @@
|
|||
|
||||
### Decoded Information from Radio Emissions for Windows Or Linux Fans ###
|
||||
|
||||
In the early days of Amateur Packet Radio, it was necessary to use a “Terminal Node Controller” (TNC) with specialized hardware. Those days are gone. You can now get better results at lower cost by connecting your radio to the “soundcard” interface of a computer and using software to decode the signals.
|
||||
In the early days of Amateur Packet Radio, it was necessary to use an expensive “Terminal Node Controller” (TNC) with specialized hardware. Those days are gone. You can now get better results at lower cost by connecting your radio to the “soundcard” interface of a computer and using software to decode the signals.
|
||||
|
||||
Dire Wolf is a software "soundcard" modem/TNC and [APRS](http://www.aprs.org/) encoder/decoder. It can be used stand-alone to observe APRS traffic, as a digipeater, [APRStt](http://www.aprs.org/aprstt.html) gateway, or Internet Gateway (IGate). It can also be used as a virtual TNC for other applications such as [APRSIS32](http://aprsisce.wikidot.com/), [UI-View32](http://www.ui-view.net/), [Xastir](http://xastir.org/index.php/Main_Page), [APRS-TW](http://aprstw.blandranch.net/), [YAAC](http://www.ka2ddo.org/ka2ddo/YAAC.html), [UISS](http://users.belgacom.net/hamradio/uiss.htm), [Linux AX25](http://www.linux-ax25.org/wiki/Main_Page), [SARTrack](http://www.sartrack.co.nz/index.html), [RMS Express](http://www.winlink.org/RMSExpress), [BPQ32](http://www.cantab.net/users/john.wiseman/Documents/BPQ32.html), [Outpost PM](http://www.outpostpm.org/) and many others.
|
||||
|
||||
|
||||
## Features ##
|
||||
## Features & Benefits ##
|
||||
|
||||
- Lower cost, higher performance alternative to hardware TNC.
|
||||
Decodes more than 1000 error-free frames from [WA8LMF TNC Test CD](http://wa8lmf.net/TNCtest/).
|
||||
|
||||
- Ideal for building a Raspberry Pi digipeater & IGate.
|
||||
|
||||
- Data rates: 300 AFSK, 1200 AFSK, 2400 QPSK, 4800 8PSK, and 9600/19200/38400 K9NG/G3RUH.
|
||||
- Data rates: 300 AFSK, 1200 AFSK, 2400 QPSK, 4800 8PSK, and 9600/19200/38400 bps K9NG/G3RUH.
|
||||
|
||||
- Interface with applications by
|
||||
- [AGW](http://uz7.ho.ua/includes/agwpeapi.htm) network protocol
|
||||
- [KISS](http://www.ax25.net/kiss.aspx) serial port
|
||||
- [KISS](http://www.ax25.net/kiss.aspx) network protocol
|
||||
- [KISS](http://www.ax25.net/kiss.aspx) TCP network protocol
|
||||
|
||||
- Decoding of received information for troubleshooting.
|
||||
|
||||
|
@ -48,30 +48,28 @@ Decodes more than 1000 error-free frames from [WA8LMF TNC Test CD](http://wa8lmf
|
|||
|
||||
- Runs in 3 different environments:
|
||||
- Microsoft Windows XP or later
|
||||
- Linux, regular PC or single board computer such as Raspberry Pi, BeagleBone Black, or cubieboard 2
|
||||
- Linux, regular PC/laptop or single board computer such as Raspberry Pi, BeagleBone Black, cubieboard 2, or C.H.I.P.
|
||||
- Mac OS X
|
||||
|
||||
## Documentation ##
|
||||
|
||||
[Stable Version](https://github.com/wb2osz/direwolf/tree/master/doc)
|
||||
|
||||
[Latest Development Version](https://github.com/wb2osz/direwolf/tree/dev/doc)
|
||||
|
||||
|
||||
## Installation ##
|
||||
|
||||
### Windows ###
|
||||
|
||||
Go to the [releases page](https://github.com/wb2osz/direwolf/releases). Download a zip file with "win" in its name, unzip it, and run direwolf.exe from a command window.
|
||||
Go to the [**releases** page](https://github.com/wb2osz/direwolf/releases). Download a zip file with "win" in its name, unzip it, and run direwolf.exe from a command window.
|
||||
|
||||
For more details see the **User Guide** in the [doc directory](https://github.com/wb2osz/direwolf/tree/master/doc).
|
||||
For more details see the **User Guide** in the [**doc** directory](https://github.com/wb2osz/direwolf/tree/master/doc).
|
||||
|
||||
|
||||
### Linux - Download with web browser ###
|
||||
|
||||
Go to the [releases page](https://github.com/wb2osz/direwolf/releases). Chose desired release and download the source as zip or compressed tar file. Unpack the files, with "unzip" or "tar xfz," and then:
|
||||
|
||||
cd direwolf-*
|
||||
make
|
||||
sudo make install
|
||||
make install-conf
|
||||
|
||||
For more details see the **User Guide** in the [doc directory](https://github.com/wb2osz/direwolf/tree/master/doc). Special considerations for the Raspberry Pi are found in **Raspberry-Pi-APRS.pdf**
|
||||
|
||||
### Linux - Using git clone ###
|
||||
### Linux - Using git clone (recommended) ###
|
||||
|
||||
cd ~
|
||||
git clone https://www.github.com/wb2osz/direwolf
|
||||
|
@ -80,13 +78,42 @@ For more details see the **User Guide** in the [doc directory](https://github.co
|
|||
sudo make install
|
||||
make install-conf
|
||||
|
||||
This should give you the most recent stable release. If you want the latest (unstable) development version, use "git checkout dev" instead before the first "make" command.
|
||||
This should give you the most recent stable release. If you want the latest (possibly unstable) development version, use "git checkout dev" before the first "make" command.
|
||||
|
||||
For more details see the **User Guide** in the [doc directory](https://github.com/wb2osz/direwolf/tree/master/doc). Special considerations for the Raspberry Pi are found in **Raspberry-Pi-APRS.pdf**
|
||||
For more details see the **User Guide** in the [**doc** directory](https://github.com/wb2osz/direwolf/tree/master/doc). Special considerations for the Raspberry Pi are found in **Raspberry-Pi-APRS.pdf**
|
||||
|
||||
|
||||
### Linux - Using apt-get (Debian flavor operating systems) ###
|
||||
|
||||
Results will vary depending on your hardware platform and operating system version because it depends on various volunteers who perform the packaging.
|
||||
|
||||
sudo apt-get update
|
||||
apt-cache showpkg direwolf
|
||||
sudo apt-get install direwolf
|
||||
|
||||
|
||||
### Linux - Using yum (Red Hat flavor operating systems) ###
|
||||
|
||||
Results will vary depending on your hardware platform and operating system version because it depends on various volunteers who perform the packaging.
|
||||
|
||||
sudo yum check-update
|
||||
sudo yum list direwolf
|
||||
sudo yum install direwolf
|
||||
|
||||
### Linux - Download source in tar or zip file ###
|
||||
|
||||
Go to the [releases page](https://github.com/wb2osz/direwolf/releases). Chose desired release and download the source as zip or compressed tar file. Unpack the files, with "unzip" or "tar xfz," and then:
|
||||
|
||||
cd direwolf-*
|
||||
make
|
||||
sudo make install
|
||||
make install-conf
|
||||
|
||||
For more details see the **User Guide** in the [**doc** directory](https://github.com/wb2osz/direwolf/tree/master/doc). Special considerations for the Raspberry Pi are found in **Raspberry-Pi-APRS.pdf**
|
||||
|
||||
## Join the conversation ##
|
||||
|
||||
Here are some good places to share information:
|
||||
Here are some good places to ask questions and share your experience:
|
||||
|
||||
- [Dire Wolf packet TNC](https://groups.yahoo.com/neo/groups/direwolf_packet/info)
|
||||
|
||||
|
|
16
aclients.c
16
aclients.c
|
@ -51,17 +51,14 @@
|
|||
* Linux: Use the BSD socket interface.
|
||||
*/
|
||||
|
||||
#include "direwolf.h" // Sets _WIN32_WINNT for XP API level needed by ws2tcpip.h
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
#include <winsock2.h>
|
||||
// default is 0x0400
|
||||
#undef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0501 /* Minimum OS version is XP. */
|
||||
#include <ws2tcpip.h>
|
||||
#include <ws2tcpip.h> // _WIN32_WINNT must be set to 0x0501 before including this
|
||||
|
||||
#else
|
||||
//#define __USE_XOPEN2KXSI 1
|
||||
//#define __USE_XOPEN 1
|
||||
#include <stdlib.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -83,7 +80,6 @@
|
|||
#include <time.h>
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
#include "version.h"
|
||||
|
@ -468,7 +464,11 @@ static void * client_thread_net (void *arg)
|
|||
// Try each address until we find one that is successful.
|
||||
|
||||
for (n=0; n<num_hosts; n++) {
|
||||
#if __WIN32__
|
||||
SOCKET is;
|
||||
#else
|
||||
int is;
|
||||
#endif
|
||||
|
||||
ai = hosts[n];
|
||||
|
||||
|
@ -578,7 +578,7 @@ static void * client_thread_net (void *arg)
|
|||
printf ("client %d received '%c' data, data_len = %d\n",
|
||||
my_index, mon_cmd.kind_lo, mon_cmd.data_len);
|
||||
#endif
|
||||
assert (mon_cmd.data_len >= 0 && mon_cmd.data_len < sizeof(data));
|
||||
assert (mon_cmd.data_len >= 0 && mon_cmd.data_len < (int)(sizeof(data)));
|
||||
|
||||
if (mon_cmd.data_len > 0) {
|
||||
#if __WIN32__
|
||||
|
|
15
aprs_tt.c
15
aprs_tt.c
|
@ -39,6 +39,8 @@
|
|||
|
||||
#define APRS_TT_C 1
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
|
||||
// TODO: clean up terminolgy.
|
||||
// "Message" has a specific meaning in APRS and this is not it.
|
||||
|
@ -56,7 +58,6 @@
|
|||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "version.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "hdlc_rec2.h" /* for process_rec_frame */
|
||||
|
@ -105,7 +106,9 @@ static int parse_aprstt3_call (char *e);
|
|||
static int parse_location (char *e);
|
||||
static int parse_comment (char *e);
|
||||
static int expand_macro (char *e);
|
||||
#ifndef TT_MAIN
|
||||
static void raw_tt_data_to_app (int chan, char *msg);
|
||||
#endif
|
||||
static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *bstr, char *dstr, size_t valstrsize);
|
||||
|
||||
#if TT_MAIN
|
||||
|
@ -378,6 +381,7 @@ void aprs_tt_sequence (int chan, char *msg)
|
|||
#endif
|
||||
|
||||
#if TT_MAIN
|
||||
(void)err; // suppress variable set but not used warning.
|
||||
check_result (); // for unit testing.
|
||||
#else
|
||||
|
||||
|
@ -1443,7 +1447,7 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
|
|||
|
||||
len = strlen(tt_config.ttloc_ptr[ipat].pattern);
|
||||
|
||||
if (strlen(e) == len) {
|
||||
if ((int)(strlen(e)) == len) {
|
||||
|
||||
match = 1;
|
||||
strlcpy (xstr, "", valstrsize);
|
||||
|
@ -1658,6 +1662,7 @@ static int parse_comment (char *e)
|
|||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
#ifndef TT_MAIN
|
||||
|
||||
static void raw_tt_data_to_app (int chan, char *msg)
|
||||
{
|
||||
|
@ -1668,9 +1673,6 @@ static void raw_tt_data_to_app (int chan, char *msg)
|
|||
char src[10], dest[10];
|
||||
char raw_tt_msg[256];
|
||||
packet_t pp;
|
||||
char *c, *s;
|
||||
int i;
|
||||
int err;
|
||||
alevel_t alevel;
|
||||
|
||||
|
||||
|
@ -1706,7 +1708,7 @@ static void raw_tt_data_to_app (int chan, char *msg)
|
|||
alevel.mark = -2;
|
||||
alevel.space = -2;
|
||||
|
||||
dlq_append (DLQ_REC_FRAME, chan, -1, 0, pp, alevel, RETRY_NONE, "tt");
|
||||
dlq_rec_frame (chan, -1, 0, pp, alevel, RETRY_NONE, "tt");
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
|
@ -1716,6 +1718,7 @@ static void raw_tt_data_to_app (int chan, char *msg)
|
|||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
|
|
132
atest.c
132
atest.c
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016 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
|
||||
|
@ -59,6 +59,7 @@
|
|||
|
||||
// #define X 1
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
@ -79,6 +80,7 @@
|
|||
#include "hdlc_rec2.h"
|
||||
#include "dlq.h"
|
||||
#include "ptt.h"
|
||||
#include "dtime_now.h"
|
||||
|
||||
|
||||
|
||||
|
@ -178,7 +180,10 @@ int main (int argc, char *argv[])
|
|||
int err;
|
||||
int c;
|
||||
int channel;
|
||||
time_t start_time;
|
||||
|
||||
double start_time; // Time when we started so we can measure elapsed time.
|
||||
double duration; // Length of the audio file in seconds.
|
||||
double elapsed; // Time it took us to process it.
|
||||
|
||||
|
||||
#if defined(EXPERIMENT_G) || defined(EXPERIMENT_H)
|
||||
|
@ -291,35 +296,55 @@ int main (int argc, char *argv[])
|
|||
case 'B': /* -B for data Bit rate */
|
||||
/* 300 implies 1600/1800 AFSK. */
|
||||
/* 1200 implies 1200/2200 AFSK. */
|
||||
/* 2400 implies V.26 */
|
||||
/* 9600 implies scrambled. */
|
||||
|
||||
my_audio_config.achan[0].baud = atoi(optarg);
|
||||
|
||||
dw_printf ("Data rate set to %d bits / second.\n", my_audio_config.achan[0].baud);
|
||||
|
||||
if (my_audio_config.achan[0].baud < 100 || my_audio_config.achan[0].baud > 10000) {
|
||||
if (my_audio_config.achan[0].baud < MIN_BAUD || my_audio_config.achan[0].baud > MAX_BAUD) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Use a more reasonable bit rate in range of 100 - 10000.\n");
|
||||
dw_printf ("Use a more reasonable bit rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* We have similar logic in direwolf.c, config.c, gen_packets.c, and atest.c, */
|
||||
/* that need to be kept in sync. Maybe it could be a common function someday. */
|
||||
|
||||
if (my_audio_config.achan[0].baud < 600) {
|
||||
my_audio_config.achan[0].modem_type = MODEM_AFSK;
|
||||
my_audio_config.achan[0].mark_freq = 1600;
|
||||
my_audio_config.achan[0].space_freq = 1800;
|
||||
strlcpy (my_audio_config.achan[0].profiles, "D", sizeof(my_audio_config.achan[0].profiles));
|
||||
}
|
||||
else if (my_audio_config.achan[0].baud > 2400) {
|
||||
else if (my_audio_config.achan[0].baud < 1800) {
|
||||
my_audio_config.achan[0].modem_type = MODEM_AFSK;
|
||||
my_audio_config.achan[0].mark_freq = DEFAULT_MARK_FREQ;
|
||||
my_audio_config.achan[0].space_freq = DEFAULT_SPACE_FREQ;
|
||||
// Should default to E+ or something similar later.
|
||||
}
|
||||
else if (my_audio_config.achan[0].baud < 3600) {
|
||||
my_audio_config.achan[0].modem_type = MODEM_QPSK;
|
||||
my_audio_config.achan[0].mark_freq = 0;
|
||||
my_audio_config.achan[0].space_freq = 0;
|
||||
strlcpy (my_audio_config.achan[0].profiles, "", sizeof(my_audio_config.achan[0].profiles));
|
||||
dw_printf ("Using V.26 QPSK rather than AFSK.\n");
|
||||
}
|
||||
else if (my_audio_config.achan[0].baud < 7200) {
|
||||
my_audio_config.achan[0].modem_type = MODEM_8PSK;
|
||||
my_audio_config.achan[0].mark_freq = 0;
|
||||
my_audio_config.achan[0].space_freq = 0;
|
||||
strlcpy (my_audio_config.achan[0].profiles, "", sizeof(my_audio_config.achan[0].profiles));
|
||||
dw_printf ("Using V.27 8PSK rather than AFSK.\n");
|
||||
}
|
||||
else {
|
||||
my_audio_config.achan[0].modem_type = MODEM_SCRAMBLE;
|
||||
my_audio_config.achan[0].mark_freq = 0;
|
||||
my_audio_config.achan[0].space_freq = 0;
|
||||
strlcpy (my_audio_config.achan[0].profiles, " ", sizeof(my_audio_config.achan[0].profiles)); // avoid getting default later.
|
||||
dw_printf ("Using scrambled baseband signal rather than AFSK.\n");
|
||||
}
|
||||
else {
|
||||
my_audio_config.achan[0].modem_type = MODEM_AFSK;
|
||||
my_audio_config.achan[0].mark_freq = 1200;
|
||||
my_audio_config.achan[0].space_freq = 2200;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'P': /* -P for modem profile. */
|
||||
|
@ -336,7 +361,7 @@ int main (int argc, char *argv[])
|
|||
if (decimate < 1 || decimate > 8) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Unreasonable value for -D.\n");
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
dw_printf ("Divide audio sample rate by %d\n", decimate);
|
||||
my_audio_config.achan[0].decimate = decimate;
|
||||
|
@ -349,7 +374,7 @@ int main (int argc, char *argv[])
|
|||
if (my_audio_config.achan[0].fix_bits < RETRY_NONE || my_audio_config.achan[0].fix_bits >= RETRY_MAX) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Invalid Fix Bits level.\n");
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -407,11 +432,10 @@ int main (int argc, char *argv[])
|
|||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Couldn't open file for read: %s\n", argv[optind]);
|
||||
//perror ("more info?");
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
start_time = time(NULL);
|
||||
|
||||
start_time = dtime_now();
|
||||
|
||||
/*
|
||||
* Read the file header.
|
||||
|
@ -437,12 +461,12 @@ int main (int argc, char *argv[])
|
|||
if (strncmp(chunk.id, "fmt ", 4) != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("WAV file error: Found \"%4.4s\" where \"fmt \" was expected.\n", chunk.id);
|
||||
exit(1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (chunk.datasize != 16 && chunk.datasize != 18) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("WAV file error: Need fmt chunk datasize of 16 or 18. Found %d.\n", chunk.datasize);
|
||||
exit(1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
err = fread (&format, (size_t)chunk.datasize, (size_t)1, fp);
|
||||
|
@ -452,12 +476,26 @@ int main (int argc, char *argv[])
|
|||
if (strncmp(wav_data.data, "data", 4) != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("WAV file error: Found \"%4.4s\" where \"data\" was expected.\n", wav_data.data);
|
||||
exit(1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// TODO: Should have proper message, not abort.
|
||||
assert (format.nchannels == 1 || format.nchannels == 2);
|
||||
assert (format.wbitspersample == 8 || format.wbitspersample == 16);
|
||||
if (format.wformattag != 1) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Sorry, I only understand audio format 1 (PCM). This file has %d.\n", format.wformattag);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (format.nchannels != 1 && format.nchannels != 2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Sorry, I only understand 1 or 2 channels. This file has %d.\n", format.nchannels);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (format.wbitspersample != 8 && format.wbitspersample != 16) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Sorry, I only understand 8 or 16 bits per sample. This file has %d.\n", format.wbitspersample);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
my_audio_config.adev[0].samples_per_sec = format.nsamplespersec;
|
||||
my_audio_config.adev[0].bits_per_sample = format.wbitspersample;
|
||||
|
@ -467,13 +505,17 @@ int main (int argc, char *argv[])
|
|||
if (format.nchannels == 2) my_audio_config.achan[1].valid = 1;
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("%d samples per second\n", my_audio_config.adev[0].samples_per_sec);
|
||||
dw_printf ("%d bits per sample\n", my_audio_config.adev[0].bits_per_sample);
|
||||
dw_printf ("%d audio channels\n", my_audio_config.adev[0].num_channels);
|
||||
dw_printf ("%d audio bytes in file\n", (int)(wav_data.datasize));
|
||||
dw_printf ("%d samples per second. %d bits per sample. %d audio channels.\n",
|
||||
my_audio_config.adev[0].samples_per_sec,
|
||||
my_audio_config.adev[0].bits_per_sample,
|
||||
my_audio_config.adev[0].num_channels);
|
||||
duration = (double) wav_data.datasize /
|
||||
((my_audio_config.adev[0].bits_per_sample / 8) * my_audio_config.adev[0].num_channels * my_audio_config.adev[0].samples_per_sec);
|
||||
dw_printf ("%d audio bytes in file. Duration = %.1f seconds.\n",
|
||||
(int)(wav_data.datasize),
|
||||
duration);
|
||||
dw_printf ("Fix Bits level = %d\n", my_audio_config.achan[0].fix_bits);
|
||||
|
||||
|
||||
/*
|
||||
* Initialize the AFSK demodulator and HDLC decoder.
|
||||
*/
|
||||
|
@ -496,8 +538,10 @@ int main (int argc, char *argv[])
|
|||
|
||||
audio_sample = demod_get_sample (ACHAN2ADEV(c));
|
||||
|
||||
if (audio_sample >= 256 * 256)
|
||||
if (audio_sample >= 256 * 256) {
|
||||
e_o_f = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == 0) sample_number++;
|
||||
|
||||
|
@ -527,20 +571,24 @@ int main (int argc, char *argv[])
|
|||
dw_printf ("%d\n", count[j]);
|
||||
}
|
||||
#endif
|
||||
dw_printf ("%d packets decoded in %d seconds.\n", packets_decoded, (int)(time(NULL) - start_time));
|
||||
|
||||
|
||||
elapsed = dtime_now() - start_time;
|
||||
|
||||
dw_printf ("%d packets decoded in %.3f seconds. %.1f x realtime\n", packets_decoded, elapsed, duration/elapsed);
|
||||
|
||||
if (error_if_less_than != -1 && packets_decoded < error_if_less_than) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\n * * * TEST FAILED: number decoded is less than %d * * * \n", error_if_less_than);
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
if (error_if_greater_than != -1 && packets_decoded > error_if_greater_than) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\n * * * TEST FAILED: number decoded is greater than %d * * * \n", error_if_greater_than);
|
||||
exit (1);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
exit (0);
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
|
@ -596,7 +644,7 @@ void rdq_append (rrbb_t rrbb)
|
|||
* This is called when we have a good frame.
|
||||
*/
|
||||
|
||||
void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
|
||||
void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
|
||||
{
|
||||
|
||||
char stemp[500];
|
||||
|
@ -695,6 +743,24 @@ void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp,
|
|||
ax25_safe_print ((char *)pinfo, info_len, 0);
|
||||
dw_printf ("\n");
|
||||
|
||||
#if 1 // temp experiment TODO: remove this.
|
||||
|
||||
#include "decode_aprs.h"
|
||||
#include "log.h"
|
||||
|
||||
if (ax25_is_aprs(pp)) {
|
||||
|
||||
decode_aprs_t A;
|
||||
|
||||
decode_aprs (&A, pp, 0);
|
||||
|
||||
// Temp experiment to see how different systems set the RR bits in the source and destination.
|
||||
// log_rr_bits (&A, pp);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
ax25_delete (pp);
|
||||
|
||||
} /* end fake dlq_append */
|
||||
|
@ -740,7 +806,7 @@ static void usage (void) {
|
|||
dw_printf ("\n");
|
||||
dw_printf (" -0 Use channel 0 (left) of stereo audio (default).\n");
|
||||
dw_printf (" -1 use channel 1 (right) of stereo audio.\n");
|
||||
dw_printf (" -1 decode both channels of stereo audio.\n");
|
||||
dw_printf (" -2 decode both channels of stereo audio.\n");
|
||||
dw_printf ("\n");
|
||||
dw_printf (" wav-file-in is a WAV format audio file.\n");
|
||||
dw_printf ("\n");
|
||||
|
|
11
audio.c
11
audio.c
|
@ -60,6 +60,7 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
@ -88,7 +89,6 @@
|
|||
#endif
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "audio.h"
|
||||
#include "audio_stats.h"
|
||||
#include "textcolor.h"
|
||||
|
@ -375,8 +375,8 @@ int audio_open (struct audio_s *pa)
|
|||
|
||||
{
|
||||
struct sockaddr_in si_me;
|
||||
int slen=sizeof(si_me);
|
||||
int data_size = 0;
|
||||
//int slen=sizeof(si_me);
|
||||
//int data_size = 0;
|
||||
|
||||
//Create UDP Socket
|
||||
if ((adev[a].udp_sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
|
||||
|
@ -1003,7 +1003,7 @@ int audio_get (int a)
|
|||
case AUDIO_IN_TYPE_SDR_UDP:
|
||||
|
||||
while (adev[a].inbuf_next >= adev[a].inbuf_len) {
|
||||
int ch, res,i;
|
||||
int res;
|
||||
|
||||
assert (adev[a].udp_sock > 0);
|
||||
res = recv(adev[a].udp_sock, adev[a].inbuf_ptr, adev[a].inbuf_size_in_bytes, 0);
|
||||
|
@ -1038,7 +1038,8 @@ int audio_get (int a)
|
|||
case AUDIO_IN_TYPE_STDIN:
|
||||
|
||||
while (adev[a].inbuf_next >= adev[a].inbuf_len) {
|
||||
int ch, res,i;
|
||||
//int ch, res,i;
|
||||
int res;
|
||||
|
||||
res = read(STDIN_FILENO, adev[a].inbuf_ptr, (size_t)adev[a].inbuf_size_in_bytes);
|
||||
if (res <= 0) {
|
||||
|
|
66
audio.h
66
audio.h
|
@ -89,6 +89,14 @@ struct audio_s {
|
|||
/* statistics reports. This is set by */
|
||||
/* the "-a" option. 0 to disable feature. */
|
||||
|
||||
int xmit_error_rate; /* For testing purposes, we can generate frames with an invalid CRC */
|
||||
/* to simulate corruption while going over the air. */
|
||||
/* This is the probability, in per cent, of randomly corrupting it. */
|
||||
/* Normally this is 0. 25 would mean corrupt it 25% of the time. */
|
||||
|
||||
int recv_error_rate; /* Similar but the % probablity of dropping a received frame. */
|
||||
|
||||
|
||||
/* Properties for each audio channel, common to receive and transmit. */
|
||||
/* Can be different for each radio channel. */
|
||||
|
||||
|
@ -101,11 +109,12 @@ struct audio_s {
|
|||
/* Could all be the same or different. */
|
||||
|
||||
|
||||
enum modem_t { MODEM_AFSK, MODEM_BASEBAND, MODEM_SCRAMBLE, MODEM_OFF } modem_type;
|
||||
enum modem_t { MODEM_AFSK, MODEM_BASEBAND, MODEM_SCRAMBLE, MODEM_QPSK, MODEM_8PSK, MODEM_OFF } modem_type;
|
||||
|
||||
/* Usual AFSK. */
|
||||
/* Baseband signal. Not used yet. */
|
||||
/* Scrambled http://www.amsat.org/amsat/articles/g3ruh/109/fig03.gif */
|
||||
/* Might try MFJ-2400 / CCITT v.26 / Bell 201 someday. */
|
||||
/* No modem. Might want this for DTMF only channel. */
|
||||
|
||||
|
||||
|
@ -130,8 +139,9 @@ struct audio_s {
|
|||
int mark_freq; /* Two tones for AFSK modulation, in Hz. */
|
||||
int space_freq; /* Standard tones are 1200 and 2200 for 1200 baud. */
|
||||
|
||||
int baud; /* Data bits (more accurately, symbols) per second. */
|
||||
int baud; /* Data bits per second. */
|
||||
/* Standard rates are 1200 for VHF and 300 for HF. */
|
||||
/* This should really be called bits per second. */
|
||||
|
||||
/* Next 3 come from config file or command line. */
|
||||
|
||||
|
@ -164,18 +174,20 @@ struct audio_s {
|
|||
int passall; /* Allow thru even with bad CRC. */
|
||||
|
||||
|
||||
|
||||
/* Additional properties for transmit. */
|
||||
|
||||
/* Originally we had control outputs only for PTT. */
|
||||
/* In version 1.2, we generalize this to allow others such as DCD. */
|
||||
/* In version 1.4 we add CON for connected to another station. */
|
||||
/* Index following structure by one of these: */
|
||||
|
||||
|
||||
#define OCTYPE_PTT 0
|
||||
#define OCTYPE_DCD 1
|
||||
#define OCTYPE_FUTURE 2
|
||||
#define OCTYPE_CON 2
|
||||
|
||||
#define NUM_OCTYPES 4 /* number of values above */
|
||||
#define NUM_OCTYPES 3 /* number of values above. i.e. last value +1. */
|
||||
|
||||
struct {
|
||||
|
||||
|
@ -187,7 +199,18 @@ struct audio_s {
|
|||
ptt_line_t ptt_line; /* Control line when using serial port. PTT_LINE_RTS, PTT_LINE_DTR. */
|
||||
ptt_line_t ptt_line2; /* Optional second one: PTT_LINE_NONE when not used. */
|
||||
|
||||
int ptt_gpio; /* GPIO number. */
|
||||
int out_gpio_num; /* GPIO number. Originally this was only for PTT. */
|
||||
/* It is now more general. */
|
||||
/* octrl array is indexed by PTT, DCD, or CONnected indicator. */
|
||||
|
||||
#define MAX_GPIO_NAME_LEN 16 // 12 would cover any case I've seen so this should be safe
|
||||
|
||||
char out_gpio_name[MAX_GPIO_NAME_LEN];
|
||||
/* orginally, gpio number NN was assumed to simply */
|
||||
/* have the name gpioNN but this turned out not to be */
|
||||
/* the case for CubieBoard where it was longer. */
|
||||
/* This is filled in by ptt_init so we don't have to */
|
||||
/* recalculate it each time we access it. */
|
||||
|
||||
int ptt_lpt_bit; /* Bit number for parallel printer port. */
|
||||
/* Bit 0 = pin 2, ..., bit 7 = pin 9. */
|
||||
|
@ -202,13 +225,26 @@ struct audio_s {
|
|||
|
||||
} octrl[NUM_OCTYPES];
|
||||
|
||||
|
||||
/* Each channel can also have associated input lines. */
|
||||
/* So far, we just have one for transmit inhibit. */
|
||||
|
||||
#define ICTYPE_TXINH 0
|
||||
|
||||
#define NUM_ICTYPES 1
|
||||
#define NUM_ICTYPES 1 /* number of values above. i.e. last value +1. */
|
||||
|
||||
struct {
|
||||
ptt_method_t method; /* none, serial port, GPIO, LPT. */
|
||||
int gpio; /* GPIO number */
|
||||
|
||||
int in_gpio_num; /* GPIO number */
|
||||
|
||||
char in_gpio_name[MAX_GPIO_NAME_LEN];
|
||||
/* orginally, gpio number NN was assumed to simply */
|
||||
/* have the name gpioNN but this turned out not to be */
|
||||
/* the case for CubieBoard where it was longer. */
|
||||
/* This is filled in by ptt_init so we don't have to */
|
||||
/* recalculate it each time we access it. */
|
||||
|
||||
int invert; /* 1 = active low */
|
||||
} ictrl[NUM_ICTYPES];
|
||||
|
||||
|
@ -278,8 +314,12 @@ struct audio_s {
|
|||
/* 44100 works a little better than 22050. */
|
||||
/* If you have a reasonable machine, use the highest rate. */
|
||||
#define MIN_SAMPLES_PER_SEC 8000
|
||||
#define MAX_SAMPLES_PER_SEC 48000 /* Formerly 44100. */
|
||||
/* Software defined radio often uses 48000. */
|
||||
//#define MAX_SAMPLES_PER_SEC 48000 /* Originally 44100. Later increased because */
|
||||
/* Software Defined Radio often uses 48000. */
|
||||
|
||||
#define MAX_SAMPLES_PER_SEC 192000 /* The cheap USB-audio adapters (e.g. CM108) can handle 44100 and 48000. */
|
||||
/* The "soundcard" in my desktop PC can do 96kHz or even 192kHz. */
|
||||
/* We will probably need to increase the sample rate to go much above 9600 baud. */
|
||||
|
||||
#define DEFAULT_BITS_PER_SAMPLE 16
|
||||
|
||||
|
@ -301,12 +341,12 @@ struct audio_s {
|
|||
#define DEFAULT_BAUD 1200
|
||||
|
||||
/* Used for sanity checking in config file and command line options. */
|
||||
/* 9600 is known to work. */
|
||||
/* TODO: Is 19200 possible with a soundcard at 44100 samples/sec? */
|
||||
/* 9600 baud is known to work. */
|
||||
/* TODO: Is 19200 possible with a soundcard at 44100 samples/sec or do we need a higher sample rate? */
|
||||
|
||||
#define MIN_BAUD 100
|
||||
#define MAX_BAUD 10000
|
||||
|
||||
//#define MAX_BAUD 10000
|
||||
#define MAX_BAUD 40000 // Anyone want to try 38.4 k baud?
|
||||
|
||||
/*
|
||||
* Typical transmit timings for VHF.
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
|
||||
#if defined(USE_PORTAUDIO)
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -54,7 +56,6 @@
|
|||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "audio.h"
|
||||
#include "audio_stats.h"
|
||||
#include "textcolor.h"
|
||||
|
|
|
@ -50,6 +50,9 @@
|
|||
*---------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -57,8 +60,9 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "audio_stats.h"
|
||||
#include "textcolor.h"
|
||||
#include "dtime_now.h"
|
||||
|
|
29
audio_win.c
29
audio_win.c
|
@ -39,6 +39,10 @@
|
|||
*---------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "direwolf.h" // Sets _WIN32_WINNT for XP API level needed by ws2tcpip.h
|
||||
// Also includes windows.h.
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -48,7 +52,6 @@
|
|||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
#ifndef WAVE_FORMAT_96M16
|
||||
|
@ -57,11 +60,9 @@
|
|||
#endif
|
||||
|
||||
#include <winsock2.h>
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#include <ws2tcpip.h>
|
||||
#include <ws2tcpip.h> // _WIN32_WINNT must be set to 0x0501 before including this
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "audio.h"
|
||||
#include "audio_stats.h"
|
||||
#include "textcolor.h"
|
||||
|
@ -286,7 +287,7 @@ int audio_open (struct audio_s *pa)
|
|||
|
||||
A->udp_sock = INVALID_SOCKET;
|
||||
|
||||
in_dev_no[a] = WAVE_MAPPER; /* = -1 */
|
||||
in_dev_no[a] = WAVE_MAPPER; /* = ((UINT)-1) in mmsystem.h */
|
||||
out_dev_no[a] = WAVE_MAPPER;
|
||||
|
||||
/*
|
||||
|
@ -319,16 +320,16 @@ int audio_open (struct audio_s *pa)
|
|||
|
||||
/* Otherwise, does it have search string? */
|
||||
|
||||
if (in_dev_no[a] == WAVE_MAPPER && strlen(pa->adev[a].adevice_in) >= 1) {
|
||||
if ((UINT)(in_dev_no[a]) == WAVE_MAPPER && strlen(pa->adev[a].adevice_in) >= 1) {
|
||||
num_devices = waveInGetNumDevs();
|
||||
for (n=0 ; n<num_devices && in_dev_no[a] == WAVE_MAPPER ; n++) {
|
||||
for (n=0 ; n<num_devices && (UINT)(in_dev_no[a]) == WAVE_MAPPER ; n++) {
|
||||
if ( ! waveInGetDevCaps(n, &wic, sizeof(WAVEINCAPS))) {
|
||||
if (strstr(wic.szPname, pa->adev[a].adevice_in) != NULL) {
|
||||
in_dev_no[a] = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (in_dev_no[a] == WAVE_MAPPER) {
|
||||
if ((UINT)(in_dev_no[a]) == WAVE_MAPPER) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\"%s\" doesn't match any of the input devices.\n", pa->adev[a].adevice_in);
|
||||
}
|
||||
|
@ -344,16 +345,16 @@ int audio_open (struct audio_s *pa)
|
|||
out_dev_no[a] = atoi(pa->adev[a].adevice_out);
|
||||
}
|
||||
|
||||
if (out_dev_no[a] == WAVE_MAPPER && strlen(pa->adev[a].adevice_out) >= 1) {
|
||||
if ((UINT)(out_dev_no[a]) == WAVE_MAPPER && strlen(pa->adev[a].adevice_out) >= 1) {
|
||||
num_devices = waveOutGetNumDevs();
|
||||
for (n=0 ; n<num_devices && out_dev_no[a] == WAVE_MAPPER ; n++) {
|
||||
for (n=0 ; n<num_devices && (UINT)(out_dev_no[a]) == WAVE_MAPPER ; n++) {
|
||||
if ( ! waveOutGetDevCaps(n, &woc, sizeof(WAVEOUTCAPS))) {
|
||||
if (strstr(woc.szPname, pa->adev[a].adevice_out) != NULL) {
|
||||
out_dev_no[a] = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (out_dev_no[a] == WAVE_MAPPER) {
|
||||
if ((UINT)(out_dev_no[a]) == WAVE_MAPPER) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\"%s\" doesn't match any of the output devices.\n", pa->adev[a].adevice_out);
|
||||
}
|
||||
|
@ -798,7 +799,7 @@ int audio_get (int a)
|
|||
|
||||
p = (WAVEHDR*)(A->in_headp); /* no need to be volatile at this point */
|
||||
|
||||
if (p->dwUser == -1) {
|
||||
if (p->dwUser == (DWORD)(-1)) {
|
||||
waveInUnprepareHeader(A->audio_in_handle, p, sizeof(WAVEHDR));
|
||||
p->dwUser = 0; /* Index for next byte. */
|
||||
|
||||
|
@ -959,11 +960,11 @@ int audio_put (int a, int c)
|
|||
/* Should never be full at this point. */
|
||||
|
||||
assert (p->dwBufferLength >= 0);
|
||||
assert (p->dwBufferLength < A->outbuf_size);
|
||||
assert (p->dwBufferLength < (DWORD)(A->outbuf_size));
|
||||
|
||||
p->lpData[p->dwBufferLength++] = c;
|
||||
|
||||
if (p->dwBufferLength == A->outbuf_size) {
|
||||
if (p->dwBufferLength == (DWORD)(A->outbuf_size)) {
|
||||
return (audio_flush(a));
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,82 @@
|
|||
|
||||
/* ax25_link.h */
|
||||
|
||||
|
||||
#ifndef AX25_LINK_H
|
||||
#define AX25_LINK_H 1
|
||||
|
||||
#include "ax25_pad.h" // for AX25_MAX_INFO_LEN
|
||||
|
||||
#include "dlq.h" // for dlq_item_t
|
||||
|
||||
#include "config.h" // for struct misc_config_s
|
||||
|
||||
|
||||
|
||||
// Limits and defaults for parameters.
|
||||
|
||||
|
||||
#define AX25_N1_PACLEN_MIN 1 // Max bytes in Information part of frame.
|
||||
#define AX25_N1_PACLEN_DEFAULT 256 // some v2.0 implementations have 128
|
||||
#define AX25_N1_PACLEN_MAX AX25_MAX_INFO_LEN // from ax25_pad.h
|
||||
|
||||
|
||||
#define AX25_N2_RETRY_MIN 1 // Number of times to retry before giving up.
|
||||
#define AX25_N2_RETRY_DEFAULT 10
|
||||
#define AX25_N2_RETRY_MAX 15
|
||||
|
||||
|
||||
#define AX25_T1V_FRACK_MIN 1 // Number of seconds to wait before retrying.
|
||||
#define AX25_T1V_FRACK_DEFAULT 3 // KPC-3+ has 4. TM-D710A has 3.
|
||||
#define AX25_T1V_FRACK_MAX 15
|
||||
|
||||
|
||||
#define AX25_K_MAXFRAME_BASIC_MIN 1 // Window size - number of I frames to send before waiting for ack.
|
||||
#define AX25_K_MAXFRAME_BASIC_DEFAULT 4
|
||||
#define AX25_K_MAXFRAME_BASIC_MAX 7
|
||||
|
||||
#define AX25_K_MAXFRAME_EXTENDED_MIN 1
|
||||
#define AX25_K_MAXFRAME_EXTENDED_DEFAULT 32
|
||||
#define AX25_K_MAXFRAME_EXTENDED_MAX 63 // In theory 127 but I'm restricting as explained in SREJ handling.
|
||||
|
||||
|
||||
|
||||
// Call once at startup time.
|
||||
|
||||
void ax25_link_init (struct misc_config_s *pconfig);
|
||||
|
||||
|
||||
|
||||
// IMPORTANT:
|
||||
|
||||
// These functions must be called on a single thread, one at a time.
|
||||
// The Data Link Queue (DLQ) is used to serialize events from multiple sources.
|
||||
|
||||
|
||||
void dl_connect_request (dlq_item_t *E);
|
||||
|
||||
void dl_disconnect_request (dlq_item_t *E);
|
||||
|
||||
void dl_data_request (dlq_item_t *E);
|
||||
|
||||
void dl_register_callsign (dlq_item_t *E);
|
||||
|
||||
void dl_unregister_callsign (dlq_item_t *E);
|
||||
|
||||
void dl_client_cleanup (dlq_item_t *E);
|
||||
|
||||
|
||||
void lm_data_indication (dlq_item_t *E);
|
||||
|
||||
void lm_channel_busy (dlq_item_t *E);
|
||||
|
||||
|
||||
void dl_timer_expiry (void);
|
||||
|
||||
|
||||
double ax25_link_get_next_timer_expiry (void);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/* end ax25_link.h */
|
513
ax25_pad.c
513
ax25_pad.c
|
@ -24,6 +24,10 @@
|
|||
*
|
||||
* Purpose: Packet assembler and disasembler.
|
||||
*
|
||||
* This was written when I was only concerned about APRS which
|
||||
* uses only UI frames. ax25_pad2.c, added years later, has
|
||||
* functions for dealing with other types of frames.
|
||||
*
|
||||
* We can obtain AX.25 packets from different sources:
|
||||
*
|
||||
* (a) from an HDLC frame.
|
||||
|
@ -77,11 +81,24 @@
|
|||
* SSID = substation ID
|
||||
* 0 = zero
|
||||
*
|
||||
* The AX.25 spec states that the RR bits should be 11 if not used.
|
||||
* There are a couple documents talking about possible uses for APRS.
|
||||
* I'm ignoring them for now.
|
||||
* http://www.aprs.org/aprs12/preemptive-digipeating.txt
|
||||
* http://www.aprs.org/aprs12/RR-bits.txt
|
||||
*
|
||||
* I don't recall why I originally intended to set the source/destination C bits both to 1.
|
||||
* Reviewing this 5 years later, after spending more time delving into the
|
||||
* AX.25 spec, I think it should be 1 for destination and 0 for source.
|
||||
* In practice you see all four combinations being used by APRS stations
|
||||
* and no one really cares about these two bits.
|
||||
*
|
||||
* The final octet of the Source has the form:
|
||||
*
|
||||
* C R R SSID 0, where,
|
||||
*
|
||||
* C = command/response = 1
|
||||
* C = command/response = 1 (originally, now I think it should be 0 for source.)
|
||||
* (Haven't gone back to check to see what code actually does.)
|
||||
* R R = Reserved = 1 1
|
||||
* SSID = substation ID
|
||||
* 0 = zero (or 1 if no repeaters)
|
||||
|
@ -146,16 +163,13 @@
|
|||
|
||||
#define AX25_PAD_C /* this will affect behavior of ax25_pad.h */
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#ifndef _POSIX_C_SOURCE
|
||||
|
||||
#define _POSIX_C_SOURCE 1
|
||||
#endif
|
||||
|
||||
#include "regex.h"
|
||||
|
||||
|
@ -163,9 +177,9 @@
|
|||
char *strtok_r(char *str, const char *delim, char **saveptr);
|
||||
#endif
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "ax25_pad.h"
|
||||
|
||||
#include "textcolor.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "fcs_calc.h"
|
||||
|
||||
/*
|
||||
|
@ -235,7 +249,13 @@ packet_t ax25_new (void)
|
|||
/*
|
||||
* check for memory leak.
|
||||
*/
|
||||
if (new_count > delete_count + 100) {
|
||||
|
||||
// version 1.4 push up the threshold. We could have considerably more with connected mode.
|
||||
|
||||
//if (new_count > delete_count + 100) {
|
||||
if (new_count > delete_count + 256) {
|
||||
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Report to WB2OSZ - Memory leak for packet objects. new=%d, delete=%d\n", new_count, delete_count);
|
||||
#if AX25MEMDEBUG
|
||||
|
@ -315,9 +335,18 @@ void ax25_delete (packet_t this_p)
|
|||
* Purpose: Parse a frame in human-readable monitoring format and change
|
||||
* to internal representation.
|
||||
*
|
||||
* Input: monitor - "TNC-2" format of a monitored packet. i.e.
|
||||
* Input: monitor - "TNC-2" monitor format for packet. i.e.
|
||||
* source>dest[,repeater1,repeater2,...]:information
|
||||
*
|
||||
* The information part can have non-printable characters
|
||||
* in the form of <0xff>. This will be converted to single
|
||||
* bytes. e.g. <0x0d> is carriage return.
|
||||
* In version 1.4H we will allow nul characters which means
|
||||
* we have to maintain a length rather than using strlen().
|
||||
* I maintain that it violates the spec but want to handle it
|
||||
* because it does happen and we want to preserve it when
|
||||
* acting as an IGate rather than corrupting it.
|
||||
*
|
||||
* strict - True to enforce rules for packets sent over the air.
|
||||
* False to be more lenient for packets from IGate server.
|
||||
*
|
||||
|
@ -349,17 +378,11 @@ packet_t ax25_from_text (char *monitor, int strict)
|
|||
char *pa;
|
||||
char *saveptr; /* Used with strtok_r because strtok is not thread safe. */
|
||||
|
||||
static int first_time = 1;
|
||||
static regex_t unhex_re;
|
||||
int e;
|
||||
char emsg[100];
|
||||
#define MAXMATCH 1
|
||||
regmatch_t match[MAXMATCH];
|
||||
int keep_going;
|
||||
char temp[512];
|
||||
int ssid_temp, heard_temp;
|
||||
char atemp[AX25_MAX_ADDR_LEN];
|
||||
|
||||
char info_part[AX25_MAX_INFO_LEN+1];
|
||||
int info_len;
|
||||
|
||||
packet_t this_p = ax25_new ();
|
||||
|
||||
|
@ -372,58 +395,15 @@ packet_t ax25_from_text (char *monitor, int strict)
|
|||
|
||||
/* Is it possible to have a nul character (zero byte) in the */
|
||||
/* information field of an AX.25 frame? */
|
||||
/* Yes, but it would be difficult in the from-text case. */
|
||||
/* At this point, we have a normal C string. */
|
||||
/* It is possible that will convert <0x00> to a nul character later. */
|
||||
/* There we need to maintain a separate length and not use normal C string functions. */
|
||||
|
||||
strlcpy (stuff, monitor, sizeof(stuff));
|
||||
|
||||
/*
|
||||
* Translate hexadecimal values like <0xff> to non-printing characters.
|
||||
* MIC-E message type uses 5 different non-printing characters.
|
||||
*/
|
||||
|
||||
if (first_time)
|
||||
{
|
||||
e = regcomp (&unhex_re, "<0x[0-9a-fA-F][0-9a-fA-F]>", 0);
|
||||
if (e) {
|
||||
regerror (e, &unhex_re, emsg, sizeof(emsg));
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("%s:%d: %s\n", __FILE__, __LINE__, emsg);
|
||||
}
|
||||
|
||||
first_time = 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("BEFORE: %s\n", stuff);
|
||||
ax25_safe_print (stuff, -1, 0);
|
||||
dw_printf ("\n");
|
||||
#endif
|
||||
keep_going = 1;
|
||||
while (keep_going) {
|
||||
if (regexec (&unhex_re, stuff, MAXMATCH, match, 0) == 0) {
|
||||
int n;
|
||||
char *p;
|
||||
|
||||
stuff[match[0].rm_so + 5] = '\0';
|
||||
n = strtol (stuff + match[0].rm_so + 3, &p, 16);
|
||||
stuff[match[0].rm_so] = n;
|
||||
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;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("AFTER: %s\n", stuff);
|
||||
ax25_safe_print (stuff, -1, 0);
|
||||
dw_printf ("\n");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialize the packet with two addresses and control/pid
|
||||
* Initialize the packet structure with two addresses and control/pid
|
||||
* for APRS.
|
||||
*/
|
||||
memset (this_p->frame_data + AX25_DESTINATION*7, ' ' << 1, 6);
|
||||
|
@ -433,7 +413,7 @@ packet_t ax25_from_text (char *monitor, int strict)
|
|||
this_p->frame_data[AX25_SOURCE*7+6] = SSID_H_MASK | SSID_RR_MASK | SSID_LAST_MASK;
|
||||
|
||||
this_p->frame_data[14] = AX25_UI_FRAME;
|
||||
this_p->frame_data[15] = AX25_NO_LAYER_3;
|
||||
this_p->frame_data[15] = AX25_PID_NO_LAYER_3;
|
||||
|
||||
this_p->frame_len = 7 + 7 + 1 + 1;
|
||||
this_p->num_addr = (-1);
|
||||
|
@ -453,12 +433,6 @@ packet_t ax25_from_text (char *monitor, int strict)
|
|||
*pinfo = '\0';
|
||||
pinfo++;
|
||||
|
||||
if (strlen(pinfo) > AX25_MAX_INFO_LEN) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Warning: Information part truncated to %d characters.\n", AX25_MAX_INFO_LEN);
|
||||
pinfo[AX25_MAX_INFO_LEN] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* Separate the addresses.
|
||||
* Note that source and destination order is swappped.
|
||||
|
@ -515,7 +489,6 @@ packet_t ax25_from_text (char *monitor, int strict)
|
|||
*/
|
||||
while (( pa = strtok_r (NULL, ",", &saveptr)) != NULL && this_p->num_addr < AX25_MAX_ADDRS ) {
|
||||
|
||||
//char *last;
|
||||
int k;
|
||||
|
||||
k = this_p->num_addr;
|
||||
|
@ -541,11 +514,61 @@ packet_t ax25_from_text (char *monitor, int strict)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Finally, process the information part.
|
||||
*
|
||||
* Translate hexadecimal values like <0xff> to single bytes.
|
||||
* MIC-E format uses 5 different non-printing characters.
|
||||
* We might want to manually generate UTF-8 characters such as degree.
|
||||
*/
|
||||
|
||||
//#define DEBUG14H 1
|
||||
|
||||
#if DEBUG14H
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("BEFORE: %s\nSAFE: ", pinfo);
|
||||
ax25_safe_print (pinfo, -1, 0);
|
||||
dw_printf ("\n");
|
||||
#endif
|
||||
|
||||
info_len = 0;
|
||||
while (*pinfo != '\0' && info_len < AX25_MAX_INFO_LEN) {
|
||||
|
||||
if (strlen(pinfo) >= 6 &&
|
||||
pinfo[0] == '<' &&
|
||||
pinfo[1] == '0' &&
|
||||
pinfo[2] == 'x' &&
|
||||
isxdigit(pinfo[3]) &&
|
||||
isxdigit(pinfo[4]) &&
|
||||
pinfo[5] == '>') {
|
||||
|
||||
char *p;
|
||||
|
||||
info_part[info_len] = strtol (pinfo + 3, &p, 16);
|
||||
info_len++;
|
||||
pinfo += 6;
|
||||
}
|
||||
else {
|
||||
info_part[info_len] = *pinfo;
|
||||
info_len++;
|
||||
pinfo++;
|
||||
}
|
||||
}
|
||||
info_part[info_len] = '\0';
|
||||
|
||||
#if DEBUG14H
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("AFTER: %s\nSAFE: ", info_part);
|
||||
ax25_safe_print (info_part, info_len, 0);
|
||||
dw_printf ("\n");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Append the info part.
|
||||
*/
|
||||
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);
|
||||
memcpy ((char*)(this_p->frame_data+this_p->frame_len), info_part, info_len);
|
||||
this_p->frame_len += info_len;
|
||||
|
||||
return (this_p);
|
||||
}
|
||||
|
@ -891,7 +914,10 @@ packet_t ax25_unwrap_third_party (packet_t from_pp)
|
|||
|
||||
(void) ax25_get_info (from_pp, &info_p);
|
||||
|
||||
result_pp = ax25_from_text((char *)info_p + 1, 0);
|
||||
// Want strict because addresses should conform to AX.25 here.
|
||||
// That's not the case for something from an Internet Server.
|
||||
|
||||
result_pp = ax25_from_text((char *)info_p + 1, 1);
|
||||
|
||||
return (result_pp);
|
||||
}
|
||||
|
@ -1513,15 +1539,48 @@ int ax25_get_first_not_repeated(packet_t this_p)
|
|||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: ax25_get_rr
|
||||
*
|
||||
* Purpose: Return the two reserved "RR" bits in the specified address field.
|
||||
*
|
||||
* Inputs: pp - Packet object.
|
||||
*
|
||||
* n - Index of address. Use the symbols
|
||||
* AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc.
|
||||
*
|
||||
* Returns: 0, 1, 2, or 3.
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
int ax25_get_rr (packet_t this_p, int n)
|
||||
{
|
||||
|
||||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
assert (n >= 0 && n < this_p->num_addr);
|
||||
|
||||
if (n >= 0 && n < this_p->num_addr) {
|
||||
return ((this_p->frame_data[n*7+6] & SSID_RR_MASK) >> SSID_RR_SHIFT);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error: ax25_get_rr(%d), num_addr=%d\n", n, this_p->num_addr);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: ax25_get_info
|
||||
*
|
||||
* Purpose: Obtain Information part of current packet.
|
||||
*
|
||||
* Inputs: None.
|
||||
* Inputs: this_p - Packet object pointer.
|
||||
*
|
||||
* Outputs: paddr - Starting address is returned here.
|
||||
* Outputs: paddr - Starting address of information part is returned here.
|
||||
*
|
||||
* Assumption: ax25_from_text or ax25_from_frame was called first.
|
||||
*
|
||||
|
@ -1562,6 +1621,56 @@ int ax25_get_info (packet_t this_p, unsigned char **paddr)
|
|||
|
||||
*paddr = info_ptr;
|
||||
return (info_len);
|
||||
|
||||
} /* end ax25_get_info */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: ax25_cut_at_crlf
|
||||
*
|
||||
* Purpose: Truncate the information part at the first CR or LF.
|
||||
* This is used for the RF>IS IGate function.
|
||||
* CR/LF is used as record separator so we must remove it
|
||||
* before packaging up packet to sending to server.
|
||||
*
|
||||
* Inputs: this_p - Packet object pointer.
|
||||
*
|
||||
* Outputs: Packet is modified in place.
|
||||
*
|
||||
* Returns: Number of characters removed from the end.
|
||||
* 0 if not changed.
|
||||
*
|
||||
* Assumption: ax25_from_text or ax25_from_frame was called first.
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
int ax25_cut_at_crlf (packet_t this_p)
|
||||
{
|
||||
unsigned char *info_ptr;
|
||||
int info_len;
|
||||
int j;
|
||||
|
||||
|
||||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
info_len = ax25_get_info (this_p, &info_ptr);
|
||||
|
||||
// Can't use strchr because there is potential of nul character.
|
||||
|
||||
for (j = 0; j < info_len; j++) {
|
||||
|
||||
if (info_ptr[j] == '\r' || info_ptr[j] == '\n') {
|
||||
|
||||
int chop = info_len - j;
|
||||
|
||||
this_p->frame_len -= chop;
|
||||
return (chop);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1674,6 +1783,25 @@ double ax25_get_release_time (packet_t this_p)
|
|||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: ax25_set_modulo
|
||||
*
|
||||
* Purpose: Set modulo value for I and S frame sequence numbers.
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
void ax25_set_modulo (packet_t this_p, int modulo)
|
||||
{
|
||||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
this_p->modulo = modulo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
|
@ -1745,6 +1873,64 @@ void ax25_format_addrs (packet_t this_p, char *result)
|
|||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: ax25_format_via_path
|
||||
*
|
||||
* Purpose: Format via path addresses suitable for printing.
|
||||
*
|
||||
* Inputs: Current packet.
|
||||
*
|
||||
* result_size - Number of bytes available for result.
|
||||
* We can have up to 8 addresses x 9 characters
|
||||
* plus 7 commas, possible *, and nul = 81 minimum.
|
||||
*
|
||||
* Outputs: result - Digipeater field addresses combined into a single string of the form:
|
||||
*
|
||||
* "repeater, repeater ..."
|
||||
*
|
||||
* An asterisk is displayed after the last digipeater
|
||||
* with the "H" bit set. e.g. If we hear RPT2,
|
||||
*
|
||||
* RPT1,RPT2*,RPT3
|
||||
*
|
||||
* No asterisk means the source is being heard directly.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
void ax25_format_via_path (packet_t this_p, char *result, size_t result_size)
|
||||
{
|
||||
int i;
|
||||
int heard;
|
||||
char stemp[AX25_MAX_ADDR_LEN];
|
||||
|
||||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
*result = '\0';
|
||||
|
||||
/* Don't get upset if no addresses. */
|
||||
/* This will allow packets that do not comply to AX.25 format. */
|
||||
|
||||
if (this_p->num_addr == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
heard = ax25_get_heard(this_p);
|
||||
|
||||
for (i=(int)AX25_REPEATER_1; i<this_p->num_addr; i++) {
|
||||
if (i > (int)AX25_REPEATER_1) {
|
||||
strlcat (result, ",", result_size);
|
||||
}
|
||||
ax25_get_addr_with_ssid (this_p, i, stemp);
|
||||
strlcat (result, stemp, result_size);
|
||||
if (i == heard) {
|
||||
strlcat (result, "*", result_size);
|
||||
}
|
||||
}
|
||||
|
||||
} /* end ax25_format_via_path */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: ax25_pack
|
||||
|
@ -1789,12 +1975,11 @@ int ax25_pack (packet_t this_p, unsigned char result[AX25_MAX_PACKET_LEN])
|
|||
*
|
||||
* Inputs: this_p - pointer to packet object.
|
||||
*
|
||||
* modulo - We often need to know this because context is
|
||||
* required to know if control is 1 or 2 bytes.
|
||||
*
|
||||
* Outputs: desc - Text description such as "I frame" or
|
||||
* "U frame SABME".
|
||||
* Supply 16 bytes to be safe.
|
||||
* Supply 40 bytes to be safe.
|
||||
*
|
||||
* cr - Command or response?
|
||||
*
|
||||
* pf - P/F - Poll/Final or -1 if not applicable
|
||||
*
|
||||
|
@ -1807,17 +1992,20 @@ 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 32
|
||||
// Should pass in as parameter.
|
||||
#define DESC_SIZ 40
|
||||
|
||||
ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *desc, int *pf, int *nr, int *ns)
|
||||
|
||||
ax25_frame_type_t ax25_frame_type (packet_t this_p, cmdres_t *cr, char *desc, int *pf, int *nr, int *ns)
|
||||
{
|
||||
int c; // U frames are always one control byte.
|
||||
int c2; // I & S frames can have second Control byte.
|
||||
int c2 = 0; // I & S frames can have second Control byte.
|
||||
|
||||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
strlcpy (desc, "????", DESC_SIZ);
|
||||
*cr = cr_11;
|
||||
*pf = -1;
|
||||
*nr = -1;
|
||||
*ns = -1;
|
||||
|
@ -1827,16 +2015,65 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *
|
|||
strlcpy (desc, "Not AX.25", DESC_SIZ);
|
||||
return (frame_not_AX25);
|
||||
}
|
||||
if (modulo == modulo_128) {
|
||||
|
||||
/*
|
||||
* TERRIBLE HACK :-( for display purposes.
|
||||
*
|
||||
* I and S frames can have 1 or 2 control bytes but there is
|
||||
* no good way to determine this without dipping into the data
|
||||
* link state machine. Can we guess?
|
||||
*
|
||||
* S frames have no protocol id or information so if there is one
|
||||
* more byte beyond the control field, we could assume there are
|
||||
* two control bytes.
|
||||
*
|
||||
* For I frames, the protocol id will usually be 0xf0. If we find
|
||||
* that as the first byte of the information field, it is probably
|
||||
* the pid and not part of the information. Ditto for segments 0x08.
|
||||
* Not fool proof but good enough for troubleshooting text out.
|
||||
*
|
||||
* If we have a link to the peer station, this will be set properly
|
||||
* before it needs to be used for other reasons.
|
||||
*
|
||||
* Setting one of the RR bits (find reference!) is sounding better and better.
|
||||
* It's in common usage so I should lobby to get that in the official protocol spec.
|
||||
*/
|
||||
|
||||
if (this_p->modulo == 0 && (c & 3) == 1 && ax25_get_c2(this_p) != -1) {
|
||||
this_p->modulo = modulo_128;
|
||||
}
|
||||
else if (this_p->modulo == 0 && (c & 1) == 0 && this_p->frame_data[ax25_get_info_offset(this_p)] == 0xF0) {
|
||||
this_p->modulo = modulo_128;
|
||||
}
|
||||
else if (this_p->modulo == 0 && (c & 1) == 0 && this_p->frame_data[ax25_get_info_offset(this_p)] == 0x08) { // same for segments
|
||||
this_p->modulo = modulo_128;
|
||||
}
|
||||
|
||||
|
||||
if (this_p->modulo == modulo_128) {
|
||||
c2 = ax25_get_c2 (this_p);
|
||||
}
|
||||
|
||||
int dst_c = this_p->frame_data[AX25_DESTINATION * 7 + 6] & SSID_H_MASK;
|
||||
int src_c = this_p->frame_data[AX25_SOURCE * 7 + 6] & SSID_H_MASK;
|
||||
|
||||
char cr_text[8];
|
||||
char pf_text[8];
|
||||
|
||||
if (dst_c) {
|
||||
if (src_c) { *cr = cr_11; strcpy(cr_text,"cc=11"); strcpy(pf_text,"p/f"); }
|
||||
else { *cr = cr_cmd; strcpy(cr_text,"cmd"); strcpy(pf_text,"p"); }
|
||||
}
|
||||
else {
|
||||
if (src_c) { *cr = cr_res; strcpy(cr_text,"res"); strcpy(pf_text,"f"); }
|
||||
else { *cr = cr_00; strcpy(cr_text,"cc=00"); strcpy(pf_text,"p/f"); }
|
||||
}
|
||||
|
||||
if ((c & 1) == 0) {
|
||||
|
||||
// Information rrr p sss 0 or sssssss 0 rrrrrrr p
|
||||
|
||||
if (modulo == modulo_128) {
|
||||
if (this_p->modulo == modulo_128) {
|
||||
*ns = (c >> 1) & 0x7f;
|
||||
*pf = c2 & 1;
|
||||
*nr = (c2 >> 1) & 0x7f;
|
||||
|
@ -1846,14 +2083,16 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *
|
|||
*pf = (c >> 4) & 1;
|
||||
*nr = (c >> 5) & 7;
|
||||
}
|
||||
snprintf (desc, DESC_SIZ, "I frame, n(s)= %d, n(r)=%d, p=%d", *ns, *nr, *pf);
|
||||
|
||||
//snprintf (desc, DESC_SIZ, "I %s, n(s)=%d, n(r)=%d, %s=%d", cr_text, *ns, *nr, pf_text, *pf);
|
||||
snprintf (desc, DESC_SIZ, "I %s, n(s)=%d, n(r)=%d, %s=%d, pid=0x%02x", cr_text, *ns, *nr, pf_text, *pf, ax25_get_pid(this_p));
|
||||
return (frame_type_I);
|
||||
}
|
||||
else if ((c & 2) == 0) {
|
||||
|
||||
// Supervisory rrr p/f ss 0 1 or 0000 ss 0 1 rrrrrrr p/f
|
||||
|
||||
if (modulo == modulo_128) {
|
||||
if (this_p->modulo == modulo_128) {
|
||||
*pf = c2 & 1;
|
||||
*nr = (c2 >> 1) & 0x7f;
|
||||
}
|
||||
|
@ -1862,11 +2101,12 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *
|
|||
*nr = (c >> 5) & 7;
|
||||
}
|
||||
|
||||
|
||||
switch ((c >> 2) & 3) {
|
||||
case 0: snprintf (desc, DESC_SIZ, "S frame RR, n(r)=%d, p/f=%d", *nr, *pf); return (frame_type_S_RR); break;
|
||||
case 1: snprintf (desc, DESC_SIZ, "S frame RNR, n(r)=%d, p/f=%d", *nr, *pf); return (frame_type_S_RNR); break;
|
||||
case 2: snprintf (desc, DESC_SIZ, "S frame REJ, n(r)=%d, p/f=%d", *nr, *pf); return (frame_type_S_REJ); break;
|
||||
case 3: snprintf (desc, DESC_SIZ, "S frame SREJ, n(r)=%d, p/f=%d", *nr, *pf); return (frame_type_S_SREJ); break;
|
||||
case 0: snprintf (desc, DESC_SIZ, "RR %s, n(r)=%d, %s=%d", cr_text, *nr, pf_text, *pf); return (frame_type_S_RR); break;
|
||||
case 1: snprintf (desc, DESC_SIZ, "RNR %s, n(r)=%d, %s=%d", cr_text, *nr, pf_text, *pf); return (frame_type_S_RNR); break;
|
||||
case 2: snprintf (desc, DESC_SIZ, "REJ %s, n(r)=%d, %s=%d", cr_text, *nr, pf_text, *pf); return (frame_type_S_REJ); break;
|
||||
case 3: snprintf (desc, DESC_SIZ, "SREJ %s, n(r)=%d, %s=%d", cr_text, *nr, pf_text, *pf); return (frame_type_S_SREJ); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -1877,16 +2117,16 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *
|
|||
|
||||
switch (c & 0xef) {
|
||||
|
||||
case 0x6f: snprintf (desc, DESC_SIZ, "U frame SABME, p=%d", *pf); return (frame_type_U_SABME); break;
|
||||
case 0x2f: snprintf (desc, DESC_SIZ, "U frame SABM, p=%d", *pf); return (frame_type_U_SABM); break;
|
||||
case 0x43: snprintf (desc, DESC_SIZ, "U frame DISC, p=%d", *pf); return (frame_type_U_DISC); break;
|
||||
case 0x0f: snprintf (desc, DESC_SIZ, "U frame DM, f=%d", *pf); return (frame_type_U_DM); break;
|
||||
case 0x63: snprintf (desc, DESC_SIZ, "U frame UA, f=%d", *pf); return (frame_type_U_UA); break;
|
||||
case 0x87: snprintf (desc, DESC_SIZ, "U frame FRMR, f=%d", *pf); return (frame_type_U_FRMR); break;
|
||||
case 0x03: snprintf (desc, DESC_SIZ, "U frame UI, pf=%d", *pf); return (frame_type_U_UI); break;
|
||||
case 0xaf: snprintf (desc, DESC_SIZ, "U frame XID, pf=%d", *pf); return (frame_type_U_XID); break;
|
||||
case 0xe3: snprintf (desc, DESC_SIZ, "U frame TEST, pf=%d", *pf); return (frame_type_U_TEST); break;
|
||||
default: snprintf (desc, DESC_SIZ, "U frame ???"); return (frame_type_U); break;
|
||||
case 0x6f: snprintf (desc, DESC_SIZ, "SABME %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_SABME); break;
|
||||
case 0x2f: snprintf (desc, DESC_SIZ, "SABM %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_SABM); break;
|
||||
case 0x43: snprintf (desc, DESC_SIZ, "DISC %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_DISC); break;
|
||||
case 0x0f: snprintf (desc, DESC_SIZ, "DM %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_DM); break;
|
||||
case 0x63: snprintf (desc, DESC_SIZ, "UA %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_UA); break;
|
||||
case 0x87: snprintf (desc, DESC_SIZ, "FRMR %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_FRMR); break;
|
||||
case 0x03: snprintf (desc, DESC_SIZ, "UI %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_UI); break;
|
||||
case 0xaf: snprintf (desc, DESC_SIZ, "XID %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_XID); break;
|
||||
case 0xe3: snprintf (desc, DESC_SIZ, "TEST %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_TEST); break;
|
||||
default: snprintf (desc, DESC_SIZ, "U other???"); return (frame_type_U); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1897,6 +2137,8 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *
|
|||
|
||||
} /* end ax25_frame_type */
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: ax25_hex_dump
|
||||
|
@ -1935,6 +2177,7 @@ static void hex_dump (unsigned char *p, int len)
|
|||
}
|
||||
|
||||
/* Text description of control octet. */
|
||||
// FIXME: this is wrong. It doesn't handle modulo 128.
|
||||
|
||||
// TODO: use ax25_frame_type() instead.
|
||||
|
||||
|
@ -2083,10 +2326,12 @@ int ax25_is_aprs (packet_t this_p)
|
|||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
if (this_p->frame_len == 0) return(0);
|
||||
|
||||
ctrl = ax25_get_control(this_p);
|
||||
pid = ax25_get_pid(this_p);
|
||||
|
||||
is_aprs = this_p->num_addr >= 2 && ctrl == AX25_UI_FRAME && pid == AX25_NO_LAYER_3;
|
||||
is_aprs = this_p->num_addr >= 2 && ctrl == AX25_UI_FRAME && pid == AX25_PID_NO_LAYER_3;
|
||||
|
||||
#if 0
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
|
@ -2095,6 +2340,41 @@ int ax25_is_aprs (packet_t this_p)
|
|||
return (is_aprs);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: ax25_is_null_frame
|
||||
*
|
||||
* Purpose: Is this packet structure empty?
|
||||
*
|
||||
* Inputs: this_p - pointer to packet object.
|
||||
*
|
||||
* Returns: True if frame data length is 0.
|
||||
*
|
||||
* Description: This is used when we want to wake up the
|
||||
* transmit queue processing thread but don't
|
||||
* want to transmit a frame.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
|
||||
int ax25_is_null_frame (packet_t this_p)
|
||||
{
|
||||
int is_null;
|
||||
|
||||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
is_null = this_p->frame_len == 0;
|
||||
|
||||
#if 0
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ax25_is_null_frame(): is_null=%d\n", is_null);
|
||||
#endif
|
||||
return (is_null);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: ax25_get_control
|
||||
|
@ -2115,6 +2395,8 @@ int ax25_get_control (packet_t this_p)
|
|||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
if (this_p->frame_len == 0) return(-1);
|
||||
|
||||
if (this_p->num_addr >= 2) {
|
||||
return (this_p->frame_data[ax25_get_control_offset(this_p)]);
|
||||
}
|
||||
|
@ -2126,10 +2408,19 @@ int ax25_get_c2 (packet_t this_p)
|
|||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
if (this_p->frame_len == 0) return(-1);
|
||||
|
||||
if (this_p->num_addr >= 2) {
|
||||
return (this_p->frame_data[ax25_get_control_offset(this_p)+1]);
|
||||
int offset2 = ax25_get_control_offset(this_p)+1;
|
||||
|
||||
if (offset2 < this_p->frame_len) {
|
||||
return (this_p->frame_data[offset2]);
|
||||
}
|
||||
return (-1);
|
||||
else {
|
||||
return (-1); /* attempt to go beyond the end of frame. */
|
||||
}
|
||||
}
|
||||
return (-1); /* not AX.25 */
|
||||
}
|
||||
|
||||
|
||||
|
@ -2159,6 +2450,8 @@ int ax25_get_pid (packet_t this_p)
|
|||
// TODO: handle 2 control byte case.
|
||||
// TODO: sanity check: is it I or UI frame?
|
||||
|
||||
if (this_p->frame_len == 0) return(-1);
|
||||
|
||||
if (this_p->num_addr >= 2) {
|
||||
return (this_p->frame_data[ax25_get_pid_offset(this_p)]);
|
||||
}
|
||||
|
@ -2296,7 +2589,7 @@ unsigned short ax25_m_m_crc (packet_t pp)
|
|||
*
|
||||
* Inputs: pstr - Pointer to string.
|
||||
*
|
||||
* len - Maximum length if not -1.
|
||||
* len - Number of bytes. If < 0 we use strlen().
|
||||
*
|
||||
* ascii_only - Restrict output to only ASCII.
|
||||
* Normally we allow UTF-8.
|
||||
|
@ -2347,7 +2640,7 @@ void ax25_safe_print (char *pstr, int len, int ascii_only)
|
|||
if (len > MAXSAFE)
|
||||
len = MAXSAFE;
|
||||
|
||||
while (len > 0 && *pstr != '\0')
|
||||
while (len > 0)
|
||||
{
|
||||
ch = *((unsigned char *)pstr);
|
||||
|
||||
|
@ -2437,7 +2730,11 @@ int ax25_alevel_to_text (alevel_t alevel, char text[AX25_ALEVEL_TO_TEXT_SIZE])
|
|||
|
||||
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 */
|
||||
else if (alevel.mark == -1 && alevel.space == -1) { /* PSK - single number. */
|
||||
|
||||
snprintf (text, AX25_ALEVEL_TO_TEXT_SIZE, "%d", alevel.rec);
|
||||
}
|
||||
else if (alevel.mark == -2 && alevel.space == -2) { /* DTMF - single number. */
|
||||
|
||||
snprintf (text, AX25_ALEVEL_TO_TEXT_SIZE, "%d", alevel.rec);
|
||||
}
|
||||
|
|
85
ax25_pad.h
85
ax25_pad.h
|
@ -64,8 +64,10 @@
|
|||
*/
|
||||
|
||||
#define AX25_UI_FRAME 3 /* Control field value. */
|
||||
#define AX25_NO_LAYER_3 0xf0 /* protocol ID */
|
||||
|
||||
#define AX25_PID_NO_LAYER_3 0xf0 /* protocol ID used for APRS */
|
||||
#define AX25_PID_SEGMENTATION_FRAGMENT 0x08
|
||||
#define AX25_PID_ESCAPE_CHARACTER 0xff
|
||||
|
||||
|
||||
#ifdef AX25_PAD_C /* Keep this hidden - implementation could change. */
|
||||
|
@ -100,7 +102,8 @@ struct packet_s {
|
|||
* Changed to 1 when position has been used.
|
||||
*
|
||||
* for source & destination it is called
|
||||
* command/response and is normally 1.
|
||||
* command/response. Normally both 1 for APRS.
|
||||
* They should be opposites for connected mode.
|
||||
*
|
||||
* R R Reserved. Normally set to 1 1.
|
||||
*
|
||||
|
@ -109,6 +112,7 @@ struct packet_s {
|
|||
* 0 Usually 0 but 1 for last address.
|
||||
*/
|
||||
|
||||
|
||||
#define SSID_H_MASK 0x80
|
||||
#define SSID_H_SHIFT 7
|
||||
|
||||
|
@ -123,6 +127,15 @@ struct packet_s {
|
|||
|
||||
int frame_len; /* Frame length without CRC. */
|
||||
|
||||
int modulo; /* I & S frames have sequence numbers of either 3 bits (modulo 8) */
|
||||
/* or 7 bits (modulo 128). This is conveyed by either 1 or 2 */
|
||||
/* control bytes. Unfortunately, we can't determine this by looking */
|
||||
/* at an isolated frame. We need to know about the context. If we */
|
||||
/* are part of the conversation, we would know. But if we are */
|
||||
/* just listening to others, this would be more difficult to determine. */
|
||||
|
||||
/* For U frames: set to 0 - not applicable */
|
||||
/* For I & S frames: 8 or 128 if known. 0 if unknown. */
|
||||
|
||||
unsigned char frame_data[AX25_MAX_PACKET_LEN+1];
|
||||
/* Raw frame contents, without the CRC. */
|
||||
|
@ -145,30 +158,53 @@ struct packet_s {
|
|||
|
||||
typedef struct packet_s *packet_t;
|
||||
|
||||
typedef enum cmdres_e { cr_00 = 2, cr_cmd = 1, cr_res = 0, cr_11 = 3 } cmdres_t;
|
||||
|
||||
|
||||
extern packet_t ax25_new (void);
|
||||
|
||||
|
||||
#ifdef AX25_PAD_C /* Keep this hidden - implementation could change. */
|
||||
|
||||
extern packet_t ax25_new (void);
|
||||
|
||||
/*
|
||||
* APRS always has one control octet of 0x03 but the more
|
||||
* general AX.25 case is one or two control bytes depending on
|
||||
* "modulo 128 operation" is in effect. Unfortunately, it seems
|
||||
* this can be determined only by examining the XID frames and
|
||||
* keeping this information for each connection.
|
||||
* We can assume 1 for our current purposes.
|
||||
* whether "modulo 128 operation" is in effect.
|
||||
*/
|
||||
|
||||
//#define DEBUGX 1
|
||||
|
||||
static inline int ax25_get_control_offset (packet_t this_p)
|
||||
{
|
||||
//return (0);
|
||||
return (this_p->num_addr*7);
|
||||
}
|
||||
|
||||
static inline int ax25_get_num_control (packet_t this_p)
|
||||
{
|
||||
return (1); // TODO: always be 1 for U frame. More complicated for I and S.
|
||||
int c;
|
||||
|
||||
c = this_p->frame_data[ax25_get_control_offset(this_p)];
|
||||
|
||||
if ( (c & 0x01) == 0 ) { /* I xxxx xxx0 */
|
||||
#if DEBUGX
|
||||
dw_printf ("ax25_get_num_control, %02x is I frame, returns %d\n", c, (this_p->modulo == 128) ? 2 : 1);
|
||||
#endif
|
||||
return ((this_p->modulo == 128) ? 2 : 1);
|
||||
}
|
||||
|
||||
if ( (c & 0x03) == 1 ) { /* S xxxx xx01 */
|
||||
#if DEBUGX
|
||||
dw_printf ("ax25_get_num_control, %02x is S frame, returns %d\n", c, (this_p->modulo == 128) ? 2 : 1);
|
||||
#endif
|
||||
return ((this_p->modulo == 128) ? 2 : 1);
|
||||
}
|
||||
|
||||
#if DEBUGX
|
||||
dw_printf ("ax25_get_num_control, %02x is U frame, always returns 1.\n", c);
|
||||
#endif
|
||||
|
||||
return (1); /* U xxxx xx11 */
|
||||
}
|
||||
|
||||
|
||||
|
@ -194,11 +230,17 @@ static int ax25_get_num_pid (packet_t this_p)
|
|||
c == 0x03 || c == 0x13) { /* UI 000x 0011 */
|
||||
|
||||
pid = this_p->frame_data[ax25_get_pid_offset(this_p)];
|
||||
if (pid == 0xff) {
|
||||
#if DEBUGX
|
||||
dw_printf ("ax25_get_num_pid, %02x is I or UI frame, pid = %02x, returns %d\n", c, pid, (pid==AX25_PID_ESCAPE_CHARACTER) ? 2 : 1);
|
||||
#endif
|
||||
if (pid == AX25_PID_ESCAPE_CHARACTER) {
|
||||
return (2); /* pid 1111 1111 means another follows. */
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
#if DEBUGX
|
||||
dw_printf ("ax25_get_num_pid, %02x is neither I nor UI frame, returns 0\n", c);
|
||||
#endif
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -217,10 +259,14 @@ static int ax25_get_num_pid (packet_t this_p)
|
|||
|
||||
static inline int ax25_get_info_offset (packet_t this_p)
|
||||
{
|
||||
return (ax25_get_control_offset (this_p) + ax25_get_num_control(this_p) + ax25_get_num_pid(this_p));
|
||||
int offset = ax25_get_control_offset (this_p) + ax25_get_num_control(this_p) + ax25_get_num_pid(this_p);
|
||||
#if DEBUGX
|
||||
dw_printf ("ax25_get_info_offset, returns %d\n", offset);
|
||||
#endif
|
||||
return (offset);
|
||||
}
|
||||
|
||||
static int ax25_get_num_info (packet_t this_p)
|
||||
static inline int ax25_get_num_info (packet_t this_p)
|
||||
{
|
||||
int len;
|
||||
|
||||
|
@ -237,11 +283,11 @@ static int ax25_get_num_info (packet_t this_p)
|
|||
#endif
|
||||
|
||||
|
||||
typedef enum ax25_modulo_e { modulo_8 = 8, modulo_128 = 128 } ax25_modulo_t;
|
||||
typedef enum ax25_modulo_e { modulo_unknown = 0, modulo_8 = 8, modulo_128 = 128 } ax25_modulo_t;
|
||||
|
||||
typedef enum ax25_frame_type_e {
|
||||
|
||||
frame_type_I, // Information
|
||||
frame_type_I = 0, // Information
|
||||
|
||||
frame_type_S_RR, // Receive Ready - System Ready To Receive
|
||||
frame_type_S_RNR, // Receive Not Ready - TNC Buffer Full
|
||||
|
@ -260,6 +306,8 @@ typedef enum ax25_frame_type_e {
|
|||
frame_type_U, // other Unnumbered, not used by AX.25.
|
||||
|
||||
frame_not_AX25 // Could not get control byte from frame.
|
||||
// This must be last because value plus 1 is
|
||||
// for the size of an array.
|
||||
|
||||
} ax25_frame_type_t;
|
||||
|
||||
|
@ -346,7 +394,10 @@ extern int ax25_get_heard(packet_t this_p);
|
|||
|
||||
extern int ax25_get_first_not_repeated(packet_t pp);
|
||||
|
||||
extern int ax25_get_rr (packet_t this_p, int n);
|
||||
|
||||
extern int ax25_get_info (packet_t pp, unsigned char **paddr);
|
||||
extern int ax25_cut_at_crlf (packet_t this_p);
|
||||
|
||||
extern void ax25_set_nextp (packet_t this_p, packet_t next_p);
|
||||
|
||||
|
@ -357,15 +408,19 @@ extern packet_t ax25_get_nextp (packet_t this_p);
|
|||
extern void ax25_set_release_time (packet_t this_p, double release_time);
|
||||
extern double ax25_get_release_time (packet_t this_p);
|
||||
|
||||
extern void ax25_set_modulo (packet_t this_p, int modulo);
|
||||
|
||||
extern void ax25_format_addrs (packet_t pp, char *);
|
||||
extern void ax25_format_via_path (packet_t this_p, char *result, size_t result_size);
|
||||
|
||||
extern int ax25_pack (packet_t pp, unsigned char result[AX25_MAX_PACKET_LEN]);
|
||||
|
||||
extern ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *desc, int *pf, int *nr, int *ns);
|
||||
extern ax25_frame_type_t ax25_frame_type (packet_t this_p, cmdres_t *cr, char *desc, int *pf, int *nr, int *ns);
|
||||
|
||||
extern void ax25_hex_dump (packet_t this_p);
|
||||
|
||||
extern int ax25_is_aprs (packet_t pp);
|
||||
extern int ax25_is_null_frame (packet_t this_p);
|
||||
|
||||
extern int ax25_get_control (packet_t this_p);
|
||||
extern int ax25_get_c2 (packet_t this_p);
|
||||
|
|
|
@ -0,0 +1,889 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2016 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/>.
|
||||
//
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: ax25_pad2.c
|
||||
*
|
||||
* Purpose: Packet assembler and disasembler, part 2.
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* The original ax25_pad.c was written with APRS in mind.
|
||||
* It handles UI frames and transparency for a KISS TNC.
|
||||
* Here we add new functions that can handle the
|
||||
* more general cases of AX.25 frames.
|
||||
*
|
||||
*
|
||||
* * Destination Address (note: opposite order in printed format)
|
||||
*
|
||||
* * Source Address
|
||||
*
|
||||
* * 0-8 Digipeater Addresses
|
||||
* (The AX.25 v2.2 spec reduced this number to
|
||||
* a maximum of 2 but I allow the original 8.)
|
||||
*
|
||||
* Each address is composed of:
|
||||
*
|
||||
* * 6 upper case letters or digits, blank padded.
|
||||
* These are shifted left one bit, leaving the LSB always 0.
|
||||
*
|
||||
* * a 7th octet containing the SSID and flags.
|
||||
* The LSB is always 0 except for the last octet of the address field.
|
||||
*
|
||||
* The final octet of the Destination has the form:
|
||||
*
|
||||
* C R R SSID 0, where,
|
||||
*
|
||||
* C = command/response. Set to 1 for command.
|
||||
* R R = Reserved = 1 1 (See RR note, below)
|
||||
* SSID = substation ID
|
||||
* 0 = zero
|
||||
*
|
||||
* The final octet of the Source has the form:
|
||||
*
|
||||
* C R R SSID 0, where,
|
||||
*
|
||||
* C = command/response. Must be inverse of destination C bit.
|
||||
* R R = Reserved = 1 1 (See RR note, below)
|
||||
* SSID = substation ID
|
||||
* 0 = zero (or 1 if no repeaters)
|
||||
*
|
||||
* The final octet of each repeater has the form:
|
||||
*
|
||||
* H R R SSID 0, where,
|
||||
*
|
||||
* H = has-been-repeated = 0 initially.
|
||||
* Set to 1 after this address has been used.
|
||||
* R R = Reserved = 1 1
|
||||
* SSID = substation ID
|
||||
* 0 = zero (or 1 if last repeater in list)
|
||||
*
|
||||
* A digipeater would repeat this frame if it finds its address
|
||||
* with the "H" bit set to 0 and all earlier repeater addresses
|
||||
* have the "H" bit set to 1.
|
||||
* The "H" bit would be set to 1 in the repeated frame.
|
||||
*
|
||||
* In standard monitoring format, an asterisk is displayed after the last
|
||||
* digipeater with the "H" bit set. That indicates who you are hearing
|
||||
* over the radio.
|
||||
*
|
||||
*
|
||||
* Next we have:
|
||||
*
|
||||
* * One or two byte Control Field - A U frame always has one control byte.
|
||||
* When using modulo 128 sequence numbers, the
|
||||
* I and S frames can have a second byte allowing
|
||||
* 7 bit fields instead of 3 bit fields.
|
||||
* Unfortunately, we can't tell which we have by looking
|
||||
* at a frame out of context. :-(
|
||||
* If we are one end of the link, we would know this
|
||||
* from SABM/SABME and possible later negotiation
|
||||
* with XID. But if we start monitoring two other
|
||||
* stations that are already conversing, we don't know.
|
||||
*
|
||||
* RR note: It seems that some implementations put a hint
|
||||
* in the "RR" reserved bits.
|
||||
* http://www.tapr.org/pipermail/ax25-layer2/2005-October/000297.html
|
||||
* The RR bits can also be used for "DAMA" which is
|
||||
* some sort of channel access coordination scheme.
|
||||
* http://internet.freepage.de/cgi-bin/feets/freepage_ext/41030x030A/rewrite/hennig/afu/afudoc/afudama.html
|
||||
* Neither is part of the official protocol spec.
|
||||
*
|
||||
* * One byte Protocol ID - Only for I and UI frames.
|
||||
* Normally we would use 0xf0 for no layer 3.
|
||||
*
|
||||
* Finally the Information Field. The initial max size is 256 but it
|
||||
* can be negotiated higher if both ends agree.
|
||||
*
|
||||
* Only these types of frames can have an information part:
|
||||
* - I
|
||||
* - UI
|
||||
* - XID
|
||||
* - TEST
|
||||
* - FRMR
|
||||
*
|
||||
* The 2 byte CRC is not stored here.
|
||||
*
|
||||
*
|
||||
* Constructors:
|
||||
* ax25_u_frame - Construct a U frame.
|
||||
* ax25_s_frame - Construct a S frame.
|
||||
* ax25_i_frame - Construct a I frame.
|
||||
*
|
||||
* Get methods: .... ???
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
#define AX25_PAD_C /* this will affect behavior of ax25_pad.h */
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
#include "textcolor.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "ax25_pad2.h"
|
||||
|
||||
|
||||
|
||||
extern int ax25memdebug;
|
||||
|
||||
static int set_addrs (packet_t pp, char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr);
|
||||
|
||||
//#if AX25MEMDEBUG
|
||||
//#undef AX25MEMDEBUG
|
||||
//#endif
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: ax25_u_frame
|
||||
*
|
||||
* Purpose: Construct a U frame.
|
||||
*
|
||||
* Input: addrs - Array of addresses.
|
||||
*
|
||||
* num_addr - Number of addresses, range 2 .. 10.
|
||||
*
|
||||
* cr - cr_cmd command frame, cr_res for a response frame.
|
||||
*
|
||||
* ftype - One of:
|
||||
* frame_type_U_SABME // Set Async Balanced Mode, Extended
|
||||
* frame_type_U_SABM // Set Async Balanced Mode
|
||||
* frame_type_U_DISC // Disconnect
|
||||
* frame_type_U_DM // Disconnect Mode
|
||||
* frame_type_U_UA // Unnumbered Acknowledge
|
||||
* frame_type_U_FRMR // Frame Reject
|
||||
* frame_type_U_UI // Unnumbered Information
|
||||
* frame_type_U_XID // Exchange Identification
|
||||
* frame_type_U_TEST // Test
|
||||
*
|
||||
* pf - Poll/Final flag.
|
||||
*
|
||||
* pid - Protocol ID. >>> Used ONLY for the UI type. <<<
|
||||
* Normally 0xf0 meaning no level 3.
|
||||
* Could be other values for NET/ROM, etc.
|
||||
*
|
||||
* pinfo - Pointer to data for Info field. Allowed only for UI, XID, TEST, FRMR.
|
||||
*
|
||||
* info_len - Length for Info field.
|
||||
*
|
||||
*
|
||||
* Returns: Pointer to new packet object.
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
#if AX25MEMDEBUG
|
||||
packet_t ax25_u_frame_debug (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int pf, int pid, unsigned char *pinfo, int info_len, char *src_file, int src_line)
|
||||
#else
|
||||
packet_t ax25_u_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int pf, int pid, unsigned char *pinfo, int info_len)
|
||||
#endif
|
||||
{
|
||||
packet_t this_p;
|
||||
unsigned char *p;
|
||||
int ctrl = 0;
|
||||
unsigned int t = 999; // 1 = must be cmd, 0 = must be response, 2 = can be either.
|
||||
int i = 0; // Is Info part allowed?
|
||||
|
||||
this_p = ax25_new ();
|
||||
|
||||
#if AX25MEMDEBUG
|
||||
if (ax25memdebug) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("ax25_u_frame, seq=%d, called from %s %d\n", this_p->seq, src_file, src_line);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (this_p == NULL) return (NULL);
|
||||
|
||||
this_p->modulo = 0;
|
||||
|
||||
if ( ! set_addrs (this_p, addrs, num_addr, cr)) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: Could not set addresses for U frame.\n", __func__);
|
||||
ax25_delete (this_p);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
switch (ftype) {
|
||||
// 1 = cmd only, 0 = res only, 2 = either
|
||||
case frame_type_U_SABME: ctrl = 0x6f; t = 1; break;
|
||||
case frame_type_U_SABM: ctrl = 0x2f; t = 1; break;
|
||||
case frame_type_U_DISC: ctrl = 0x43; t = 1; break;
|
||||
case frame_type_U_DM: ctrl = 0x0f; t = 0; break;
|
||||
case frame_type_U_UA: ctrl = 0x63; t = 0; break;
|
||||
case frame_type_U_FRMR: ctrl = 0x87; t = 0; i = 1; break;
|
||||
case frame_type_U_UI: ctrl = 0x03; t = 2; i = 1; break;
|
||||
case frame_type_U_XID: ctrl = 0xaf; t = 2; i = 1; break;
|
||||
case frame_type_U_TEST: ctrl = 0xe3; t = 2; i = 1; break;
|
||||
|
||||
default:
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: Invalid ftype %d for U frame.\n", __func__, ftype);
|
||||
ax25_delete (this_p);
|
||||
return (NULL);
|
||||
break;
|
||||
}
|
||||
if (pf) ctrl |= 0x10;
|
||||
|
||||
if (t != 2) {
|
||||
if (cr != t) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: U frame, cr is %d but must be %d. ftype=%d\n", __func__, cr, t, ftype);
|
||||
}
|
||||
}
|
||||
|
||||
p = this_p->frame_data + this_p->frame_len;
|
||||
*p++ = ctrl;
|
||||
this_p->frame_len++;
|
||||
|
||||
if (ftype == frame_type_U_UI) {
|
||||
|
||||
// Definitely don't want pid value of 0 (not in valid list)
|
||||
// or 0xff (which means more bytes follow).
|
||||
|
||||
if (pid < 0 || pid == 0 || pid == 0xff) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: U frame, Invalid pid value 0x%02x.\n", __func__, pid);
|
||||
pid = AX25_PID_NO_LAYER_3;
|
||||
}
|
||||
*p++ = pid;
|
||||
this_p->frame_len++;
|
||||
}
|
||||
|
||||
if (i) {
|
||||
if (pinfo != NULL && info_len > 0) {
|
||||
if (info_len > AX25_MAX_INFO_LEN) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: U frame, Invalid information field length %d.\n", __func__, info_len);
|
||||
info_len = AX25_MAX_INFO_LEN;
|
||||
}
|
||||
memcpy (p, pinfo, info_len);
|
||||
p += info_len;
|
||||
this_p->frame_len += info_len;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (pinfo != NULL && info_len > 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: Info part not allowed for U frame type.\n", __func__);
|
||||
}
|
||||
}
|
||||
*p = '\0';
|
||||
|
||||
assert (p == this_p->frame_data + this_p->frame_len);
|
||||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
#if PAD2TEST
|
||||
ax25_frame_type_t check_ftype;
|
||||
cmdres_t check_cr;
|
||||
char check_desc[80];
|
||||
int check_pf;
|
||||
int check_nr;
|
||||
int check_ns;
|
||||
|
||||
check_ftype = ax25_frame_type (this_p, &check_cr, check_desc, &check_pf, &check_nr, &check_ns);
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("check: ftype=%d, desc=\"%s\", pf=%d\n", check_ftype, check_desc, check_pf);
|
||||
|
||||
assert (check_cr == cr);
|
||||
assert (check_ftype == ftype);
|
||||
assert (check_pf == pf);
|
||||
assert (check_nr == -1);
|
||||
assert (check_ns == -1);
|
||||
|
||||
#endif
|
||||
|
||||
return (this_p);
|
||||
|
||||
} /* end ax25_u_frame */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: ax25_s_frame
|
||||
*
|
||||
* Purpose: Construct an S frame.
|
||||
*
|
||||
* Input: addrs - Array of addresses.
|
||||
*
|
||||
* num_addr - Number of addresses, range 2 .. 10.
|
||||
*
|
||||
* cr - cr_cmd command frame, cr_res for a response frame.
|
||||
*
|
||||
* ftype - One of:
|
||||
* frame_type_S_RR, // Receive Ready - System Ready To Receive
|
||||
* frame_type_S_RNR, // Receive Not Ready - TNC Buffer Full
|
||||
* frame_type_S_REJ, // Reject Frame - Out of Sequence or Duplicate
|
||||
* frame_type_S_SREJ, // Selective Reject - Request single frame repeat
|
||||
*
|
||||
* modulo - 8 or 128. Determines if we have 1 or 2 control bytes.
|
||||
*
|
||||
* nr - N(R) field --- describe.
|
||||
*
|
||||
* pf - Poll/Final flag.
|
||||
*
|
||||
*
|
||||
* Returns: Pointer to new packet object.
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
#if AX25MEMDEBUG
|
||||
packet_t ax25_s_frame_debug (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int modulo, int nr, int pf, char *src_file, int src_line)
|
||||
#else
|
||||
packet_t ax25_s_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int modulo, int nr, int pf)
|
||||
#endif
|
||||
{
|
||||
packet_t this_p;
|
||||
unsigned char *p;
|
||||
int ctrl = 0;
|
||||
|
||||
this_p = ax25_new ();
|
||||
|
||||
#if AX25MEMDEBUG
|
||||
if (ax25memdebug) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("ax25_s_frame, seq=%d, called from %s %d\n", this_p->seq, src_file, src_line);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (this_p == NULL) return (NULL);
|
||||
|
||||
if ( ! set_addrs (this_p, addrs, num_addr, cr)) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: Could not set addresses for U frame.\n", __func__);
|
||||
ax25_delete (this_p);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (modulo != 8 && modulo != 128) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: Invalid modulo %d for S frame.\n", __func__, modulo);
|
||||
modulo = 8;
|
||||
}
|
||||
this_p->modulo = modulo;
|
||||
|
||||
if (nr < 0 || nr >= modulo) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: Invalid N(R) %d for S frame.\n", __func__, nr);
|
||||
nr &= (modulo - 1);
|
||||
}
|
||||
|
||||
switch (ftype) {
|
||||
|
||||
case frame_type_S_RR: ctrl = 0x01; break;
|
||||
case frame_type_S_RNR: ctrl = 0x05; break;
|
||||
case frame_type_S_REJ: ctrl = 0x09; break;
|
||||
case frame_type_S_SREJ: ctrl = 0x0d; break;
|
||||
|
||||
default:
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: Invalid ftype %d for S frame.\n", __func__, ftype);
|
||||
ax25_delete (this_p);
|
||||
return (NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
p = this_p->frame_data + this_p->frame_len;
|
||||
|
||||
if (modulo == 8) {
|
||||
if (pf) ctrl |= 0x10;
|
||||
ctrl |= nr << 5;
|
||||
*p++ = ctrl;
|
||||
this_p->frame_len++;
|
||||
}
|
||||
else {
|
||||
*p++ = ctrl;
|
||||
this_p->frame_len++;
|
||||
|
||||
ctrl = pf & 1;
|
||||
ctrl |= nr << 1;
|
||||
*p++ = ctrl;
|
||||
this_p->frame_len++;
|
||||
}
|
||||
|
||||
*p = '\0';
|
||||
|
||||
assert (p == this_p->frame_data + this_p->frame_len);
|
||||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
#if PAD2TEST
|
||||
|
||||
ax25_frame_type_t check_ftype;
|
||||
cmdres_t check_cr;
|
||||
char check_desc[80];
|
||||
int check_pf;
|
||||
int check_nr;
|
||||
int check_ns;
|
||||
|
||||
// todo modulo must be input.
|
||||
check_ftype = ax25_frame_type (this_p, &check_cr, check_desc, &check_pf, &check_nr, &check_ns);
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("check: ftype=%d, desc=\"%s\", pf=%d, nr=%d\n", check_ftype, check_desc, check_pf, check_nr);
|
||||
|
||||
assert (check_cr == cr);
|
||||
assert (check_ftype == ftype);
|
||||
assert (check_pf == pf);
|
||||
assert (check_nr == nr);
|
||||
assert (check_ns == -1);
|
||||
|
||||
#endif
|
||||
return (this_p);
|
||||
|
||||
} /* end ax25_s_frame */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: ax25_i_frame
|
||||
*
|
||||
* Purpose: Construct an I frame.
|
||||
*
|
||||
* Input: addrs - Array of addresses.
|
||||
*
|
||||
* num_addr - Number of addresses, range 2 .. 10.
|
||||
*
|
||||
* cr - cr_cmd command frame, cr_res for a response frame.
|
||||
*
|
||||
* modulo - 8 or 128.
|
||||
*
|
||||
* nr - N(R) field --- describe.
|
||||
*
|
||||
* ns - N(S) field --- describe.
|
||||
*
|
||||
* pf - Poll/Final flag.
|
||||
*
|
||||
* pid - Protocol ID.
|
||||
* Normally 0xf0 meaning no level 3.
|
||||
* Could be other values for NET/ROM, etc.
|
||||
*
|
||||
* pinfo - Pointer to data for Info field.
|
||||
*
|
||||
* info_len - Length for Info field.
|
||||
*
|
||||
*
|
||||
* Returns: Pointer to new packet object.
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
#if AX25MEMDEBUG
|
||||
packet_t ax25_i_frame_debug (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, int modulo, int nr, int ns, int pf, int pid, unsigned char *pinfo, int info_len, char *src_file, int src_line)
|
||||
#else
|
||||
packet_t ax25_i_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, int modulo, int nr, int ns, int pf, int pid, unsigned char *pinfo, int info_len)
|
||||
#endif
|
||||
{
|
||||
packet_t this_p;
|
||||
unsigned char *p;
|
||||
int ctrl = 0;
|
||||
|
||||
this_p = ax25_new ();
|
||||
|
||||
#if AX25MEMDEBUG
|
||||
if (ax25memdebug) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("ax25_i_frame, seq=%d, called from %s %d\n", this_p->seq, src_file, src_line);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (this_p == NULL) return (NULL);
|
||||
|
||||
if ( ! set_addrs (this_p, addrs, num_addr, cr)) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: Could not set addresses for I frame.\n", __func__);
|
||||
ax25_delete (this_p);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (modulo != 8 && modulo != 128) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: Invalid modulo %d for I frame.\n", __func__, modulo);
|
||||
modulo = 8;
|
||||
}
|
||||
this_p->modulo = modulo;
|
||||
|
||||
if (nr < 0 || nr >= modulo) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: Invalid N(R) %d for I frame.\n", __func__, nr);
|
||||
nr &= (modulo - 1);
|
||||
}
|
||||
|
||||
if (ns < 0 || ns >= modulo) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: Invalid N(S) %d for I frame.\n", __func__, ns);
|
||||
ns &= (modulo - 1);
|
||||
}
|
||||
|
||||
p = this_p->frame_data + this_p->frame_len;
|
||||
|
||||
if (modulo == 8) {
|
||||
ctrl = (nr << 5) | (ns << 1);
|
||||
if (pf) ctrl |= 0x10;
|
||||
*p++ = ctrl;
|
||||
this_p->frame_len++;
|
||||
}
|
||||
else {
|
||||
ctrl = ns << 1;
|
||||
*p++ = ctrl;
|
||||
this_p->frame_len++;
|
||||
|
||||
ctrl = nr << 1;
|
||||
if (pf) ctrl |= 0x01;
|
||||
*p++ = ctrl;
|
||||
this_p->frame_len++;
|
||||
}
|
||||
|
||||
// Definitely don't want pid value of 0 (not in valid list)
|
||||
// or 0xff (which means more bytes follow).
|
||||
|
||||
if (pid < 0 || pid == 0 || pid == 0xff) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Warning: Client application provided invalid PID value, 0x%02x, for I frame.\n", pid);
|
||||
pid = AX25_PID_NO_LAYER_3;
|
||||
}
|
||||
*p++ = pid;
|
||||
this_p->frame_len++;
|
||||
|
||||
if (pinfo != NULL && info_len > 0) {
|
||||
if (info_len > AX25_MAX_INFO_LEN) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: I frame, Invalid information field length %d.\n", __func__, info_len);
|
||||
info_len = AX25_MAX_INFO_LEN;
|
||||
}
|
||||
memcpy (p, pinfo, info_len);
|
||||
p += info_len;
|
||||
this_p->frame_len += info_len;
|
||||
}
|
||||
|
||||
*p = '\0';
|
||||
|
||||
assert (p == this_p->frame_data + this_p->frame_len);
|
||||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
#if PAD2TEST
|
||||
|
||||
ax25_frame_type_t check_ftype;
|
||||
cmdres_t check_cr;
|
||||
char check_desc[80];
|
||||
int check_pf;
|
||||
int check_nr;
|
||||
int check_ns;
|
||||
unsigned char *check_pinfo;
|
||||
int check_info_len;
|
||||
|
||||
check_ftype = ax25_frame_type (this_p, &check_cr, check_desc, &check_pf, &check_nr, &check_ns);
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("check: ftype=%d, desc=\"%s\", pf=%d, nr=%d, ns=%d\n", check_ftype, check_desc, check_pf, check_nr, check_ns);
|
||||
|
||||
check_info_len = ax25_get_info (this_p, &check_pinfo);
|
||||
|
||||
assert (check_cr == cr);
|
||||
assert (check_ftype == frame_type_I);
|
||||
assert (check_pf == pf);
|
||||
assert (check_nr == nr);
|
||||
assert (check_ns == ns);
|
||||
|
||||
assert (check_info_len == info_len);
|
||||
assert (strcmp((char*)check_pinfo,(char*)pinfo) == 0);
|
||||
#endif
|
||||
|
||||
return (this_p);
|
||||
|
||||
} /* end ax25_i_frame */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: set_addrs
|
||||
*
|
||||
* Purpose: Set address fields
|
||||
*
|
||||
* Input: pp - Packet object.
|
||||
*
|
||||
* addrs - Array of addresses. Same order as in frame.
|
||||
*
|
||||
* num_addr - Number of addresses, range 2 .. 10.
|
||||
*
|
||||
* cr - cr_cmd command frame, cr_res for a response frame.
|
||||
*
|
||||
* Output: pp->frame_data - 7 bytes for each address.
|
||||
*
|
||||
* pp->frame_len - num_addr * 7
|
||||
*
|
||||
* p->num_addr - num_addr
|
||||
*
|
||||
* Returns: 1 for success. 0 for failure.
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static int set_addrs (packet_t pp, char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr)
|
||||
{
|
||||
int n;
|
||||
|
||||
assert (pp->frame_len == 0);
|
||||
assert (cr == cr_cmd || cr == cr_res);
|
||||
|
||||
if (num_addr < AX25_MIN_ADDRS || num_addr > AX25_MAX_ADDRS) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("INTERNAL ERROR: %s %s %d, num_addr = %d\n", __FILE__, __func__, __LINE__, num_addr);
|
||||
return (0);
|
||||
}
|
||||
|
||||
for (n = 0; n < num_addr; n++) {
|
||||
|
||||
unsigned char *pa = pp->frame_data + n * 7;
|
||||
int ok;
|
||||
int strict = 1;
|
||||
char oaddr[AX25_MAX_ADDR_LEN];
|
||||
int ssid;
|
||||
int heard;
|
||||
int j;
|
||||
|
||||
ok = ax25_parse_addr (n, addrs[n], strict, oaddr, &ssid, &heard);
|
||||
|
||||
if (! ok) return (0);
|
||||
|
||||
// Fill in address.
|
||||
|
||||
memset (pa, ' ' << 1, 6);
|
||||
for (j = 0; oaddr[j]; j++) {
|
||||
pa[j] = oaddr[j] << 1;
|
||||
}
|
||||
pa += 6;
|
||||
|
||||
// Fill in SSID.
|
||||
|
||||
*pa = 0x60 | ((ssid & 0xf) << 1);
|
||||
|
||||
// Command / response flag.
|
||||
|
||||
switch (n) {
|
||||
case AX25_DESTINATION:
|
||||
if (cr == cr_cmd) *pa |= 0x80;
|
||||
break;
|
||||
case AX25_SOURCE:
|
||||
if (cr == cr_res) *pa |= 0x80;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Is this the end of address field?
|
||||
|
||||
if (n == num_addr - 1) {
|
||||
*pa |= 1;
|
||||
}
|
||||
|
||||
pp->frame_len += 7;
|
||||
}
|
||||
|
||||
pp->num_addr = num_addr;
|
||||
return (1);
|
||||
|
||||
} /* end set_addrs */
|
||||
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: main
|
||||
*
|
||||
* Purpose: Quick unit test for this file.
|
||||
*
|
||||
* Description: Generate a variety of frames.
|
||||
* Each function calls ax25_frame_type to verify results.
|
||||
*
|
||||
* $ gcc -DPAD2TEST -DUSE_REGEX_STATIC -Iregex ax25_pad.c ax25_pad2.c fcs_calc.o textcolor.o regex.a misc.a
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
#if PAD2TEST
|
||||
|
||||
int main ()
|
||||
{
|
||||
char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN];
|
||||
int num_addr = 2;
|
||||
cmdres_t cr;
|
||||
ax25_frame_type_t ftype;
|
||||
int pf = 0;
|
||||
int pid = 0xf0;
|
||||
int modulo;
|
||||
int nr, ns;
|
||||
unsigned char *pinfo = NULL;
|
||||
int info_len = 0;
|
||||
packet_t pp;
|
||||
|
||||
strcpy (addrs[0], "W2UB");
|
||||
strcpy (addrs[1], "WB2OSZ-15");
|
||||
num_addr = 2;
|
||||
|
||||
/* U frame */
|
||||
|
||||
for (ftype = frame_type_U_SABME; ftype <= frame_type_U_TEST; ftype++) {
|
||||
|
||||
for (pf = 0; pf <= 1; pf++) {
|
||||
|
||||
int cmin, cmax;
|
||||
|
||||
switch (ftype) {
|
||||
// 0 = response, 1 = command
|
||||
case frame_type_U_SABME: cmin = 1; cmax = 1; break;
|
||||
case frame_type_U_SABM: cmin = 1; cmax = 1; break;
|
||||
case frame_type_U_DISC: cmin = 1; cmax = 1; break;
|
||||
case frame_type_U_DM: cmin = 0; cmax = 0; break;
|
||||
case frame_type_U_UA: cmin = 0; cmax = 0; break;
|
||||
case frame_type_U_FRMR: cmin = 0; cmax = 0; break;
|
||||
case frame_type_U_UI: cmin = 0; cmax = 1; break;
|
||||
case frame_type_U_XID: cmin = 0; cmax = 1; break;
|
||||
case frame_type_U_TEST: cmin = 0; cmax = 1; break;
|
||||
default: break; // avoid compiler warning.
|
||||
}
|
||||
|
||||
for (cr = cmin; cr <= cmax; cr++) {
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("\nConstruct U frame, cr=%d, ftype=%d, pid=0x%02x\n", cr, ftype, pid);
|
||||
|
||||
pp = ax25_u_frame (addrs, num_addr, cr, ftype, pf, pid, pinfo, info_len);
|
||||
ax25_hex_dump (pp);
|
||||
ax25_delete (pp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dw_printf ("\n----------\n\n");
|
||||
|
||||
/* S frame */
|
||||
|
||||
strcpy (addrs[2], "DIGI1-1");
|
||||
num_addr = 3;
|
||||
|
||||
for (ftype = frame_type_S_RR; ftype <= frame_type_S_SREJ; ftype++) {
|
||||
|
||||
for (pf = 0; pf <= 1; pf++) {
|
||||
|
||||
modulo = 8;
|
||||
nr = modulo / 2 + 1;
|
||||
|
||||
for (cr = 0; cr <= 1; cr++) {
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("\nConstruct S frame, cmd=%d, ftype=%d, pid=0x%02x\n", cr, ftype, pid);
|
||||
|
||||
pp = ax25_s_frame (addrs, num_addr, cr, ftype, modulo, nr, pf);
|
||||
|
||||
ax25_hex_dump (pp);
|
||||
ax25_delete (pp);
|
||||
}
|
||||
|
||||
modulo = 128;
|
||||
nr = modulo / 2 + 1;
|
||||
|
||||
for (cr = 0; cr <= 1; cr++) {
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("\nConstruct S frame, cmd=%d, ftype=%d, pid=0x%02x\n", cr, ftype, pid);
|
||||
|
||||
pp = ax25_s_frame (addrs, num_addr, cr, ftype, modulo, nr, pf);
|
||||
|
||||
ax25_hex_dump (pp);
|
||||
ax25_delete (pp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dw_printf ("\n----------\n\n");
|
||||
|
||||
/* I frame */
|
||||
|
||||
pinfo = (unsigned char*)"The rain in Spain stays mainly on the plain.";
|
||||
info_len = strlen((char*)pinfo);
|
||||
|
||||
for (pf = 0; pf <= 1; pf++) {
|
||||
|
||||
modulo = 8;
|
||||
nr = 0x55 & (modulo - 1);
|
||||
ns = 0xaa & (modulo - 1);
|
||||
|
||||
for (cr = 0; cr <= 1; cr++) {
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("\nConstruct I frame, cmd=%d, ftype=%d, pid=0x%02x\n", cr, ftype, pid);
|
||||
|
||||
pp = ax25_i_frame (addrs, num_addr, cr, modulo, nr, ns, pf, pid, pinfo, info_len);
|
||||
|
||||
ax25_hex_dump (pp);
|
||||
ax25_delete (pp);
|
||||
}
|
||||
|
||||
modulo = 128;
|
||||
nr = 0x55 & (modulo - 1);
|
||||
ns = 0xaa & (modulo - 1);
|
||||
|
||||
for (cr = 0; cr <= 1; cr++) {
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("\nConstruct I frame, cmd=%d, ftype=%d, pid=0x%02x\n", cr, ftype, pid);
|
||||
|
||||
pp = ax25_i_frame (addrs, num_addr, cr, modulo, nr, ns, pf, pid, pinfo, info_len);
|
||||
|
||||
ax25_hex_dump (pp);
|
||||
ax25_delete (pp);
|
||||
}
|
||||
}
|
||||
|
||||
text_color_set(DW_COLOR_REC);
|
||||
dw_printf ("\n----------\n\n");
|
||||
dw_printf ("\nSUCCESS!\n");
|
||||
|
||||
exit (EXIT_SUCCESS);
|
||||
|
||||
} /* end main */
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* end ax25_pad2.c */
|
|
@ -0,0 +1,55 @@
|
|||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: ax25_pad2.h
|
||||
*
|
||||
* Purpose: Header file for using ax25_pad2.c
|
||||
* ax25_pad dealt only with UI frames.
|
||||
* This adds a facility for the other types: U, s, I.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
#ifndef AX25_PAD2_H
|
||||
#define AX25_PAD2_H 1
|
||||
|
||||
#include "ax25_pad.h"
|
||||
|
||||
|
||||
|
||||
|
||||
#if AX25MEMDEBUG // to investigate a memory leak problem
|
||||
|
||||
|
||||
|
||||
packet_t ax25_u_frame_debug (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int pf, int pid, unsigned char *pinfo, int info_len, char *src_file, int src_line);
|
||||
|
||||
packet_t ax25_s_frame_debug (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int modulo, int nr, int pf, char *src_file, int src_line);
|
||||
|
||||
packet_t ax25_i_frame_debug (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, int modulo, int nr, int ns, int pf, int pid, unsigned char *pinfo, int info_len, char *src_file, int src_line);
|
||||
|
||||
|
||||
#define ax25_u_frame(a,n,c,f,p,q,i,l) ax25_u_frame_debug(a,n,c,f,p,q,i,l,__FILE__,__LINE__)
|
||||
|
||||
#define ax25_s_frame(a,n,c,f,m,r,p) ax25_s_frame_debug(a,n,c,f,m,r,p,__FILE__,__LINE__)
|
||||
|
||||
#define ax25_i_frame(a,n,c,m,r,s,p,q,i,l) ax25_i_frame_debug(a,n,c,m,r,s,p,q,i,l,__FILE__,__LINE__)
|
||||
|
||||
|
||||
#else
|
||||
|
||||
packet_t ax25_u_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int pf, int pid, unsigned char *pinfo, int info_len);
|
||||
|
||||
packet_t ax25_s_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int modulo, int nr, int pf);
|
||||
|
||||
packet_t ax25_i_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, int modulo, int nr, int ns, int pf, int pid, unsigned char *pinfo, int info_len);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* AX25_PAD2_H */
|
||||
|
||||
/* end ax25_pad2.h */
|
||||
|
||||
|
53
beacon.c
53
beacon.c
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2011, 2013, 2014, 2015 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2013, 2014, 2015, 2016 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
|
||||
|
@ -30,6 +30,8 @@
|
|||
|
||||
//#define DEBUG 1
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
@ -40,7 +42,6 @@
|
|||
#include <time.h>
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
#include "audio.h"
|
||||
|
@ -55,6 +56,7 @@
|
|||
#include "log.h"
|
||||
#include "dlq.h"
|
||||
#include "aprs_tt.h" // for dw_run_cmd - should relocate someday.
|
||||
#include "mheard.h"
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
|
@ -83,6 +85,7 @@ struct tm *localtime_r(time_t *clock, struct tm *res)
|
|||
|
||||
static struct audio_s *g_modem_config_p;
|
||||
static struct misc_config_s *g_misc_config_p;
|
||||
static struct igate_config_s *g_igate_config_p;
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
|
@ -118,6 +121,10 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo);
|
|||
* Used only to find valid channels.
|
||||
*
|
||||
* pconfig - misc. configuration from config file.
|
||||
* Beacon stuff ended up here.
|
||||
*
|
||||
* pigate - IGate configuration.
|
||||
* Need this for calculating IGate statistics.
|
||||
*
|
||||
*
|
||||
* Outputs: Remember required information for future use.
|
||||
|
@ -131,7 +138,7 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo);
|
|||
|
||||
|
||||
|
||||
void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig)
|
||||
void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct igate_config_s *pigate)
|
||||
{
|
||||
time_t now;
|
||||
int j;
|
||||
|
@ -156,6 +163,7 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig)
|
|||
*/
|
||||
g_modem_config_p = pmodem;
|
||||
g_misc_config_p = pconfig;
|
||||
g_igate_config_p = pigate;
|
||||
|
||||
/*
|
||||
* Precompute the packet contents so any errors are
|
||||
|
@ -228,6 +236,22 @@ void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig)
|
|||
}
|
||||
break;
|
||||
|
||||
case BEACON_IGATE:
|
||||
|
||||
/* Doesn't make sense if IGate is not configured. */
|
||||
|
||||
if (strlen(g_igate_config_p->t2_server_name) == 0 ||
|
||||
strlen(g_igate_config_p->t2_login) == 0 ||
|
||||
strlen(g_igate_config_p->t2_passcode) == 0) {
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file, line %d: Doesn't make sense to use IBEACON without IGate Configured.\n", g_misc_config_p->beacon[j].lineno);
|
||||
dw_printf ("IBEACON has been disabled.\n");
|
||||
g_misc_config_p->beacon[j].btype = BEACON_IGNORE;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case BEACON_IGNORE:
|
||||
break;
|
||||
}
|
||||
|
@ -870,6 +894,25 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
|
|||
}
|
||||
break;
|
||||
|
||||
case BEACON_IGATE:
|
||||
|
||||
{
|
||||
int last_minutes = 30;
|
||||
char stuff[256];
|
||||
|
||||
snprintf (stuff, sizeof(stuff), "<IGATE,MSG_CNT=%d,PKT_CNT=%d,DIR_CNT=%d,LOC_CNT=%d,RF_CNT=%d,UPL_CNT=%d,DNL_CNT=%d",
|
||||
igate_get_msg_cnt(),
|
||||
igate_get_pkt_cnt(),
|
||||
mheard_count(0,last_minutes),
|
||||
mheard_count(g_igate_config_p->max_digi_hops,last_minutes),
|
||||
mheard_count(8,last_minutes),
|
||||
igate_get_upl_cnt(),
|
||||
igate_get_dnl_cnt());
|
||||
|
||||
strlcat (beacon_text, stuff, sizeof(beacon_text));
|
||||
}
|
||||
break;
|
||||
|
||||
case BEACON_IGNORE:
|
||||
default:
|
||||
break;
|
||||
|
@ -911,10 +954,10 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
|
|||
|
||||
case SENDTO_RECV:
|
||||
|
||||
/* Simulated reception. */
|
||||
/* Simulated reception from radio. */
|
||||
|
||||
memset (&alevel, 0xff, sizeof(alevel));
|
||||
dlq_append (DLQ_REC_FRAME, g_misc_config_p->beacon[j].sendto_chan, 0, 0, pp, alevel, 0, "");
|
||||
dlq_rec_frame (g_misc_config_p->beacon[j].sendto_chan, 0, 0, pp, alevel, 0, "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
2
beacon.h
2
beacon.h
|
@ -1,6 +1,6 @@
|
|||
|
||||
/* beacon.h */
|
||||
|
||||
void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig);
|
||||
void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct igate_config_s *pigate);
|
||||
|
||||
void beacon_tracker_set_debug (int level);
|
||||
|
|
|
@ -0,0 +1,317 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2016 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/>.
|
||||
//
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: cdigipeater.c
|
||||
*
|
||||
* Purpose: Act as an digital repeater for connected AX.25 mode.
|
||||
* Similar digipeater.c is for APRS.
|
||||
*
|
||||
*
|
||||
* Description: Decide whether the specified packet should
|
||||
* be digipeated. Put my callsign in the digipeater field used.
|
||||
*
|
||||
* APRS and connected mode were two split into two
|
||||
* separate files. Yes, there is duplicate code but they
|
||||
* are significantly different and I thought it would be
|
||||
* too confusing to munge them together.
|
||||
*
|
||||
* References: The Ax.25 protcol barely mentions digipeaters and
|
||||
* and doesn't describe how they should work.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
#define CDIGIPEATER_C
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h> /* for isdigit, isupper */
|
||||
#include "regex.h"
|
||||
#include <sys/unistd.h>
|
||||
|
||||
#include "ax25_pad.h"
|
||||
#include "cdigipeater.h"
|
||||
#include "textcolor.h"
|
||||
#include "tq.h"
|
||||
#include "pfilter.h"
|
||||
|
||||
|
||||
static packet_t cdigipeat_match (int from_chan, packet_t pp, char *mycall_rec, char *mycall_xmit,
|
||||
regex_t *alias, int to_chan, char *filter_str);
|
||||
|
||||
|
||||
/*
|
||||
* Keep pointer to configuration options.
|
||||
* Set by cdigipeater_init and used later.
|
||||
*/
|
||||
|
||||
|
||||
static struct audio_s *save_audio_config_p;
|
||||
static struct cdigi_config_s *save_cdigi_config_p;
|
||||
|
||||
|
||||
/*
|
||||
* Maintain count of packets digipeated for each combination of from/to channel.
|
||||
*/
|
||||
|
||||
static int cdigi_count[MAX_CHANS][MAX_CHANS];
|
||||
|
||||
int cdigipeater_get_count (int from_chan, int to_chan) {
|
||||
return (cdigi_count[from_chan][to_chan]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: cdigipeater_init
|
||||
*
|
||||
* Purpose: Initialize with stuff from configuration file.
|
||||
*
|
||||
* Inputs: p_audio_config - Configuration for audio channels.
|
||||
*
|
||||
* p_cdigi_config - Connected Digipeater configuration details.
|
||||
*
|
||||
* Outputs: Save pointers to configuration for later use.
|
||||
*
|
||||
* Description: Called once at application startup time.
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
void cdigipeater_init (struct audio_s *p_audio_config, struct cdigi_config_s *p_cdigi_config)
|
||||
{
|
||||
save_audio_config_p = p_audio_config;
|
||||
save_cdigi_config_p = p_cdigi_config;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: cdigipeater
|
||||
*
|
||||
* Purpose: Re-transmit packet if it matches the rules.
|
||||
*
|
||||
* Inputs: chan - Radio channel where it was received.
|
||||
*
|
||||
* pp - Packet object.
|
||||
*
|
||||
* Returns: None.
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
||||
void cdigipeater (int from_chan, packet_t pp)
|
||||
{
|
||||
int to_chan;
|
||||
|
||||
|
||||
if ( from_chan < 0 || from_chan >= MAX_CHANS || ( ! save_audio_config_p->achan[from_chan].valid) ) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("cdigipeater: Did not expect to receive on invalid channel %d.\n", from_chan);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* First pass: Look at packets being digipeated to same channel.
|
||||
*
|
||||
* There was a reason for two passes for APRS.
|
||||
* Might not have a benefit here.
|
||||
*/
|
||||
|
||||
for (to_chan=0; to_chan<MAX_CHANS; to_chan++) {
|
||||
if (save_cdigi_config_p->enabled[from_chan][to_chan]) {
|
||||
if (to_chan == from_chan) {
|
||||
packet_t result;
|
||||
|
||||
result = cdigipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall,
|
||||
save_audio_config_p->achan[to_chan].mycall,
|
||||
&save_cdigi_config_p->alias[from_chan][to_chan], to_chan,
|
||||
save_cdigi_config_p->filter_str[from_chan][to_chan]);
|
||||
if (result != NULL) {
|
||||
tq_append (to_chan, TQ_PRIO_0_HI, result);
|
||||
cdigi_count[from_chan][to_chan]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Second pass: Look at packets being digipeated to different channel.
|
||||
*/
|
||||
|
||||
for (to_chan=0; to_chan<MAX_CHANS; to_chan++) {
|
||||
if (save_cdigi_config_p->enabled[from_chan][to_chan]) {
|
||||
if (to_chan != from_chan) {
|
||||
packet_t result;
|
||||
|
||||
result = cdigipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall,
|
||||
save_audio_config_p->achan[to_chan].mycall,
|
||||
&save_cdigi_config_p->alias[from_chan][to_chan], to_chan,
|
||||
save_cdigi_config_p->filter_str[from_chan][to_chan]);
|
||||
if (result != NULL) {
|
||||
tq_append (to_chan, TQ_PRIO_0_HI, result);
|
||||
cdigi_count[from_chan][to_chan]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* end cdigipeater */
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: cdigipeat_match
|
||||
*
|
||||
* Purpose: A simple digipeater for connected mode AX.25.
|
||||
*
|
||||
* Input: pp - Pointer to a packet object.
|
||||
*
|
||||
* mycall_rec - Call of my station, with optional SSID,
|
||||
* associated with the radio channel where the
|
||||
* packet was received.
|
||||
*
|
||||
* mycall_xmit - Call of my station, with optional SSID,
|
||||
* associated with the radio channel where the
|
||||
* packet is to be transmitted. Could be the same as
|
||||
* mycall_rec or different.
|
||||
*
|
||||
* alias - Compiled pattern for my station aliases.
|
||||
* Could be NULL if no aliases.
|
||||
*
|
||||
* to_chan - Channel number that we are transmitting to.
|
||||
*
|
||||
* filter_str - Filter expression string or NULL.
|
||||
*
|
||||
* Returns: Packet object for transmission or NULL.
|
||||
* The original packet is not modified. The caller is responsible for freeing it.
|
||||
* 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:
|
||||
*
|
||||
* - mycall_rec
|
||||
* - alias list
|
||||
*
|
||||
* APRS digipeating drops duplicates within 30 seconds but we don't do that here.
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static packet_t cdigipeat_match (int from_chan, packet_t pp, char *mycall_rec, char *mycall_xmit,
|
||||
regex_t *alias, int to_chan, char *filter_str)
|
||||
{
|
||||
int r;
|
||||
char repeater[AX25_MAX_ADDR_LEN];
|
||||
int err;
|
||||
char err_msg[100];
|
||||
|
||||
/*
|
||||
* First check if filtering has been configured.
|
||||
*/
|
||||
|
||||
if (filter_str != NULL) {
|
||||
|
||||
if (pfilter(from_chan, to_chan, filter_str, pp, 0) != 1) {
|
||||
return(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Find the first repeater station which doesn't have "has been repeated" set.
|
||||
*
|
||||
* r = index of the address position in the frame.
|
||||
*/
|
||||
r = ax25_get_first_not_repeated(pp);
|
||||
|
||||
if (r < AX25_REPEATER_1) {
|
||||
return (NULL); // Nothing to do.
|
||||
}
|
||||
|
||||
ax25_get_addr_with_ssid(pp, r, repeater);
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("First unused digipeater is %s\n", repeater);
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* First check for explicit use of my call.
|
||||
* Note that receive and transmit channels could have different callsigns.
|
||||
*/
|
||||
|
||||
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);
|
||||
ax25_set_h (result, r);
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have an alias match, substitute MYCALL.
|
||||
*/
|
||||
if (alias != NULL) {
|
||||
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);
|
||||
}
|
||||
else if (err != REG_NOMATCH) {
|
||||
regerror(err, alias, err_msg, sizeof(err_msg));
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("%s\n", err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't repeat it if we get here.
|
||||
*/
|
||||
return (NULL);
|
||||
|
||||
} /* end cdigipeat_match */
|
||||
|
||||
|
||||
|
||||
/* end cdigipeater.c */
|
|
@ -0,0 +1,59 @@
|
|||
|
||||
|
||||
#ifndef CDIGIPEATER_H
|
||||
#define CDIGIPEATER_H 1
|
||||
|
||||
#include "regex.h"
|
||||
|
||||
#include "direwolf.h" /* for MAX_CHANS */
|
||||
#include "ax25_pad.h" /* for packet_t */
|
||||
#include "audio.h" /* for radio channel properties */
|
||||
|
||||
|
||||
/*
|
||||
* Information required for Connected mode digipeating.
|
||||
*
|
||||
* The configuration file reader fills in this information
|
||||
* and it is passed to cdigipeater_init at application start up time.
|
||||
*/
|
||||
|
||||
|
||||
struct cdigi_config_s {
|
||||
|
||||
/*
|
||||
* Rules for each of the [from_chan][to_chan] combinations.
|
||||
*/
|
||||
|
||||
regex_t alias[MAX_CHANS][MAX_CHANS];
|
||||
|
||||
int enabled[MAX_CHANS][MAX_CHANS];
|
||||
|
||||
char *filter_str[MAX_CHANS+1][MAX_CHANS+1];
|
||||
// NULL or optional Packet Filter strings such as "t/m".
|
||||
// Notice the size of arrays is one larger than normal.
|
||||
// That extra position is for the IGate.
|
||||
};
|
||||
|
||||
/*
|
||||
* Call once at application start up time.
|
||||
*/
|
||||
|
||||
extern void cdigipeater_init (struct audio_s *p_audio_config, struct cdigi_config_s *p_cdigi_config);
|
||||
|
||||
/*
|
||||
* Call this for each packet received.
|
||||
* Suitable packets will be queued for transmission.
|
||||
*/
|
||||
|
||||
extern void cdigipeater (int from_chan, packet_t pp);
|
||||
|
||||
|
||||
/* Make statistics available. */
|
||||
|
||||
int cdigipeater_get_count (int from_chan, int to_chan);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/* end cdigipeater.h */
|
||||
|
38
config.h
38
config.h
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "audio.h" /* for struct audio_s */
|
||||
#include "digipeater.h" /* for struct digi_config_s */
|
||||
#include "cdigipeater.h" /* for struct cdigi_config_s */
|
||||
#include "aprs_tt.h" /* for struct tt_config_s */
|
||||
#include "igate.h" /* for struct igate_config_s */
|
||||
|
||||
|
@ -23,7 +24,7 @@
|
|||
* This wasn't thought out. It just happened.
|
||||
*/
|
||||
|
||||
enum beacon_type_e { BEACON_IGNORE, BEACON_POSITION, BEACON_OBJECT, BEACON_TRACKER, BEACON_CUSTOM };
|
||||
enum beacon_type_e { BEACON_IGNORE, BEACON_POSITION, BEACON_OBJECT, BEACON_TRACKER, BEACON_CUSTOM, BEACON_IGATE };
|
||||
|
||||
enum sendto_type_e { SENDTO_XMIT, SENDTO_IGATE, SENDTO_RECV };
|
||||
|
||||
|
@ -43,6 +44,7 @@ struct misc_config_s {
|
|||
|
||||
char gpsnmea_port[20]; /* Serial port name for reading NMEA sentences from GPS. */
|
||||
/* e.g. COM22, /dev/ttyACM0 */
|
||||
/* Currently no option for setting non-standard speed. */
|
||||
|
||||
char gpsd_host[20]; /* Host for gpsd server. */
|
||||
/* e.g. localhost, 192.168.1.2 */
|
||||
|
@ -50,9 +52,19 @@ struct misc_config_s {
|
|||
int gpsd_port; /* Port number for gpsd server. */
|
||||
/* Default is 2947. */
|
||||
|
||||
|
||||
char waypoint_port[20]; /* Serial port name for sending NMEA waypoint sentences */
|
||||
/* to a GPS map display or other mapping application. */
|
||||
/* e.g. COM22, /dev/ttyACM0 */
|
||||
char nmea_port[20]; /* Serial port name for NMEA communication with GPS */
|
||||
/* receiver and/or mapping application. Change this. */
|
||||
/* Currently no option for setting non-standard speed. */
|
||||
|
||||
int waypoint_formats; /* Which sentence formats should be generated? */
|
||||
|
||||
#define WPT_FORMAT_NMEA_GENERIC 0x01 /* N $GPWPT */
|
||||
#define WPT_FORMAT_GARMIN 0x02 /* G $PGRMW */
|
||||
#define WPT_FORMAT_MAGELLAN 0x04 /* M $PMGNWPL */
|
||||
#define WPT_FORMAT_KENWOOD 0x08 /* K $PKWDWPL */
|
||||
|
||||
|
||||
char logdir[80]; /* Directory for saving activity logs. */
|
||||
|
||||
|
@ -65,6 +77,25 @@ struct misc_config_s {
|
|||
int sb_turn_angle; /* degrees */
|
||||
int sb_turn_slope; /* degrees * MPH */
|
||||
|
||||
// AX.25 connected mode.
|
||||
|
||||
int frack; /* Number of seconds to wait for ack to transmission. */
|
||||
|
||||
int retry; /* Number of times to retry before giving up. */
|
||||
|
||||
int paclen; /* Max number of bytes in information part of frame. */
|
||||
|
||||
int maxframe_basic; /* Max frames to send before ACK. mod 8 "Window" size. */
|
||||
|
||||
int maxframe_extended; /* Max frames to send before ACK. mod 128 "Window" size. */
|
||||
|
||||
int maxv22; /* Maximum number of unanswered SABME frames sent before */
|
||||
/* switching to SABM. This is to handle the case of an old */
|
||||
/* TNC which simply ignores SABME rather than replying with FRMR. */
|
||||
|
||||
|
||||
|
||||
// Beacons.
|
||||
|
||||
int num_beacons; /* Number of beacons defined. */
|
||||
|
||||
|
@ -155,6 +186,7 @@ struct misc_config_s {
|
|||
|
||||
extern void config_init (char *fname, struct audio_s *p_modem,
|
||||
struct digi_config_s *digi_config,
|
||||
struct cdigi_config_s *cdigi_config,
|
||||
struct tt_config_s *p_tt_config,
|
||||
struct igate_config_s *p_igate_config,
|
||||
struct misc_config_s *misc_config);
|
||||
|
|
234
decode_aprs.c
234
decode_aprs.c
|
@ -33,6 +33,8 @@
|
|||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
@ -43,12 +45,8 @@
|
|||
#include <ctype.h> /* for isdigit */
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifndef _POSIX_C_SOURCE
|
||||
#define _POSIX_C_SOURCE 1
|
||||
#endif
|
||||
#include "regex.h"
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
#include "symbols.h"
|
||||
|
@ -110,7 +108,7 @@ static void aprs_station_capabilities (decode_aprs_t *A, char *, int);
|
|||
static void aprs_status_report (decode_aprs_t *A, char *, int);
|
||||
static void aprs_general_query (decode_aprs_t *A, char *, int, int quiet);
|
||||
static void aprs_directed_station_query (decode_aprs_t *A, char *addressee, char *query, int quiet);
|
||||
static void aprs_telemetry (decode_aprs_t *A, char *, int, int quiet);
|
||||
static void aprs_telemetry (decode_aprs_t *A, char *info, int info_len, int quiet);
|
||||
static void aprs_raw_touch_tone (decode_aprs_t *A, char *, int);
|
||||
static void aprs_morse_code (decode_aprs_t *A, char *, int);
|
||||
static void aprs_positionless_weather_report (decode_aprs_t *A, unsigned char *, int);
|
||||
|
@ -166,7 +164,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet)
|
|||
|
||||
A->g_quiet = quiet;
|
||||
|
||||
snprintf (A->g_msg_type, sizeof(A->g_msg_type), "Unknown message type %c", *pinfo);
|
||||
snprintf (A->g_msg_type, sizeof(A->g_msg_type), "Unknown APRS Data Type Indicator \"%c\"", *pinfo);
|
||||
|
||||
A->g_symbol_table = '/'; /* Default to primary table. */
|
||||
A->g_symbol_code = ' '; /* What should we have for default symbol? */
|
||||
|
@ -201,6 +199,33 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet)
|
|||
ax25_get_addr_with_ssid (pp, AX25_SOURCE, A->g_src);
|
||||
ax25_get_addr_with_ssid (pp, AX25_DESTINATION, dest);
|
||||
|
||||
/*
|
||||
* Report error if the information part contains a nul character.
|
||||
* There are two known cases where this can happen.
|
||||
*
|
||||
* - The Kenwood TM-D710A sometimes sends packets like this:
|
||||
*
|
||||
* VA3AJ-9>T2QU6X,VE3WRC,WIDE1,K8UNS,WIDE2*:4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00>`nW<0x1f>oS8>/]"6M}driving fast=
|
||||
* K4JH-9>S5UQ6X,WR4AGC-3*,WIDE1*:4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00>`jP}l"&>/]"47}QRV from the EV =
|
||||
*
|
||||
* Notice that the data type indicator of "4" is not valid. If we remove
|
||||
* 4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00> we are left with a good MIC-E format.
|
||||
* This same thing has been observed from others and is intermittent.
|
||||
*
|
||||
* - AGW Tracker can send UTF-16 if an option is selected. This can introduce nul bytes.
|
||||
* This is wrong, it should be using UTF-8.
|
||||
*/
|
||||
|
||||
if ( ( ! A->g_quiet ) && ( (int)strlen((char*)pinfo) != info_len) ) {
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("'nul' character found in Information part. This should never happen.\n");
|
||||
dw_printf("It seems that %s is transmitting with defective software.\n", A->g_src);
|
||||
|
||||
if (strcmp((char*)pinfo, "4P") == 0) {
|
||||
dw_printf("The TM-D710 will do this intermittently. A firmware upgrade is needed to fix it.\n");
|
||||
}
|
||||
}
|
||||
|
||||
switch (*pinfo) { /* "DTI" data type identifier. */
|
||||
|
||||
|
@ -254,8 +279,9 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet)
|
|||
break;
|
||||
|
||||
|
||||
case ':': /* Message */
|
||||
case ':': /* Message: for one person, a group, or a bulletin. */
|
||||
/* Directed Station Query */
|
||||
/* Telemetry metadata. */
|
||||
|
||||
aprs_message (A, pinfo, info_len, quiet);
|
||||
break;
|
||||
|
@ -638,14 +664,14 @@ void decode_aprs_print (decode_aprs_t *A) {
|
|||
if ( ! A->g_quiet) {
|
||||
|
||||
for (j=0; j<n; j++) {
|
||||
if ((unsigned)A->g_comment[j] == (char)0xb0 && (j == 0 || ! (A->g_comment[j-1] & 0x80))) {
|
||||
if ((unsigned char)(A->g_comment[j]) == 0xb0 && (j == 0 || ! (A->g_comment[j-1] & 0x80))) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Character code 0xb0 is probably an attempt at a degree symbol.\n");
|
||||
dw_printf("The correct encoding is 0xc2 0xb0 in UTF-8.\n");
|
||||
}
|
||||
}
|
||||
for (j=0; j<n; j++) {
|
||||
if ((unsigned)A->g_comment[j] == (char)0xf8 && (j == n-1 || (A->g_comment[j+1] & 0xc0) != 0xc0)) {
|
||||
if ((unsigned char)(A->g_comment[j]) == 0xf8 && (j == n-1 || (A->g_comment[j+1] & 0xc0) != 0xc0)) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Character code 0xf8 is probably an attempt at a degree symbol.\n");
|
||||
dw_printf("The correct encoding is 0xc2 0xb0 in UTF-8.\n");
|
||||
|
@ -974,6 +1000,7 @@ N1ZZN-9>T2SP0W:`c_Vm6hk/ "49}Originl Mic-E (leading space)
|
|||
|
||||
N1ZZN-9>T2SP0W:`c_Vm6hk/>"49}TH-D7A walkie Talkie
|
||||
N1ZZN-9>T2SP0W:`c_Vm6hk/>"49}TH-D72 walkie Talkie=
|
||||
W6GPS>S4PT3R:`p(1oR0K\>TH-D74A^
|
||||
N1ZZN-9>T2SP0W:`c_Vm6hk/]"49}TM-D700 MObile Radio
|
||||
N1ZZN-9>T2SP0W:`c_Vm6hk/]"49}TM-D710 Mobile Radio=
|
||||
|
||||
|
@ -1328,16 +1355,18 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
|
|||
|
||||
#define isT(c) ((c) == ' ' || (c) == '>' || (c) == ']' || (c) == '`' || (c) == '\'')
|
||||
|
||||
// Last updated Sept. 2016 for TH-D74A
|
||||
|
||||
if (isT(*pfirst)) {
|
||||
|
||||
if (*pfirst == ' ') { strlcpy (A->g_mfr, "Original MIC-E", sizeof(A->g_mfr)); pfirst++; }
|
||||
if (*pfirst == ' ' ) { strlcpy (A->g_mfr, "Original MIC-E", sizeof(A->g_mfr)); pfirst++; }
|
||||
|
||||
else if (*pfirst == '>' && *plast == '=') { strlcpy (A->g_mfr, "Kenwood TH-D72", sizeof(A->g_mfr)); pfirst++; plast--; }
|
||||
else if (*pfirst == '>') { strlcpy (A->g_mfr, "Kenwood TH-D7A", sizeof(A->g_mfr)); pfirst++; }
|
||||
else if (*pfirst == '>' && *plast == '^') { strlcpy (A->g_mfr, "Kenwood TH-D74", sizeof(A->g_mfr)); pfirst++; plast--; }
|
||||
else if (*pfirst == '>' ) { strlcpy (A->g_mfr, "Kenwood TH-D7A", sizeof(A->g_mfr)); pfirst++; }
|
||||
|
||||
else if (*pfirst == ']' && *plast == '=') { strlcpy (A->g_mfr, "Kenwood TM-D710", sizeof(A->g_mfr)); pfirst++; plast--; }
|
||||
else if (*pfirst == ']') { strlcpy (A->g_mfr, "Kenwood TM-D700", sizeof(A->g_mfr)); pfirst++; }
|
||||
else if (*pfirst == ']' ) { strlcpy (A->g_mfr, "Kenwood TM-D700", sizeof(A->g_mfr)); pfirst++; }
|
||||
|
||||
else if (*pfirst == '`' && *(plast-1) == '_' && *plast == ' ') { strlcpy (A->g_mfr, "Yaesu VX-8", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '"') { strlcpy (A->g_mfr, "Yaesu FTM-350", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
|
@ -1347,19 +1376,23 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
|
|||
else if (*pfirst == '`' && *(plast-1) == '_' && *plast == ')') { strlcpy (A->g_mfr, "Yaesu FTM-100D", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
else if (*pfirst == '`' && *(plast-1) == '_' && *plast == '(') { strlcpy (A->g_mfr, "Yaesu FT2D", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
|
||||
else if (*pfirst == '`' && *(plast-1) == ' ' && *plast == 'X') { strlcpy (A->g_mfr, "AP510", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
|
||||
else if (*pfirst == '`' ) { strlcpy (A->g_mfr, "Mic-Emsg", sizeof(A->g_mfr)); pfirst++; }
|
||||
|
||||
else if (*pfirst == '\'' && *(plast-1) == '|' && *plast == '3') { strlcpy (A->g_mfr, "Byonics TinyTrack3", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
else if (*pfirst == '\'' && *(plast-1) == '|' && *plast == '4') { strlcpy (A->g_mfr, "Byonics TinyTrack4", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
|
||||
else if (*(plast-1) == '\\') { strlcpy (A->g_mfr, "Hamhud ?", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
else if (*(plast-1) == '/') { strlcpy (A->g_mfr, "Argent ?", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
else if (*(plast-1) == '^') { strlcpy (A->g_mfr, "HinzTec anyfrog", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
else if (*(plast-1) == '*') { strlcpy (A->g_mfr, "APOZxx www.KissOZ.dk Tracker. OZ1EKD and OZ7HVO", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
else if (*(plast-1) == '~') { strlcpy (A->g_mfr, "OTHER", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
else if (*pfirst == '\'' && *(plast-1) == ':' && *plast == '4') { strlcpy (A->g_mfr, "SCS GmbH & Co. P4dragon DR-7400 modems", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
else if (*pfirst == '\'' && *(plast-1) == ':' && *plast == '8') { strlcpy (A->g_mfr, "SCS GmbH & Co. P4dragon DR-7800 modems", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
|
||||
// Should Original Mic-E and Kenwood be moved down to here?
|
||||
else if (*pfirst == '\'' ) { strlcpy (A->g_mfr, "McTrackr", sizeof(A->g_mfr)); pfirst++; }
|
||||
|
||||
else if (*pfirst == '`') { strlcpy (A->g_mfr, "Mic-Emsg", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
else if (*pfirst == '\'') { strlcpy (A->g_mfr, "McTrackr", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
else if ( *(plast-1) == '\\' ) { strlcpy (A->g_mfr, "Hamhud ?", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
else if ( *(plast-1) == '/' ) { strlcpy (A->g_mfr, "Argent ?", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
else if ( *(plast-1) == '^' ) { strlcpy (A->g_mfr, "HinzTec anyfrog", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
else if ( *(plast-1) == '*' ) { strlcpy (A->g_mfr, "APOZxx www.KissOZ.dk Tracker. OZ1EKD and OZ7HVO", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
else if ( *(plast-1) == '~' ) { strlcpy (A->g_mfr, "OTHER", sizeof(A->g_mfr)); pfirst++; plast-=2; }
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1409,22 +1442,45 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
|
|||
*
|
||||
* Function: aprs_message
|
||||
*
|
||||
* Purpose: Decode "Message Format"
|
||||
* Purpose: Decode "Message Format."
|
||||
* The word message is used loosely all over the place, but it has a very specific meaning here.
|
||||
*
|
||||
* Inputs: info - Pointer to Information field.
|
||||
* ilen - Information field length.
|
||||
* quiet - supress error messages.
|
||||
*
|
||||
* Outputs: ??? TBD
|
||||
* Outputs: A->g_msg_type Text description for screen display.
|
||||
*
|
||||
* A->g_addressee To whom is it addressed.
|
||||
* Could be a specific station, alias, bulletin, etc.
|
||||
* For telemetry metadata is is about this station,
|
||||
* not being sent to it.
|
||||
*
|
||||
* A->g_message_subtype Subtype so caller might avoid replicating
|
||||
* all the code to distinguish them.
|
||||
*
|
||||
* A->g_message_number Message number if any. Required for ack/rej.
|
||||
*
|
||||
* Description: An APRS message is a text string with a specifed addressee.
|
||||
*
|
||||
* It's a lot more complicated with different types of addressees
|
||||
* and replies with acknowledgement or rejection.
|
||||
*
|
||||
* There is even a special case for telemetry metadata.
|
||||
*
|
||||
* Examples: ...
|
||||
*
|
||||
* Cases: :xxxxxxxxx:PARM. Telemetry metadata, parameter name
|
||||
* :xxxxxxxxx:UNIT. Telemetry metadata, unit/label
|
||||
* :xxxxxxxxx:EQNS. Telemetry metadata, Equation Coefficents
|
||||
* :xxxxxxxxx:BITS. Telemetry metadata, Bit Sense/Project Name
|
||||
* :xxxxxxxxx:? Directed Station Query
|
||||
* :xxxxxxxxx:ack Message acknowledged (received)
|
||||
* :xxxxxxxxx:rej Message rejected (unable to accept)
|
||||
*
|
||||
* :xxxxxxxxx: ... Message with no message number.
|
||||
* (Text may not contain the { character because
|
||||
* it indicates beginning of optional message number.)
|
||||
* :xxxxxxxxx: ... {num Message with message number.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
|
@ -1436,7 +1492,7 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q
|
|||
char addressee[9];
|
||||
char colon; /* : */
|
||||
char message[73]; /* 0-67 characters for message */
|
||||
/* { followed by 1-5 characters for message number */
|
||||
/* Optional { followed by 1-5 characters for message number */
|
||||
|
||||
/* If the first chracter is '?' it is a Directed Station Query. */
|
||||
} *p;
|
||||
|
@ -1447,20 +1503,23 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q
|
|||
p = (struct aprs_message_s *)info;
|
||||
|
||||
strlcpy (A->g_msg_type, "APRS Message", sizeof(A->g_msg_type));
|
||||
A->g_message_subtype = message_subtype_message; /* until found otherwise */
|
||||
|
||||
if (ilen < 11) {
|
||||
if (! quiet) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Message must have a minimum of 11 characters for : addressee :\n");
|
||||
dw_printf("APRS Message must have a minimum of 11 characters for : 9 character addressee :\n");
|
||||
}
|
||||
A->g_message_subtype = message_subtype_invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->colon != ':') {
|
||||
if (! quiet) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Message must begin with : addressee :\n");
|
||||
dw_printf("APRS Message must begin with : 9 character addressee :\n");
|
||||
}
|
||||
A->g_message_subtype = message_subtype_invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1475,6 +1534,7 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q
|
|||
|
||||
strlcpy (A->g_addressee, addressee, sizeof(A->g_addressee));
|
||||
|
||||
|
||||
/*
|
||||
* Special message formats contain telemetry metadata.
|
||||
* It applies to the addressee, not the sender.
|
||||
|
@ -1488,18 +1548,22 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q
|
|||
|
||||
if (strncmp(p->message,"PARM.",5) == 0) {
|
||||
snprintf (A->g_msg_type, sizeof(A->g_msg_type), "Telemetry Parameter Name Message for \"%s\"", addressee);
|
||||
A->g_message_subtype = message_subtype_telem_parm;
|
||||
telemetry_name_message (addressee, p->message+5);
|
||||
}
|
||||
else if (strncmp(p->message,"UNIT.",5) == 0) {
|
||||
snprintf (A->g_msg_type, sizeof(A->g_msg_type), "Telemetry Unit/Label Message for \"%s\"", addressee);
|
||||
A->g_message_subtype = message_subtype_telem_unit;
|
||||
telemetry_unit_label_message (addressee, p->message+5);
|
||||
}
|
||||
else if (strncmp(p->message,"EQNS.",5) == 0) {
|
||||
snprintf (A->g_msg_type, sizeof(A->g_msg_type), "Telemetry Equation Coefficents Message for \"%s\"", addressee);
|
||||
A->g_message_subtype = message_subtype_telem_eqns;
|
||||
telemetry_coefficents_message (addressee, p->message+5, quiet);
|
||||
}
|
||||
else if (strncmp(p->message,"BITS.",5) == 0) {
|
||||
snprintf (A->g_msg_type, sizeof(A->g_msg_type), "Telemetry Bit Sense/Project Name Message for \"%s\"", addressee);
|
||||
A->g_message_subtype = message_subtype_telem_bits;
|
||||
telemetry_bit_sense_message (addressee, p->message+5, quiet);
|
||||
}
|
||||
|
||||
|
@ -1510,11 +1574,33 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q
|
|||
else if (p->message[0] == '?') {
|
||||
|
||||
strlcpy (A->g_msg_type, "Directed Station Query", sizeof(A->g_msg_type));
|
||||
A->g_message_subtype = message_subtype_directed_query;
|
||||
|
||||
aprs_directed_station_query (A, addressee, p->message+1, quiet);
|
||||
}
|
||||
|
||||
/* ack or rej? Message number is required for these. */
|
||||
|
||||
else if (strncmp(p->message,"ack",3) == 0) {
|
||||
strlcpy (A->g_message_number, p->message + 3, sizeof(A->g_message_number));
|
||||
snprintf (A->g_msg_type, sizeof(A->g_msg_type), "ACK message %s for \"%s\"", A->g_message_number, addressee);
|
||||
A->g_message_subtype = message_subtype_ack;
|
||||
}
|
||||
else if (strncmp(p->message,"rej",3) == 0) {
|
||||
strlcpy (A->g_message_number, p->message + 3, sizeof(A->g_message_number));
|
||||
snprintf (A->g_msg_type, sizeof(A->g_msg_type), "REJ message %s for \"%s\"", A->g_message_number, addressee);
|
||||
A->g_message_subtype = message_subtype_ack;
|
||||
}
|
||||
|
||||
/* message number is optional here. */
|
||||
|
||||
else {
|
||||
snprintf (A->g_msg_type, sizeof(A->g_msg_type), "APRS Message for \"%s\"", addressee);
|
||||
char *pno = strchr(p->message, '{');
|
||||
if (pno != NULL) {
|
||||
strlcpy (A->g_message_number, pno+1, sizeof(A->g_message_number));
|
||||
}
|
||||
snprintf (A->g_msg_type, sizeof(A->g_msg_type), "APRS Message %s for \"%s\"", A->g_message_number, addressee);
|
||||
A->g_message_subtype = message_subtype_message;
|
||||
|
||||
/* No location so don't use process_comment () */
|
||||
|
||||
|
@ -1666,25 +1752,27 @@ static void aprs_item (decode_aprs_t *A, unsigned char *info, int ilen)
|
|||
{
|
||||
|
||||
struct aprs_item_s {
|
||||
char dti; /* ) */
|
||||
char name[9]; /* Actually variable length 3 - 9 bytes. */
|
||||
char dti; /* ')' */
|
||||
char name[10]; /* Actually variable length 3 - 9 bytes. */
|
||||
/* DON'T refer to the rest of this structure; */
|
||||
/* the offsets will be wrong! */
|
||||
/* We make it 10 here so we don't get subscript out of bounds */
|
||||
/* warning when looking for following '!' or '_' character. */
|
||||
|
||||
char live_killed; /* ! for live or _ for killed */
|
||||
position_t pos;
|
||||
char comment[43]; /* First 7 bytes could be data extension. */
|
||||
char live_killed__; /* ! for live or _ for killed */
|
||||
position_t pos__;
|
||||
char comment__[43]; /* First 7 bytes could be data extension. */
|
||||
} *p;
|
||||
|
||||
struct aprs_compressed_item_s {
|
||||
char dti; /* ) */
|
||||
char name[9]; /* Actually variable length 3 - 9 bytes. */
|
||||
char dti; /* ')' */
|
||||
char name[10]; /* Actually variable length 3 - 9 bytes. */
|
||||
/* DON'T refer to the rest of this structure; */
|
||||
/* the offsets will be wrong! */
|
||||
|
||||
char live_killed; /* ! for live or _ for killed */
|
||||
compressed_position_t cpos;
|
||||
char comment[40]; /* No data extension in this case. */
|
||||
char live_killed__; /* ! for live or _ for killed */
|
||||
compressed_position_t cpos__;
|
||||
char comment__[40]; /* No data extension in this case. */
|
||||
} *q;
|
||||
|
||||
|
||||
|
@ -3014,7 +3102,7 @@ double get_latitude_8 (char *p, int quiet)
|
|||
return (G_UNKNOWN);
|
||||
}
|
||||
|
||||
if (plat->minn[0] >= '0' || plat->minn[0] <= '5')
|
||||
if (plat->minn[0] >= '0' && plat->minn[0] <= '5')
|
||||
result += ((plat->minn[0]) - '0') * (10. / 60.);
|
||||
else if (plat->minn[0] == ' ')
|
||||
;
|
||||
|
@ -3177,7 +3265,7 @@ double get_longitude_9 (char *p, int quiet)
|
|||
return (G_UNKNOWN);
|
||||
}
|
||||
|
||||
if (plon->minn[0] >= '0' || plon->minn[0] <= '5')
|
||||
if (plon->minn[0] >= '0' && plon->minn[0] <= '5')
|
||||
result += ((plon->minn[0]) - '0') * (10. / 60.);
|
||||
else if (plon->minn[0] == ' ')
|
||||
;
|
||||
|
@ -3621,7 +3709,15 @@ static int data_extension_comment (decode_aprs_t *A, char *pdext)
|
|||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
#define MAX_TOCALLS 150
|
||||
// If I was more ambitious, this would dynamically allocate enough
|
||||
// storage based on the file contents. Just stick in a constant for
|
||||
// now. This takes an insignificant amount of space and
|
||||
// I don't anticipate tocalls.txt growing that quickly.
|
||||
// Version 1.4 - add message if too small instead of silently ignoring the rest.
|
||||
|
||||
// Dec. 2016 tocalls.txt has 153 destination addresses.
|
||||
|
||||
#define MAX_TOCALLS 200
|
||||
|
||||
static struct tocalls_s {
|
||||
unsigned char len;
|
||||
|
@ -3718,7 +3814,7 @@ static void decode_tocall (decode_aprs_t *A, char *dest)
|
|||
if (strlen(tocalls[num_tocalls].prefix) > 2) {
|
||||
tocalls[num_tocalls].description = strdup(stuff+14);
|
||||
tocalls[num_tocalls].len = strlen(tocalls[num_tocalls].prefix);
|
||||
// dw_printf("debug: %d '%s' -> '%s'\n", tocalls[num_tocalls].len, tocalls[num_tocalls].prefix, tocalls[num_tocalls].description);
|
||||
// dw_printf("debug %d: %d '%s' -> '%s'\n", num_tocalls, tocalls[num_tocalls].len, tocalls[num_tocalls].prefix, tocalls[num_tocalls].description);
|
||||
|
||||
num_tocalls++;
|
||||
}
|
||||
|
@ -3742,11 +3838,15 @@ static void decode_tocall (decode_aprs_t *A, char *dest)
|
|||
if (strlen(tocalls[num_tocalls].prefix) > 2) {
|
||||
tocalls[num_tocalls].description = strdup(stuff+14);
|
||||
tocalls[num_tocalls].len = strlen(tocalls[num_tocalls].prefix);
|
||||
// dw_printf("debug: %d '%s' -> '%s'\n", tocalls[num_tocalls].len, tocalls[num_tocalls].prefix, tocalls[num_tocalls].description);
|
||||
// dw_printf("debug %d: %d '%s' -> '%s'\n", num_tocalls, tocalls[num_tocalls].len, tocalls[num_tocalls].prefix, tocalls[num_tocalls].description);
|
||||
|
||||
num_tocalls++;
|
||||
}
|
||||
}
|
||||
if (num_tocalls == MAX_TOCALLS) { // oops. might have discarded some.
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("MAX_TOCALLS needs to be larger than %d to handle contents of 'tocalls.txt'.\n", MAX_TOCALLS);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
|
@ -3771,8 +3871,11 @@ static void decode_tocall (decode_aprs_t *A, char *dest)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
first_time = 0;
|
||||
|
||||
//for (n=0; n<num_tocalls; n++) {
|
||||
// dw_printf("sorted %d: %d '%s' -> '%s'\n", n, tocalls[n].len, tocalls[n].prefix, tocalls[n].description);
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
|
@ -3862,6 +3965,49 @@ static void substr_se (char *dest, const char *src, int start, int endp1)
|
|||
*
|
||||
* /A=123456 Altitude
|
||||
*
|
||||
* What can appear in a comment?
|
||||
*
|
||||
* Chapter 5 of the APRS spec ( http://www.aprs.org/doc/APRS101.PDF ) says:
|
||||
*
|
||||
* "The comment may contain any printable ASCII characters (except | and ~,
|
||||
* which are reserved for TNC channel switching)."
|
||||
*
|
||||
* "Printable" would exclude character values less than space (00100000), e.g.
|
||||
* tab, carriage return, line feed, nul. Sometimes we see carriage return
|
||||
* (00001010) at the end of APRS packets. This would be in violation of the
|
||||
* specification.
|
||||
*
|
||||
* The base 91 telemetry format (http://he.fi/doc/aprs-base91-comment-telemetry.txt ),
|
||||
* which is not part of the APRS spec, uses the | character in the comment to delimit encoded
|
||||
* telemetry data. This would be in violation of the original spec.
|
||||
*
|
||||
* The APRS Spec Addendum 1.2 Proposals ( http://www.aprs.org/aprs12/datum.txt)
|
||||
* adds use of UTF-8 (https://en.wikipedia.org/wiki/UTF-8 )for the free form text in
|
||||
* messages and comments. It can't be used in the fixed width fields.
|
||||
*
|
||||
* Non-ASCII characters are represented by multi-byte sequences. All bytes in these
|
||||
* multi-byte sequences have the most significant bit set to 1. Using UTF-8 would not
|
||||
* add any nul (00000000) bytes to the stream.
|
||||
*
|
||||
* There are two known cases where we can have a nul character value.
|
||||
*
|
||||
* * The Kenwood TM-D710A sometimes sends packets like this:
|
||||
*
|
||||
* VA3AJ-9>T2QU6X,VE3WRC,WIDE1,K8UNS,WIDE2*:4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00>`nW<0x1f>oS8>/]"6M}driving fast=
|
||||
* K4JH-9>S5UQ6X,WR4AGC-3*,WIDE1*:4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00>`jP}l"&>/]"47}QRV from the EV =
|
||||
*
|
||||
* Notice that the data type indicator of "4" is not valid. If we remove
|
||||
* 4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00> we are left with a good MIC-E format.
|
||||
* This same thing has been observed from others and is intermittent.
|
||||
*
|
||||
* * AGW Tracker can send UTF-16 if an option is selected. This can introduce nul bytes.
|
||||
* This is wrong. It should be using UTF-8 and I'm not going to accomodate it here.
|
||||
*
|
||||
*
|
||||
* The digipeater and IGate functions should pass along anything exactly the
|
||||
* we received it, even if it is invalid. If different implementations try to fix it up
|
||||
* somehow, like changing unprintable characters to spaces, we will only make things
|
||||
* worse and thwart the duplicate detection.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
|
@ -4026,7 +4172,7 @@ static void process_comment (decode_aprs_t *A, char *pstart, int clen)
|
|||
* digipeated, causing the comment to be hundreds of characters long.
|
||||
*/
|
||||
|
||||
if (clen > sizeof(A->g_comment) - 1) {
|
||||
if (clen > (int)(sizeof(A->g_comment) - 1)) {
|
||||
if ( ! A->g_quiet) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Comment is extremely long, %d characters.\n", clen);
|
||||
|
|
|
@ -26,7 +26,9 @@ typedef struct decode_aprs_s {
|
|||
|
||||
char g_src[AX25_MAX_ADDR_LEN];
|
||||
|
||||
char g_msg_type[60]; /* Message type. Telemetry descriptions get pretty long. */
|
||||
char g_msg_type[60]; /* APRS data type. Telemetry descriptions get pretty long. */
|
||||
/* Putting msg in the name was a poor choice because */
|
||||
/* "message" has a specific meaning. Rename it someday. */
|
||||
|
||||
char g_symbol_table; /* The Symbol Table Identifier character selects one */
|
||||
/* of the two Symbol Tables, or it may be used as */
|
||||
|
@ -64,6 +66,19 @@ typedef struct decode_aprs_s {
|
|||
/* Also for Directed Station Query which is a */
|
||||
/* special case of message. */
|
||||
|
||||
enum message_subtype_e { message_subtype_invalid = 0,
|
||||
message_subtype_message,
|
||||
message_subtype_ack,
|
||||
message_subtype_rej,
|
||||
message_subtype_telem_parm,
|
||||
message_subtype_telem_unit,
|
||||
message_subtype_telem_eqns,
|
||||
message_subtype_telem_bits,
|
||||
message_subtype_directed_query
|
||||
} g_message_subtype; /* Various cases of the overloaded "message." */
|
||||
|
||||
char g_message_number[8]; /* Message number. Should be 1 - 5 characters if used. */
|
||||
|
||||
float g_speed_mph; /* Speed in MPH. */
|
||||
|
||||
float g_course; /* 0 = North, 90 = East, etc. */
|
||||
|
|
1
dedupe.c
1
dedupe.c
|
@ -96,6 +96,7 @@
|
|||
|
||||
#define DEDUPE_C
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
253
demod.c
253
demod.c
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016 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
|
||||
|
@ -18,14 +18,6 @@
|
|||
//
|
||||
|
||||
|
||||
// #define DEBUG1 1 /* display debugging info */
|
||||
|
||||
// #define DEBUG3 1 /* print carrier detect changes. */
|
||||
|
||||
// #define DEBUG4 1 /* capture AFSK demodulator output to log files */
|
||||
|
||||
// #define DEBUG5 1 /* capture 9600 output to log files */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
|
@ -39,6 +31,7 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
@ -49,7 +42,6 @@
|
|||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "audio.h"
|
||||
#include "demod.h"
|
||||
#include "tune.h"
|
||||
|
@ -60,6 +52,7 @@
|
|||
#include "textcolor.h"
|
||||
#include "demod_9600.h"
|
||||
#include "demod_afsk.h"
|
||||
#include "demod_psk.h"
|
||||
|
||||
|
||||
|
||||
|
@ -68,13 +61,16 @@
|
|||
static struct audio_s *save_audio_config_p;
|
||||
|
||||
|
||||
// TODO: temp experiment.
|
||||
|
||||
static int upsample = 2; // temp experiment.
|
||||
static int zerostuff = 1; // temp experiment.
|
||||
|
||||
// Current state of all the decoders.
|
||||
|
||||
static struct demodulator_state_s demodulator_state[MAX_CHANS][MAX_SUBCHANS];
|
||||
|
||||
|
||||
#define UPSAMPLE 2
|
||||
|
||||
static int sample_sum[MAX_CHANS][MAX_SUBCHANS];
|
||||
static int sample_count[MAX_CHANS][MAX_SUBCHANS];
|
||||
|
||||
|
@ -188,7 +184,7 @@ int demod_init (struct audio_s *pa)
|
|||
}
|
||||
}
|
||||
|
||||
assert (num_letters == strlen(just_letters));
|
||||
assert (num_letters == (int)(strlen(just_letters)));
|
||||
|
||||
/*
|
||||
* Pick a good default demodulator if none specified.
|
||||
|
@ -225,7 +221,8 @@ int demod_init (struct audio_s *pa)
|
|||
num_letters = 1;
|
||||
}
|
||||
|
||||
assert (num_letters == strlen(just_letters));
|
||||
|
||||
assert (num_letters == (int)(strlen(just_letters)));
|
||||
|
||||
/*
|
||||
* Put it back together again.
|
||||
|
@ -403,11 +400,11 @@ int demod_init (struct audio_s *pa)
|
|||
D->num_slicers = MAX_SLICERS;
|
||||
}
|
||||
|
||||
/* For siginal level reporting, we want a longer term view. */
|
||||
/* For signal level reporting, we want a longer term view. */
|
||||
// TODO: Should probably move this into the init functions.
|
||||
|
||||
D->quick_attack = D->agc_fast_attack * 0.2;
|
||||
D->sluggish_decay = D->agc_slow_decay * 0.2;
|
||||
D->quick_attack = D->agc_fast_attack * 0.2f;
|
||||
D->sluggish_decay = D->agc_slow_decay * 0.2f;
|
||||
}
|
||||
}
|
||||
else if (have_plus) {
|
||||
|
@ -459,10 +456,10 @@ int demod_init (struct audio_s *pa)
|
|||
D->num_slicers = MAX_SLICERS;
|
||||
}
|
||||
|
||||
/* For siginal level reporting, we want a longer term view. */
|
||||
/* For signal level reporting, we want a longer term view. */
|
||||
|
||||
D->quick_attack = D->agc_fast_attack * 0.2;
|
||||
D->sluggish_decay = D->agc_slow_decay * 0.2;
|
||||
D->quick_attack = D->agc_fast_attack * 0.2f;
|
||||
D->sluggish_decay = D->agc_slow_decay * 0.2f;
|
||||
}
|
||||
else {
|
||||
int d;
|
||||
|
@ -512,15 +509,123 @@ int demod_init (struct audio_s *pa)
|
|||
D->num_slicers = MAX_SLICERS;
|
||||
}
|
||||
|
||||
/* For siginal level reporting, we want a longer term view. */
|
||||
/* For signal level reporting, we want a longer term view. */
|
||||
|
||||
D->quick_attack = D->agc_fast_attack * 0.2;
|
||||
D->sluggish_decay = D->agc_slow_decay * 0.2;
|
||||
D->quick_attack = D->agc_fast_attack * 0.2f;
|
||||
D->sluggish_decay = D->agc_slow_decay * 0.2f;
|
||||
|
||||
} /* for each freq pair */
|
||||
}
|
||||
break;
|
||||
|
||||
case MODEM_QPSK: // New for 1.4
|
||||
|
||||
// TODO: See how much CPU this takes on ARM and decide if we should have different defaults.
|
||||
|
||||
if (strlen(save_audio_config_p->achan[chan].profiles) == 0) {
|
||||
//#if __arm__
|
||||
// strlcpy (save_audio_config_p->achan[chan].profiles, "R", sizeof(save_audio_config_p->achan[chan].profiles));
|
||||
//#else
|
||||
strlcpy (save_audio_config_p->achan[chan].profiles, "PQRS", sizeof(save_audio_config_p->achan[chan].profiles));
|
||||
//#endif
|
||||
}
|
||||
save_audio_config_p->achan[chan].num_subchan = strlen(save_audio_config_p->achan[chan].profiles);
|
||||
|
||||
save_audio_config_p->achan[chan].decimate = 1; // think about this later.
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Channel %d: %d bps, QPSK, %s, %d sample rate",
|
||||
chan, save_audio_config_p->achan[chan].baud,
|
||||
save_audio_config_p->achan[chan].profiles,
|
||||
save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec);
|
||||
if (save_audio_config_p->achan[chan].decimate != 1)
|
||||
dw_printf (" / %d", save_audio_config_p->achan[chan].decimate);
|
||||
if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF)
|
||||
dw_printf (", DTMF decoder enabled");
|
||||
dw_printf (".\n");
|
||||
|
||||
int d;
|
||||
for (d = 0; d < save_audio_config_p->achan[chan].num_subchan; d++) {
|
||||
|
||||
assert (d >= 0 && d < MAX_SUBCHANS);
|
||||
struct demodulator_state_s *D;
|
||||
D = &demodulator_state[chan][d];
|
||||
profile = save_audio_config_p->achan[chan].profiles[d];
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("About to call demod_psk_init for Q-PSK case, modem_type=%d, profile='%c'\n",
|
||||
// save_audio_config_p->achan[chan].modem_type, profile);
|
||||
|
||||
demod_psk_init (save_audio_config_p->achan[chan].modem_type,
|
||||
save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate,
|
||||
save_audio_config_p->achan[chan].baud,
|
||||
profile,
|
||||
D);
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("Returned from demod_psk_init\n");
|
||||
|
||||
/* For signal level reporting, we want a longer term view. */
|
||||
/* Guesses based on 9600. Maybe revisit someday. */
|
||||
|
||||
D->quick_attack = 0.080 * 0.2;
|
||||
D->sluggish_decay = 0.00012 * 0.2;
|
||||
}
|
||||
break;
|
||||
|
||||
case MODEM_8PSK: // New for 1.4
|
||||
|
||||
// TODO: See how much CPU this takes on ARM and decide if we should have different defaults.
|
||||
|
||||
if (strlen(save_audio_config_p->achan[chan].profiles) == 0) {
|
||||
//#if __arm__
|
||||
// strlcpy (save_audio_config_p->achan[chan].profiles, "V", sizeof(save_audio_config_p->achan[chan].profiles));
|
||||
//#else
|
||||
strlcpy (save_audio_config_p->achan[chan].profiles, "TUVW", sizeof(save_audio_config_p->achan[chan].profiles));
|
||||
//#endif
|
||||
}
|
||||
save_audio_config_p->achan[chan].num_subchan = strlen(save_audio_config_p->achan[chan].profiles);
|
||||
|
||||
save_audio_config_p->achan[chan].decimate = 1; // think about this later
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Channel %d: %d bps, 8PSK, %s, %d sample rate",
|
||||
chan, save_audio_config_p->achan[chan].baud,
|
||||
save_audio_config_p->achan[chan].profiles,
|
||||
save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec);
|
||||
if (save_audio_config_p->achan[chan].decimate != 1)
|
||||
dw_printf (" / %d", save_audio_config_p->achan[chan].decimate);
|
||||
if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF)
|
||||
dw_printf (", DTMF decoder enabled");
|
||||
dw_printf (".\n");
|
||||
|
||||
//int d;
|
||||
for (d = 0; d < save_audio_config_p->achan[chan].num_subchan; d++) {
|
||||
|
||||
assert (d >= 0 && d < MAX_SUBCHANS);
|
||||
struct demodulator_state_s *D;
|
||||
D = &demodulator_state[chan][d];
|
||||
profile = save_audio_config_p->achan[chan].profiles[d];
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("About to call demod_psk_init for 8-PSK case, modem_type=%d, profile='%c'\n",
|
||||
// save_audio_config_p->achan[chan].modem_type, profile);
|
||||
|
||||
demod_psk_init (save_audio_config_p->achan[chan].modem_type,
|
||||
save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate,
|
||||
save_audio_config_p->achan[chan].baud,
|
||||
profile,
|
||||
D);
|
||||
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("Returned from demod_psk_init\n");
|
||||
|
||||
/* For signal level reporting, we want a longer term view. */
|
||||
/* Guesses based on 9600. Maybe revisit someday. */
|
||||
|
||||
D->quick_attack = 0.080 * 0.2;
|
||||
D->sluggish_decay = 0.00012 * 0.2;
|
||||
}
|
||||
break;
|
||||
|
||||
//TODO: how about MODEM_OFF case?
|
||||
|
||||
case MODEM_BASEBAND:
|
||||
|
@ -539,11 +644,20 @@ int demod_init (struct audio_s *pa)
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef TUNE_UPSAMPLE
|
||||
upsample = TUNE_UPSAMPLE;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef TUNE_ZEROSTUFF
|
||||
zerostuff = TUNE_ZEROSTUFF;
|
||||
#endif
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Channel %d: %d baud, K9NG/G3RUH, %s, %d sample rate x %d",
|
||||
chan, save_audio_config_p->achan[chan].baud,
|
||||
save_audio_config_p->achan[chan].profiles,
|
||||
save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec, UPSAMPLE);
|
||||
save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec, upsample);
|
||||
if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF)
|
||||
dw_printf (", DTMF decoder enabled");
|
||||
dw_printf (".\n");
|
||||
|
@ -562,7 +676,36 @@ int demod_init (struct audio_s *pa)
|
|||
save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
|
||||
}
|
||||
|
||||
demod_9600_init (UPSAMPLE * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec, save_audio_config_p->achan[chan].baud, D);
|
||||
|
||||
/* We need a minimum number of audio samples per bit time for good performance. */
|
||||
/* Easier to check here because demod_9600_init might have an adjusted sample rate. */
|
||||
|
||||
float ratio = (float)(save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec)
|
||||
/ (float)(save_audio_config_p->achan[chan].baud);
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("The ratio of audio samples per sec (%d) to data rate in baud (%d) is %.1f\n",
|
||||
save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec,
|
||||
save_audio_config_p->achan[chan].baud,
|
||||
(double)ratio);
|
||||
if (ratio < 3) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("There is little hope of success with such a low ratio. Use a higher sample rate.\n");
|
||||
}
|
||||
else if (ratio < 5) {
|
||||
dw_printf ("This is on the low side for best performance. Can you use a higher sample rate?\n");
|
||||
}
|
||||
else if (ratio < 6) {
|
||||
dw_printf ("Increasing the sample rate should improve decoder performance.\n");
|
||||
}
|
||||
else if (ratio > 15) {
|
||||
dw_printf ("Sample rate is more than adequate. You might lower it if CPU load is a concern.\n");
|
||||
}
|
||||
else {
|
||||
dw_printf ("This is a suitable ratio for good performance.\n");
|
||||
}
|
||||
|
||||
demod_9600_init (upsample * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec, save_audio_config_p->achan[chan].baud, D);
|
||||
|
||||
if (strchr(save_audio_config_p->achan[chan].profiles, '+') != NULL) {
|
||||
|
||||
|
@ -573,10 +716,10 @@ int demod_init (struct audio_s *pa)
|
|||
D->num_slicers = MAX_SLICERS;
|
||||
}
|
||||
|
||||
/* For siginal level reporting, we want a longer term view. */
|
||||
/* For signal level reporting, we want a longer term view. */
|
||||
|
||||
D->quick_attack = D->agc_fast_attack * 0.2;
|
||||
D->sluggish_decay = D->agc_slow_decay * 0.2;
|
||||
D->quick_attack = D->agc_fast_attack * 0.2f;
|
||||
D->sluggish_decay = D->agc_slow_decay * 0.2f;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -699,17 +842,9 @@ __attribute__((hot))
|
|||
void demod_process_sample (int chan, int subchan, int sam)
|
||||
{
|
||||
float fsam;
|
||||
//float abs_fsam;
|
||||
int k;
|
||||
|
||||
|
||||
#if DEBUG4
|
||||
static FILE *demod_log_fp = NULL;
|
||||
static int seq = 0; /* for log file name */
|
||||
#endif
|
||||
|
||||
//int j;
|
||||
//int demod_data;
|
||||
struct demodulator_state_s *D;
|
||||
|
||||
assert (chan >= 0 && chan < MAX_CHANS);
|
||||
|
@ -778,30 +913,44 @@ void demod_process_sample (int chan, int subchan, int sam)
|
|||
}
|
||||
break;
|
||||
|
||||
case MODEM_QPSK:
|
||||
case MODEM_8PSK:
|
||||
|
||||
if (save_audio_config_p->achan[chan].decimate > 1) {
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Invalid combination of options. Exiting.\n");
|
||||
// Would probably work but haven't thought about it or tested yet.
|
||||
exit (1);
|
||||
}
|
||||
else {
|
||||
demod_psk_process_sample (chan, subchan, sam, D);
|
||||
}
|
||||
break;
|
||||
|
||||
case MODEM_BASEBAND:
|
||||
case MODEM_SCRAMBLE:
|
||||
default:
|
||||
|
||||
#define ZEROSTUFF 1
|
||||
|
||||
|
||||
#if ZEROSTUFF
|
||||
if (zerostuff) {
|
||||
/* Literature says this is better if followed */
|
||||
/* by appropriate low pass filter. */
|
||||
/* So far, both are same in tests with different */
|
||||
/* optimal low pass filter parameters. */
|
||||
|
||||
for (k=1; k<UPSAMPLE; k++) {
|
||||
for (k=1; k<upsample; k++) {
|
||||
demod_9600_process_sample (chan, 0, D);
|
||||
}
|
||||
demod_9600_process_sample (chan, sam*UPSAMPLE, D);
|
||||
#else
|
||||
demod_9600_process_sample (chan, sam * upsample, D);
|
||||
}
|
||||
else {
|
||||
|
||||
/* Linear interpolation. */
|
||||
static int prev_sam;
|
||||
switch (UPSAMPLE) {
|
||||
case 1:
|
||||
demod_9600_process_sample (chan, sam);
|
||||
|
||||
switch (upsample) {
|
||||
case 1:
|
||||
demod_9600_process_sample (chan, sam, D);
|
||||
break;
|
||||
case 2:
|
||||
demod_9600_process_sample (chan, (prev_sam + sam) / 2, D);
|
||||
|
@ -823,9 +972,10 @@ void demod_process_sample (int chan, int subchan, int sam)
|
|||
break;
|
||||
}
|
||||
prev_sam = sam;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
} /* switch modem_type */
|
||||
return;
|
||||
|
||||
} /* end demod_process_sample */
|
||||
|
@ -868,8 +1018,11 @@ alevel_t demod_get_audio_level (int chan, int subchan)
|
|||
|
||||
alevel.mark = (int) ((D->alevel_mark_peak ) * 100.0f + 0.5f);
|
||||
alevel.space = (int) ((D->alevel_space_peak ) * 100.0f + 0.5f);
|
||||
|
||||
//alevel.ms_ratio = D->alevel_mark_peak / D->alevel_space_peak; // TODO: remove after temp test
|
||||
}
|
||||
else if (save_audio_config_p->achan[chan].modem_type == MODEM_QPSK ||
|
||||
save_audio_config_p->achan[chan].modem_type == MODEM_8PSK) {
|
||||
alevel.mark = -1;
|
||||
alevel.space = -1;
|
||||
}
|
||||
else {
|
||||
|
||||
|
|
256
demod_9600.c
256
demod_9600.c
|
@ -18,7 +18,7 @@
|
|||
//
|
||||
|
||||
|
||||
// #define DEBUG5 1 /* capture 9600 output to log files */
|
||||
//#define DEBUG4 1 /* capture 9600 output to log files */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
|
@ -33,6 +33,8 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
@ -42,7 +44,6 @@
|
|||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "tune.h"
|
||||
#include "fsk_demod_state.h"
|
||||
#include "hdlc_rec.h"
|
||||
|
@ -72,25 +73,10 @@ static inline float convolve (const float *__restrict__ data, const float *__res
|
|||
float sum = 0.0f;
|
||||
int j;
|
||||
|
||||
#if 0
|
||||
// As suggested here, http://locklessinc.com/articles/vectorize/
|
||||
// Unfortunately, older compilers don't recognize it.
|
||||
|
||||
// Get more information by using -ftree-vectorizer-verbose=5
|
||||
|
||||
float *d = __builtin_assume_aligned(data, 16);
|
||||
float *f = __builtin_assume_aligned(filter, 16);
|
||||
|
||||
#pragma GCC ivdep
|
||||
for (j=0; j<filter_size; j++) {
|
||||
sum += f[j] * d[j];
|
||||
}
|
||||
#else
|
||||
#pragma GCC ivdep // ignored until gcc 4.9
|
||||
//#pragma GCC ivdep // ignored until gcc 4.9
|
||||
for (j=0; j<filter_size; j++) {
|
||||
sum += filter[j] * data[j];
|
||||
}
|
||||
#endif
|
||||
return (sum);
|
||||
}
|
||||
|
||||
|
@ -101,21 +87,21 @@ __attribute__((hot)) __attribute__((always_inline))
|
|||
static inline float agc (float in, float fast_attack, float slow_decay, float *ppeak, float *pvalley)
|
||||
{
|
||||
if (in >= *ppeak) {
|
||||
*ppeak = in * fast_attack + *ppeak * (1. - fast_attack);
|
||||
*ppeak = in * fast_attack + *ppeak * (1.0f - fast_attack);
|
||||
}
|
||||
else {
|
||||
*ppeak = in * slow_decay + *ppeak * (1. - slow_decay);
|
||||
*ppeak = in * slow_decay + *ppeak * (1.0f - slow_decay);
|
||||
}
|
||||
|
||||
if (in <= *pvalley) {
|
||||
*pvalley = in * fast_attack + *pvalley * (1. - fast_attack);
|
||||
*pvalley = in * fast_attack + *pvalley * (1.0f - fast_attack);
|
||||
}
|
||||
else {
|
||||
*pvalley = in * slow_decay + *pvalley * (1. - slow_decay);
|
||||
*pvalley = in * slow_decay + *pvalley * (1.0f - slow_decay);
|
||||
}
|
||||
|
||||
if (*ppeak > *pvalley) {
|
||||
return ((in - 0.5 * (*ppeak + *pvalley)) / (*ppeak - *pvalley));
|
||||
return ((in - 0.5f * (*ppeak + *pvalley)) / (*ppeak - *pvalley));
|
||||
}
|
||||
return (0.0);
|
||||
}
|
||||
|
@ -125,7 +111,7 @@ static inline float agc (float in, float fast_attack, float slow_decay, float *p
|
|||
*
|
||||
* Name: demod_9600_init
|
||||
*
|
||||
* Purpose: Initialize the 9600 baud demodulator.
|
||||
* Purpose: Initialize the 9600 (or higher) baud demodulator.
|
||||
*
|
||||
* Inputs: samples_per_sec - Number of samples per second.
|
||||
* Might be upsampled in hopes of
|
||||
|
@ -147,21 +133,33 @@ void demod_9600_init (int samples_per_sec, int baud, struct demodulator_state_s
|
|||
memset (D, 0, sizeof(struct demodulator_state_s));
|
||||
D->num_slicers = 1;
|
||||
|
||||
//dw_printf ("demod_9600_init(rate=%d, baud=%d, D ptr)\n", samples_per_sec, baud);
|
||||
// Multiple profiles in future?
|
||||
|
||||
D->pll_step_per_sample =
|
||||
(int) round(TICKS_PER_PLL_CYCLE * (double) baud / (double)samples_per_sec);
|
||||
// switch (profile) {
|
||||
|
||||
// case 'J': // upsample x2 with filtering.
|
||||
// case 'K': // upsample x3 with filtering.
|
||||
// case 'L': // upsample x4 with filtering.
|
||||
|
||||
D->lp_filter_len_bits = 76 * 9600.0 / (44100.0 * 2.0);
|
||||
|
||||
// Works best with odd number in some tests. Even is better in others.
|
||||
//D->lp_filter_size = ((int) (0.5f * ( D->lp_filter_len_bits * (float)samples_per_sec / (float)baud ))) * 2 + 1;
|
||||
D->lp_filter_size = (int) (( D->lp_filter_len_bits * (float)samples_per_sec / baud) + 0.5f);
|
||||
|
||||
D->lp_filter_len_bits = 72 * 9600.0 / (44100.0 * 2.0);
|
||||
D->lp_filter_size = (int) (( D->lp_filter_len_bits * (float)samples_per_sec / baud) + 0.5);
|
||||
D->lp_window = BP_WINDOW_HAMMING;
|
||||
D->lpf_baud = 0.59;
|
||||
D->lpf_baud = 0.62;
|
||||
|
||||
D->agc_fast_attack = 0.080;
|
||||
D->agc_slow_decay = 0.00012;
|
||||
|
||||
D->pll_locked_inertia = 0.88;
|
||||
D->pll_locked_inertia = 0.89;
|
||||
D->pll_searching_inertia = 0.67;
|
||||
// break;
|
||||
// }
|
||||
|
||||
D->pll_step_per_sample =
|
||||
(int) round(TICKS_PER_PLL_CYCLE * (double) baud / (double)samples_per_sec);
|
||||
|
||||
|
||||
#ifdef TUNE_LP_WINDOW
|
||||
|
@ -184,8 +182,11 @@ void demod_9600_init (int samples_per_sec, int baud, struct demodulator_state_s
|
|||
D->agc_slow_decay = TUNE_AGC_SLOW;
|
||||
#endif
|
||||
|
||||
#if defined(TUNE_PLL_LOCKED) && defined(TUNE_PLL_SEARCHING)
|
||||
#if defined(TUNE_PLL_LOCKED)
|
||||
D->pll_locked_inertia = TUNE_PLL_LOCKED;
|
||||
#endif
|
||||
|
||||
#if defined(TUNE_PLL_SEARCHING)
|
||||
D->pll_searching_inertia = TUNE_PLL_SEARCHING;
|
||||
#endif
|
||||
|
||||
|
@ -198,7 +199,7 @@ void demod_9600_init (int samples_per_sec, int baud, struct demodulator_state_s
|
|||
/* Version 1.2: Experiment with different slicing levels. */
|
||||
|
||||
for (j = 0; j < MAX_SUBCHANS; j++) {
|
||||
slice_point[j] = 0.02 * (j - 0.5 * (MAX_SUBCHANS-1));
|
||||
slice_point[j] = 0.02f * (j - 0.5f * (MAX_SUBCHANS-1));
|
||||
//dw_printf ("slice_point[%d] = %+5.2f\n", j, slice_point[j]);
|
||||
}
|
||||
|
||||
|
@ -260,23 +261,22 @@ void demod_9600_init (int samples_per_sec, int baud, struct demodulator_state_s
|
|||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static void inline nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D);
|
||||
inline static void nudge_pll (int chan, int subchan, int slice, float demod_out, struct demodulator_state_s *D);
|
||||
|
||||
__attribute__((hot))
|
||||
void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D)
|
||||
{
|
||||
|
||||
float fsam;
|
||||
//float abs_fsam;
|
||||
float amp;
|
||||
float demod_out;
|
||||
|
||||
#if DEBUG5
|
||||
#if DEBUG4
|
||||
static FILE *demod_log_fp = NULL;
|
||||
static int seq = 0; /* for log file name */
|
||||
static int log_file_seq = 0; /* Part of log file name */
|
||||
#endif
|
||||
|
||||
//int j;
|
||||
|
||||
int subchan = 0;
|
||||
int demod_data; /* Still scrambled. */
|
||||
|
||||
|
@ -306,6 +306,12 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D
|
|||
|
||||
fsam = sam / 16384.0;
|
||||
|
||||
#if defined(TUNE_ZEROSTUFF) && TUNE_ZEROSTUFF == 0
|
||||
// experiment - no filtering.
|
||||
|
||||
amp = fsam;
|
||||
|
||||
#else
|
||||
push_sample (fsam, D->raw_cb, D->lp_filter_size);
|
||||
|
||||
/*
|
||||
|
@ -313,7 +319,7 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D
|
|||
*/
|
||||
|
||||
amp = convolve (D->raw_cb, D->lp_filter, D->lp_filter_size);
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Version 1.2: Capture the post-filtering amplitude for display.
|
||||
|
@ -324,18 +330,20 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D
|
|||
* Here we keep + and - peaks because there could be a DC bias.
|
||||
*/
|
||||
|
||||
// TODO: probably no need for this. Just use D->m_peak, D->m_valley
|
||||
|
||||
if (amp >= D->alevel_mark_peak) {
|
||||
D->alevel_mark_peak = amp * D->quick_attack + D->alevel_mark_peak * (1. - D->quick_attack);
|
||||
D->alevel_mark_peak = amp * D->quick_attack + D->alevel_mark_peak * (1.0f - D->quick_attack);
|
||||
}
|
||||
else {
|
||||
D->alevel_mark_peak = amp * D->sluggish_decay + D->alevel_mark_peak * (1. - D->sluggish_decay);
|
||||
D->alevel_mark_peak = amp * D->sluggish_decay + D->alevel_mark_peak * (1.0f - D->sluggish_decay);
|
||||
}
|
||||
|
||||
if (amp <= D->alevel_space_peak) {
|
||||
D->alevel_space_peak = amp * D->quick_attack + D->alevel_space_peak * (1. - D->quick_attack);
|
||||
D->alevel_space_peak = amp * D->quick_attack + D->alevel_space_peak * (1.0f - D->quick_attack);
|
||||
}
|
||||
else {
|
||||
D->alevel_space_peak = amp * D->sluggish_decay + D->alevel_space_peak * (1. - D->sluggish_decay);
|
||||
D->alevel_space_peak = amp * D->sluggish_decay + D->alevel_space_peak * (1.0f - D->sluggish_decay);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -349,28 +357,10 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D
|
|||
|
||||
demod_out = agc (amp, D->agc_fast_attack, D->agc_slow_decay, &(D->m_peak), &(D->m_valley));
|
||||
|
||||
|
||||
// TODO: There is potential for multiple decoders with one filter.
|
||||
|
||||
//dw_printf ("peak=%.2f valley=%.2f amp=%.2f norm=%.2f\n", D->m_peak, D->m_valley, amp, norm);
|
||||
|
||||
/* Throw in a little Hysteresis??? */
|
||||
/* (Not to be confused with Hysteria.) */
|
||||
/* Doesn't seem to have any value. */
|
||||
/* Using a level of .02 makes things worse. */
|
||||
/* Might want to experiment with this again someday. */
|
||||
|
||||
|
||||
// if (demod_out > 0.03) {
|
||||
// demod_data = 1;
|
||||
// }
|
||||
// else if (demod_out < -0.03) {
|
||||
// demod_data = 0;
|
||||
// }
|
||||
// else {
|
||||
// demod_data = D->slicer[subchan].prev_demod_data;
|
||||
// }
|
||||
|
||||
if (D->num_slicers <= 1) {
|
||||
|
||||
/* Normal case of one demodulator to one HDLC decoder. */
|
||||
|
@ -378,7 +368,7 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D
|
|||
/* AGC should generally keep this around -1 to +1 range. */
|
||||
|
||||
demod_data = demod_out > 0;
|
||||
nudge_pll (chan, subchan, 0, demod_data, D);
|
||||
nudge_pll (chan, subchan, 0, demod_out, D);
|
||||
}
|
||||
else {
|
||||
int slice;
|
||||
|
@ -386,20 +376,82 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D
|
|||
/* Multiple slicers each feeding its own HDLC decoder. */
|
||||
|
||||
for (slice=0; slice<D->num_slicers; slice++) {
|
||||
demod_data = demod_out > slice_point[slice];
|
||||
nudge_pll (chan, subchan, slice, demod_data, D);
|
||||
demod_data = demod_out - slice_point[slice] > 0;
|
||||
nudge_pll (chan, subchan, slice, demod_out - slice_point[slice], D);
|
||||
}
|
||||
}
|
||||
|
||||
// demod_data is used only for debug out.
|
||||
// suppress compiler warning about it not being used.
|
||||
(void) demod_data;
|
||||
|
||||
#if DEBUG4
|
||||
|
||||
if (chan == 0) {
|
||||
|
||||
if (1) {
|
||||
//if (hdlc_rec_gathering (chan, subchan, slice)) {
|
||||
char fname[30];
|
||||
int slice = 0;
|
||||
|
||||
if (demod_log_fp == NULL) {
|
||||
log_file_seq++;
|
||||
snprintf (fname, sizeof(fname), "demod/%04d.csv", log_file_seq);
|
||||
//if (log_file_seq == 1) mkdir ("demod", 0777);
|
||||
if (log_file_seq == 1) mkdir ("demod");
|
||||
|
||||
demod_log_fp = fopen (fname, "w");
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Starting demodulator log file %s\n", fname);
|
||||
fprintf (demod_log_fp, "Audio, Filtered, Max, Min, Normalized, Sliced, Clock\n");
|
||||
}
|
||||
|
||||
fprintf (demod_log_fp, "%.3f, %.3f, %.3f, %.3f, %.3f, %d, %.2f\n",
|
||||
fsam + 6,
|
||||
amp + 4,
|
||||
D->m_peak + 4,
|
||||
D->m_valley + 4,
|
||||
demod_out + 2,
|
||||
demod_data + 2,
|
||||
(D->slicer[slice].data_clock_pll & 0x80000000) ? .5 : .0);
|
||||
|
||||
fflush (demod_log_fp);
|
||||
}
|
||||
else {
|
||||
if (demod_log_fp != NULL) {
|
||||
fclose (demod_log_fp);
|
||||
demod_log_fp = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} /* end demod_9600_process_sample */
|
||||
|
||||
|
||||
__attribute__((hot))
|
||||
static void inline nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D)
|
||||
{
|
||||
|
||||
/*
|
||||
* Next, a PLL is used to sample near the centers of the data bits.
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: nudge_pll
|
||||
*
|
||||
* Purpose: Update the PLL state for each audio sample.
|
||||
*
|
||||
* (2) Descramble it.
|
||||
* (2) Recover clock and data.
|
||||
*
|
||||
* Inputs: chan - Audio channel. 0 for left, 1 for right.
|
||||
*
|
||||
* subchan - Which demodulator. We could have several running in parallel.
|
||||
*
|
||||
* slice - Determines which Slicing level & HDLC decoder to use.
|
||||
*
|
||||
* demod_out_f - Demodulator output, possibly shifted by slicing level
|
||||
* It will be compared with 0.0 to bit binary value out.
|
||||
*
|
||||
* D - Demodulator state for this channel / subchannel.
|
||||
*
|
||||
* Returns: None
|
||||
*
|
||||
* Descripton: A PLL is used to sample near the centers of the data bits.
|
||||
*
|
||||
* D->data_clock_pll is a SIGNED 32 bit variable.
|
||||
* When it overflows from a large positive value to a negative value, we
|
||||
|
@ -420,42 +472,51 @@ static void inline nudge_pll (int chan, int subchan, int slice, int demod_data,
|
|||
* I don't think the optimal value will depend on the audio sample rate
|
||||
* because this happens for each transition from the demodulator.
|
||||
*
|
||||
* This was optimized for 1200 baud AFSK. There might be some opportunity
|
||||
* for improvement here.
|
||||
* Version 1.4: Previously, we would always pull the PLL phase toward 0 after
|
||||
* after a zero crossing was detetected. This adds extra jitter,
|
||||
* especially when the ratio of audio sample rate to baud is low.
|
||||
* Now, we interpolate between the two samples to get an estimate
|
||||
* on when the zero crossing happened. The PLL is pulled toward
|
||||
* this point.
|
||||
*
|
||||
* Results??? TBD
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
__attribute__((hot))
|
||||
inline static void nudge_pll (int chan, int subchan, int slice, float demod_out_f, struct demodulator_state_s *D)
|
||||
{
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
D->slicer[slice].prev_d_c_pll = D->slicer[slice].data_clock_pll;
|
||||
D->slicer[slice].data_clock_pll += D->pll_step_per_sample;
|
||||
|
||||
if (D->slicer[slice].data_clock_pll < 0 && D->slicer[slice].prev_d_c_pll > 0) {
|
||||
if ( D->slicer[slice].prev_d_c_pll > 1000000000 && D->slicer[slice].data_clock_pll < -1000000000) {
|
||||
|
||||
/* Overflow. */
|
||||
/* Overflow. Was large positive, wrapped around, now large negative. */
|
||||
|
||||
/*
|
||||
* At this point, we need to descramble the data as
|
||||
* in hardware based designs by G3RUH and K9NG.
|
||||
*
|
||||
* Future Idea: allow unscrambled baseband data.
|
||||
*
|
||||
* http://www.amsat.org/amsat/articles/g3ruh/109/fig03.gif
|
||||
*/
|
||||
// Warning: 'descram' set but not used.
|
||||
// It's used in conditional debug code below.
|
||||
// descram =
|
||||
descramble (demod_data, &(D->slicer[slice].lfsr));
|
||||
|
||||
hdlc_rec_bit (chan, subchan, slice, demod_data, 1, D->slicer[slice].lfsr);
|
||||
hdlc_rec_bit (chan, subchan, slice, demod_out_f > 0, 1, D->slicer[slice].lfsr);
|
||||
}
|
||||
|
||||
if (demod_data != D->slicer[slice].prev_demod_data) {
|
||||
/*
|
||||
* Zero crossing?
|
||||
*/
|
||||
if ((D->slicer[slice].prev_demod_out_f < 0 && demod_out_f > 0) ||
|
||||
(D->slicer[slice].prev_demod_out_f > 0 && demod_out_f < 0)) {
|
||||
|
||||
// Note: Test for this demodulator, not overall for channel.
|
||||
|
||||
float target = 0;
|
||||
|
||||
target = D->pll_step_per_sample * demod_out_f / (demod_out_f - D->slicer[slice].prev_demod_out_f);
|
||||
|
||||
if (hdlc_rec_gathering (chan, subchan, slice)) {
|
||||
D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_locked_inertia);
|
||||
D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_locked_inertia + target * (1.0f - D->pll_locked_inertia) );
|
||||
}
|
||||
else {
|
||||
D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_searching_inertia);
|
||||
D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_searching_inertia + target * (1.0f - D->pll_searching_inertia) );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -483,10 +544,10 @@ static void inline nudge_pll (int chan, int subchan, int slice, int demod_data,
|
|||
fprintf (demod_log_fp, "Audio, Peak, Valley, Demod, SData, Descram, Clock\n");
|
||||
}
|
||||
fprintf (demod_log_fp, "%.3f, %.3f, %.3f, %.3f, %.2f, %.2f, %.2f\n",
|
||||
0.5 * fsam + 3.5,
|
||||
0.5 * D->m_peak + 3.5,
|
||||
0.5 * D->m_valley + 3.5,
|
||||
0.5 * demod_out + 2.0,
|
||||
0.5f * fsam + 3.5,
|
||||
0.5f * D->m_peak + 3.5,
|
||||
0.5f * D->m_valley + 3.5,
|
||||
0.5f * demod_out + 2.0,
|
||||
demod_data ? 1.35 : 1.0,
|
||||
descram ? .9 : .55,
|
||||
(D->data_clock_pll & 0x80000000) ? .1 : .45);
|
||||
|
@ -506,12 +567,9 @@ static void inline nudge_pll (int chan, int subchan, int slice, int demod_data,
|
|||
* Remember demodulator output (pre-descrambling) so we can compare next time
|
||||
* for the DPLL sync.
|
||||
*/
|
||||
D->slicer[slice].prev_demod_data = demod_data;
|
||||
D->slicer[slice].prev_demod_out_f = demod_out_f;
|
||||
|
||||
} /* end nudge_pll */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* end demod_9600.c */
|
||||
|
|
42
demod_afsk.c
42
demod_afsk.c
|
@ -39,7 +39,7 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
@ -50,9 +50,7 @@
|
|||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "audio.h"
|
||||
|
||||
#include "tune.h"
|
||||
#include "fsk_demod_state.h"
|
||||
#include "fsk_gen_filter.h"
|
||||
|
@ -65,7 +63,7 @@
|
|||
#define MAX(a,b) ((a)>(b)?(a):(b))
|
||||
|
||||
|
||||
|
||||
#ifndef GEN_FFF
|
||||
|
||||
/* Quick approximation to sqrt(x*x+y*y) */
|
||||
/* No benefit for regular PC. */
|
||||
|
@ -105,7 +103,7 @@ static inline float convolve (const float *__restrict__ data, const float *__res
|
|||
int j;
|
||||
|
||||
|
||||
#pragma GCC ivdep // ignored until gcc 4.9
|
||||
//#pragma GCC ivdep // ignored until gcc 4.9
|
||||
for (j=0; j<filter_size; j++) {
|
||||
sum += filter[j] * data[j];
|
||||
}
|
||||
|
@ -139,6 +137,8 @@ static inline float agc (float in, float fast_attack, float slow_decay, float *p
|
|||
return (0.0f);
|
||||
}
|
||||
|
||||
#endif // ifndef GEN_FFF
|
||||
|
||||
|
||||
/*
|
||||
* for multi-slicer experiment.
|
||||
|
@ -530,7 +530,7 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
|
|||
for (j=0; j<D->ms_filter_size; j++) {
|
||||
float am;
|
||||
float center;
|
||||
float shape = 1; /* Shape is an attempt to smooth out the */
|
||||
float shape = 1.0f; /* Shape is an attempt to smooth out the */
|
||||
/* abrupt edges in hopes of reducing */
|
||||
/* overshoot and ringing. */
|
||||
/* My first thought was to use a cosine shape. */
|
||||
|
@ -538,16 +538,16 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
|
|||
/* windows mentioned in the literature. */
|
||||
/* http://en.wikipedia.org/wiki/Window_function */
|
||||
|
||||
center = 0.5 * (D->ms_filter_size - 1);
|
||||
am = ((float)(j - center) / (float)samples_per_sec) * ((float)mark_freq) * (2 * M_PI);
|
||||
center = 0.5f * (D->ms_filter_size - 1);
|
||||
am = ((float)(j - center) / (float)samples_per_sec) * ((float)mark_freq) * (2.0f * (float)M_PI);
|
||||
|
||||
shape = window (D->ms_window, D->ms_filter_size, j);
|
||||
|
||||
D->m_sin_table[j] = sin(am) * shape;
|
||||
D->m_cos_table[j] = cos(am) * shape;
|
||||
D->m_sin_table[j] = sinf(am) * shape;
|
||||
D->m_cos_table[j] = cosf(am) * shape;
|
||||
|
||||
Gs += D->m_sin_table[j] * sin(am);
|
||||
Gc += D->m_cos_table[j] * cos(am);
|
||||
Gs += D->m_sin_table[j] * sinf(am);
|
||||
Gc += D->m_cos_table[j] * cosf(am);
|
||||
|
||||
#if DEBUG1
|
||||
dw_printf ("%6d %6.2f %6.2f %6.2f\n", j, shape, D->m_sin_table[j], D->m_cos_table[j]) ;
|
||||
|
@ -578,18 +578,18 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
|
|||
for (j=0; j<D->ms_filter_size; j++) {
|
||||
float as;
|
||||
float center;
|
||||
float shape = 1;
|
||||
float shape = 1.0f;
|
||||
|
||||
center = 0.5 * (D->ms_filter_size - 1);
|
||||
as = ((float)(j - center) / (float)samples_per_sec) * ((float)space_freq) * (2 * M_PI);
|
||||
as = ((float)(j - center) / (float)samples_per_sec) * ((float)space_freq) * (2.0f * (float)M_PI);
|
||||
|
||||
shape = window (D->ms_window, D->ms_filter_size, j);
|
||||
|
||||
D->s_sin_table[j] = sin(as) * shape;
|
||||
D->s_cos_table[j] = cos(as) * shape;
|
||||
D->s_sin_table[j] = sinf(as) * shape;
|
||||
D->s_cos_table[j] = cosf(as) * shape;
|
||||
|
||||
Gs += D->s_sin_table[j] * sin(as);
|
||||
Gc += D->s_cos_table[j] * cos(as);
|
||||
Gs += D->s_sin_table[j] * sinf(as);
|
||||
Gc += D->s_cos_table[j] * cosf(as);
|
||||
|
||||
#if DEBUG1
|
||||
dw_printf ("%6d %6.2f %6.2f %6.2f\n", j, shape, D->s_sin_table[j], D->s_cos_table[j] ) ;
|
||||
|
@ -756,7 +756,7 @@ int main (void)
|
|||
emit_macro ("CALC_S_SUM1", ds.ms_filter_size, ds.s_sin_table);
|
||||
emit_macro ("CALC_S_SUM2", ds.ms_filter_size, ds.s_cos_table);
|
||||
|
||||
exit(0);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -808,7 +808,7 @@ int main (void)
|
|||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static void inline nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D);
|
||||
inline static void nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D);
|
||||
|
||||
__attribute__((hot))
|
||||
void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulator_state_s *D)
|
||||
|
@ -1088,7 +1088,7 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat
|
|||
|
||||
|
||||
__attribute__((hot))
|
||||
static void inline nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D)
|
||||
inline static void nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D)
|
||||
{
|
||||
|
||||
/*
|
||||
|
|
|
@ -0,0 +1,856 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2016 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 DEBUG1 1 /* display debugging info */
|
||||
|
||||
//#define DEBUG3 1 /* print carrier detect changes. */
|
||||
|
||||
//#define DEBUG4 1 /* capture PSK demodulator output to log files */
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Module: demod_psk.c
|
||||
*
|
||||
* Purpose: Demodulator for Phase Shift Keying (PSK).
|
||||
*
|
||||
* This is my initial attempt at implementing a 2400 bps mode.
|
||||
* The MFJ-2400 & AEA PK232-2400 used V.26 / Bell 201 so I will follow that precedent.
|
||||
*
|
||||
*
|
||||
* Input: Audio samples from either a file or the "sound card."
|
||||
*
|
||||
* Outputs: Calls hdlc_rec_bit() for each bit demodulated.
|
||||
*
|
||||
* Current Status: New for Version 1.4.
|
||||
*
|
||||
* Don't know if this is correct and/or compatible with
|
||||
* other implementations.
|
||||
* There is a lot of stuff going on here with phase
|
||||
* shifting, gray code, bit order for the dibit, NRZI and
|
||||
* bit-stuffing for HDLC. Plenty of opportunity for
|
||||
* misinterpreting a protocol spec or just stupid mistakes.
|
||||
*
|
||||
* References: MFJ-2400 Product description and manual:
|
||||
*
|
||||
* http://www.mfjenterprises.com/Product.php?productid=MFJ-2400
|
||||
* http://www.mfjenterprises.com/Downloads/index.php?productid=MFJ-2400&filename=MFJ-2400.pdf&company=mfj
|
||||
*
|
||||
* AEA had a 2400 bps packet modem, PK232-2400.
|
||||
*
|
||||
* http://www.repeater-builder.com/aea/pk232/pk232-2400-baud-dpsk-modem.pdf
|
||||
*
|
||||
* There was also a Kantronics KPC-2400 that had 2400 bps.
|
||||
*
|
||||
* http://www.brazoriacountyares.org/winlink-collection/TNC%20manuals/Kantronics/2400_modem_operators_guide@rgf.pdf
|
||||
*
|
||||
*
|
||||
* The MFJ and AEA both use the EXAR XR-2123 PSK modem chip.
|
||||
* The Kantronics has a P423 ???
|
||||
*
|
||||
* Can't find the chip specs on the EXAR website so Google it.
|
||||
*
|
||||
* http://www.komponenten.es.aau.dk/fileadmin/komponenten/Data_Sheet/Linear/XR2123.pdf
|
||||
*
|
||||
* The XR-2123 implements the V.26 / Bell 201 standard:
|
||||
*
|
||||
* https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-V.26-198811-I!!PDF-E&type=items
|
||||
* https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-V.26bis-198811-I!!PDF-E&type=items
|
||||
* https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-V.26ter-198811-I!!PDF-E&type=items
|
||||
*
|
||||
* "bis" and "ter" are from Latin for second and third.
|
||||
* I used the "ter" version which has phase shifts of 0, 90, 180, and 270 degrees.
|
||||
*
|
||||
* There are other references to an alternative B which uses other multiples of 45.
|
||||
* The XR-2123 data sheet mentions only multiples of 90. That's what I went with.
|
||||
*
|
||||
* The XR-2123 does not perform the scrambling as specified in V.26 so I wonder if
|
||||
* the vendors implemented it in software or just left it out.
|
||||
* I left out scrambling for now. Eventually, I'd like to get my hands on an old
|
||||
* 2400 bps TNC for compatibility testing.
|
||||
*
|
||||
* After getting QPSK working, it was not much more effort to add V.27 with 8 phases.
|
||||
*
|
||||
* https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-V.27bis-198811-I!!PDF-E&type=items
|
||||
* https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-V.27ter-198811-I!!PDF-E&type=items
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
#include "audio.h"
|
||||
#include "tune.h"
|
||||
#include "fsk_demod_state.h"
|
||||
#include "fsk_gen_filter.h"
|
||||
#include "hdlc_rec.h"
|
||||
#include "textcolor.h"
|
||||
#include "demod_psk.h"
|
||||
#include "dsp.h"
|
||||
|
||||
|
||||
/* Add sample to buffer and shift the rest down. */
|
||||
|
||||
__attribute__((hot)) __attribute__((always_inline))
|
||||
static inline void push_sample (float val, float *buff, int size)
|
||||
{
|
||||
memmove(buff+1,buff,(size-1)*sizeof(float));
|
||||
buff[0] = val;
|
||||
}
|
||||
|
||||
|
||||
/* FIR filter kernel. */
|
||||
|
||||
__attribute__((hot)) __attribute__((always_inline))
|
||||
static inline float convolve (const float *__restrict__ data, const float *__restrict__ filter, int filter_size)
|
||||
{
|
||||
float sum = 0.0;
|
||||
int j;
|
||||
|
||||
for (j=0; j<filter_size; j++) {
|
||||
sum += filter[j] * data[j];
|
||||
}
|
||||
|
||||
return (sum);
|
||||
}
|
||||
|
||||
|
||||
/* Might replace this with faster, lower precision version someday. */
|
||||
|
||||
static inline float my_atan2f (float y, float x)
|
||||
{
|
||||
if ( y == 0 && x == 0) return (0.0); // different atan2 implementations behave differently.
|
||||
|
||||
return (atan2f(y,x));
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: demod_psk_init
|
||||
*
|
||||
* Purpose: Initialization for an psk demodulator.
|
||||
* Select appropriate parameters and set up filters.
|
||||
*
|
||||
* Inputs: modem_type - MODEM_QPSK or MODEM_8PSK.
|
||||
*
|
||||
* samples_per_sec - Audio sample rate.
|
||||
*
|
||||
* bps - Bits per second.
|
||||
* Should be 2400 for V.26 but we don't enforce it.
|
||||
* The carrier frequency will be proportional.
|
||||
*
|
||||
* profile - Select different variations. For QPSK:
|
||||
*
|
||||
* P - Using self-correlation technique.
|
||||
* Q - Same preceded by bandpass filter.
|
||||
* R - Using local oscillator to derive phase.
|
||||
* S - Same with bandpass filter.
|
||||
*
|
||||
* For 8-PSK:
|
||||
*
|
||||
* T, U, V, W same as above.
|
||||
*
|
||||
* D - Pointer to demodulator state for given channel.
|
||||
*
|
||||
* Outputs: D->ms_filter_size
|
||||
*
|
||||
* Returns: None.
|
||||
*
|
||||
* Bugs: This doesn't do much error checking so don't give it
|
||||
* anything crazy.
|
||||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
void demod_psk_init (enum modem_t modem_type, int samples_per_sec, int bps, char profile, struct demodulator_state_s *D)
|
||||
{
|
||||
int correct_baud; // baud is not same as bits/sec here!
|
||||
int carrier_freq;
|
||||
int j;
|
||||
|
||||
|
||||
memset (D, 0, sizeof(struct demodulator_state_s));
|
||||
|
||||
D->modem_type = modem_type;
|
||||
D->num_slicers = 1; // Haven't thought about this yet. Is it even applicable?
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef TUNE_PROFILE
|
||||
profile = TUNE_PROFILE;
|
||||
#endif
|
||||
|
||||
if (modem_type == MODEM_QPSK) {
|
||||
|
||||
correct_baud = bps / 2;
|
||||
// Originally I thought of scaling it to the data rate,
|
||||
// e.g. 2400 bps -> 1800 Hz, but decided to make it a
|
||||
// constant since it is the same for V.26 and V.27.
|
||||
carrier_freq = 1800;
|
||||
|
||||
#if DEBUG1
|
||||
dw_printf ("demod_psk_init QPSK (sample rate=%d, bps=%d, baud=%d, carrier=%d, profile=%c\n",
|
||||
samples_per_sec, bps, correct_baud, carrier_freq, profile);
|
||||
#endif
|
||||
|
||||
switch (toupper(profile)) {
|
||||
|
||||
case 'P': /* Self correlation technique. */
|
||||
|
||||
D->use_prefilter = 0; /* No bandpass filter. */
|
||||
|
||||
D->lpf_baud = 0.60;
|
||||
D->lp_filter_len_bits = 39. * 1200. / 44100.;
|
||||
D->lp_window = BP_WINDOW_COSINE;
|
||||
|
||||
D->pll_locked_inertia = 0.95;
|
||||
D->pll_searching_inertia = 0.50;
|
||||
|
||||
break;
|
||||
|
||||
case 'Q': /* Self correlation technique. */
|
||||
|
||||
D->use_prefilter = 1; /* Add a bandpass filter. */
|
||||
D->prefilter_baud = 1.3;
|
||||
D->pre_filter_len_bits = 55. * 1200. / 44100.;
|
||||
D->pre_window = BP_WINDOW_COSINE;
|
||||
|
||||
D->lpf_baud = 0.60;
|
||||
D->lp_filter_len_bits = 39. * 1200. / 44100.;
|
||||
D->lp_window = BP_WINDOW_COSINE;
|
||||
|
||||
D->pll_locked_inertia = 0.87;
|
||||
D->pll_searching_inertia = 0.50;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Invalid demodulator profile %c for v.26 QPSK. Valid choices are P, Q, R, S. Using default.\n", profile);
|
||||
// fall thru.
|
||||
|
||||
case 'R': /* Mix with local oscillator. */
|
||||
|
||||
D->psk_use_lo = 1;
|
||||
|
||||
D->use_prefilter = 0; /* No bandpass filter. */
|
||||
|
||||
D->lpf_baud = 0.70;
|
||||
D->lp_filter_len_bits = 37. * 1200. / 44100.;
|
||||
D->lp_window = BP_WINDOW_TRUNCATED;
|
||||
|
||||
D->pll_locked_inertia = 0.925;
|
||||
D->pll_searching_inertia = 0.50;
|
||||
|
||||
break;
|
||||
|
||||
case 'S': /* Mix with local oscillator. */
|
||||
|
||||
D->psk_use_lo = 1;
|
||||
|
||||
D->use_prefilter = 1; /* Add a bandpass filter. */
|
||||
D->prefilter_baud = 0.55;
|
||||
D->pre_filter_len_bits = 74. * 1200. / 44100.;
|
||||
D->pre_window = BP_WINDOW_FLATTOP;
|
||||
|
||||
D->lpf_baud = 0.60;
|
||||
D->lp_filter_len_bits = 39. * 1200. / 44100.;
|
||||
D->lp_window = BP_WINDOW_COSINE;
|
||||
|
||||
D->pll_locked_inertia = 0.925;
|
||||
D->pll_searching_inertia = 0.50;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
D->ms_filter_len_bits = 1.25; // Delay line > 13/12 * symbol period
|
||||
|
||||
D->coffs = (int) round( (11.f / 12.f) * (float)samples_per_sec / (float)correct_baud );
|
||||
D->boffs = (int) round( (float)samples_per_sec / (float)correct_baud );
|
||||
D->soffs = (int) round( (13.f / 12.f) * (float)samples_per_sec / (float)correct_baud );
|
||||
}
|
||||
else {
|
||||
|
||||
correct_baud = bps / 3;
|
||||
carrier_freq = 1800;
|
||||
|
||||
#if DEBUG1
|
||||
dw_printf ("demod_psk_init 8-PSK (sample rate=%d, bps=%d, baud=%d, carrier=%d, profile=%c\n",
|
||||
samples_per_sec, bps, correct_baud, carrier_freq, profile);
|
||||
#endif
|
||||
|
||||
switch (toupper(profile)) {
|
||||
|
||||
|
||||
case 'T': /* Self correlation technique. */
|
||||
|
||||
D->use_prefilter = 0; /* No bandpass filter. */
|
||||
|
||||
D->lpf_baud = 1.15;
|
||||
D->lp_filter_len_bits = 32. * 1200. / 44100.;
|
||||
D->lp_window = BP_WINDOW_COSINE;
|
||||
|
||||
D->pll_locked_inertia = 0.95;
|
||||
D->pll_searching_inertia = 0.50;
|
||||
|
||||
break;
|
||||
|
||||
case 'U': /* Self correlation technique. */
|
||||
|
||||
D->use_prefilter = 1; /* Add a bandpass filter. */
|
||||
D->prefilter_baud = 0.9;
|
||||
D->pre_filter_len_bits = 21. * 1200. / 44100.;
|
||||
D->pre_window = BP_WINDOW_FLATTOP;
|
||||
|
||||
D->lpf_baud = 1.15;
|
||||
D->lp_filter_len_bits = 32. * 1200. / 44100.;
|
||||
D->lp_window = BP_WINDOW_COSINE;
|
||||
|
||||
D->pll_locked_inertia = 0.87;
|
||||
D->pll_searching_inertia = 0.50;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Invalid demodulator profile %c for v.27 8PSK. Valid choices are T, U, V, W. Using default.\n", profile);
|
||||
// fall thru.
|
||||
|
||||
case 'V': /* Mix with local oscillator. */
|
||||
|
||||
D->psk_use_lo = 1;
|
||||
|
||||
D->use_prefilter = 0; /* No bandpass filter. */
|
||||
|
||||
D->lpf_baud = 0.85;
|
||||
D->lp_filter_len_bits = 31. * 1200. / 44100.;
|
||||
D->lp_window = BP_WINDOW_COSINE;
|
||||
|
||||
D->pll_locked_inertia = 0.925;
|
||||
D->pll_searching_inertia = 0.50;
|
||||
|
||||
break;
|
||||
|
||||
case 'W': /* Mix with local oscillator. */
|
||||
|
||||
D->psk_use_lo = 1;
|
||||
|
||||
D->use_prefilter = 1; /* Add a bandpass filter. */
|
||||
D->prefilter_baud = 0.85;
|
||||
D->pre_filter_len_bits = 31. * 1200. / 44100.;
|
||||
D->pre_window = BP_WINDOW_COSINE;
|
||||
|
||||
D->lpf_baud = 0.85;
|
||||
D->lp_filter_len_bits = 31. * 1200. / 44100.;
|
||||
D->lp_window = BP_WINDOW_COSINE;
|
||||
|
||||
D->pll_locked_inertia = 0.925;
|
||||
D->pll_searching_inertia = 0.50;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
D->ms_filter_len_bits = 1.25; // Delay line > 10/9 * symbol period
|
||||
|
||||
D->coffs = (int) round( (8.f / 9.f) * (float)samples_per_sec / (float)correct_baud );
|
||||
D->boffs = (int) round( (float)samples_per_sec / (float)correct_baud );
|
||||
D->soffs = (int) round( (10.f / 9.f) * (float)samples_per_sec / (float)correct_baud );
|
||||
}
|
||||
|
||||
|
||||
if (D->psk_use_lo) {
|
||||
D->lo_step = (int) round( 256. * 256. * 256. * 256. * carrier_freq / (double)samples_per_sec);
|
||||
|
||||
assert (MAX_FILTER_SIZE >= 256);
|
||||
for (j = 0; j < 256; j++) {
|
||||
D->m_sin_table[j] = sinf(2.f * (float)M_PI * j / 256.f);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TUNE_PRE_BAUD
|
||||
D->prefilter_baud = TUNE_PRE_BAUD;
|
||||
#endif
|
||||
#ifdef TUNE_PRE_WINDOW
|
||||
D->pre_window = TUNE_PRE_WINDOW;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TUNE_LPF_BAUD
|
||||
D->lpf_baud = TUNE_LPF_BAUD;
|
||||
#endif
|
||||
#ifdef TUNE_LP_WINDOW
|
||||
D->lp_window = TUNE_LP_WINDOW;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef TUNE_HYST
|
||||
D->hysteresis = TUNE_HYST;
|
||||
#endif
|
||||
|
||||
#if defined(TUNE_PLL_SEARCHING)
|
||||
D->pll_searching_inertia = TUNE_PLL_SEARCHING;
|
||||
#endif
|
||||
#if defined(TUNE_PLL_LOCKED)
|
||||
D->pll_locked_inertia = TUNE_PLL_LOCKED;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Calculate constants used for timing.
|
||||
* The audio sample rate must be at least a few times the data rate.
|
||||
*/
|
||||
|
||||
D->pll_step_per_sample = (int) round((TICKS_PER_PLL_CYCLE * (double)correct_baud) / ((double)samples_per_sec));
|
||||
|
||||
/*
|
||||
* Convert number of symbol times to number of taps.
|
||||
*/
|
||||
|
||||
D->pre_filter_size = (int) round( D->pre_filter_len_bits * (float)samples_per_sec / (float)correct_baud );
|
||||
D->ms_filter_size = (int) round( D->ms_filter_len_bits * (float)samples_per_sec / (float)correct_baud );
|
||||
D->lp_filter_size = (int) round( D->lp_filter_len_bits * (float)samples_per_sec / (float)correct_baud );
|
||||
|
||||
|
||||
#ifdef TUNE_PRE_FILTER_SIZE
|
||||
D->pre_filter_size = TUNE_PRE_FILTER_SIZE;
|
||||
#endif
|
||||
|
||||
#ifdef TUNE_LP_FILTER_SIZE
|
||||
D->lp_filter_size = TUNE_LP_FILTER_SIZE;
|
||||
#endif
|
||||
|
||||
|
||||
if (D->pre_filter_size > MAX_FILTER_SIZE)
|
||||
{
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Calculated filter size of %d is too large.\n", D->pre_filter_size);
|
||||
dw_printf ("Decrease the audio sample rate or increase the baud rate or\n");
|
||||
dw_printf ("recompile the application with MAX_FILTER_SIZE larger than %d.\n",
|
||||
MAX_FILTER_SIZE);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (D->ms_filter_size > MAX_FILTER_SIZE)
|
||||
{
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Calculated filter size of %d is too large.\n", D->ms_filter_size);
|
||||
dw_printf ("Decrease the audio sample rate or increase the baud rate or\n");
|
||||
dw_printf ("recompile the application with MAX_FILTER_SIZE larger than %d.\n",
|
||||
MAX_FILTER_SIZE);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (D->lp_filter_size > MAX_FILTER_SIZE)
|
||||
{
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Calculated filter size of %d is too large.\n", D->pre_filter_size);
|
||||
dw_printf ("Decrease the audio sample rate or increase the baud rate or\n");
|
||||
dw_printf ("recompile the application with MAX_FILTER_SIZE larger than %d.\n",
|
||||
MAX_FILTER_SIZE);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Optionally apply a bandpass ("pre") filter to attenuate
|
||||
* frequencies outside the range of interest.
|
||||
*/
|
||||
|
||||
if (D->use_prefilter) {
|
||||
float f1, f2;
|
||||
|
||||
f1 = carrier_freq - D->prefilter_baud * correct_baud;
|
||||
f2 = carrier_freq + D->prefilter_baud * correct_baud;
|
||||
#if 0
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Generating prefilter %.0f to %.0f Hz.\n", (double)f1, (double)f2);
|
||||
#endif
|
||||
if (f1 <= 0) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Prefilter of %.0f to %.0f Hz doesn't make sense.\n", (double)f1, (double)f2);
|
||||
f1 = 10;
|
||||
}
|
||||
|
||||
f1 = f1 / (float)samples_per_sec;
|
||||
f2 = f2 / (float)samples_per_sec;
|
||||
|
||||
gen_bandpass (f1, f2, D->pre_filter, D->pre_filter_size, D->pre_window);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now the lowpass filter.
|
||||
*/
|
||||
|
||||
float fc = correct_baud * D->lpf_baud / (float)samples_per_sec;
|
||||
gen_lowpass (fc, D->lp_filter, D->lp_filter_size, D->lp_window);
|
||||
|
||||
/*
|
||||
* No point in having multiple numbers for signal level.
|
||||
*/
|
||||
|
||||
D->alevel_mark_peak = -1;
|
||||
D->alevel_space_peak = -1;
|
||||
|
||||
} /* demod_psk_init */
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: demod_psk_process_sample
|
||||
*
|
||||
* Purpose: (1) Demodulate the psk signal into I & Q components.
|
||||
* (2) Recover clock and sample data at the right time.
|
||||
* (3) Produce two bits per symbol based on phase change from previous.
|
||||
*
|
||||
* Inputs: chan - Audio channel. 0 for left, 1 for right.
|
||||
* subchan - modem of the channel.
|
||||
* sam - One sample of audio.
|
||||
* Should be in range of -32768 .. 32767.
|
||||
*
|
||||
* Outputs: For each recovered data bit, we call:
|
||||
*
|
||||
* hdlc_rec (channel, demodulated_bit);
|
||||
*
|
||||
* to decode HDLC frames from the stream of bits.
|
||||
*
|
||||
* Returns: None
|
||||
*
|
||||
* Descripion: All the literature, that I could find, described mixing
|
||||
* with a local oscillator. First we multiply the input by
|
||||
* cos and sin then low pass filter each. This gives us
|
||||
* correlation to the different phases. The signs of these two
|
||||
* results produces two data bits per symbol period.
|
||||
*
|
||||
* An 1800 Hz local oscillator was derived from the 1200 Hz
|
||||
* PLL used to sample the data.
|
||||
* This worked wonderfully for the ideal condition where
|
||||
* we start off with the proper phase and all the timing
|
||||
* is perfect. However, when random delays were added
|
||||
* before the frame, the PLL would lock on only about
|
||||
* half the time.
|
||||
*
|
||||
* Late one night, it dawned on me that there is no
|
||||
* need for a local oscillator (LO) at the carrier frequency.
|
||||
* Simply correlate the signal with the previous symbol,
|
||||
* phase shifted by + and - 45 degrees.
|
||||
* The code is much simpler and very reliable.
|
||||
*
|
||||
* Later, I realized it was not necessary to synchronize the LO
|
||||
* because we only care about the phase shift between symbols.
|
||||
*
|
||||
* This works better under noisy conditions because we are
|
||||
* including the noise from only the current symbol and not
|
||||
* the previous one.
|
||||
*
|
||||
* Finally, once we know how to distinguish 4 different phases,
|
||||
* it is not much effort to use 8 phases to double the bit rate.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
||||
inline static void nudge_pll (int chan, int subchan, int slice, int demod_bits, struct demodulator_state_s *D);
|
||||
|
||||
__attribute__((hot))
|
||||
void demod_psk_process_sample (int chan, int subchan, int sam, struct demodulator_state_s *D)
|
||||
{
|
||||
float fsam;
|
||||
float sam_x_cos, sam_x_sin;
|
||||
float I, Q;
|
||||
int demod_phase_shift; // Phase shift relative to previous symbol.
|
||||
// range 0-3, 1 unit for each 90 degrees.
|
||||
int slice = 0;
|
||||
|
||||
#if DEBUG4
|
||||
static FILE *demod_log_fp = NULL;
|
||||
static int log_file_seq = 0; /* Part of log file name */
|
||||
#endif
|
||||
|
||||
|
||||
assert (chan >= 0 && chan < MAX_CHANS);
|
||||
assert (subchan >= 0 && subchan < MAX_SUBCHANS);
|
||||
|
||||
|
||||
/* Scale to nice number for plotting during debug. */
|
||||
|
||||
fsam = sam / 16384.0f;
|
||||
|
||||
|
||||
/*
|
||||
* Optional bandpass filter before the phase detector.
|
||||
*/
|
||||
|
||||
if (D->use_prefilter) {
|
||||
push_sample (fsam, D->raw_cb, D->pre_filter_size);
|
||||
fsam = convolve (D->raw_cb, D->pre_filter, D->pre_filter_size);
|
||||
}
|
||||
|
||||
if (D->psk_use_lo) {
|
||||
float a, delta;
|
||||
int id;
|
||||
/*
|
||||
* Mix with local oscillator to obtain phase.
|
||||
* The absolute phase doesn't matter.
|
||||
* We are just concerned with the change since the previous symbol.
|
||||
*/
|
||||
|
||||
sam_x_cos = fsam * D->m_sin_table[((D->lo_phase >> 24) + 64) & 0xff];
|
||||
|
||||
sam_x_sin = fsam * D->m_sin_table[(D->lo_phase >> 24) & 0xff];
|
||||
|
||||
push_sample (sam_x_cos, D->m_amp_cb, D->lp_filter_size);
|
||||
I = convolve (D->m_amp_cb, D->lp_filter, D->lp_filter_size);
|
||||
|
||||
push_sample (sam_x_sin, D->s_amp_cb, D->lp_filter_size);
|
||||
Q = convolve (D->s_amp_cb, D->lp_filter, D->lp_filter_size);
|
||||
|
||||
a = my_atan2f(I,Q);
|
||||
push_sample (a, D->ms_in_cb, D->ms_filter_size);
|
||||
|
||||
delta = a - D->ms_in_cb[D->boffs];
|
||||
|
||||
/* 256 units/cycle makes modulo processing easier. */
|
||||
/* Make sure it is positive before truncating to integer. */
|
||||
|
||||
id = ((int)((delta / (2.f * (float)M_PI) + 1.f) * 256.f)) & 0xff;
|
||||
|
||||
if (D->modem_type == MODEM_QPSK) {
|
||||
demod_phase_shift = ((id + 32) >> 6) & 0x3;
|
||||
}
|
||||
else {
|
||||
demod_phase_shift = ((id + 16) >> 5) & 0x7;
|
||||
}
|
||||
nudge_pll (chan, subchan, slice, demod_phase_shift, D);
|
||||
|
||||
D->lo_phase += D->lo_step;
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Correlate with previous symbol. We are looking for the phase shift.
|
||||
*/
|
||||
push_sample (fsam, D->ms_in_cb, D->ms_filter_size);
|
||||
|
||||
sam_x_cos = fsam * D->ms_in_cb[D->coffs];
|
||||
sam_x_sin = fsam * D->ms_in_cb[D->soffs];
|
||||
|
||||
push_sample (sam_x_cos, D->m_amp_cb, D->lp_filter_size);
|
||||
I = convolve (D->m_amp_cb, D->lp_filter, D->lp_filter_size);
|
||||
|
||||
push_sample (sam_x_sin, D->s_amp_cb, D->lp_filter_size);
|
||||
Q = convolve (D->s_amp_cb, D->lp_filter, D->lp_filter_size);
|
||||
|
||||
if (D->modem_type == MODEM_QPSK) {
|
||||
|
||||
#if 1 // Speed up special case.
|
||||
if (I > 0) {
|
||||
if (Q > 0)
|
||||
demod_phase_shift = 0; /* 0 to 90 degrees, etc. */
|
||||
else
|
||||
demod_phase_shift = 1;
|
||||
}
|
||||
else {
|
||||
if (Q > 0)
|
||||
demod_phase_shift = 3;
|
||||
else
|
||||
demod_phase_shift = 2;
|
||||
}
|
||||
#else
|
||||
a = my_atan2f(I,Q);
|
||||
int id = ((int)((a / (2.f * (float)M_PI) + 1.f) * 256.f)) & 0xff;
|
||||
// 128 compensates for 180 degree phase shift due
|
||||
// to 1 1/2 carrier cycles per symbol period.
|
||||
demod_phase_shift = ((id + 128) >> 6) & 0x3;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
float a;
|
||||
int idelta;
|
||||
|
||||
a = my_atan2f(I,Q);
|
||||
idelta = ((int)((a / (2.f * (float)M_PI) + 1.f) * 256.f)) & 0xff;
|
||||
// 32 (90 degrees) compensates for 1800 carrier vs. 1800 baud.
|
||||
// 16 is to set threshold between constellation points.
|
||||
demod_phase_shift = ((idelta - 32 - 16) >> 5) & 0x7;
|
||||
}
|
||||
|
||||
nudge_pll (chan, subchan, slice, demod_phase_shift, D);
|
||||
}
|
||||
|
||||
#if DEBUG4
|
||||
|
||||
if (chan == 0) {
|
||||
|
||||
if (1) {
|
||||
//if (hdlc_rec_gathering (chan, subchan, slice)) {
|
||||
char fname[30];
|
||||
|
||||
|
||||
if (demod_log_fp == NULL) {
|
||||
log_file_seq++;
|
||||
snprintf (fname, sizeof(fname), "demod/%04d.csv", log_file_seq);
|
||||
//if (log_file_seq == 1) mkdir ("demod", 0777);
|
||||
if (log_file_seq == 1) mkdir ("demod");
|
||||
|
||||
demod_log_fp = fopen (fname, "w");
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Starting demodulator log file %s\n", fname);
|
||||
fprintf (demod_log_fp, "Audio, sin, cos, *cos, *sin, I, Q, phase, Clock\n");
|
||||
}
|
||||
|
||||
fprintf (demod_log_fp, "%.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.2f, %.2f, %.2f\n",
|
||||
fsam + 2,
|
||||
- D->ms_in_cb[D->soffs] + 6,
|
||||
- D->ms_in_cb[D->coffs] + 6,
|
||||
sam_x_cos + 8,
|
||||
sam_x_sin + 10,
|
||||
2 * I + 12,
|
||||
2 * Q + 12,
|
||||
demod_phase_shift * 2. / 3. + 14.,
|
||||
(D->slicer[slice].data_clock_pll & 0x80000000) ? .5 : .0);
|
||||
|
||||
fflush (demod_log_fp);
|
||||
}
|
||||
else {
|
||||
if (demod_log_fp != NULL) {
|
||||
fclose (demod_log_fp);
|
||||
demod_log_fp = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
} /* end demod_psk_process_sample */
|
||||
|
||||
static const int phase_to_gray_v26[4] = {0, 1, 3, 2};
|
||||
static const int phase_to_gray_v27[8] = {1, 0, 2, 3, 7, 6, 4, 5};
|
||||
|
||||
|
||||
|
||||
__attribute__((hot))
|
||||
inline static void nudge_pll (int chan, int subchan, int slice, int demod_bits, struct demodulator_state_s *D)
|
||||
{
|
||||
|
||||
/*
|
||||
* Finally, a PLL is used to sample near the centers of the data bits.
|
||||
*
|
||||
* D points to a demodulator for a channel/subchannel pair so we don't
|
||||
* have to keep recalculating it.
|
||||
*
|
||||
* D->data_clock_pll is a SIGNED 32 bit variable.
|
||||
* When it overflows from a large positive value to a negative value, we
|
||||
* sample a data bit from the demodulated signal.
|
||||
*
|
||||
* Ideally, the the demodulated signal transitions should be near
|
||||
* zero we we sample mid way between the transitions.
|
||||
*
|
||||
* Nudge the PLL by removing some small fraction from the value of
|
||||
* data_clock_pll, pushing it closer to zero.
|
||||
*
|
||||
* This adjustment will never change the sign so it won't cause
|
||||
* any erratic data bit sampling.
|
||||
*
|
||||
* If we adjust it too quickly, the clock will have too much jitter.
|
||||
* If we adjust it too slowly, it will take too long to lock on to a new signal.
|
||||
*
|
||||
* Be a little more agressive about adjusting the PLL
|
||||
* phase when searching for a signal.
|
||||
* Don't change it as much when locked on to a signal.
|
||||
*
|
||||
* I don't think the optimal value will depend on the audio sample rate
|
||||
* because this happens for each transition from the demodulator.
|
||||
*/
|
||||
|
||||
|
||||
D->slicer[slice].prev_d_c_pll = D->slicer[slice].data_clock_pll;
|
||||
|
||||
D->slicer[slice].data_clock_pll += D->pll_step_per_sample;
|
||||
|
||||
if (D->slicer[slice].data_clock_pll < 0 && D->slicer[slice].prev_d_c_pll >= 0) {
|
||||
|
||||
/* Overflow of PLL counter. */
|
||||
/* This is where we sample the data. */
|
||||
|
||||
if (D->modem_type == MODEM_QPSK) {
|
||||
|
||||
int gray = phase_to_gray_v26[ demod_bits ];
|
||||
|
||||
#if DEBUG4
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
|
||||
dw_printf ("a=%.2f deg, delta=%.2f deg, phaseshift=%d, bits= %d %d \n",
|
||||
a * 360 / (2*M_PI), delta * 360 / (2*M_PI), demod_bits, (gray >> 1) & 1, gray & 1);
|
||||
|
||||
//dw_printf ("phaseshift=%d, bits= %d %d \n", demod_bits, (gray >> 1) & 1, gray & 1);
|
||||
#endif
|
||||
hdlc_rec_bit (chan, subchan, slice, (gray >> 1) & 1, 0, -1);
|
||||
hdlc_rec_bit (chan, subchan, slice, gray & 1, 0, -1);
|
||||
}
|
||||
else {
|
||||
int gray = phase_to_gray_v27[ demod_bits ];
|
||||
|
||||
hdlc_rec_bit (chan, subchan, slice, (gray >> 2) & 1, 0, -1);
|
||||
hdlc_rec_bit (chan, subchan, slice, (gray >> 1) & 1, 0, -1);
|
||||
hdlc_rec_bit (chan, subchan, slice, gray & 1, 0, -1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If demodulated data has changed,
|
||||
* Pull the PLL phase closer to zero.
|
||||
* Use "floor" instead of simply casting so the sign won't flip.
|
||||
* For example if we had -0.7 we want to end up with -1 rather than 0.
|
||||
*/
|
||||
|
||||
// TODO: demod_9600 has an improved technique. Would it help us here?
|
||||
|
||||
if (demod_bits != D->slicer[slice].prev_demod_data) {
|
||||
|
||||
if (hdlc_rec_gathering (chan, subchan, slice)) {
|
||||
D->slicer[slice].data_clock_pll = (int)floorf((float)(D->slicer[slice].data_clock_pll) * D->pll_locked_inertia);
|
||||
}
|
||||
else {
|
||||
D->slicer[slice].data_clock_pll = (int)floorf((float)(D->slicer[slice].data_clock_pll) * D->pll_searching_inertia);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Remember demodulator output so we can compare next time.
|
||||
*/
|
||||
D->slicer[slice].prev_demod_data = demod_bits;
|
||||
|
||||
} /* end nudge_pll */
|
||||
|
||||
|
||||
|
||||
/* end demod_psk.c */
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
/* demod_psk.h */
|
||||
|
||||
|
||||
void demod_psk_init (enum modem_t modem_type, int samples_per_sec, int bps, char profile, struct demodulator_state_s *D);
|
||||
|
||||
void demod_psk_process_sample (int chan, int subchan, int sam, struct demodulator_state_s *D);
|
163
digipeater.c
163
digipeater.c
|
@ -23,6 +23,7 @@
|
|||
* Name: digipeater.c
|
||||
*
|
||||
* Purpose: Act as an APRS digital repeater.
|
||||
* Similar cdigipeater.c is for connected mode.
|
||||
*
|
||||
*
|
||||
* Description: Decide whether the specified packet should
|
||||
|
@ -53,6 +54,7 @@
|
|||
|
||||
#define DIGIPEATER_C
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -62,7 +64,6 @@
|
|||
#include "regex.h"
|
||||
#include <sys/unistd.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "digipeater.h"
|
||||
#include "textcolor.h"
|
||||
|
@ -74,8 +75,6 @@
|
|||
static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, char *mycall_xmit,
|
||||
regex_t *uidigi, regex_t *uitrace, int to_chan, enum preempt_e preempt, char *type_filter);
|
||||
|
||||
//static int filter_by_type (char *source, char *infop, char *type_filter);
|
||||
|
||||
|
||||
/*
|
||||
* Keep pointer to configuration options.
|
||||
|
@ -87,6 +86,18 @@ static struct audio_s *save_audio_config_p;
|
|||
static struct digi_config_s *save_digi_config_p;
|
||||
|
||||
|
||||
/*
|
||||
* Maintain count of packets digipeated for each combination of from/to channel.
|
||||
*/
|
||||
|
||||
static int digi_count[MAX_CHANS][MAX_CHANS];
|
||||
|
||||
int digipeater_get_count (int from_chan, int to_chan) {
|
||||
return (digi_count[from_chan][to_chan]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: digipeater_init
|
||||
|
@ -165,6 +176,7 @@ void digipeater (int from_chan, packet_t pp)
|
|||
if (result != NULL) {
|
||||
dedupe_remember (pp, to_chan);
|
||||
tq_append (to_chan, TQ_PRIO_0_HI, result);
|
||||
digi_count[from_chan][to_chan]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,6 +202,7 @@ void digipeater (int from_chan, packet_t pp)
|
|||
if (result != NULL) {
|
||||
dedupe_remember (pp, to_chan);
|
||||
tq_append (to_chan, TQ_PRIO_1_LO, result);
|
||||
digi_count[from_chan][to_chan]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -243,6 +256,10 @@ void digipeater (int from_chan, packet_t pp)
|
|||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
#define OBSOLETE14 1
|
||||
|
||||
|
||||
#ifndef OBSOLETE14
|
||||
static char *dest_ssid_path[16] = {
|
||||
"", /* Use VIA path */
|
||||
"WIDE1-1",
|
||||
|
@ -260,11 +277,13 @@ static char *dest_ssid_path[16] = {
|
|||
"WIDE2-2", /* South */
|
||||
"WIDE2-2", /* East */
|
||||
"WIDE2-2" }; /* West */
|
||||
#endif
|
||||
|
||||
|
||||
static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, char *mycall_xmit,
|
||||
regex_t *alias, regex_t *wide, int to_chan, enum preempt_e preempt, char *filter_str)
|
||||
{
|
||||
char source[AX25_MAX_ADDR_LEN];
|
||||
int ssid;
|
||||
int r;
|
||||
char repeater[AX25_MAX_ADDR_LEN];
|
||||
|
@ -274,19 +293,9 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
/*
|
||||
* First check if filtering has been configured.
|
||||
*/
|
||||
|
||||
|
||||
if (filter_str != NULL) {
|
||||
|
||||
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);
|
||||
//#endif
|
||||
if (pfilter(from_chan, to_chan, filter_str, pp, 1) != 1) {
|
||||
return(NULL);
|
||||
}
|
||||
}
|
||||
|
@ -310,11 +319,15 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
* Otherwise we don't want to modify the input because this could be called multiple times.
|
||||
*/
|
||||
|
||||
#ifndef OBSOLETE14 // Took it out in 1.4
|
||||
|
||||
if (ax25_get_num_repeaters(pp) == 0 && (ssid = ax25_get_ssid(pp, AX25_DESTINATION)) > 0) {
|
||||
ax25_set_addr(pp, AX25_REPEATER_1, dest_ssid_path[ssid]);
|
||||
ax25_set_ssid(pp, AX25_DESTINATION, 0);
|
||||
/* Continue with general case, below. */
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Find the first repeater station which doesn't have "has been repeated" set.
|
||||
|
@ -337,10 +350,13 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
|
||||
|
||||
/*
|
||||
* First check for explicit use of my call.
|
||||
* First check for explicit use of my call, including SSID.
|
||||
* Someone might explicitly specify a particular path for testing purposes.
|
||||
* This will bypass the usual checks for duplicates and my call in the source.
|
||||
*
|
||||
* In this case, we don't check the history so it would be possible
|
||||
* to have a loop (of limited size) if someone constructed the digipeater paths
|
||||
* correctly.
|
||||
* correctly. I would expect it only for testing purposes.
|
||||
*/
|
||||
|
||||
if (strcmp(repeater, mycall_rec) == 0) {
|
||||
|
@ -356,13 +372,24 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't digipeat my own. Fixed in 1.4 dev H.
|
||||
* Alternatively we might feed everything transmitted into
|
||||
* dedupe_remember rather than only frames out of digipeater.
|
||||
*/
|
||||
ax25_get_addr_with_ssid(pp, AX25_SOURCE, source);
|
||||
if (strcmp(source, mycall_rec) == 0) {
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Next try to avoid retransmitting redundant information.
|
||||
* Duplicates are detected by comparing only:
|
||||
* - source
|
||||
* - destination
|
||||
* - info part
|
||||
* - but none of the digipeaters
|
||||
* - but not the via path. (digipeater addresses)
|
||||
* A history is kept for some amount of time, typically 30 seconds.
|
||||
* For efficiency, only a checksum, rather than the complete fields
|
||||
* might be kept but the result is the same.
|
||||
|
@ -371,7 +398,6 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
*
|
||||
*/
|
||||
|
||||
|
||||
if (dedupe_check(pp, to_chan)) {
|
||||
//#if DEBUG
|
||||
/* Might be useful if people are wondering why */
|
||||
|
@ -385,7 +411,10 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
|
||||
/*
|
||||
* For the alias pattern, we unconditionally digipeat it once.
|
||||
* i.e. Just replace it with MYCALL don't even look at the ssid.
|
||||
* i.e. Just replace it with MYCALL.
|
||||
*
|
||||
* My call should be an implied member of this set.
|
||||
* In this implementation, we already caught it further up.
|
||||
*/
|
||||
err = regexec(alias,repeater,0,NULL,0);
|
||||
if (err == 0) {
|
||||
|
@ -584,13 +613,11 @@ static int failed;
|
|||
|
||||
static enum preempt_e preempt = PREEMPT_OFF;
|
||||
|
||||
static char typefilter[20] = "";
|
||||
|
||||
|
||||
static void test (char *in, char *out)
|
||||
{
|
||||
packet_t pp, result;
|
||||
//int should_repeat;
|
||||
char rec[256];
|
||||
char xmit[256];
|
||||
unsigned char *pinfo;
|
||||
|
@ -610,6 +637,7 @@ static void test (char *in, char *out)
|
|||
|
||||
ax25_format_addrs (pp, rec);
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
(void)info_len;
|
||||
strlcat (rec, (char*)pinfo, sizeof(rec));
|
||||
|
||||
if (strcmp(in, rec) != 0) {
|
||||
|
@ -784,17 +812,22 @@ int main (int argc, char *argv[])
|
|||
|
||||
|
||||
/*
|
||||
* Change destination SSID to normal digipeater if none specified.
|
||||
* Change destination SSID to normal digipeater if none specified. (Obsolete, removed.)
|
||||
*/
|
||||
test ( "W1ABC>TEST-3:",
|
||||
"W1ABC>TEST,WB2OSZ-9*,WIDE3-2:");
|
||||
|
||||
test ( "W1ABC>TEST-3:",
|
||||
#ifndef OBSOLETE14
|
||||
"W1ABC>TEST,WB2OSZ-9*,WIDE3-2:");
|
||||
#else
|
||||
"");
|
||||
#endif
|
||||
test ( "W1DEF>TEST-3,WIDE2-2:",
|
||||
"W1DEF>TEST-3,WB2OSZ-9*,WIDE2-1:");
|
||||
|
||||
/*
|
||||
* Drop duplicates within specified time interval.
|
||||
* Only the first 1 of 3 should be retransmitted.
|
||||
* The 4th case might be controversial.
|
||||
*/
|
||||
|
||||
test ( "W1XYZ>TEST,R1*,WIDE3-2:info1",
|
||||
|
@ -806,6 +839,10 @@ int main (int argc, char *argv[])
|
|||
test ( "W1XYZ>TEST,R3*,WIDE3-2:info1",
|
||||
"");
|
||||
|
||||
test ( "W1XYZ>TEST,R1*,WB2OSZ-9:has explicit routing",
|
||||
"W1XYZ>TEST,R1,WB2OSZ-9*:has explicit routing");
|
||||
|
||||
|
||||
/*
|
||||
* Allow same thing after adequate time.
|
||||
*/
|
||||
|
@ -863,73 +900,25 @@ int main (int argc, char *argv[])
|
|||
"");
|
||||
|
||||
|
||||
#if 0 /* changed strategy */
|
||||
/*
|
||||
* New in version 1.2.
|
||||
*/
|
||||
|
||||
|
||||
// no filter.
|
||||
if (filter_by_type ("CWAPID", ":NWS-TTTTT:DDHHMMz,ADVISETYPE,zcs{seq#", "") != 1)
|
||||
{ text_color_set(DW_COLOR_ERROR); dw_printf ("filter_by_type case 1\n"); failed++; }
|
||||
|
||||
// message should not match psqt
|
||||
if (filter_by_type ("CWAPID", ":NWS-TTTTT:DDHHMMz,ADVISETYPE,zcs{seq#", "pqst") != 0)
|
||||
{ text_color_set(DW_COLOR_ERROR); dw_printf ("filter_by_type case 2\n"); failed++; }
|
||||
|
||||
// This should match position
|
||||
if (filter_by_type ("N3LEE-7", "`cHDl <0x1c>[/\"5j}", "qstp") != 1)
|
||||
{ text_color_set(DW_COLOR_ERROR); dw_printf ("filter_by_type case 3\n"); failed++; }
|
||||
|
||||
// This should match nws
|
||||
if (filter_by_type ("CWAPID", ":NWS-TTTTT:DDHHMMz,ADVISETYPE,zcs{seq#", "n") != 1)
|
||||
{ text_color_set(DW_COLOR_ERROR); dw_printf ("filter_by_type case 4\n"); failed++; }
|
||||
|
||||
// But not this.
|
||||
if (filter_by_type ("CWAPID", ":zzz-TTTTT:DDHHMMz,ADVISETYPE,zcs{seq#", "n") != 0)
|
||||
{ text_color_set(DW_COLOR_ERROR); dw_printf ("filter_by_type case 5\n"); failed++; }
|
||||
|
||||
// This should match nws
|
||||
if (filter_by_type ("CWAPID", ";CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", "n") != 1)
|
||||
{ text_color_set(DW_COLOR_ERROR); dw_printf ("filter_by_type case 6\n"); failed++; }
|
||||
|
||||
// But not this due do addressee prefix mismatch
|
||||
if (filter_by_type ("CWAPID", ";NWSttttz *DDHHMMzLATLONICONADVISETYPE{seq#", "n") != 0)
|
||||
{ text_color_set(DW_COLOR_ERROR); dw_printf ("filter_by_type case 7\n"); failed++; }
|
||||
|
||||
|
||||
/*
|
||||
* Filtering integrated with rest of process...
|
||||
*/
|
||||
|
||||
strlcpy (typefilter, "w", sizeof(typefilter));
|
||||
|
||||
test ( "N8VIM>APN391,WIDE2-1:$ULTW00000000010E097D2884FFF389DC000102430002033400000000",
|
||||
"N8VIM>APN391,WB2OSZ-9*:$ULTW00000000010E097D2884FFF389DC000102430002033400000000");
|
||||
|
||||
test ( "AB1OC-10>APWW10,WIDE1-1,WIDE2-1:>FN42er/# Hollis, NH iGate Operational",
|
||||
"");
|
||||
|
||||
strlcpy (typefilter, "s", sizeof(typefilter));
|
||||
|
||||
test ( "AB1OC-10>APWW10,WIDE1-1,WIDE2-1:>FN42er/# Hollis, NH iGate Operational",
|
||||
"AB1OC-10>APWW10,WB2OSZ-9*,WIDE2-1:>FN42er/# Hollis, NH iGate Operational");
|
||||
|
||||
test ( "K1ABC-9>TR4R8R,WIDE1-1:`c6LlIb>/`\"4K}_%",
|
||||
"");
|
||||
|
||||
strlcpy (typefilter, "up", sizeof(typefilter));
|
||||
|
||||
test ( "K1ABC-9>TR4R8R,WIDE1-1:`c6LlIb>/`\"4K}_%",
|
||||
"K1ABC-9>TR4R8R,WB2OSZ-9*:`c6LlIb>/`\"4K}_%");
|
||||
|
||||
strlcpy (typefilter, "", sizeof(typefilter));
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Did I miss any cases?
|
||||
* Yes. Don't retransmit my own. 1.4H
|
||||
*/
|
||||
|
||||
test ( "WB2OSZ-7>TEST14,WIDE1-1,WIDE1-1:stuff",
|
||||
"WB2OSZ-7>TEST14,WB2OSZ-9*,WIDE1-1:stuff");
|
||||
|
||||
test ( "WB2OSZ-9>TEST14,WIDE1-1,WIDE1-1:from myself",
|
||||
"");
|
||||
|
||||
test ( "WB2OSZ-9>TEST14,WIDE1-1*,WB2OSZ-9:from myself but explicit routing",
|
||||
"WB2OSZ-9>TEST14,WIDE1-1,WB2OSZ-9*:from myself but explicit routing");
|
||||
|
||||
test ( "WB2OSZ-15>TEST14,WIDE1-1,WIDE1-1:stuff",
|
||||
"WB2OSZ-15>TEST14,WB2OSZ-9*,WIDE1-1:stuff");
|
||||
|
||||
|
||||
|
||||
if (failed == 0) {
|
||||
dw_printf ("SUCCESS -- All digipeater tests passed.\n");
|
||||
}
|
||||
|
|
|
@ -65,6 +65,11 @@ extern void digipeater (int from_chan, packet_t pp);
|
|||
void digi_regen (int from_chan, packet_t pp);
|
||||
|
||||
|
||||
/* Make statistics available. */
|
||||
|
||||
int digipeater_get_count (int from_chan, int to_chan);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/* end digipeater.h */
|
||||
|
|
237
direwolf.c
237
direwolf.c
|
@ -35,6 +35,13 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
|
||||
#define DIREWOLF_C 1
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -72,9 +79,7 @@
|
|||
#endif
|
||||
|
||||
|
||||
#define DIREWOLF_C 1
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "version.h"
|
||||
#include "audio.h"
|
||||
#include "config.h"
|
||||
|
@ -83,29 +88,34 @@
|
|||
#include "hdlc_rec.h"
|
||||
#include "hdlc_rec2.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "xid.h"
|
||||
#include "decode_aprs.h"
|
||||
#include "textcolor.h"
|
||||
#include "server.h"
|
||||
#include "kiss.h"
|
||||
#include "kissnet.h"
|
||||
#include "kiss_frame.h"
|
||||
#include "nmea.h"
|
||||
#include "waypoint.h"
|
||||
#include "gen_tone.h"
|
||||
#include "digipeater.h"
|
||||
#include "cdigipeater.h"
|
||||
#include "tq.h"
|
||||
#include "xmit.h"
|
||||
#include "ptt.h"
|
||||
#include "beacon.h"
|
||||
#include "redecode.h"
|
||||
#include "dtmf.h"
|
||||
#include "aprs_tt.h"
|
||||
#include "tt_user.h"
|
||||
#include "igate.h"
|
||||
#include "pfilter.h"
|
||||
#include "symbols.h"
|
||||
#include "dwgps.h"
|
||||
#include "waypoint.h"
|
||||
#include "log.h"
|
||||
#include "recv.h"
|
||||
#include "morse.h"
|
||||
#include "mheard.h"
|
||||
#include "ax25_link.h"
|
||||
|
||||
|
||||
//static int idx_decoded = 0;
|
||||
|
@ -152,14 +162,19 @@ static void __cpuid(int cpuinfo[4], int infotype){
|
|||
|
||||
static struct audio_s audio_config;
|
||||
static struct tt_config_s tt_config;
|
||||
struct digi_config_s digi_config;
|
||||
//struct digi_config_s digi_config;
|
||||
//struct cdigi_config_s cdigi_config;
|
||||
|
||||
static const int audio_amplitude = 100; /* % of audio sample range. */
|
||||
/* This translates to +-32k for 16 bit samples. */
|
||||
/* Currently no option to change this. */
|
||||
|
||||
static int d_u_opt = 0; /* "-d u" command line option to print UTF-8 also in hexadecimal. */
|
||||
static int d_p_opt = 0; /* "-d p" option for dumping packets over radio. */
|
||||
|
||||
static int q_h_opt = 0; /* "-q h" Quiet, suppress the "heard" line with audio level. */
|
||||
static int q_d_opt = 0; /* "-q d" Quiet, suppress the decoding of APRS packets. */
|
||||
static int q_d_opt = 0; /* "-q d" Quiet, suppress the printing of decoded of APRS packets. */
|
||||
|
||||
|
||||
|
||||
static struct misc_config_s misc_config;
|
||||
|
@ -174,6 +189,7 @@ int main (int argc, char *argv[])
|
|||
int xmit_calibrate_option = 0;
|
||||
int enable_pseudo_terminal = 0;
|
||||
struct digi_config_s digi_config;
|
||||
struct cdigi_config_s cdigi_config;
|
||||
struct igate_config_s igate_config;
|
||||
int r_opt = 0, n_opt = 0, b_opt = 0, B_opt = 0, D_opt = 0; /* Command line options. */
|
||||
char P_opt[16];
|
||||
|
@ -189,9 +205,13 @@ int main (int argc, char *argv[])
|
|||
int d_g_opt = 0; /* "-d g" option for GPS. Can be repeated for more detail. */
|
||||
int d_o_opt = 0; /* "-d o" option for output control such as PTT and DCD. */
|
||||
int d_i_opt = 0; /* "-d i" option for IGate. Repeat for more detail */
|
||||
int d_m_opt = 0; /* "-d m" option for mheard list. */
|
||||
int d_f_opt = 0; /* "-d f" option for filtering. Repeat for more detail. */
|
||||
#if USE_HAMLIB
|
||||
int d_h_opt = 0; /* "-d h" option for hamlib debugging. Repeat for more detail */
|
||||
#endif
|
||||
int E_tx_opt = 0; /* "-E n" Error rate % for clobbering trasmit frames. */
|
||||
int E_rx_opt = 0; /* "-E Rn" Error rate % for clobbering receive frames. */
|
||||
|
||||
strlcpy(l_opt, "", sizeof(l_opt));
|
||||
strlcpy(P_opt, "", sizeof(P_opt));
|
||||
|
@ -234,10 +254,13 @@ int main (int argc, char *argv[])
|
|||
// TODO: control development/beta/release by version.h instead of changing here.
|
||||
// Print platform. This will provide more information when people send a copy the information displayed.
|
||||
|
||||
// Might want to print OS version here. For Windows, see:
|
||||
// https://msdn.microsoft.com/en-us/library/ms724451(v=VS.85).aspx
|
||||
|
||||
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, "K", __DATE__);
|
||||
//dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "H", __DATE__);
|
||||
dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
|
||||
|
||||
#if defined(ENABLE_GPSD) || defined(USE_HAMLIB)
|
||||
|
@ -322,7 +345,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:Sa:",
|
||||
c = getopt_long(argc, argv, "P:B:D:c:pxr:b:n:d:q:t:Ul:Sa:E:",
|
||||
long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
@ -367,9 +390,9 @@ int main (int argc, char *argv[])
|
|||
case 'B': /* -B baud rate and modem properties. */
|
||||
|
||||
B_opt = atoi(optarg);
|
||||
if (B_opt < 100 || B_opt > 10000) {
|
||||
if (B_opt < MIN_BAUD || B_opt > MAX_BAUD) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Use a more reasonable data baud rate in range of 100 - 10000.\n");
|
||||
dw_printf ("Use a more reasonable data baud rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
|
@ -451,15 +474,17 @@ int main (int argc, char *argv[])
|
|||
// separate out gps & waypoints.
|
||||
|
||||
case 'g': d_g_opt++; break;
|
||||
case 'w': waypoint_set_debug (1); break; // not documented yet.
|
||||
case 't': d_t_opt++; beacon_tracker_set_debug (d_t_opt); break;
|
||||
|
||||
case 'w': nmea_set_debug (1); break; // not documented yet.
|
||||
case 'p': d_p_opt = 1; break; // TODO: packet dump for xmit side.
|
||||
case 'o': d_o_opt++; ptt_set_debug(d_o_opt); break;
|
||||
case 'i': d_i_opt++; break;
|
||||
case 'm': d_m_opt++; break;
|
||||
case 'f': d_f_opt++; break;
|
||||
#if AX25MEMDEBUG
|
||||
case 'm': ax25memdebug_set(); break; // Track down memory leak. Not documented.
|
||||
#endif
|
||||
case 'l': ax25memdebug_set(); break; // Track down memory Leak. Not documented.
|
||||
#endif // Previously 'm' but that is now used for mheard.
|
||||
#if USE_HAMLIB
|
||||
case 'h': d_h_opt++; break; // Hamlib verbose level.
|
||||
#endif
|
||||
|
@ -508,6 +533,27 @@ int main (int argc, char *argv[])
|
|||
exit (0);
|
||||
break;
|
||||
|
||||
case 'E': /* -E Error rate (%) for corrupting frames. */
|
||||
/* Just a number is transmit. Precede by R for receive. */
|
||||
|
||||
if (*optarg == 'r' || *optarg == 'R') {
|
||||
E_rx_opt = atoi(optarg+1);
|
||||
if (E_rx_opt < 1 || E_rx_opt > 99) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("-ER must be in range of 1 to 99.\n");
|
||||
E_rx_opt = 10;
|
||||
}
|
||||
}
|
||||
else {
|
||||
E_tx_opt = atoi(optarg);
|
||||
if (E_tx_opt < 1 || E_tx_opt > 99) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("-E must be in range of 1 to 99.\n");
|
||||
E_tx_opt = 10;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
/* Should not be here. */
|
||||
|
@ -542,7 +588,7 @@ int main (int argc, char *argv[])
|
|||
|
||||
symbols_init ();
|
||||
|
||||
config_init (config_file, &audio_config, &digi_config, &tt_config, &igate_config, &misc_config);
|
||||
config_init (config_file, &audio_config, &digi_config, &cdigi_config, &tt_config, &igate_config, &misc_config);
|
||||
|
||||
if (r_opt != 0) {
|
||||
audio_config.adev[0].samples_per_sec = r_opt;
|
||||
|
@ -559,22 +605,43 @@ int main (int argc, char *argv[])
|
|||
if (B_opt != 0) {
|
||||
audio_config.achan[0].baud = B_opt;
|
||||
|
||||
/* We have similar logic in direwolf.c, config.c, gen_packets.c, and atest.c, */
|
||||
/* that need to be kept in sync. Maybe it could be a common function someday. */
|
||||
|
||||
if (audio_config.achan[0].baud < 600) {
|
||||
audio_config.achan[0].modem_type = MODEM_AFSK;
|
||||
audio_config.achan[0].mark_freq = 1600;
|
||||
audio_config.achan[0].mark_freq = 1600; // Typical for HF SSB.
|
||||
audio_config.achan[0].space_freq = 1800;
|
||||
audio_config.achan[0].decimate = 3;
|
||||
audio_config.achan[0].decimate = 3; // Reduce CPU load.
|
||||
}
|
||||
else if (audio_config.achan[0].baud > 2400) {
|
||||
else if (audio_config.achan[0].baud < 1800) {
|
||||
audio_config.achan[0].modem_type = MODEM_AFSK;
|
||||
audio_config.achan[0].mark_freq = DEFAULT_MARK_FREQ;
|
||||
audio_config.achan[0].space_freq = DEFAULT_SPACE_FREQ;
|
||||
}
|
||||
else if (audio_config.achan[0].baud < 3600) {
|
||||
audio_config.achan[0].modem_type = MODEM_QPSK;
|
||||
audio_config.achan[0].mark_freq = 0;
|
||||
audio_config.achan[0].space_freq = 0;
|
||||
if (audio_config.achan[0].baud != 2400) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Bit rate should be standard 2400 rather than specified %d.\n", audio_config.achan[0].baud);
|
||||
}
|
||||
}
|
||||
else if (audio_config.achan[0].baud < 7200) {
|
||||
audio_config.achan[0].modem_type = MODEM_8PSK;
|
||||
audio_config.achan[0].mark_freq = 0;
|
||||
audio_config.achan[0].space_freq = 0;
|
||||
if (audio_config.achan[0].baud != 4800) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Bit rate should be standard 4800 rather than specified %d.\n", audio_config.achan[0].baud);
|
||||
}
|
||||
}
|
||||
else {
|
||||
audio_config.achan[0].modem_type = MODEM_SCRAMBLE;
|
||||
audio_config.achan[0].mark_freq = 0;
|
||||
audio_config.achan[0].space_freq = 0;
|
||||
}
|
||||
else {
|
||||
audio_config.achan[0].modem_type = MODEM_AFSK;
|
||||
audio_config.achan[0].mark_freq = 1200;
|
||||
audio_config.achan[0].space_freq = 2200;
|
||||
}
|
||||
}
|
||||
|
||||
audio_config.statistics_interval = a_opt;
|
||||
|
@ -590,6 +657,12 @@ int main (int argc, char *argv[])
|
|||
audio_config.achan[0].decimate = D_opt;
|
||||
}
|
||||
|
||||
// temp - only xmit errors.
|
||||
|
||||
audio_config.xmit_error_rate = E_tx_opt;
|
||||
audio_config.recv_error_rate = E_rx_opt;
|
||||
|
||||
|
||||
if (strlen(l_opt) > 0) {
|
||||
strlcpy (misc_config.logdir, l_opt, sizeof(misc_config.logdir));
|
||||
}
|
||||
|
@ -621,14 +694,14 @@ int main (int argc, char *argv[])
|
|||
}
|
||||
|
||||
/*
|
||||
* Initialize the AFSK demodulator and HDLC decoder.
|
||||
* Initialize the demodulator(s) and HDLC decoder.
|
||||
*/
|
||||
multi_modem_init (&audio_config);
|
||||
|
||||
/*
|
||||
* Initialize the touch tone decoder & APRStt gateway.
|
||||
*/
|
||||
dtmf_init (&audio_config);
|
||||
dtmf_init (&audio_config, audio_amplitude);
|
||||
aprs_tt_init (&tt_config);
|
||||
tt_user_init (&audio_config, &tt_config);
|
||||
|
||||
|
@ -637,8 +710,8 @@ int main (int argc, char *argv[])
|
|||
* Note: This is not the same as a volume control you would see on the screen.
|
||||
* It is the range of the digital sound representation.
|
||||
*/
|
||||
gen_tone_init (&audio_config, 100);
|
||||
morse_init (&audio_config, 100);
|
||||
gen_tone_init (&audio_config, audio_amplitude, 0);
|
||||
morse_init (&audio_config, audio_amplitude);
|
||||
|
||||
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);
|
||||
|
@ -680,6 +753,9 @@ int main (int argc, char *argv[])
|
|||
*/
|
||||
digipeater_init (&audio_config, &digi_config);
|
||||
igate_init (&audio_config, &igate_config, &digi_config, d_i_opt);
|
||||
cdigipeater_init (&audio_config, &cdigi_config);
|
||||
pfilter_init (&igate_config, d_f_opt);
|
||||
ax25_link_init (&misc_config);
|
||||
|
||||
/*
|
||||
* Provide the AGW & KISS socket interfaces for use by a client application.
|
||||
|
@ -698,12 +774,7 @@ int main (int argc, char *argv[])
|
|||
*/
|
||||
dwgps_init (&misc_config, d_g_opt);
|
||||
|
||||
nmea_init (&misc_config); // TODO: revisit.
|
||||
|
||||
/*
|
||||
* Create thread for trying to salvage frames with bad FCS.
|
||||
*/
|
||||
redecode_init (&audio_config);
|
||||
waypoint_init (&misc_config);
|
||||
|
||||
/*
|
||||
* Enable beaconing.
|
||||
|
@ -712,7 +783,8 @@ int main (int argc, char *argv[])
|
|||
*/
|
||||
|
||||
log_init(misc_config.logdir);
|
||||
beacon_init (&audio_config, &misc_config);
|
||||
mheard_init (d_m_opt);
|
||||
beacon_init (&audio_config, &misc_config, &igate_config);
|
||||
|
||||
|
||||
/*
|
||||
|
@ -863,7 +935,7 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
text_color_set(DW_COLOR_REC);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
text_color_set(DW_COLOR_DECODED);
|
||||
}
|
||||
|
||||
if (audio_config.achan[chan].num_subchan > 1 && audio_config.achan[chan].num_slicers == 1) {
|
||||
|
@ -882,6 +954,36 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
|
||||
dw_printf ("%s", stemp); /* stations followed by : */
|
||||
|
||||
/* Demystify non-APRS. Use same format for transmitted frames in xmit.c. */
|
||||
|
||||
if ( ! ax25_is_aprs(pp)) {
|
||||
ax25_frame_type_t ftype;
|
||||
cmdres_t cr;
|
||||
char desc[80];
|
||||
int pf;
|
||||
int nr;
|
||||
int ns;
|
||||
|
||||
ftype = ax25_frame_type (pp, &cr, desc, &pf, &nr, &ns);
|
||||
|
||||
/* Could change by 1, since earlier call, if we guess at modulo 128. */
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
|
||||
dw_printf ("(%s)", desc);
|
||||
if (ftype == frame_type_U_XID) {
|
||||
struct xid_param_s param;
|
||||
char info2text[100];
|
||||
|
||||
xid_parse (pinfo, info_len, ¶m, info2text, sizeof(info2text));
|
||||
dw_printf (" %s\n", info2text);
|
||||
}
|
||||
else {
|
||||
ax25_safe_print ((char *)pinfo, info_len, ( ! ax25_is_aprs(pp)) && ( ! d_u_opt) );
|
||||
dw_printf ("\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// for APRS we generally want to display non-ASCII to see UTF-8.
|
||||
// for other, probably want to restrict to ASCII only because we are
|
||||
// more likely to have compressed data than UTF-8 text.
|
||||
|
@ -890,6 +992,8 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
|
||||
ax25_safe_print ((char *)pinfo, info_len, ( ! ax25_is_aprs(pp)) && ( ! d_u_opt) );
|
||||
dw_printf ("\n");
|
||||
}
|
||||
|
||||
|
||||
// Also display in pure ASCII if non-ASCII characters and "-d u" option specified.
|
||||
|
||||
|
@ -920,18 +1024,28 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
}
|
||||
|
||||
|
||||
/* Decode the contents of APRS frames and display in human-readable form. */
|
||||
/* Suppress decoding if "-q d" option used. */
|
||||
/*
|
||||
* Decode the contents of UI frames and display in human-readable form.
|
||||
* Could be APRS or anything random for old fashioned packet beacons.
|
||||
*
|
||||
* Suppress printed decoding if "-q d" option used.
|
||||
*/
|
||||
|
||||
if ( ( ! q_d_opt ) && ax25_is_aprs(pp)) {
|
||||
if (ax25_is_aprs(pp)) {
|
||||
|
||||
decode_aprs_t A;
|
||||
|
||||
decode_aprs (&A, pp, 0);
|
||||
// we still want to decode it for logging and other processing.
|
||||
// Just be quiet about errors if "-qd" is set.
|
||||
|
||||
//Print it all out in human readable format.
|
||||
decode_aprs (&A, pp, q_d_opt);
|
||||
|
||||
if ( ! q_d_opt ) {
|
||||
|
||||
// Print it all out in human readable format unless "-q d" option used.
|
||||
|
||||
decode_aprs_print (&A);
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform validity check on each address.
|
||||
|
@ -943,10 +1057,18 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
|
||||
log_write (chan, &A, pp, alevel, retries);
|
||||
|
||||
// temp experiment.
|
||||
//log_rr_bits (&A, pp);
|
||||
|
||||
// Add to list of stations heard over the radio.
|
||||
|
||||
mheard_save_rf (chan, &A, pp, alevel, retries);
|
||||
|
||||
|
||||
// Convert to NMEA waypoint sentence if we have a location.
|
||||
|
||||
if (A.g_lat != G_UNKNOWN && A.g_lon != G_UNKNOWN) {
|
||||
nmea_send_waypoint (strlen(A.g_name) > 0 ? A.g_name : A.g_src,
|
||||
waypoint_send_sentence (strlen(A.g_name) > 0 ? A.g_name : A.g_src,
|
||||
A.g_lat, A.g_lon, A.g_symbol_table, A.g_symbol_code,
|
||||
DW_FEET_TO_METERS(A.g_altitude_ft), A.g_course, DW_MPH_TO_KNOTS(A.g_speed_mph),
|
||||
A.g_comment);
|
||||
|
@ -955,7 +1077,7 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
|
||||
|
||||
/* Send to another application if connected. */
|
||||
// TODO1.3: Put a wrapper around this so we only call one function to send by all methods.
|
||||
// TODO: 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];
|
||||
|
@ -998,23 +1120,30 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* APRS digipeater.
|
||||
* Use only those with correct CRC; We don't want to spread corrupted data!
|
||||
*/
|
||||
|
||||
if (ax25_is_aprs(pp) && retries == RETRY_NONE) {
|
||||
|
||||
digipeater (chan, pp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Connected mode digipeater.
|
||||
* Use only those with correct CRC.
|
||||
*/
|
||||
|
||||
if (retries == RETRY_NONE) {
|
||||
|
||||
cdigipeater (chan, pp);
|
||||
}
|
||||
}
|
||||
|
||||
ax25_delete (pp);
|
||||
|
||||
} /* end app_process_rec_packet */
|
||||
|
||||
|
||||
|
||||
/* Process control C and window close events. */
|
||||
|
||||
#if __WIN32__
|
||||
|
@ -1026,6 +1155,7 @@ static BOOL cleanup_win (int ctrltype)
|
|||
dw_printf ("\nQRT\n");
|
||||
log_term ();
|
||||
ptt_term ();
|
||||
waypoint_term ();
|
||||
dwgps_term ();
|
||||
SLEEP_SEC(1);
|
||||
ExitProcess (0);
|
||||
|
@ -1065,10 +1195,12 @@ static void usage (char **argv)
|
|||
dw_printf (" -r n Audio sample rate, per sec.\n");
|
||||
dw_printf (" -n n Number of audio channels, 1 or 2.\n");
|
||||
dw_printf (" -b n Bits per audio sample, 8 or 16.\n");
|
||||
dw_printf (" -B n Data rate in bits/sec for channel 0. Standard values are 300, 1200, 9600.\n");
|
||||
dw_printf (" If < 600, AFSK tones are set to 1600 & 1800.\n");
|
||||
dw_printf (" If > 2400, K9NG/G3RUH style encoding is used.\n");
|
||||
dw_printf (" Otherwise, AFSK tones are set to 1200 & 2200.\n");
|
||||
dw_printf (" -B n Data rate in bits/sec for channel 0. Standard values are 300, 1200, 2400, 4800, 9600.\n");
|
||||
dw_printf (" 300 bps defaults to AFSK tones of 1600 & 1800.\n");
|
||||
dw_printf (" 1200 bps uses AFSK tones of 1200 & 2200.\n");
|
||||
dw_printf (" 2400 bps uses QPSK based on V.26 standard.\n");
|
||||
dw_printf (" 4800 bps uses 8PSK based on V.27 standard.\n");
|
||||
dw_printf (" 9600 bps and up uses K9NG/G3RUH standard.\n");
|
||||
dw_printf (" -D n Divide audio sample rate by n for channel 0.\n");
|
||||
dw_printf (" -d Debug options:\n");
|
||||
dw_printf (" a a = AGWPE network protocol client.\n");
|
||||
|
@ -1077,9 +1209,12 @@ static void usage (char **argv)
|
|||
dw_printf (" u u = Display non-ASCII text in hexadecimal.\n");
|
||||
dw_printf (" p p = dump Packets in hexadecimal.\n");
|
||||
dw_printf (" g g = GPS interface.\n");
|
||||
dw_printf (" w w = Waypoints for Position or Object Reports.\n");
|
||||
dw_printf (" t t = Tracker beacon.\n");
|
||||
dw_printf (" o o = output controls such as PTT and DCD.\n");
|
||||
dw_printf (" i i = IGate.\n");
|
||||
dw_printf (" m m = Monitor heard station list.\n");
|
||||
dw_printf (" f f = packet Filtering.\n");
|
||||
#if USE_HAMLIB
|
||||
dw_printf (" h h = hamlib increase verbose level.\n");
|
||||
#endif
|
||||
|
|
41
direwolf.h
41
direwolf.h
|
@ -1,7 +1,37 @@
|
|||
|
||||
/* direwolf.h - Common stuff used many places. */
|
||||
|
||||
// TODO: include this file first before anything else in each .c file.
|
||||
|
||||
|
||||
#ifndef DIREWOLF_H
|
||||
#define DIREWOLF_H 1
|
||||
|
||||
/*
|
||||
* Support Windows XP and later.
|
||||
*
|
||||
* We need this before "#include <ws2tcpip.h>".
|
||||
*
|
||||
* Don't know what other impact it might have on others.
|
||||
*/
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
#ifdef _WIN32_WINNT
|
||||
#error Include "direwolf.h" before any windows system files.
|
||||
#endif
|
||||
#ifdef WINVER
|
||||
#error Include "direwolf.h" before any windows system files.
|
||||
#endif
|
||||
|
||||
#define _WIN32_WINNT 0x0501 /* Minimum OS version is XP. */
|
||||
#define WINVER 0x0501 /* Minimum OS version is XP. */
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Previously, we could handle only a single audio device.
|
||||
|
@ -9,6 +39,8 @@
|
|||
* In version 1.2, we relax this restriction and allow more audio devices.
|
||||
* Three is probably adequate for standard version.
|
||||
* Larger reasonable numbers should also be fine.
|
||||
*
|
||||
* For example, if you wanted to use 4 audio devices at once, change this to 4.
|
||||
*/
|
||||
|
||||
#define MAX_ADEVS 3
|
||||
|
@ -69,7 +101,6 @@
|
|||
|
||||
|
||||
#if __WIN32__
|
||||
#include <windows.h>
|
||||
#define SLEEP_SEC(n) Sleep((n)*1000)
|
||||
#define SLEEP_MS(n) Sleep(n)
|
||||
#else
|
||||
|
@ -191,9 +222,8 @@ char *strsep(char **stringp, const char *delim);
|
|||
char *strtok_r(char *str, const char *delim, char **saveptr);
|
||||
#endif
|
||||
|
||||
//#if __WIN32__
|
||||
// Don't recall why for everyone.
|
||||
char *strcasestr(const char *S, const char *FIND);
|
||||
//#endif
|
||||
|
||||
|
||||
#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__APPLE__)
|
||||
|
@ -203,6 +233,11 @@ char *strcasestr(const char *S, const char *FIND);
|
|||
#else // Use our own copy
|
||||
|
||||
|
||||
// These prevent /usr/include/gps.h from providing its own definition.
|
||||
#define HAVE_STRLCAT 1
|
||||
#define HAVE_STRLCPY 1
|
||||
|
||||
|
||||
#define DEBUG_STRL 1
|
||||
|
||||
#if DEBUG_STRL
|
||||
|
|
121
dlq.h
121
dlq.h
|
@ -12,17 +12,128 @@
|
|||
#include "audio.h"
|
||||
|
||||
|
||||
void dlq_init (void);
|
||||
/* A transmit or receive data block for connected mode. */
|
||||
|
||||
typedef struct cdata_s {
|
||||
int magic; /* For integrity checking. */
|
||||
|
||||
#define TXDATA_MAGIC 0x09110911
|
||||
|
||||
struct cdata_s *next; /* Pointer to next when part of a list. */
|
||||
|
||||
int pid; /* Protocol id. */
|
||||
|
||||
int size; /* Number of bytes allocated. */
|
||||
|
||||
int len; /* Number of bytes actually used. */
|
||||
|
||||
char data[]; /* Variable length data. */
|
||||
|
||||
} cdata_t;
|
||||
|
||||
|
||||
|
||||
/* Types of things that can be in queue. */
|
||||
|
||||
typedef enum dlq_type_e {DLQ_REC_FRAME} dlq_type_t;
|
||||
typedef enum dlq_type_e {DLQ_REC_FRAME, DLQ_CONNECT_REQUEST, DLQ_DISCONNECT_REQUEST, DLQ_XMIT_DATA_REQUEST, DLQ_REGISTER_CALLSIGN, DLQ_UNREGISTER_CALLSIGN, DLQ_CHANNEL_BUSY, DLQ_CLIENT_CLEANUP} dlq_type_t;
|
||||
|
||||
void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum);
|
||||
|
||||
void dlq_wait_while_empty (void);
|
||||
/* A queue item. */
|
||||
|
||||
// TODO: call this event rather than item.
|
||||
// TODO: should add fences.
|
||||
|
||||
typedef struct dlq_item_s {
|
||||
|
||||
struct dlq_item_s *nextp; /* Next item in queue. */
|
||||
|
||||
dlq_type_t type; /* Type of item. */
|
||||
/* See enum definition above. */
|
||||
|
||||
int chan; /* Radio channel of origin. */
|
||||
|
||||
// I'm not worried about amount of memory used but this might be a
|
||||
// little clearer if a union was used for the different event types.
|
||||
|
||||
// Used for received frame.
|
||||
|
||||
int subchan; /* Winning "subchannel" when using multiple */
|
||||
/* decoders on one channel. */
|
||||
/* Special case, -1 means DTMF decoder. */
|
||||
/* Maybe we should have a different type in this case? */
|
||||
|
||||
int slice; /* Winning slicer. */
|
||||
|
||||
packet_t pp; /* Pointer to frame structure. */
|
||||
|
||||
alevel_t alevel; /* Audio level. */
|
||||
|
||||
retry_t retries; /* Effort expended to get a valid CRC. */
|
||||
|
||||
char spectrum[MAX_SUBCHANS*MAX_SLICERS+1]; /* "Spectrum" display for multi-decoders. */
|
||||
|
||||
// Used by requests from a client application, connect, etc.
|
||||
|
||||
char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN];
|
||||
|
||||
int num_addr; /* Range 2 .. 10. */
|
||||
|
||||
int client;
|
||||
|
||||
|
||||
// Used only by client request to transmit connected data.
|
||||
|
||||
cdata_t *txdata;
|
||||
|
||||
// Used for channel activity change.
|
||||
// It is useful to know when the channel is busy either for carrier detect
|
||||
// or when we are transmitting.
|
||||
|
||||
int activity; /* OCTYPE_PTT for my transmission start/end. */
|
||||
/* OCTYPE_DCD if we hear someone else. */
|
||||
|
||||
int status; /* 1 for active or 0 for quiet. */
|
||||
|
||||
} dlq_item_t;
|
||||
|
||||
|
||||
|
||||
void dlq_init (void);
|
||||
|
||||
|
||||
|
||||
void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum);
|
||||
|
||||
void dlq_connect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client, int pid);
|
||||
|
||||
void dlq_disconnect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client);
|
||||
|
||||
void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client, int pid, char *xdata_ptr, int xdata_len);
|
||||
|
||||
void dlq_register_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client);
|
||||
|
||||
void dlq_unregister_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client);
|
||||
|
||||
void dlq_channel_busy (int chan, int activity, int status);
|
||||
|
||||
void dlq_client_cleanup (int client);
|
||||
|
||||
|
||||
|
||||
int dlq_wait_while_empty (double timeout_val);
|
||||
|
||||
struct dlq_item_s *dlq_remove (void);
|
||||
|
||||
void dlq_delete (struct dlq_item_s *pitem);
|
||||
|
||||
|
||||
|
||||
cdata_t *cdata_new (int pid, char *data, int len);
|
||||
|
||||
void cdata_delete (cdata_t *txdata);
|
||||
|
||||
void cdata_check_leak (void);
|
||||
|
||||
int dlq_remove (dlq_type_t *type, int *chan, int *subchan, int *slice, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum, size_t spectrumsize);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,13 +1,14 @@
|
|||
# Documentation for Dire Wolf #
|
||||
|
||||
Click on the document name to view in your web browser or the link following to download the PDF file.
|
||||
|
||||
## Essential Reading ##
|
||||
|
||||
- [User Guide](User-Guide.pdf)
|
||||
- [**User Guide**](User-Guide.pdf) [ [*download*](../../../raw/dev/doc/User-Guide.pdf) ]
|
||||
|
||||
This is your primary source of information about installation, operation, and configuration.
|
||||
|
||||
- [Raspberry Pi APRS](Raspberry-Pi-APRS.pdf)
|
||||
- [**Raspberry Pi APRS**](Raspberry-Pi-APRS.pdf) [ [*download*](../../../raw/dev/doc/Raspberry-Pi-APRS.pdf) ]
|
||||
|
||||
The Raspberry Pi has some special considerations that
|
||||
make it different from other generic Linux systems.
|
||||
|
@ -18,37 +19,60 @@
|
|||
|
||||
These dive into more detail for specialized topics or typical usage scenarios.
|
||||
|
||||
- [**Successful APRS IGate Operation**](Successful-APRS-IGate-Operation.pdf) [ [*download*](../../../raw/dev/doc/Successful-APRS-IGate-Operation.pdf) ]
|
||||
|
||||
- [APRStt Implementation Notes](APRStt-Implementation-Notes.pdf)
|
||||
Dire Wolf can serve as a gateway between the APRS radio network and APRS-IS servers on the Internet.
|
||||
|
||||
This explains how it all works, proper configuration, and troubleshooting.
|
||||
|
||||
- [**APRStt Implementation Notes**](APRStt-Implementation-Notes.pdf) [ [*download*](../../../raw/dev/doc/APRStt-Implementation-Notes.pdf) ]
|
||||
|
||||
Very few hams have portable equipment for APRS but nearly everyone has a handheld radio that can send DTMF tones. APRStt allows a user, equipped with only DTMF (commonly known as Touch Tone) generation capability, to enter information into the global APRS data network.
|
||||
This document explains how the APRStt concept was implemented in the Dire Wolf application.
|
||||
|
||||
- [APRStt Interface for SARTrack](APRStt-interface-for-SARTrack.pdf)
|
||||
- [**APRStt Interface for SARTrack**](APRStt-interface-for-SARTrack.pdf) [ [*download*](../../../raw/dev/doc/APRStt-interface-for-SARTrack.pdf) ]
|
||||
|
||||
This example illustrates how APRStt can be integrated with other applications such as SARTrack, APRSISCE/32, YAAC, or Xastir.
|
||||
|
||||
- [APRStt Listening Example](APRStt-Listening-Example.pdf)
|
||||
- [**APRStt Listening Example**](APRStt-Listening-Example.pdf) [ [*download*](../../../raw/dev/doc/APRStt-Listening-Example.pdf) ]
|
||||
|
||||
|
||||
WB4APR described a useful application for the [QIKCOM-2 Satallite Transponder](http://www.tapr.org/pipermail/aprssig/2015-November/045035.html).
|
||||
|
||||
Don’t have your own QIKCOM-2 Satellite Transponder? No Problem. You can do the same thing with an ordinary computer and the APRStt gateway built into Dire Wolf. Here’s how.
|
||||
|
||||
- [Raspberry Pi SDR IGate](Raspberry-Pi-SDR-IGate.pdf)
|
||||
- [**Raspberry Pi APRS Tracker**](Raspberry-Pi-APRS-Tracker.pdf) [ [*download*](../../../raw/dev/doc/Raspberry-Pi-APRS-Tracker.pdf) ]
|
||||
|
||||
Build a tracking device which transmits position from a GPS receiver.
|
||||
|
||||
- [**Raspberry Pi SDR IGate**](Raspberry-Pi-SDR-IGate.pdf) [ [*download*](../../../raw/dev/doc/Raspberry-Pi-SDR-IGate.pdf) ]
|
||||
|
||||
It's easy to build a receive-only APRS Internet Gateway (IGate) with only a Raspberry Pi and a software defined radio (RTL-SDR) dongle. Here’s how.
|
||||
|
||||
- [APRS Telemetry Toolkit](APRS-Telemetry-Toolkit.pdf)
|
||||
- [**APRS Telemetry Toolkit**](APRS-Telemetry-Toolkit.pdf) [ [*download*](../../../raw/dev/doc/APRS-Telemetry-Toolkit.pdf) ]
|
||||
|
||||
Describes scripts and methods to generate telemetry.
|
||||
Includes a complete example of attaching an analog to
|
||||
digital converter to a Raspberry Pi and transmitting
|
||||
a measured voltage.
|
||||
|
||||
|
||||
|
||||
- [**2400 & 4800 bps PSK for APRS / Packet Radio**](2400-4800-PSK-for-APRS-Packet-Radio.pdf) [ [*download*](../../../raw/dev/doc/2400-4800-PSK-for-APRS-Packet-Radio.pdf) ]
|
||||
|
||||
|
||||
Double or quadruple your data rate by sending multiple bits at the same time.
|
||||
|
||||
- [**Going beyond 9600 baud**](Going-beyond-9600-baud.pdf) [ [*download*](../../../raw/dev/doc/Going-beyond-9600-baud.pdf) ]
|
||||
|
||||
|
||||
Why stop at 9600 baud? Go faster if your soundcard and radio can handle it.
|
||||
|
||||
|
||||
## Miscellaneous ##
|
||||
|
||||
|
||||
- [A Better APRS Packet Demodulator, part 1, 1200 baud](A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf)
|
||||
- [**A Better APRS Packet Demodulator, part 1, 1200 baud**](A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf) [ [*download*](../../../raw/dev/doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf) ]
|
||||
|
||||
Sometimes it's a little mystifying why an
|
||||
APRS / AX.25 Packet TNC will decode some signals
|
||||
|
@ -60,7 +84,7 @@ and a couple things that can be done about it.
|
|||
|
||||
|
||||
|
||||
- [A Better APRS Packet Demodulator, part 2, 9600 baud](A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf)
|
||||
- [**A Better APRS Packet Demodulator, part 2, 9600 baud**](A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf) [ [*download*](../../../raw/dev/doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf) ]
|
||||
|
||||
In the first part of this series we discussed 1200 baud audio frequency shift keying (AFSK). The mismatch
|
||||
between FM transmitter pre-emphasis and the
|
||||
|
@ -69,13 +93,28 @@ and a couple things that can be done about it.
|
|||
This makes it more difficult to demodulate them accurately.
|
||||
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)
|
||||
- [**WA8LMF TNC Test CD Results a.k.a. Battle of the TNCs**](WA8LMF-TNC-Test-CD-Results.pdf) [ [*download*](../../../raw/dev/doc/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.
|
||||
|
||||
- [A Closer Look at the WA8LMF TNC Test CD](A-Closer-Look-at-the-WA8LMF-TNC-Test-CD.pdf)
|
||||
- [**A Closer Look at the WA8LMF TNC Test CD**](A-Closer-Look-at-the-WA8LMF-TNC-Test-CD.pdf) [ [*download*](../../../raw/dev/doc/A-Closer-Look-at-the-WA8LMF-TNC-Test-CD.pdf) ]
|
||||
|
||||
Here, we take a closer look at some of the frames on the TNC Test CD in hopes of gaining some insights into why some are easily decoded and others are more difficult.
|
||||
There are a lot of ugly signals out there. Many can be improved by decreasing the transmit volume. Others are just plain weird and you have to wonder how they are being generated.
|
||||
|
||||
## Questions? Experiences to share? ##
|
||||
|
||||
Here are some good places to ask questions and share your experiences:
|
||||
|
||||
- [Dire Wolf packet TNC](https://groups.yahoo.com/neo/groups/direwolf_packet/info)
|
||||
|
||||
- [Raspberry Pi 4 Ham Radio](https://groups.yahoo.com/neo/groups/Raspberry_Pi_4-Ham_RADIO/info)
|
||||
|
||||
- [linuxham](https://groups.yahoo.com/neo/groups/linuxham/info)
|
||||
|
||||
- [TAPR aprssig](http://www.tapr.org/pipermail/aprssig/)
|
||||
|
||||
|
||||
The github "issues" section is for reporting software defects and enhancement requests. It is NOT a place to ask questions or have general discussions. Please use one of the locations above.
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
5
dsp.c
5
dsp.c
|
@ -26,6 +26,8 @@
|
|||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
@ -34,7 +36,6 @@
|
|||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "audio.h"
|
||||
#include "fsk_demod_state.h"
|
||||
#include "fsk_gen_filter.h"
|
||||
|
@ -51,7 +52,7 @@
|
|||
// Don't remove this. It serves as a reminder that an experiment is underway.
|
||||
|
||||
#if defined(TUNE_MS_FILTER_SIZE) || defined(TUNE_AGC_FAST) || defined(TUNE_LPF_BAUD) || defined(TUNE_PLL_LOCKED) || defined(TUNE_PROFILE)
|
||||
#define DEBUG1 1
|
||||
#define DEBUG1 1 // Don't remove this.
|
||||
#endif
|
||||
|
||||
|
||||
|
|
360
dtmf.c
360
dtmf.c
|
@ -5,7 +5,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2013, 2014, 2015 John Langner, WB2OSZ
|
||||
// Copyright (C) 2013, 2014, 2015, 2016 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
|
||||
|
@ -32,23 +32,28 @@
|
|||
* References: http://eetimes.com/design/embedded/4024443/The-Goertzel-Algorithm
|
||||
* http://www.ti.com/ww/cn/uprogram/share/ppt/c5000/17dtmf_v13.ppt
|
||||
*
|
||||
* Revisions: 1.4 - Added transmit capability.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "dtmf.h"
|
||||
#include "hdlc_rec.h" // for dcd_change
|
||||
#include "textcolor.h"
|
||||
#include "gen_tone.h"
|
||||
|
||||
|
||||
|
||||
#if DTMF_TEST
|
||||
#define TIMEOUT_SEC 1 /* short for unit test below. */
|
||||
#define DEBUG 1
|
||||
#define DEBUG 1 // Don't remove this. We want more output for test.
|
||||
#else
|
||||
#define TIMEOUT_SEC 5 /* for normal operation. */
|
||||
#endif
|
||||
|
@ -78,8 +83,11 @@ static struct dd_s { /* Separate for each audio channel. */
|
|||
} dd[MAX_CHANS];
|
||||
|
||||
|
||||
static int s_amplitude = 100; // range of 0 .. 100
|
||||
|
||||
|
||||
static void push_button (int chan, char button, int ms);
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
|
@ -99,17 +107,23 @@ static struct dd_s { /* Separate for each audio channel. */
|
|||
* In version 1.2, we can have multiple soundcards
|
||||
* with potentially different sample rates.
|
||||
*
|
||||
* amp - Signal amplitude, for transmit, on scale of 0 .. 100.
|
||||
*
|
||||
* 100 will produce maximum amplitude of +-32k samples.
|
||||
*
|
||||
* Returns: None.
|
||||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
|
||||
void dtmf_init (struct audio_s *p_audio_config)
|
||||
void dtmf_init (struct audio_s *p_audio_config, int amp)
|
||||
{
|
||||
int j; /* Loop over all tones frequencies. */
|
||||
int c; /* Loop over all audio channels. */
|
||||
|
||||
|
||||
s_amplitude = amp;
|
||||
|
||||
/*
|
||||
* Pick a suitable processing block size.
|
||||
* Larger = narrower bandwidth, slower response.
|
||||
|
@ -118,16 +132,17 @@ void dtmf_init (struct audio_s *p_audio_config)
|
|||
for (c=0; c<MAX_CHANS; c++) {
|
||||
struct dd_s *D = &(dd[c]);
|
||||
int a = ACHAN2ADEV(c);
|
||||
|
||||
D->sample_rate = p_audio_config->adev[a].samples_per_sec;
|
||||
|
||||
if (p_audio_config->achan[c].dtmf_decode != DTMF_DECODE_OFF) {
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("channel %d:\n", c);
|
||||
#endif
|
||||
|
||||
D->sample_rate = p_audio_config->adev[a].samples_per_sec;
|
||||
D->block_size = (205 * D->sample_rate) / 8000;
|
||||
|
||||
|
||||
#if DEBUG
|
||||
dw_printf (" freq k coef \n");
|
||||
#endif
|
||||
|
@ -142,9 +157,9 @@ void dtmf_init (struct audio_s *p_audio_config)
|
|||
|
||||
k = D->block_size * (float)(dtmf_tones[j]) / (float)(D->sample_rate);
|
||||
|
||||
D->coef[j] = 2 * cos(2 * M_PI * (float)k / (float)(D->block_size));
|
||||
D->coef[j] = 2.0f * cosf(2.0f * (float)M_PI * (float)k / (float)(D->block_size));
|
||||
|
||||
assert (D->coef[j] > 0 && D->coef[j] < 2.0);
|
||||
assert (D->coef[j] > 0.0f && D->coef[j] < 2.0f);
|
||||
#if DEBUG
|
||||
dw_printf ("%8d %5.1f %8.5f \n", dtmf_tones[j], k, D->coef[j]);
|
||||
#endif
|
||||
|
@ -240,7 +255,7 @@ char dtmf_sample (int c, float input)
|
|||
*/
|
||||
|
||||
|
||||
#define THRESHOLD 1.74
|
||||
#define THRESHOLD 1.74f
|
||||
|
||||
if (output[0] > THRESHOLD * ( output[1] + output[2] + output[3])) row = 0;
|
||||
else if (output[1] > THRESHOLD * (output[0] + output[2] + output[3])) row = 1;
|
||||
|
@ -273,7 +288,9 @@ char dtmf_sample (int c, float input)
|
|||
|
||||
// Update Data Carrier Detect Indicator.
|
||||
|
||||
#ifndef DTMF_TEST
|
||||
dcd_change (c, MAX_SUBCHANS, 0, decoded != ' ');
|
||||
#endif
|
||||
|
||||
/* Reset timeout timer. */
|
||||
if (decoded != ' ') {
|
||||
|
@ -312,6 +329,182 @@ char dtmf_sample (int c, float input)
|
|||
}
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: dtmf_send
|
||||
*
|
||||
* Purpose: Generate DTMF tones from text string.
|
||||
*
|
||||
* Inputs: chan - Radio channel number.
|
||||
* str - Character string to send. 0-9, A-D, *, #
|
||||
* speed - Number of tones per second. Range 1 to 10.
|
||||
* txdelay - Delay (ms) from PTT to start.
|
||||
* txtail - Delay (ms) from end to PTT off.
|
||||
*
|
||||
* Returns: Total number of milliseconds to activate PTT.
|
||||
* This includes delays before the first tone
|
||||
* and after the last to avoid chopping off part of it.
|
||||
*
|
||||
* Description: xmit_thread calls this instead of the usual hdlc_send
|
||||
* when we have a special packet that means send DTMF.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
int dtmf_send (int chan, char *str, int speed, int txdelay, int txtail)
|
||||
{
|
||||
char *p;
|
||||
int len_ms; // Length of tone or gap between.
|
||||
|
||||
len_ms = (int) ( ( 500.0f / (float)speed ) + 0.5f);
|
||||
|
||||
push_button (chan, ' ', txdelay);
|
||||
|
||||
for (p = str; *p != '\0'; p++) {
|
||||
|
||||
push_button (chan, *p, len_ms);
|
||||
push_button (chan, ' ', len_ms);
|
||||
}
|
||||
|
||||
push_button (chan, ' ', txtail);
|
||||
|
||||
#ifndef DTMF_TEST
|
||||
audio_flush(ACHAN2ADEV(chan));
|
||||
#endif
|
||||
return (txdelay +
|
||||
(int) (1000.0f * (float)strlen(str) / (float)speed + 0.5f) +
|
||||
txtail);
|
||||
|
||||
} /* end dtmf_send */
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: push_button
|
||||
*
|
||||
* Purpose: Generate DTMF tone for a button push.
|
||||
*
|
||||
* Inputs: chan - Radio channel number.
|
||||
*
|
||||
* button - One of 0-9, A-D, *, #. Others result in silence.
|
||||
* '?' is a special case used only for unit testing.
|
||||
*
|
||||
* ms - Duration in milliseconds.
|
||||
* Use 50 ms for tone and 50 ms of silence for max rate of 10 per second.
|
||||
*
|
||||
* Outputs: Audio is sent to radio.
|
||||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
static void push_button (int chan, char button, int ms)
|
||||
{
|
||||
float phasea = 0;
|
||||
float phaseb = 0;
|
||||
float fa = 0;
|
||||
float fb = 0;
|
||||
int i;
|
||||
float dtmf; // Audio. Sum of two sine waves.
|
||||
#if DTMF_TEST
|
||||
char x;
|
||||
static char result[100];
|
||||
static int result_len = 0;
|
||||
#endif
|
||||
|
||||
switch (button) {
|
||||
case '1': fa = dtmf_tones[0]; fb = dtmf_tones[4]; break;
|
||||
case '2': fa = dtmf_tones[0]; fb = dtmf_tones[5]; break;
|
||||
case '3': fa = dtmf_tones[0]; fb = dtmf_tones[6]; break;
|
||||
case 'a':
|
||||
case 'A': fa = dtmf_tones[0]; fb = dtmf_tones[7]; break;
|
||||
|
||||
case '4': fa = dtmf_tones[1]; fb = dtmf_tones[4]; break;
|
||||
case '5': fa = dtmf_tones[1]; fb = dtmf_tones[5]; break;
|
||||
case '6': fa = dtmf_tones[1]; fb = dtmf_tones[6]; break;
|
||||
case 'b':
|
||||
case 'B': fa = dtmf_tones[1]; fb = dtmf_tones[7]; break;
|
||||
|
||||
case '7': fa = dtmf_tones[2]; fb = dtmf_tones[4]; break;
|
||||
case '8': fa = dtmf_tones[2]; fb = dtmf_tones[5]; break;
|
||||
case '9': fa = dtmf_tones[2]; fb = dtmf_tones[6]; break;
|
||||
case 'c':
|
||||
case 'C': fa = dtmf_tones[2]; fb = dtmf_tones[7]; break;
|
||||
|
||||
case '*': fa = dtmf_tones[3]; fb = dtmf_tones[4]; break;
|
||||
case '0': fa = dtmf_tones[3]; fb = dtmf_tones[5]; break;
|
||||
case '#': fa = dtmf_tones[3]; fb = dtmf_tones[6]; break;
|
||||
case 'd':
|
||||
case 'D': fa = dtmf_tones[3]; fb = dtmf_tones[7]; break;
|
||||
|
||||
#if DTMF_TEST
|
||||
|
||||
case '?': /* check result */
|
||||
|
||||
if (strcmp(result, "123A456B789C*0#D123$789$") == 0) {
|
||||
text_color_set(DW_COLOR_REC);
|
||||
dw_printf ("\nSuccess!\n");
|
||||
}
|
||||
else if (strcmp(result, "123A456B789C*0#D123789") == 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\n * Time-out failed, otherwise OK *\n");
|
||||
dw_printf ("\"%s\"\n", result);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\n *** TEST FAILED ***\n");
|
||||
dw_printf ("\"%s\"\n", result);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
//dw_printf ("push_button (%d, '%c', %d), fa=%.0f, fb=%.0f. %d samples\n", chan, button, ms, fa, fb, (ms*dd[chan].sample_rate)/1000);
|
||||
|
||||
for (i = 0; i < (ms*dd[chan].sample_rate)/1000; i++) {
|
||||
|
||||
// This could be more efficient with a precomputed sine wave table
|
||||
// but I'm not that worried about it.
|
||||
// With a Raspberry Pi, model 2, default 1200 receiving takes about 14% of one CPU core.
|
||||
// When transmitting tones, it briefly shoots up to about 33%.
|
||||
|
||||
if (fa > 0 && fb > 0) {
|
||||
dtmf = sinf(phasea) + sinf(phaseb);
|
||||
phasea += 2.0f * (float)M_PI * fa / dd[chan].sample_rate;
|
||||
phaseb += 2.0f * (float)M_PI * fb / dd[chan].sample_rate;
|
||||
}
|
||||
else {
|
||||
dtmf = 0;
|
||||
}
|
||||
|
||||
#if DTMF_TEST
|
||||
|
||||
/* Make sure it is insensitive to signal amplitude. */
|
||||
/* (Uncomment each of below when testing.) */
|
||||
|
||||
x = dtmf_sample (0, dtmf);
|
||||
//x = dtmf_sample (0, dtmf * 1000);
|
||||
//x = dtmf_sample (0, dtmf * 0.001);
|
||||
|
||||
if (x != ' ' && x != '.') {
|
||||
result[result_len] = x;
|
||||
result_len++;
|
||||
result[result_len] = '\0';
|
||||
}
|
||||
#else
|
||||
|
||||
// 'dtmf' can be in range of +-2.0 because it is sum of two sine waves.
|
||||
// Amplitude of 100 would use full +-32k range.
|
||||
|
||||
int sam = (int)(dtmf * 16383.0f * (float)s_amplitude / 100.0f);
|
||||
gen_tone_put_sample (chan, ACHAN2ADEV(chan), sam);
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: main
|
||||
|
@ -319,142 +512,79 @@ char dtmf_sample (int c, float input)
|
|||
* Purpose: Unit test for functions above.
|
||||
*
|
||||
* Usage: rm a.exe ; gcc -DDTMF_TEST dtmf.c textcolor.c ; ./a.exe
|
||||
* or
|
||||
* make dtmftest
|
||||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
|
||||
#if DTMF_TEST
|
||||
|
||||
push_button (char button, int ms)
|
||||
{
|
||||
static float phasea = 0;
|
||||
static float phaseb = 0;
|
||||
float fa, fb;
|
||||
int i;
|
||||
float input;
|
||||
char x;
|
||||
static char result[100];
|
||||
static int result_len = 0;
|
||||
int c = 0; // fake channel number.
|
||||
|
||||
|
||||
switch (button) {
|
||||
case '1': fa = dtmf_tones[0]; fb = dtmf_tones[4]; break;
|
||||
case '2': fa = dtmf_tones[0]; fb = dtmf_tones[5]; break;
|
||||
case '3': fa = dtmf_tones[0]; fb = dtmf_tones[6]; break;
|
||||
case 'A': fa = dtmf_tones[0]; fb = dtmf_tones[7]; break;
|
||||
case '4': fa = dtmf_tones[1]; fb = dtmf_tones[4]; break;
|
||||
case '5': fa = dtmf_tones[1]; fb = dtmf_tones[5]; break;
|
||||
case '6': fa = dtmf_tones[1]; fb = dtmf_tones[6]; break;
|
||||
case 'B': fa = dtmf_tones[1]; fb = dtmf_tones[7]; break;
|
||||
case '7': fa = dtmf_tones[2]; fb = dtmf_tones[4]; break;
|
||||
case '8': fa = dtmf_tones[2]; fb = dtmf_tones[5]; break;
|
||||
case '9': fa = dtmf_tones[2]; fb = dtmf_tones[6]; break;
|
||||
case 'C': fa = dtmf_tones[2]; fb = dtmf_tones[7]; break;
|
||||
case '*': fa = dtmf_tones[3]; fb = dtmf_tones[4]; break;
|
||||
case '0': fa = dtmf_tones[3]; fb = dtmf_tones[5]; break;
|
||||
case '#': fa = dtmf_tones[3]; fb = dtmf_tones[6]; break;
|
||||
case 'D': fa = dtmf_tones[3]; fb = dtmf_tones[7]; break;
|
||||
case '?':
|
||||
|
||||
// TODO: why are timeouts failing. Do we care?
|
||||
|
||||
if (strcmp(result, "123A456B789C*0#D123$789$") == 0) {
|
||||
dw_printf ("\nSuccess!\n");
|
||||
}
|
||||
else if (strcmp(result, "123A456B789C*0#D123789") == 0) {
|
||||
dw_printf ("\n * Time-out failed, otherwise OK *\n");
|
||||
dw_printf ("\"%s\"\n", result);
|
||||
}
|
||||
else {
|
||||
dw_printf ("\n *** TEST FAILED ***\n");
|
||||
dw_printf ("\"%s\"\n", result);
|
||||
}
|
||||
break;
|
||||
|
||||
default: fa = 0; fb = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < (ms*dd[c].sample_rate)/1000; i++) {
|
||||
|
||||
input = sin(phasea) + sin(phaseb);
|
||||
phasea += 2 * M_PI * fa / dd[c].sample_rate;
|
||||
phaseb += 2 * M_PI * fb / dd[c].sample_rate;
|
||||
|
||||
/* Make sure it is insensitive to signal amplitude. */
|
||||
|
||||
x = dtmf_sample (0, input);
|
||||
//x = dtmf_sample (0, input * 1000);
|
||||
//x = dtmf_sample (0, input * 0.001);
|
||||
|
||||
if (x != ' ' && x != '.') {
|
||||
result[result_len] = x;
|
||||
result_len++;
|
||||
result[result_len] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct audio_s my_audio_config;
|
||||
|
||||
|
||||
main ()
|
||||
int main ()
|
||||
{
|
||||
int c = 0; // radio channel.
|
||||
|
||||
memset (&my_audio_config, 0, sizeof(my_audio_config));
|
||||
my_audio_config.adev[0].defined = 1;
|
||||
my_audio_config.adev[0].samples_per_sec = 44100;
|
||||
my_audio_config.achan[0].valid = 1;
|
||||
my_audio_config.achan[0].dtmf_decode = DTMF_DECODE_ON;
|
||||
my_audio_config.adev[ACHAN2ADEV(c)].defined = 1;
|
||||
my_audio_config.adev[ACHAN2ADEV(c)].samples_per_sec = 44100;
|
||||
my_audio_config.achan[c].valid = 1;
|
||||
my_audio_config.achan[c].dtmf_decode = DTMF_DECODE_ON;
|
||||
|
||||
dtmf_init(&my_audio_config);
|
||||
dtmf_init(&my_audio_config, 50);
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("\nFirst, check all button tone pairs. \n\n");
|
||||
/* Max auto dialing rate is 10 per second. */
|
||||
|
||||
push_button ('1', 50); push_button (' ', 50);
|
||||
push_button ('2', 50); push_button (' ', 50);
|
||||
push_button ('3', 50); push_button (' ', 50);
|
||||
push_button ('A', 50); push_button (' ', 50);
|
||||
push_button (c, '1', 50); push_button (c, ' ', 50);
|
||||
push_button (c, '2', 50); push_button (c, ' ', 50);
|
||||
push_button (c, '3', 50); push_button (c, ' ', 50);
|
||||
push_button (c, 'A', 50); push_button (c, ' ', 50);
|
||||
|
||||
push_button ('4', 50); push_button (' ', 50);
|
||||
push_button ('5', 50); push_button (' ', 50);
|
||||
push_button ('6', 50); push_button (' ', 50);
|
||||
push_button ('B', 50); push_button (' ', 50);
|
||||
push_button (c, '4', 50); push_button (c, ' ', 50);
|
||||
push_button (c, '5', 50); push_button (c, ' ', 50);
|
||||
push_button (c, '6', 50); push_button (c, ' ', 50);
|
||||
push_button (c, 'B', 50); push_button (c, ' ', 50);
|
||||
|
||||
push_button ('7', 50); push_button (' ', 50);
|
||||
push_button ('8', 50); push_button (' ', 50);
|
||||
push_button ('9', 50); push_button (' ', 50);
|
||||
push_button ('C', 50); push_button (' ', 50);
|
||||
push_button (c, '7', 50); push_button (c, ' ', 50);
|
||||
push_button (c, '8', 50); push_button (c, ' ', 50);
|
||||
push_button (c, '9', 50); push_button (c, ' ', 50);
|
||||
push_button (c, 'C', 50); push_button (c, ' ', 50);
|
||||
|
||||
push_button ('*', 50); push_button (' ', 50);
|
||||
push_button ('0', 50); push_button (' ', 50);
|
||||
push_button ('#', 50); push_button (' ', 50);
|
||||
push_button ('D', 50); push_button (' ', 50);
|
||||
push_button (c, '*', 50); push_button (c, ' ', 50);
|
||||
push_button (c, '0', 50); push_button (c, ' ', 50);
|
||||
push_button (c, '#', 50); push_button (c, ' ', 50);
|
||||
push_button (c, 'D', 50); push_button (c, ' ', 50);
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("\nShould reject very short pulses.\n\n");
|
||||
|
||||
push_button ('1', 20); push_button (' ', 50);
|
||||
push_button ('1', 20); push_button (' ', 50);
|
||||
push_button ('1', 20); push_button (' ', 50);
|
||||
push_button ('1', 20); push_button (' ', 50);
|
||||
push_button ('1', 20); push_button (' ', 50);
|
||||
push_button (c, '1', 20); push_button (c, ' ', 50);
|
||||
push_button (c, '1', 20); push_button (c, ' ', 50);
|
||||
push_button (c, '1', 20); push_button (c, ' ', 50);
|
||||
push_button (c, '1', 20); push_button (c, ' ', 50);
|
||||
push_button (c, '1', 20); push_button (c, ' ', 50);
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("\nTest timeout after inactivity.\n\n");
|
||||
/* For this test we use 1 second. */
|
||||
/* In practice, it will probably more like 5. */
|
||||
|
||||
push_button ('1', 250); push_button (' ', 500);
|
||||
push_button ('2', 250); push_button (' ', 500);
|
||||
push_button ('3', 250); push_button (' ', 1200);
|
||||
push_button (c, '1', 250); push_button (c, ' ', 500);
|
||||
push_button (c, '2', 250); push_button (c, ' ', 500);
|
||||
push_button (c, '3', 250); push_button (c, ' ', 1200);
|
||||
|
||||
push_button ('7', 250); push_button (' ', 500);
|
||||
push_button ('8', 250); push_button (' ', 500);
|
||||
push_button ('9', 250); push_button (' ', 1200);
|
||||
push_button (c, '7', 250); push_button (c, ' ', 500);
|
||||
push_button (c, '8', 250); push_button (c, ' ', 500);
|
||||
push_button (c, '9', 250); push_button (c, ' ', 1200);
|
||||
|
||||
/* Check for expected results. */
|
||||
|
||||
push_button ('?', 0);
|
||||
push_button (c, '?', 0);
|
||||
|
||||
exit (EXIT_SUCCESS);
|
||||
|
||||
} /* end main */
|
||||
|
||||
|
|
4
dtmf.h
4
dtmf.h
|
@ -3,10 +3,12 @@
|
|||
|
||||
#include "audio.h"
|
||||
|
||||
void dtmf_init (struct audio_s *p_audio_config);
|
||||
void dtmf_init (struct audio_s *p_audio_config, int amp);
|
||||
|
||||
char dtmf_sample (int c, float input);
|
||||
|
||||
int dtmf_send (int chan, char *str, int speed, int txdelay, int txtail);
|
||||
|
||||
|
||||
/* end dtmf.h */
|
||||
|
||||
|
|
|
@ -1,100 +1,185 @@
|
|||
#!/bin/bash
|
||||
|
||||
#
|
||||
# Run this from crontab periodically to start up
|
||||
# Dire Wolf automatically.
|
||||
#
|
||||
# I prefer this method instead of putting something
|
||||
# in ~/.config/autostart. That would start an application
|
||||
# only when the desktop first starts up.
|
||||
#
|
||||
# This method will restart the application if it
|
||||
# crashes or stops for any other reason.
|
||||
#
|
||||
# This script has some specifics the Raspberry Pi.
|
||||
# Some adjustments might be needed for other Linux variations.
|
||||
#
|
||||
|
||||
# See User Guide for more discussion.
|
||||
# For release 1.4 it is section 5.7 "Automatic Start Up After Reboot"
|
||||
# but it could change in the future as more information is added.
|
||||
|
||||
|
||||
# Versioning (this file, not direwolf version)
|
||||
#-----------
|
||||
# v1.3 - KI6ZHD - added variable support for direwolf binary location
|
||||
# v1.2 - KI6ZHD - support different versions of VNC
|
||||
# v1.1 - KI6ZHD - expanded version to support running on text-only displays with
|
||||
# auto support; log placement change
|
||||
# v1.0 - WB2OSZ - original version for Xwindow displays only
|
||||
|
||||
|
||||
|
||||
#How are you running Direwolf : within a GUI (Xwindows / VNC) or CLI mode
|
||||
#
|
||||
# When running from cron, we have a very minimal environment
|
||||
# including PATH=/usr/bin:/bin.
|
||||
# AUTO mode is design to try starting direwolf with GUI support and then
|
||||
# if no GUI environment is available, it reverts to CLI support with screen
|
||||
#
|
||||
|
||||
export PATH=/usr/local/bin:$PATH
|
||||
|
||||
# First wait a little while in case we just rebooted
|
||||
# and the desktop hasn't started up yet.
|
||||
# GUI mode is suited for users with the machine running LXDE/Gnome/KDE or VNC
|
||||
# which auto-logs on (sitting at a login prompt won't work)
|
||||
#
|
||||
# CLI mode is suited for say a Raspberry Pi running the Jessie LITE version
|
||||
# where it will run from the CLI w/o requiring Xwindows - uses screen
|
||||
|
||||
sleep 30
|
||||
LOGFILE=/tmp/dw-start.log
|
||||
RUNMODE=AUTO
|
||||
|
||||
# Location of the direwolf binary. Depends on $PATH as shown.
|
||||
# change this if you want to use some other specific location.
|
||||
# e.g. DIREWOLF="/usr/local/bin/direwolf"
|
||||
|
||||
DIREWOLF="direwolf"
|
||||
|
||||
#Direwolf start up command :: two examples where example one is enabled
|
||||
#
|
||||
# Nothing to do if it is already running.
|
||||
#
|
||||
|
||||
a=`pgrep direwolf`
|
||||
if [ "$a" != "" ]
|
||||
then
|
||||
#date >> /tmp/dw-start.log
|
||||
#echo "Already running." >> $LOGFILE
|
||||
exit
|
||||
fi
|
||||
|
||||
#
|
||||
# In my case, the Raspberry Pi is not connected to a monitor.
|
||||
# I access it remotely using VNC as described here:
|
||||
# http://learn.adafruit.com/adafruit-raspberry-pi-lesson-7-remote-control-with-vnc
|
||||
#
|
||||
# If VNC server is running, use its display number.
|
||||
# Otherwise default to :0.
|
||||
#
|
||||
|
||||
date >> /tmp/dw-start.log
|
||||
|
||||
export DISPLAY=":0"
|
||||
|
||||
v=`ps -ef | grep Xtightvnc | grep -v grep`
|
||||
if [ "$v" != "" ]
|
||||
then
|
||||
d=`echo "$v" | sed 's/.*tightvnc *\(:[0-9]\).*/\1/'`
|
||||
export DISPLAY="$d"
|
||||
fi
|
||||
|
||||
echo "DISPLAY=$DISPLAY" >> $LOGFILE
|
||||
|
||||
echo "Start up application." >> $LOGFILE
|
||||
|
||||
#
|
||||
# For normal operation as TNC, digipeater, IGate, etc.
|
||||
# 1. For normal operation as TNC, digipeater, IGate, etc.
|
||||
# Print audio statistics each 100 seconds for troubleshooting.
|
||||
# Change this command to however you wish to start Direwolf
|
||||
|
||||
DWCMD="$DIREWOLF -a 100"
|
||||
|
||||
#---------------------------------------------------------------
|
||||
#
|
||||
|
||||
DWCMD="direwolf -a 100"
|
||||
|
||||
# Alternative for running with SDR receiver.
|
||||
# 2. Alternative for running with SDR receiver.
|
||||
# Piping one application into another makes it a little more complicated.
|
||||
# We need to use bash for the | to be recognized.
|
||||
|
||||
#DWCMD="bash -c 'rtl_fm -f 144.39M - | direwolf -c sdr.conf -r 24000 -D 1 -'"
|
||||
|
||||
#
|
||||
# Adjust for your particular situation: gnome-terminal, xterm, etc.
|
||||
#
|
||||
|
||||
#Where will logs go - needs to be writable by non-root users
|
||||
LOGFILE=/var/tmp/dw-start.log
|
||||
|
||||
|
||||
if [ -x /usr/bin/lxterminal ]
|
||||
then
|
||||
#-------------------------------------
|
||||
# Main functions of the script
|
||||
#-------------------------------------
|
||||
|
||||
#Status variables
|
||||
SUCCESS=0
|
||||
|
||||
function CLI {
|
||||
SCREEN=`which screen`
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "Error: screen is not installed but is required for CLI mode. Aborting"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Direwolf in CLI mode start up"
|
||||
echo "Direwolf in CLI mode start up" >> $LOGFILE
|
||||
|
||||
# Screen commands
|
||||
# -d m :: starts the command in detached mode
|
||||
# -S :: name the session
|
||||
$SCREEN -d -m -S direwolf $DWCMD >> $LOGFILE
|
||||
SUCCESS=1
|
||||
|
||||
$SCREEN -list direwolf
|
||||
$SCREEN -list direwolf >> $LOGFILE
|
||||
|
||||
echo "-----------------------"
|
||||
echo "-----------------------" >> $LOGFILE
|
||||
}
|
||||
|
||||
function GUI {
|
||||
# In this case
|
||||
# In my case, the Raspberry Pi is not connected to a monitor.
|
||||
# I access it remotely using VNC as described here:
|
||||
# http://learn.adafruit.com/adafruit-raspberry-pi-lesson-7-remote-control-with-vnc
|
||||
#
|
||||
# If VNC server is running, use its display number.
|
||||
# Otherwise default to :0 (the Xwindows on the HDMI display)
|
||||
#
|
||||
export DISPLAY=":0"
|
||||
|
||||
#Reviewing for RealVNC sessions (stock in Raspbian Pixel)
|
||||
if [ -n "`ps -ef | grep vncserver-x11-serviced | grep -v grep`" ]; then
|
||||
sleep 0.1
|
||||
echo -e "\nRealVNC found - defaults to connecting to the :0 root window"
|
||||
elif [ -n "`ps -ef | grep Xtightvnc | grep -v grep`" ]; then
|
||||
#Reviewing for TightVNC sessions
|
||||
echo -e "\nTightVNC found - defaults to connecting to the :1 root window"
|
||||
v=`ps -ef | grep Xtightvnc | grep -v grep`
|
||||
d=`echo "$v" | sed 's/.*tightvnc *\(:[0-9]\).*/\1/'`
|
||||
export DISPLAY="$d"
|
||||
fi
|
||||
|
||||
echo "Direwolf in GUI mode start up"
|
||||
echo "Direwolf in GUI mode start up" >> $LOGFILE
|
||||
echo "DISPLAY=$DISPLAY"
|
||||
echo "DISPLAY=$DISPLAY" >> $LOGFILE
|
||||
|
||||
#
|
||||
# Auto adjust the startup for your particular environment: gnome-terminal, xterm, etc.
|
||||
#
|
||||
|
||||
if [ -x /usr/bin/lxterminal ]; then
|
||||
/usr/bin/lxterminal -t "Dire Wolf" -e "$DWCMD" &
|
||||
elif [ -x /usr/bin/xterm ]
|
||||
then
|
||||
SUCCESS=1
|
||||
elif [ -x /usr/bin/xterm ]; then
|
||||
/usr/bin/xterm -bg white -fg black -e "$DWCMD" &
|
||||
elif [ -x /usr/bin/x-terminal-emulator ]
|
||||
then
|
||||
SUCCESS=1
|
||||
elif [ -x /usr/bin/x-terminal-emulator ]; then
|
||||
/usr/bin/x-terminal-emulator -e "$DWCMD" &
|
||||
else
|
||||
echo "Did not find an X terminal emulator."
|
||||
SUCCESS=1
|
||||
else
|
||||
echo "Did not find an X terminal emulator. Reverting to CLI mode"
|
||||
SUCCESS=0
|
||||
fi
|
||||
echo "-----------------------"
|
||||
echo "-----------------------" >> $LOGFILE
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# Main Script start
|
||||
# -----------------------------------------------------------
|
||||
|
||||
# When running from cron, we have a very minimal environment
|
||||
# including PATH=/usr/bin:/bin.
|
||||
#
|
||||
export PATH=/usr/local/bin:$PATH
|
||||
|
||||
#Log the start of the script run and re-run
|
||||
date >> $LOGFILE
|
||||
|
||||
# First wait a little while in case we just rebooted
|
||||
# and the desktop hasn't started up yet.
|
||||
#
|
||||
sleep 30
|
||||
|
||||
|
||||
#
|
||||
# Nothing to do if Direwolf is already running.
|
||||
#
|
||||
|
||||
a=`ps ax | grep direwolf | grep -vi -e bash -e screen -e grep | awk '{print $1}'`
|
||||
if [ -n "$a" ]
|
||||
then
|
||||
#date >> /tmp/dw-start.log
|
||||
#echo "Direwolf already running." >> $LOGFILE
|
||||
exit
|
||||
fi
|
||||
|
||||
echo "-----------------------" >> $LOGFILE
|
||||
# Main execution of the script
|
||||
|
||||
if [ $RUNMODE == "AUTO" ];then
|
||||
GUI
|
||||
if [ $SUCCESS -eq 0 ]; then
|
||||
CLI
|
||||
fi
|
||||
elif [ $RUNMODE == "GUI" ];then
|
||||
GUI
|
||||
elif [ $RUNMODE == "CLI" ];then
|
||||
CLI
|
||||
else
|
||||
echo -e "ERROR: illegal run mode given. Giving up"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
3
dwgps.c
3
dwgps.c
|
@ -48,13 +48,14 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "textcolor.h"
|
||||
#include "dwgps.h"
|
||||
#include "dwgpsnmea.h"
|
||||
|
|
25
dwgpsd.c
25
dwgpsd.c
|
@ -34,6 +34,9 @@
|
|||
*---------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -48,6 +51,7 @@
|
|||
#endif
|
||||
|
||||
#if ENABLE_GPSD
|
||||
|
||||
#include <gps.h>
|
||||
|
||||
// Debian bug report: direwolf (1.2-1) FTBFS with libgps22 as part of the gpsd transition (#803605):
|
||||
|
@ -57,15 +61,21 @@
|
|||
#error libgps API version might be incompatible.
|
||||
#endif
|
||||
|
||||
#endif
|
||||
/*
|
||||
* Information for interface to gpsd daemon.
|
||||
*/
|
||||
|
||||
static struct gps_data_t gpsdata;
|
||||
|
||||
#endif /* ENABLE_GPSD */
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "textcolor.h"
|
||||
#include "dwgps.h"
|
||||
#include "dwgpsd.h"
|
||||
|
||||
|
||||
#if ENABLE_GPSD
|
||||
|
||||
static int s_debug = 0; /* Enable debug output. */
|
||||
/* >= 1 show results from dwgps_read. */
|
||||
|
@ -73,11 +83,8 @@ static int s_debug = 0; /* Enable debug output. */
|
|||
|
||||
static void * read_gpsd_thread (void *arg);
|
||||
|
||||
/*
|
||||
* Information for interface to gpsd daemon.
|
||||
*/
|
||||
#endif
|
||||
|
||||
static struct gps_data_t gpsdata;
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
|
@ -136,7 +143,7 @@ static struct gps_data_t gpsdata;
|
|||
*
|
||||
* Update: January 2016.
|
||||
*
|
||||
* I'm told that it might work in Raspian, Jessie version.
|
||||
* I'm told that the shared memory interface might work in Raspian, Jessie version.
|
||||
* Haven't tried it yet.
|
||||
*/
|
||||
|
||||
|
@ -152,7 +159,6 @@ int dwgpsd_init (struct misc_config_s *pconfig, int debug)
|
|||
int err;
|
||||
int arg = 0;
|
||||
char sport[12];
|
||||
dwgps_info_t info;
|
||||
|
||||
s_debug = debug;
|
||||
|
||||
|
@ -174,7 +180,6 @@ int dwgpsd_init (struct misc_config_s *pconfig, int debug)
|
|||
snprintf (sport, sizeof(sport), "%d", pconfig->gpsd_port);
|
||||
err = gps_open (pconfig->gpsd_host, sport, &gpsdata);
|
||||
if (err != 0) {
|
||||
dwgps_info_t info;
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Unable to connect to GPSD stream at %s:%s.\n", pconfig->gpsd_host, sport);
|
||||
|
@ -330,7 +335,7 @@ static void * read_gpsd_thread (void *arg)
|
|||
|
||||
return(0); // Terminate thread on serious error.
|
||||
|
||||
} /* end read_gps_thread */
|
||||
} /* end read_gpsd_thread */
|
||||
|
||||
#endif
|
||||
|
||||
|
|
22
dwgpsnmea.c
22
dwgpsnmea.c
|
@ -29,6 +29,9 @@
|
|||
*---------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -38,7 +41,6 @@
|
|||
#include <time.h>
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "textcolor.h"
|
||||
#include "dwgps.h"
|
||||
#include "dwgpsnmea.h"
|
||||
|
@ -48,6 +50,7 @@
|
|||
static int s_debug = 0; /* Enable debug output. */
|
||||
/* See dwgpsnmea_init description for values. */
|
||||
|
||||
static struct misc_config_s *s_save_configp;
|
||||
|
||||
|
||||
|
||||
|
@ -105,10 +108,12 @@ int dwgpsnmea_init (struct misc_config_s *pconfig, int debug)
|
|||
HANDLE read_gps_th;
|
||||
#else
|
||||
pthread_t read_gps_tid;
|
||||
int e;
|
||||
//int e;
|
||||
#endif
|
||||
|
||||
s_debug = debug;
|
||||
s_save_configp = pconfig;
|
||||
|
||||
|
||||
if (s_debug >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
|
@ -160,6 +165,19 @@ int dwgpsnmea_init (struct misc_config_s *pconfig, int debug)
|
|||
} /* end dwgpsnmea_init */
|
||||
|
||||
|
||||
/* Return fd to share if waypoint wants same device. */
|
||||
/* Currently both are fixed speed at 4800. */
|
||||
/* If that ever becomes configurable, that needs to be compared too. */
|
||||
|
||||
MYFDTYPE dwgpsnmea_get_fd(char *wp_port_name, int speed)
|
||||
{
|
||||
if (strcmp(s_save_configp->gpsnmea_port, wp_port_name) == 0 && speed == 4800) {
|
||||
return (s_gpsnmea_port_fd);
|
||||
}
|
||||
return (MYFDERROR);
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: read_gpsnmea_thread
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
/* dwgpsnmea.h - For NMEA sentences over serial port */
|
||||
/* dwgpsnmea.h - For reading NMEA sentences over serial port */
|
||||
|
||||
|
||||
|
||||
|
@ -8,10 +8,13 @@
|
|||
|
||||
#include "dwgps.h" /* for dwfix_t */
|
||||
#include "config.h"
|
||||
#include "serial_port.h" /* for MYFDTYPE */
|
||||
|
||||
|
||||
int dwgpsnmea_init (struct misc_config_s *pconfig, int debug);
|
||||
|
||||
MYFDTYPE dwgpsnmea_get_fd(char *wp_port_name, int speed);
|
||||
|
||||
void dwgpsnmea_term (void);
|
||||
|
||||
|
||||
|
@ -19,6 +22,7 @@ dwfix_t dwgpsnmea_gprmc (char *sentence, int quiet, double *odlat, double *odlon
|
|||
|
||||
dwfix_t dwgpsnmea_gpgga (char *sentence, int quiet, double *odlat, double *odlon, float *oalt, int *onsat);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
@ -42,7 +44,6 @@
|
|||
#include <math.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "encode_aprs.h"
|
||||
#include "latlong.h"
|
||||
#include "textcolor.h"
|
||||
|
@ -598,7 +599,7 @@ int encode_position (int messaging, int compressed, double lat, double lon, int
|
|||
result_len += strlen(comment);
|
||||
}
|
||||
|
||||
if (result_len >= result_size) {
|
||||
if (result_len >= (int)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);
|
||||
}
|
||||
|
@ -682,7 +683,7 @@ int encode_object (char *name, int compressed, time_t thyme, double lat, double
|
|||
|
||||
memset (p->o.name, ' ', sizeof(p->o.name));
|
||||
n = strlen(name);
|
||||
if (n > sizeof(p->o.name)) n = sizeof(p->o.name);
|
||||
if (n > (int)(sizeof(p->o.name))) n = sizeof(p->o.name);
|
||||
memcpy (p->o.name, name, n);
|
||||
|
||||
p->o.live_killed = '*';
|
||||
|
@ -692,7 +693,7 @@ int encode_object (char *name, int compressed, time_t thyme, double lat, double
|
|||
|
||||
#define XMIT_UTC 1
|
||||
#if XMIT_UTC
|
||||
gmtime_r (&thyme, &tm);
|
||||
(void)gmtime_r (&thyme, &tm);
|
||||
#else
|
||||
/* Using local time, for this application, would make more sense to me. */
|
||||
/* On Windows, localtime_r produces UTC. */
|
||||
|
@ -748,7 +749,7 @@ int encode_object (char *name, int compressed, time_t thyme, double lat, double
|
|||
result_len += strlen(comment);
|
||||
}
|
||||
|
||||
if (result_len >= result_size) {
|
||||
if (result_len >= (int)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);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
//
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
|
|
|
@ -4,9 +4,11 @@
|
|||
|
||||
#include "rpack.h"
|
||||
|
||||
#include "audio.h" // for enum modem_t
|
||||
|
||||
/*
|
||||
* Demodulator state.
|
||||
* The name of the file is from we only had FSK. Now we have other techniques.
|
||||
* Different copy is required for each channel & subchannel being processed concurrently.
|
||||
*/
|
||||
|
||||
|
@ -24,6 +26,7 @@ struct demodulator_state_s
|
|||
/*
|
||||
* These are set once during initialization.
|
||||
*/
|
||||
enum modem_t modem_type; // MODEM_AFSK, MODEM_8PSK, etc.
|
||||
|
||||
char profile; // 'A', 'B', etc. Upper case.
|
||||
// Only needed to see if we are using 'F' to take fast path.
|
||||
|
@ -132,10 +135,28 @@ struct demodulator_state_s
|
|||
float s_sin_table[MAX_FILTER_SIZE] __attribute__((aligned(16)));
|
||||
float s_cos_table[MAX_FILTER_SIZE] __attribute__((aligned(16)));
|
||||
|
||||
/*
|
||||
* These are for PSK only.
|
||||
* They are number of delay line taps into previous symbol.
|
||||
* They are one symbol period and + or - 45 degrees of the carrier frequency.
|
||||
*/
|
||||
int boffs; /* symbol length based on sample rate and baud. */
|
||||
int coffs; /* to get cos component of previous symbol. */
|
||||
int soffs; /* to get sin component of previous symbol. */
|
||||
|
||||
unsigned int lo_step; /* How much to advance the local oscillator */
|
||||
/* phase for each audio sample. */
|
||||
|
||||
int psk_use_lo; /* Use local oscillator rather than self correlation. */
|
||||
|
||||
|
||||
/*
|
||||
* The rest are continuously updated.
|
||||
*/
|
||||
|
||||
unsigned int lo_phase; /* Local oscillator for PSK. */
|
||||
|
||||
|
||||
/*
|
||||
* Most recent raw audio samples, before/after prefiltering.
|
||||
*/
|
||||
|
@ -213,6 +234,7 @@ struct demodulator_state_s
|
|||
|
||||
int prev_demod_data; // Previous data bit detected.
|
||||
// Used to look for transitions.
|
||||
float prev_demod_out_f;
|
||||
|
||||
/* This is used only for "9600" baud data. */
|
||||
|
||||
|
|
244
gen_packets.c
244
gen_packets.c
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2011, 2013, 2014, 2015 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2013, 2014, 2015, 2016 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
|
||||
|
@ -60,7 +60,7 @@
|
|||
*------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -74,8 +74,20 @@
|
|||
#include "gen_tone.h"
|
||||
#include "textcolor.h"
|
||||
#include "morse.h"
|
||||
#include "dtmf.h"
|
||||
|
||||
|
||||
/* Own random number generator so we can get */
|
||||
/* same results on Windows and Linux. */
|
||||
|
||||
#define MY_RAND_MAX 0x7fffffff
|
||||
static int seed = 1;
|
||||
|
||||
static int my_rand (void) {
|
||||
seed = ((seed * 1103515245) + 12345) & MY_RAND_MAX;
|
||||
return (seed);
|
||||
}
|
||||
|
||||
static void usage (char **argv);
|
||||
static int audio_file_open (char *fname, struct audio_s *pa);
|
||||
static int audio_file_close (void);
|
||||
|
@ -95,9 +107,10 @@ static void send_packet (char *str)
|
|||
int flen;
|
||||
int c;
|
||||
|
||||
|
||||
if (g_morse_wpm > 0) {
|
||||
|
||||
// TODO: Why not use the destination field instead of command line option?
|
||||
|
||||
morse_send (0, str, g_morse_wpm, 100, 100);
|
||||
}
|
||||
else {
|
||||
|
@ -105,8 +118,33 @@ static void send_packet (char *str)
|
|||
flen = ax25_pack (pp, fbuf);
|
||||
for (c=0; c<modem.adev[0].num_channels; c++)
|
||||
{
|
||||
|
||||
#if 1
|
||||
int samples_per_symbol, n, j;
|
||||
|
||||
/* Insert random amount of quiet time, approx. 0 to 10 symbol times, to test */
|
||||
/* how well the clock recovery PLL can regain lock after random phase shifts. */
|
||||
|
||||
if (modem.achan[c].modem_type == MODEM_QPSK) {
|
||||
samples_per_symbol = modem.adev[0].samples_per_sec / (modem.achan[c].baud / 2);
|
||||
}
|
||||
else if (modem.achan[c].modem_type == MODEM_8PSK) {
|
||||
samples_per_symbol = modem.adev[0].samples_per_sec / (modem.achan[c].baud / 3);
|
||||
}
|
||||
else {
|
||||
samples_per_symbol = modem.adev[0].samples_per_sec / modem.achan[c].baud;
|
||||
}
|
||||
|
||||
// for 1200 baud, 44100/sec, this should be 0 to 360.
|
||||
n = samples_per_symbol * 10 * (float)my_rand() / (float)MY_RAND_MAX;
|
||||
|
||||
//dw_printf ("Random 0-360 = %d\n", n);
|
||||
for (j=0; j<n; j++) {
|
||||
gen_tone_put_sample (c, 0, 0);
|
||||
}
|
||||
#endif
|
||||
hdlc_send_flags (c, 8, 0);
|
||||
hdlc_send_frame (c, fbuf, flen);
|
||||
hdlc_send_frame (c, fbuf, flen, 0);
|
||||
hdlc_send_flags (c, 2, 1);
|
||||
}
|
||||
ax25_delete (pp);
|
||||
|
@ -123,6 +161,8 @@ int main(int argc, char **argv)
|
|||
int packet_count = 0;
|
||||
int i;
|
||||
int chan;
|
||||
int experiment = 0;
|
||||
|
||||
|
||||
/*
|
||||
* Set up default values for the modem.
|
||||
|
@ -174,7 +214,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:82M:",
|
||||
c = getopt_long(argc, argv, "gm:s:a:b:B:r:n:o:z:82M:X",
|
||||
long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
@ -196,9 +236,9 @@ int main(int argc, char **argv)
|
|||
modem.achan[0].baud = atoi(optarg);
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Data rate set to %d bits / second.\n", modem.achan[0].baud);
|
||||
if (modem.achan[0].baud < 100 || modem.achan[0].baud > 10000) {
|
||||
if (modem.achan[0].baud < MIN_BAUD || modem.achan[0].baud > MAX_BAUD) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Use a more reasonable bit rate in range of 100 - 10000.\n");
|
||||
dw_printf ("Use a more reasonable bit rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
|
@ -208,29 +248,55 @@ int main(int argc, char **argv)
|
|||
/* 1200 implies 1200/2200 AFSK. */
|
||||
/* 9600 implies scrambled. */
|
||||
|
||||
/* If you want something else, specify -B first */
|
||||
/* then anything to override these defaults. */
|
||||
|
||||
modem.achan[0].baud = atoi(optarg);
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Data rate set to %d bits / second.\n", modem.achan[0].baud);
|
||||
if (modem.achan[0].baud < 100 || modem.achan[0].baud > 10000) {
|
||||
if (modem.achan[0].baud < MIN_BAUD || modem.achan[0].baud > MAX_BAUD) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Use a more reasonable bit rate in range of 100 - 10000.\n");
|
||||
dw_printf ("Use a more reasonable bit rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
switch (modem.achan[0].baud) {
|
||||
case 300:
|
||||
modem.achan[0].mark_freq = 1600;
|
||||
/* We have similar logic in direwolf.c, config.c, gen_packets.c, and atest.c, */
|
||||
/* that need to be kept in sync. Maybe it could be a common function someday. */
|
||||
|
||||
if (modem.achan[0].baud < 600) {
|
||||
modem.achan[0].modem_type = MODEM_AFSK;
|
||||
modem.achan[0].mark_freq = 1600; // Typical for HF SSB
|
||||
modem.achan[0].space_freq = 1800;
|
||||
break;
|
||||
case 1200:
|
||||
modem.achan[0].mark_freq = 1200;
|
||||
modem.achan[0].space_freq = 2200;
|
||||
break;
|
||||
case 9600:
|
||||
}
|
||||
else if (modem.achan[0].baud < 1800) {
|
||||
modem.achan[0].modem_type = MODEM_AFSK;
|
||||
modem.achan[0].mark_freq = DEFAULT_MARK_FREQ;
|
||||
modem.achan[0].space_freq = DEFAULT_SPACE_FREQ;
|
||||
}
|
||||
else if (modem.achan[0].baud < 3600) {
|
||||
modem.achan[0].modem_type = MODEM_QPSK;
|
||||
modem.achan[0].mark_freq = 0;
|
||||
modem.achan[0].space_freq = 0;
|
||||
dw_printf ("Using V.26 QPSK rather than AFSK.\n");
|
||||
if (modem.achan[0].baud != 2400) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Bit rate should be standard 2400 rather than specified %d.\n", modem.achan[0].baud);
|
||||
}
|
||||
}
|
||||
else if (modem.achan[0].baud < 7200) {
|
||||
modem.achan[0].modem_type = MODEM_8PSK;
|
||||
modem.achan[0].mark_freq = 0;
|
||||
modem.achan[0].space_freq = 0;
|
||||
dw_printf ("Using V.27 8PSK rather than AFSK.\n");
|
||||
if (modem.achan[0].baud != 4800) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Bit rate should be standard 4800 rather than specified %d.\n", modem.achan[0].baud);
|
||||
}
|
||||
}
|
||||
else {
|
||||
modem.achan[0].modem_type = MODEM_SCRAMBLE;
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Using scrambled baseband signal rather than AFSK.\n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -341,6 +407,7 @@ int main(int argc, char **argv)
|
|||
case 'M': /* -M for morse code speed */
|
||||
|
||||
//TODO: document this.
|
||||
// Why not base it on the destination field instead?
|
||||
|
||||
g_morse_wpm = atoi(optarg);
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
|
@ -352,6 +419,11 @@ int main(int argc, char **argv)
|
|||
}
|
||||
break;
|
||||
|
||||
case 'X':
|
||||
|
||||
experiment = 1;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
|
||||
/* Unknown option message was already printed. */
|
||||
|
@ -389,16 +461,99 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
|
||||
if (experiment) {
|
||||
modem.achan[0].modem_type = MODEM_QPSK;
|
||||
modem.achan[0].baud = 2400; // really bps not baud.
|
||||
amplitude = 100;
|
||||
}
|
||||
|
||||
|
||||
gen_tone_init (&modem, amplitude/2);
|
||||
gen_tone_init (&modem, amplitude/2, 1);
|
||||
morse_init (&modem, amplitude/2);
|
||||
dtmf_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);
|
||||
|
||||
|
||||
if (experiment) {
|
||||
int chan = 0;
|
||||
int n;
|
||||
|
||||
// 6 cycles of 1800 Hz.
|
||||
for (n=0; n<8; n++) {
|
||||
tone_gen_put_bit (chan, 0);
|
||||
}
|
||||
|
||||
// Shift 90
|
||||
tone_gen_put_bit (chan, 0);
|
||||
tone_gen_put_bit (chan, 1);
|
||||
|
||||
// Shift 90
|
||||
tone_gen_put_bit (chan, 0);
|
||||
tone_gen_put_bit (chan, 1);
|
||||
|
||||
// Shift 90
|
||||
tone_gen_put_bit (chan, 0);
|
||||
tone_gen_put_bit (chan, 1);
|
||||
|
||||
// Shift 90
|
||||
tone_gen_put_bit (chan, 0);
|
||||
tone_gen_put_bit (chan, 1);
|
||||
|
||||
// Shift 180
|
||||
tone_gen_put_bit (chan, 1);
|
||||
tone_gen_put_bit (chan, 1);
|
||||
|
||||
// Shift 270
|
||||
tone_gen_put_bit (chan, 1);
|
||||
tone_gen_put_bit (chan, 0);
|
||||
|
||||
// Shift 0
|
||||
tone_gen_put_bit (chan, 0);
|
||||
tone_gen_put_bit (chan, 0);
|
||||
|
||||
// Shift 0
|
||||
tone_gen_put_bit (chan, 0);
|
||||
tone_gen_put_bit (chan, 0);
|
||||
|
||||
|
||||
// HDLC flag - six 1 in a row.
|
||||
tone_gen_put_bit (chan, 0);
|
||||
tone_gen_put_bit (chan, 1);
|
||||
tone_gen_put_bit (chan, 1);
|
||||
tone_gen_put_bit (chan, 1);
|
||||
tone_gen_put_bit (chan, 1);
|
||||
tone_gen_put_bit (chan, 1);
|
||||
tone_gen_put_bit (chan, 1);
|
||||
tone_gen_put_bit (chan, 0);
|
||||
|
||||
tone_gen_put_bit (chan, 0); // reverse even/odd position
|
||||
|
||||
tone_gen_put_bit (chan, 0);
|
||||
tone_gen_put_bit (chan, 1);
|
||||
tone_gen_put_bit (chan, 1);
|
||||
tone_gen_put_bit (chan, 1);
|
||||
tone_gen_put_bit (chan, 1);
|
||||
tone_gen_put_bit (chan, 1);
|
||||
tone_gen_put_bit (chan, 1);
|
||||
tone_gen_put_bit (chan, 0);
|
||||
|
||||
tone_gen_put_bit (chan, 0);
|
||||
|
||||
// Shift 0
|
||||
tone_gen_put_bit (chan, 0);
|
||||
tone_gen_put_bit (chan, 0);
|
||||
|
||||
// Shift 0
|
||||
tone_gen_put_bit (chan, 0);
|
||||
tone_gen_put_bit (chan, 0);
|
||||
|
||||
audio_file_close ();
|
||||
return (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get user packets(s) from file or stdin if specified.
|
||||
* "-n" option is ignored in this case.
|
||||
|
@ -466,17 +621,28 @@ int main(int argc, char **argv)
|
|||
|
||||
char stemp[80];
|
||||
|
||||
if (modem.achan[0].modem_type == MODEM_SCRAMBLE) {
|
||||
g_noise_level = 0.33 * (amplitude / 200.0) * ((float)i / packet_count);
|
||||
}
|
||||
else if (modem.achan[0].baud < 600) {
|
||||
/* About 2/3 should be decoded properly. */
|
||||
if (modem.achan[0].baud < 600) {
|
||||
/* e.g. 300 bps AFSK - About 2/3 should be decoded properly. */
|
||||
g_noise_level = amplitude *.0048 * ((float)i / packet_count);
|
||||
}
|
||||
else {
|
||||
/* About 2/3 should be decoded properly. */
|
||||
else if (modem.achan[0].baud < 1800) {
|
||||
/* e.g. 1200 bps AFSK - About 2/3 should be decoded properly. */
|
||||
g_noise_level = amplitude *.0023 * ((float)i / packet_count);
|
||||
}
|
||||
else if (modem.achan[0].baud < 3600) {
|
||||
/* e.g. 2400 bps QPSK - T.B.D. */
|
||||
g_noise_level = amplitude *.0015 * ((float)i / packet_count);
|
||||
}
|
||||
else if (modem.achan[0].baud < 7200) {
|
||||
/* e.g. 4800 bps - T.B.D. */
|
||||
g_noise_level = amplitude *.0007 * ((float)i / packet_count);
|
||||
}
|
||||
else {
|
||||
/* e.g. 9600 */
|
||||
g_noise_level = 0.33 * (amplitude / 200.0) * ((float)i / packet_count);
|
||||
// temp test
|
||||
//g_noise_level = 0.20 * (amplitude / 200.0) * ((float)i / packet_count);
|
||||
}
|
||||
|
||||
snprintf (stemp, sizeof(stemp), "WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog! %04d of %04d", i, packet_count);
|
||||
|
||||
|
@ -511,15 +677,15 @@ static void usage (char **argv)
|
|||
dw_printf ("Options:\n");
|
||||
dw_printf (" -a <number> Signal amplitude in range of 0 - 200%%. Default 50.\n");
|
||||
dw_printf (" -b <number> Bits / second for data. Default is %d.\n", DEFAULT_BAUD);
|
||||
dw_printf (" -B <number> Bits / second for data. Proper modem selected for 300, 1200, 9600.\n");
|
||||
dw_printf (" -B <number> Bits / second for data. Proper modem selected for 300, 1200, 2400, 4800, 9600.\n");
|
||||
dw_printf (" -g Scrambled baseband rather than AFSK.\n");
|
||||
dw_printf (" -m <number> Mark frequency. Default is %d.\n", DEFAULT_MARK_FREQ);
|
||||
dw_printf (" -s <number> Space frequency. Default is %d.\n", DEFAULT_SPACE_FREQ);
|
||||
dw_printf (" -r <number> Audio sample Rate. Default is %d.\n", DEFAULT_SAMPLES_PER_SEC);
|
||||
dw_printf (" -n <number> Generate specified number of frames with increasing noise.\n");
|
||||
dw_printf (" -o <file> Send output to .wav file.\n");
|
||||
// dw_printf (" -8 8 bit audio rather than 16.\n");
|
||||
// dw_printf (" -2 2 channels of audio rather than 1.\n");
|
||||
dw_printf (" -8 8 bit audio rather than 16.\n");
|
||||
dw_printf (" -2 2 channels (stereo) audio rather than one channel.\n");
|
||||
// dw_printf (" -z <number> Number of leading zero bits before frame.\n");
|
||||
// dw_printf (" Default is 12 which is .01 seconds at 1200 bits/sec.\n");
|
||||
|
||||
|
@ -690,14 +856,6 @@ static int audio_file_open (char *fname, struct audio_s *pa)
|
|||
*
|
||||
*----------------------------------------------------------------*/
|
||||
|
||||
#define MY_RAND_MAX 0x7fffffff
|
||||
|
||||
static int seed = 1;
|
||||
|
||||
static int my_rand (void) {
|
||||
seed = ((seed * 1103515245) + 12345) & MY_RAND_MAX;
|
||||
return (seed);
|
||||
}
|
||||
|
||||
int audio_put (int a, int c)
|
||||
{
|
||||
|
@ -803,3 +961,11 @@ static int audio_file_close (void)
|
|||
|
||||
} /* end audio_close */
|
||||
|
||||
|
||||
// To keep dtmf.c happy.
|
||||
|
||||
#include "hdlc_rec.h" // for dcd_change
|
||||
|
||||
void dcd_change (int chan, int subchan, int slice, int state)
|
||||
{
|
||||
}
|
268
gen_tone.c
268
gen_tone.c
|
@ -1,7 +1,10 @@
|
|||
//#define DEBUG 1
|
||||
//#define DEBUG2 1
|
||||
|
||||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2011, 2014, 2015 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2014, 2015, 2016 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
|
||||
|
@ -28,6 +31,9 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <unistd.h>
|
||||
|
@ -35,7 +41,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include "audio.h"
|
||||
#include "gen_tone.h"
|
||||
#include "textcolor.h"
|
||||
|
@ -47,7 +53,7 @@
|
|||
|
||||
// Properties of the digitized sound stream & modem.
|
||||
|
||||
static struct audio_s *save_audio_config_p;
|
||||
static struct audio_s *save_audio_config_p = NULL;
|
||||
|
||||
/*
|
||||
* 8 bit samples are unsigned bytes in range of 0 .. 255.
|
||||
|
@ -68,6 +74,7 @@ static int ticks_per_bit[MAX_CHANS];
|
|||
static int f1_change_per_sample[MAX_CHANS];
|
||||
static int f2_change_per_sample[MAX_CHANS];
|
||||
|
||||
|
||||
static short sine_table[256];
|
||||
|
||||
|
||||
|
@ -76,10 +83,28 @@ static short sine_table[256];
|
|||
static unsigned int tone_phase[MAX_CHANS]; // Phase accumulator for tone generation.
|
||||
// Upper bits are used as index into sine table.
|
||||
|
||||
#define PHASE_SHIFT_180 ( 128u << 24 )
|
||||
#define PHASE_SHIFT_90 ( 64u << 24 )
|
||||
#define PHASE_SHIFT_45 ( 32u << 24 )
|
||||
|
||||
|
||||
static int bit_len_acc[MAX_CHANS]; // To accumulate fractional samples per bit.
|
||||
|
||||
static int lfsr[MAX_CHANS]; // Shift register for scrambler.
|
||||
|
||||
static int bit_count[MAX_CHANS]; // Counter incremented for each bit transmitted
|
||||
// on the channel. This is only used for QPSK.
|
||||
// The LSB determines if we save the bit until
|
||||
// next time, or send this one with the previously saved.
|
||||
// The LSB+1 position determines if we add an
|
||||
// extra 180 degrees to the phase to compensate
|
||||
// for having 1.5 carrier cycles per symbol time.
|
||||
|
||||
// For 8PSK, it has a different meaning. It is the
|
||||
// number of bits in 'save_bit' so we can accumulate
|
||||
// three for each symbol.
|
||||
static int save_bit[MAX_CHANS];
|
||||
|
||||
|
||||
/*
|
||||
* The K9NG/G3RUH output originally took a very simple and lazy approach.
|
||||
|
@ -155,6 +180,9 @@ static int resample[MAX_CHANS];
|
|||
*
|
||||
* amp - Signal amplitude on scale of 0 .. 100.
|
||||
*
|
||||
* gen_packets - True if being called from "gen_packets" utility
|
||||
* rather than the "direwolf" application.
|
||||
*
|
||||
* Returns: 0 for success.
|
||||
* -1 for failure.
|
||||
*
|
||||
|
@ -166,11 +194,17 @@ static int resample[MAX_CHANS];
|
|||
static int amp16bit; /* for 9600 baud */
|
||||
|
||||
|
||||
int gen_tone_init (struct audio_s *audio_config_p, int amp)
|
||||
int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets)
|
||||
{
|
||||
int j;
|
||||
int chan = 0;
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("gen_tone_init ( audio_config_p=%p, amp=%d, gen_packets=%d )\n",
|
||||
audio_config_p, amp, gen_packets);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Save away modem parameters for later use.
|
||||
*/
|
||||
|
@ -187,19 +221,58 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp)
|
|||
|
||||
int a = ACHAN2ADEV(chan);
|
||||
|
||||
ticks_per_sample[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
|
||||
|
||||
ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->achan[chan].baud ) + 0.5);
|
||||
|
||||
f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
|
||||
|
||||
f2_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].space_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("gen_tone_init: chan=%d, modem_type=%d, bps=%d, samples_per_sec=%d\n",
|
||||
chan,
|
||||
save_audio_config_p->achan[chan].modem_type,
|
||||
audio_config_p->achan[chan].baud,
|
||||
audio_config_p->adev[a].samples_per_sec);
|
||||
#endif
|
||||
|
||||
tone_phase[chan] = 0;
|
||||
|
||||
bit_len_acc[chan] = 0;
|
||||
|
||||
lfsr[chan] = 0;
|
||||
|
||||
ticks_per_sample[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
|
||||
|
||||
// The terminology is all wrong here. Didn't matter with 1200 and 9600.
|
||||
// The config speed should be bits per second rather than baud.
|
||||
// ticks_per_bit should be ticks_per_symbol.
|
||||
|
||||
switch (save_audio_config_p->achan[chan].modem_type) {
|
||||
|
||||
case MODEM_QPSK:
|
||||
|
||||
audio_config_p->achan[chan].mark_freq = 1800;
|
||||
audio_config_p->achan[chan].space_freq = audio_config_p->achan[chan].mark_freq; // Not Used.
|
||||
|
||||
// symbol time is 1 / (half of bps)
|
||||
ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / ((double)audio_config_p->achan[chan].baud * 0.5)) + 0.5);
|
||||
f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
|
||||
f2_change_per_sample[chan] = f1_change_per_sample[chan]; // Not used.
|
||||
|
||||
tone_phase[chan] = PHASE_SHIFT_45; // Just to mimic first attempt.
|
||||
break;
|
||||
|
||||
case MODEM_8PSK:
|
||||
|
||||
audio_config_p->achan[chan].mark_freq = 1800;
|
||||
audio_config_p->achan[chan].space_freq = audio_config_p->achan[chan].mark_freq; // Not Used.
|
||||
|
||||
// symbol time is 1 / (third of bps)
|
||||
ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / ((double)audio_config_p->achan[chan].baud / 3.)) + 0.5);
|
||||
f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
|
||||
f2_change_per_sample[chan] = f1_change_per_sample[chan]; // Not used.
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->achan[chan].baud ) + 0.5);
|
||||
f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
|
||||
f2_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].space_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,10 +283,18 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp)
|
|||
a = ((double)(j) / 256.0) * (2 * M_PI);
|
||||
s = (int) (sin(a) * 32767 * amp / 100.0);
|
||||
|
||||
/* 16 bit sound sample is in range of -32768 .. +32767. */
|
||||
|
||||
assert (s >= -32768 && s <= 32767);
|
||||
/* 16 bit sound sample must fit in range of -32768 .. +32767. */
|
||||
|
||||
if (s < -32768) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("gen_tone_init: Excessive amplitude is being clipped.\n");
|
||||
s = -32768;
|
||||
}
|
||||
else if (s > 32767) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("gen_tone_init: Excessive amplitude is being clipped.\n");
|
||||
s = 32767;
|
||||
}
|
||||
sine_table[j] = s;
|
||||
}
|
||||
|
||||
|
@ -236,11 +317,26 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp)
|
|||
|
||||
float filter_len_bits = 88 * 9600.0 / (44100.0 * 2.0);
|
||||
/* Filter length in number of data bits. */
|
||||
/* Currently 9.58 */
|
||||
|
||||
float lpf_baud = 0.8; /* Lowpass cutoff freq as fraction of baud rate */
|
||||
|
||||
float fc; /* Cutoff frequency as fraction of sampling frequency. */
|
||||
|
||||
/*
|
||||
* Normally, we want to generate the same thing whether sending over the air
|
||||
* or putting it into a file for other testing.
|
||||
* (There is an important exception. gen_packets can introduce random noise.)
|
||||
* In this case, we want more aggressive low pass filtering so it looks more like
|
||||
* what we see coming out of a receiver.
|
||||
* Specifically, single bits of the same state have considerably reduced amplitude
|
||||
* below several same values in a row.
|
||||
*/
|
||||
|
||||
if (gen_packets) {
|
||||
filter_len_bits = 4;
|
||||
lpf_baud = 0.55; /* Lowpass cutoff freq as fraction of baud rate */
|
||||
}
|
||||
|
||||
samples_per_sec = audio_config_p->adev[a].samples_per_sec * UPSAMPLE;
|
||||
baud = audio_config_p->achan[chan].baud;
|
||||
|
@ -250,10 +346,15 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp)
|
|||
|
||||
lp_filter_size[chan] = (int) (( filter_len_bits * (float)samples_per_sec / baud) + 0.5);
|
||||
|
||||
if (lp_filter_size[chan] < 10 || lp_filter_size[chan] > MAX_FILTER_SIZE) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("gen_tone_init: INTERNAL ERROR, chan %d, lp_filter_size %d\n", chan, lp_filter_size[chan]);
|
||||
lp_filter_size[chan] = MAX_FILTER_SIZE / 2;
|
||||
if (lp_filter_size[chan] < 10) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("gen_tone_init: unexpected, chan %d, lp_filter_size %d < 10\n", chan, lp_filter_size[chan]);
|
||||
lp_filter_size[chan] = 10;
|
||||
}
|
||||
else if (lp_filter_size[chan] > MAX_FILTER_SIZE) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("gen_tone_init: unexpected, chan %d, lp_filter_size %d > %d\n", chan, lp_filter_size[chan], MAX_FILTER_SIZE);
|
||||
lp_filter_size[chan] = MAX_FILTER_SIZE;
|
||||
}
|
||||
|
||||
fc = (float)baud * lpf_baud / (float)samples_per_sec;
|
||||
|
@ -273,7 +374,7 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp)
|
|||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: gen_tone_put_bit
|
||||
* Name: tone_gen_put_bit
|
||||
*
|
||||
* Purpose: Generate tone of proper duration for one data bit.
|
||||
*
|
||||
|
@ -286,14 +387,19 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp)
|
|||
*
|
||||
* Assumption: fp is open to a file for write.
|
||||
*
|
||||
* Version 1.4: Attempt to implement 2400 and 4800 bps PSK modes.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static const int gray2phase_v26[4] = {0, 1, 3, 2};
|
||||
static const int gray2phase_v27[8] = {1, 0, 2, 3, 6, 7, 5, 4};
|
||||
|
||||
|
||||
void tone_gen_put_bit (int chan, int dat)
|
||||
{
|
||||
int a = ACHAN2ADEV(chan); /* device for channel. */
|
||||
|
||||
|
||||
assert (save_audio_config_p != NULL);
|
||||
assert (save_audio_config_p->achan[chan].valid);
|
||||
|
||||
|
||||
|
@ -303,6 +409,55 @@ void tone_gen_put_bit (int chan, int dat)
|
|||
dat = 0;
|
||||
}
|
||||
|
||||
if (save_audio_config_p->achan[chan].modem_type == MODEM_QPSK) {
|
||||
|
||||
int dibit;
|
||||
int symbol;
|
||||
|
||||
dat &= 1; // Keep only LSB to be extra safe.
|
||||
|
||||
if ( ! (bit_count[chan] & 1)) {
|
||||
save_bit[chan] = dat;
|
||||
bit_count[chan]++;
|
||||
return;
|
||||
}
|
||||
|
||||
// All zero bits should give us steady 1800 Hz.
|
||||
// All one bits should flip phase by 180 degrees each time.
|
||||
|
||||
dibit = (save_bit[chan] << 1) | dat;
|
||||
|
||||
symbol = gray2phase_v26[dibit];
|
||||
tone_phase[chan] += symbol * PHASE_SHIFT_90;
|
||||
|
||||
bit_count[chan]++;
|
||||
}
|
||||
|
||||
if (save_audio_config_p->achan[chan].modem_type == MODEM_8PSK) {
|
||||
|
||||
int tribit;
|
||||
int symbol;
|
||||
|
||||
dat &= 1; // Keep only LSB to be extra safe.
|
||||
|
||||
if (bit_count[chan] < 2) {
|
||||
save_bit[chan] = (save_bit[chan] << 1) | dat;
|
||||
bit_count[chan]++;
|
||||
return;
|
||||
}
|
||||
|
||||
// The bit pattern 001 should give us steady 1800 Hz.
|
||||
// All one bits should flip phase by 180 degrees each time.
|
||||
|
||||
tribit = (save_bit[chan] << 1) | dat;
|
||||
|
||||
symbol = gray2phase_v27[tribit];
|
||||
tone_phase[chan] += symbol * PHASE_SHIFT_45;
|
||||
|
||||
save_bit[chan] = 0;
|
||||
bit_count[chan] = 0;
|
||||
}
|
||||
|
||||
if (save_audio_config_p->achan[chan].modem_type == MODEM_SCRAMBLE) {
|
||||
int x;
|
||||
|
||||
|
@ -311,18 +466,44 @@ void tone_gen_put_bit (int chan, int dat)
|
|||
dat = x;
|
||||
}
|
||||
|
||||
do {
|
||||
do { /* until enough audio samples for this symbol. */
|
||||
|
||||
if (save_audio_config_p->achan[chan].modem_type == MODEM_AFSK) {
|
||||
int sam;
|
||||
float fsam;
|
||||
|
||||
switch (save_audio_config_p->achan[chan].modem_type) {
|
||||
|
||||
case MODEM_AFSK:
|
||||
|
||||
#if DEBUG2
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("tone_gen_put_bit %d AFSK\n", __LINE__);
|
||||
#endif
|
||||
tone_phase[chan] += dat ? f2_change_per_sample[chan] : f1_change_per_sample[chan];
|
||||
sam = sine_table[(tone_phase[chan] >> 24) & 0xff];
|
||||
gen_tone_put_sample (chan, a, sam);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
|
||||
float fsam = dat ? amp16bit : (-amp16bit);
|
||||
case MODEM_QPSK:
|
||||
case MODEM_8PSK:
|
||||
|
||||
#if DEBUG2
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("tone_gen_put_bit %d PSK\n", __LINE__);
|
||||
#endif
|
||||
tone_phase[chan] += f1_change_per_sample[chan];
|
||||
sam = sine_table[(tone_phase[chan] >> 24) & 0xff];
|
||||
gen_tone_put_sample (chan, a, sam);
|
||||
break;
|
||||
|
||||
case MODEM_BASEBAND:
|
||||
case MODEM_SCRAMBLE:
|
||||
|
||||
#if DEBUG2
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("tone_gen_put_bit %d SCR\n", __LINE__);
|
||||
#endif
|
||||
fsam = dat ? amp16bit : (-amp16bit);
|
||||
|
||||
/* version 1.2 - added a low pass filter instead of square wave out. */
|
||||
|
||||
|
@ -330,12 +511,18 @@ void tone_gen_put_bit (int chan, int dat)
|
|||
|
||||
resample[chan]++;
|
||||
if (resample[chan] >= UPSAMPLE) {
|
||||
int sam;
|
||||
|
||||
sam = (int) convolve (raw[chan], lp_filter[chan], lp_filter_size[chan]);
|
||||
resample[chan] = 0;
|
||||
gen_tone_put_sample (chan, a, sam);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("INTERNAL ERROR: %s %d achan[%d].modem_type = %d\n",
|
||||
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].modem_type);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Enough for the bit time? */
|
||||
|
@ -351,12 +538,16 @@ void tone_gen_put_bit (int chan, int dat)
|
|||
void gen_tone_put_sample (int chan, int a, int sam) {
|
||||
|
||||
/* Ship out an audio sample. */
|
||||
/* 16 bit is signed, little endian, range -32768 .. +32767 */
|
||||
/* 8 bit is unsigned, range 0 .. 255 */
|
||||
|
||||
assert (save_audio_config_p != NULL);
|
||||
|
||||
assert (save_audio_config_p->adev[a].num_channels == 1 || save_audio_config_p->adev[a].num_channels == 2);
|
||||
|
||||
/* Generalize to allow 8 bits someday? */
|
||||
assert (save_audio_config_p->adev[a].bits_per_sample == 16 || save_audio_config_p->adev[a].bits_per_sample == 8);
|
||||
|
||||
assert (save_audio_config_p->adev[a].bits_per_sample == 16);
|
||||
// TODO: Should print message telling user to reduce output level.
|
||||
|
||||
if (sam < -32767) sam = -32767;
|
||||
else if (sam > 32767) sam = 32767;
|
||||
|
@ -365,25 +556,41 @@ void gen_tone_put_sample (int chan, int a, int sam) {
|
|||
|
||||
/* Mono */
|
||||
|
||||
if (save_audio_config_p->adev[a].bits_per_sample == 8) {
|
||||
audio_put (a, ((sam+32768) >> 8) & 0xff);
|
||||
}
|
||||
else {
|
||||
audio_put (a, sam & 0xff);
|
||||
audio_put (a, (sam >> 8) & 0xff);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
if (chan == ADEVFIRSTCHAN(a)) {
|
||||
|
||||
/* Stereo, left channel. */
|
||||
|
||||
if (save_audio_config_p->adev[a].bits_per_sample == 8) {
|
||||
audio_put (a, ((sam+32768) >> 8) & 0xff);
|
||||
audio_put (a, 0);
|
||||
}
|
||||
else {
|
||||
audio_put (a, sam & 0xff);
|
||||
audio_put (a, (sam >> 8) & 0xff);
|
||||
|
||||
audio_put (a, 0);
|
||||
audio_put (a, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
/* Stereo, right channel. */
|
||||
|
||||
if (save_audio_config_p->adev[a].bits_per_sample == 8) {
|
||||
audio_put (a, 0);
|
||||
audio_put (a, ((sam+32768) >> 8) & 0xff);
|
||||
}
|
||||
else {
|
||||
audio_put (a, 0);
|
||||
audio_put (a, 0);
|
||||
|
||||
|
@ -391,6 +598,7 @@ void gen_tone_put_sample (int chan, int a, int sam) {
|
|||
audio_put (a, (sam >> 8) & 0xff);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
|
||||
|
||||
int gen_tone_init (struct audio_s *pp, int amp);
|
||||
int gen_tone_init (struct audio_s *pp, int amp, int gen_packets);
|
||||
|
||||
|
||||
//int gen_tone_open (int nchan, int sample_rate, int bit_rate, int f1, int f2, int amp, char *fname);
|
||||
|
|
|
@ -577,6 +577,9 @@ long UTM_To_USNG (long Zone,
|
|||
Northing = 0.0;
|
||||
}
|
||||
|
||||
ltr2_low_value = LETTER_A; // Make compiler shut up about possibly uninitialized value.
|
||||
// It should be set by the following but compiler doesn't know.
|
||||
|
||||
USNG_Get_Grid_Values(Zone, <r2_low_value, <r2_high_value, &pattern_offset);
|
||||
|
||||
error_code = USNG_Get_Latitude_Letter(Latitude, &letters[0]);
|
||||
|
@ -960,6 +963,9 @@ long Convert_USNG_To_UTM (char *USNG,
|
|||
else
|
||||
*Hemisphere = 'N';
|
||||
|
||||
ltr2_low_value = LETTER_A; // Make compiler shut up about possibly uninitialized values.
|
||||
ltr2_high_value = LETTER_Z; // They should be set by the following but compiler doesn't know.
|
||||
|
||||
USNG_Get_Grid_Values(*Zone, <r2_low_value, <r2_high_value, &pattern_offset);
|
||||
|
||||
/* Check that the second letter of the USNG string is within
|
||||
|
|
25
hdlc_rec.c
25
hdlc_rec.c
|
@ -27,11 +27,12 @@
|
|||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "demod.h"
|
||||
#include "hdlc_rec.h"
|
||||
#include "hdlc_rec2.h"
|
||||
|
@ -46,6 +47,7 @@
|
|||
|
||||
//#define TEST 1 /* Define for unit testing. */
|
||||
|
||||
|
||||
//#define DEBUG3 1 /* monitor the data detect signal. */
|
||||
|
||||
|
||||
|
@ -258,8 +260,15 @@ void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled,
|
|||
* with the special "flag" characters.
|
||||
*
|
||||
* Idle time of all zero bits (alternating tones at maximum rate)
|
||||
* has also been observed rarely.
|
||||
* Recognize zero(s) followed by a flag even though it vilolates the spec.
|
||||
* has also been observed rarely. It is easy to understand the reasoning.
|
||||
* The tones alternate at the maximum rate, making it symmetrical and providing
|
||||
* the most opportunity for the PLL to lock on to the edges.
|
||||
* It also violates the published protocol spec.
|
||||
*
|
||||
* Recognize zero(s) followed by a single flag even though it violates the spec.
|
||||
*
|
||||
* It has been reported that the TinyTrak4 does this.
|
||||
* https://groups.yahoo.com/neo/groups/direwolf_packet/conversations/messages/1207
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -278,6 +287,7 @@ void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled,
|
|||
* when listening to live signals. Let's try 3 and see how that works out.
|
||||
*/
|
||||
|
||||
|
||||
//if (H->flag4_det == 0x7e7e7e7e) {
|
||||
if ((H->flag4_det & 0xffffff00) == 0x7e7e7e00) {
|
||||
//if ((H->flag4_det & 0xffff0000) == 0x7e7e0000) {
|
||||
|
@ -301,6 +311,15 @@ void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled,
|
|||
/*
|
||||
* Loss of signal should result in lack of transitions.
|
||||
* (all '1' bits) for at least a little while.
|
||||
*
|
||||
* When this was written, I was only concerned about 1200 baud.
|
||||
* For 9600, added later, there is a (de)scrambling function.
|
||||
* So if there is no change in the signal, we would get pseudo random bits here.
|
||||
* Maybe we need to put in another check earlier so DCD is not held on too long
|
||||
* after loss of signal for 9600.
|
||||
* No, that would not be a good idea. part of a valid frame, when scrambled,
|
||||
* could have seven or more "1" bits in a row.
|
||||
* Needs more study.
|
||||
*/
|
||||
|
||||
|
||||
|
|
22
hdlc_rec2.c
22
hdlc_rec2.c
|
@ -84,13 +84,15 @@
|
|||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
//Optimize processing by accessing directly to decoded bits
|
||||
#define RRBB_C 1
|
||||
#include "direwolf.h"
|
||||
#include "hdlc_rec2.h"
|
||||
#include "fcs_calc.h"
|
||||
#include "textcolor.h"
|
||||
|
@ -242,6 +244,8 @@ void hdlc_rec2_block (rrbb_t block)
|
|||
/* Create an empty retry configuration */
|
||||
retry_conf_t retry_cfg;
|
||||
|
||||
memset (&retry_cfg, 0, sizeof(retry_cfg));
|
||||
|
||||
/*
|
||||
* For our first attempt we don't try to alter any bits.
|
||||
* Still let it thru if passall AND no retries are desired.
|
||||
|
@ -327,7 +331,7 @@ void hdlc_rec2_block (rrbb_t block)
|
|||
static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel)
|
||||
{
|
||||
int ok;
|
||||
int len, i,j;
|
||||
int len, i;
|
||||
retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits;
|
||||
//int passall = save_audio_config_p->achan[chan].passall;
|
||||
|
||||
|
@ -335,6 +339,9 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int slice,
|
|||
len = rrbb_get_len(block);
|
||||
/* Prepare the retry configuration */
|
||||
retry_conf_t retry_cfg;
|
||||
|
||||
memset (&retry_cfg, 0, sizeof(retry_cfg));
|
||||
|
||||
/* Will modify only contiguous bits*/
|
||||
retry_cfg.mode = RETRY_MODE_CONTIGUOUS;
|
||||
/*
|
||||
|
@ -464,14 +471,17 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int slice,
|
|||
int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel)
|
||||
{
|
||||
int ok;
|
||||
int len, i, j;
|
||||
retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits;
|
||||
//int len;
|
||||
//retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits;
|
||||
int passall = save_audio_config_p->achan[chan].passall;
|
||||
#if DEBUG_LATER
|
||||
double tstart, tend;
|
||||
#endif
|
||||
retry_conf_t retry_cfg;
|
||||
len = rrbb_get_len(block);
|
||||
|
||||
memset (&retry_cfg, 0, sizeof(retry_cfg));
|
||||
|
||||
//len = rrbb_get_len(block);
|
||||
|
||||
|
||||
/*
|
||||
|
@ -575,7 +585,7 @@ static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t
|
|||
struct hdlc_state_s H;
|
||||
int blen; /* Block length in bits. */
|
||||
int i;
|
||||
unsigned int raw; /* From demodulator. */
|
||||
int raw; /* From demodulator. Should be 0 or 1. */
|
||||
#if DEBUGx
|
||||
int crc_failed = 1;
|
||||
#endif
|
||||
|
|
42
hdlc_send.c
42
hdlc_send.c
|
@ -1,3 +1,4 @@
|
|||
|
||||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
|
@ -17,10 +18,10 @@
|
|||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "hdlc_send.h"
|
||||
#include "audio.h"
|
||||
#include "gen_tone.h"
|
||||
|
@ -33,7 +34,9 @@ static void send_bit (int, int);
|
|||
|
||||
|
||||
|
||||
static int number_of_bits_sent[MAX_CHANS];
|
||||
static int number_of_bits_sent[MAX_CHANS]; // Count number of bits sent by "hdlc_send_frame" or "hdlc_send_flags"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -49,6 +52,8 @@ static int number_of_bits_sent[MAX_CHANS];
|
|||
*
|
||||
* flen - Frame length, not including the FCS.
|
||||
*
|
||||
* bad_fcs - Append an invalid FCS for testing purposes.
|
||||
*
|
||||
* Outputs: Bits are shipped out by calling tone_gen_put_bit().
|
||||
*
|
||||
* Returns: Number of bits sent including "flags" and the
|
||||
|
@ -70,8 +75,7 @@ static int number_of_bits_sent[MAX_CHANS];
|
|||
*
|
||||
*--------------------------------------------------------------*/
|
||||
|
||||
|
||||
int hdlc_send_frame (int chan, unsigned char *fbuf, int flen)
|
||||
int hdlc_send_frame (int chan, unsigned char *fbuf, int flen, int bad_fcs)
|
||||
{
|
||||
int j, fcs;
|
||||
|
||||
|
@ -81,7 +85,7 @@ int hdlc_send_frame (int chan, unsigned char *fbuf, int flen)
|
|||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("hdlc_send_frame ( chan = %d, fbuf = %p, flen = %d )\n", chan, fbuf, flen);
|
||||
dw_printf ("hdlc_send_frame ( chan = %d, fbuf = %p, flen = %d, bad_fcs = %d)\n", chan, fbuf, flen, bad_fcs);
|
||||
fflush (stdout);
|
||||
#endif
|
||||
|
||||
|
@ -94,8 +98,15 @@ int hdlc_send_frame (int chan, unsigned char *fbuf, int flen)
|
|||
|
||||
fcs = fcs_calc (fbuf, flen);
|
||||
|
||||
if (bad_fcs) {
|
||||
/* For testing only - Simulate a frame getting corrupted along the way. */
|
||||
send_data (chan, (~fcs) & 0xff);
|
||||
send_data (chan, ((~fcs) >> 8) & 0xff);
|
||||
}
|
||||
else {
|
||||
send_data (chan, fcs & 0xff);
|
||||
send_data (chan, (fcs >> 8) & 0xff);
|
||||
}
|
||||
|
||||
send_control (chan, 0x7e); /* End frame */
|
||||
|
||||
|
@ -162,7 +173,10 @@ int hdlc_send_flags (int chan, int nflags, int finish)
|
|||
|
||||
|
||||
|
||||
static int stuff = 0;
|
||||
static int stuff[MAX_CHANS]; // Count number of "1" bits to keep track of when we
|
||||
// need to break up a long run by "bit stuffing."
|
||||
// Needs to be array because we could be transmitting
|
||||
// on multiple channels at the same time.
|
||||
|
||||
static void send_control (int chan, int x)
|
||||
{
|
||||
|
@ -173,7 +187,7 @@ static void send_control (int chan, int x)
|
|||
x >>= 1;
|
||||
}
|
||||
|
||||
stuff = 0;
|
||||
stuff[chan] = 0;
|
||||
}
|
||||
|
||||
static void send_data (int chan, int x)
|
||||
|
@ -183,13 +197,13 @@ static void send_data (int chan, int x)
|
|||
for (i=0; i<8; i++) {
|
||||
send_bit (chan, x & 1);
|
||||
if (x & 1) {
|
||||
stuff++;
|
||||
if (stuff == 5) {
|
||||
stuff[chan]++;
|
||||
if (stuff[chan] == 5) {
|
||||
send_bit (chan, 0);
|
||||
stuff = 0;
|
||||
stuff[chan] = 0;
|
||||
}
|
||||
} else {
|
||||
stuff = 0;
|
||||
stuff[chan] = 0;
|
||||
}
|
||||
x >>= 1;
|
||||
}
|
||||
|
@ -203,13 +217,13 @@ static void send_data (int chan, int x)
|
|||
|
||||
static void send_bit (int chan, int b)
|
||||
{
|
||||
static int output;
|
||||
static int output[MAX_CHANS];
|
||||
|
||||
if (b == 0) {
|
||||
output = ! output;
|
||||
output[chan] = ! output[chan];
|
||||
}
|
||||
|
||||
tone_gen_put_bit (chan, output);
|
||||
tone_gen_put_bit (chan, output[chan]);
|
||||
|
||||
number_of_bits_sent[chan]++;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
/* hdlc_send.h */
|
||||
|
||||
int hdlc_send_frame (int chan, unsigned char *fbuf, int flen);
|
||||
int hdlc_send_frame (int chan, unsigned char *fbuf, int flen, int bad_fcs);
|
||||
|
||||
int hdlc_send_flags (int chan, int flags, int finish);
|
||||
|
||||
|
|
550
igate.c
550
igate.c
|
@ -63,18 +63,15 @@
|
|||
* Cygwin: Can use either one.
|
||||
*/
|
||||
|
||||
#include "direwolf.h" // Sets _WIN32_WINNT for XP API level needed by ws2tcpip.h
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
/* The goal is to support Windows XP and later. */
|
||||
|
||||
#include <winsock2.h>
|
||||
// default is 0x0400
|
||||
#undef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0501 /* Minimum OS version is XP. */
|
||||
//#define _WIN32_WINNT 0x0502 /* Minimum OS version is XP with SP2. */
|
||||
//#define _WIN32_WINNT 0x0600 /* Minimum OS version is Vista. */
|
||||
#include <ws2tcpip.h>
|
||||
#include <ws2tcpip.h> // _WIN32_WINNT must be set to 0x0501 before including this
|
||||
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <netdb.h>
|
||||
|
@ -102,6 +99,7 @@
|
|||
#include "latlong.h"
|
||||
#include "pfilter.h"
|
||||
#include "dtime_now.h"
|
||||
#include "mheard.h"
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
|
@ -120,8 +118,8 @@ static packet_t dp_queue_head;
|
|||
|
||||
static void satgate_delay_packet (packet_t pp, int chan);
|
||||
static void send_packet_to_server (packet_t pp, int chan);
|
||||
static void send_msg_to_server (const char *msg);
|
||||
static void xmit_packet (char *message, int chan);
|
||||
static void send_msg_to_server (const char *msg, int msg_len);
|
||||
static void maybe_xmit_packet_from_igate (char *message, int chan);
|
||||
|
||||
static void rx_to_ig_init (void);
|
||||
static void rx_to_ig_remember (packet_t pp);
|
||||
|
@ -268,7 +266,7 @@ int main (int argc, char *argv[])
|
|||
SLEEP_SEC (20);
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Send received packet\n");
|
||||
send_msg_to_server ("W1ABC>APRS:?");
|
||||
send_msg_to_server ("W1ABC>APRS:?", strlen("W1ABC>APRS:?");
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
|
@ -293,8 +291,10 @@ static int s_debug;
|
|||
|
||||
|
||||
/*
|
||||
* Statistics.
|
||||
* TODO: need print function.
|
||||
* Statistics for IGate function.
|
||||
* Note that the RF related counters are just a subset of what is happening on radio channels.
|
||||
*
|
||||
* TODO: should have debug option to print these occasionally.
|
||||
*/
|
||||
|
||||
static int stats_failed_connect; /* Number of times we tried to connect to */
|
||||
|
@ -312,8 +312,10 @@ static time_t stats_connect_at; /* Most recent time connection was established.
|
|||
/* can be used to determine elapsed connect time. */
|
||||
|
||||
static int stats_rf_recv_packets; /* Number of candidate packets from the radio. */
|
||||
/* This is not the total number of AX.25 frames received */
|
||||
/* over the radio; only APRS packets get this far. */
|
||||
|
||||
static int stats_rx_igate_packets; /* Number of packets passed along to the IGate */
|
||||
static int stats_uplink_packets; /* Number of packets passed along to the IGate */
|
||||
/* server after filtering. */
|
||||
|
||||
static int stats_uplink_bytes; /* Total number of bytes sent to IGate server */
|
||||
|
@ -322,40 +324,44 @@ static int stats_uplink_bytes; /* Total number of bytes sent to IGate server */
|
|||
static int stats_downlink_bytes; /* Total number of bytes from IGate server including */
|
||||
/* packets, heartbeats, other messages. */
|
||||
|
||||
static int stats_tx_igate_packets; /* Number of packets from IGate server. */
|
||||
static int stats_downlink_packets; /* Number of packets from IGate server for possible transmission. */
|
||||
/* Fewer might be transmitted due to filtering or rate limiting. */
|
||||
|
||||
static int stats_rf_xmit_packets; /* Number of packets passed along to radio */
|
||||
/* after rate limiting or other restrictions. */
|
||||
static int stats_rf_xmit_packets; /* Number of packets passed along to radio, for the IGate function, */
|
||||
/* after filtering, rate limiting, or other restrictions. */
|
||||
/* Number of packets transmitted for beacons, digipeating, */
|
||||
/* or client applications are not included here. */
|
||||
|
||||
/* We have some statistics. What do we do with them?
|
||||
static int stats_msg_cnt; /* Number of "messages" transmitted. Subset of above. */
|
||||
/* A "message" has the data type indicator of ":" and it is */
|
||||
/* not the special case of telemetry metadata. */
|
||||
|
||||
|
||||
IGate stations often send packets like this:
|
||||
/*
|
||||
* Make some of these available for IGate statistics beacon like
|
||||
*
|
||||
* WB2OSZ>APDW14,WIDE1-1:<IGATE,MSG_CNT=2,PKT_CNT=0,DIR_CNT=10,LOC_CNT=35,RF_CNT=45
|
||||
*
|
||||
* MSG_CNT is only "messages." From original spec.
|
||||
* PKT_CNT is other (non-message) packets. Followed precedent of APRSISCE32.
|
||||
*/
|
||||
|
||||
<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
|
||||
int igate_get_msg_cnt (void) {
|
||||
return (stats_msg_cnt);
|
||||
}
|
||||
|
||||
What does it all mean?
|
||||
Why do some have spaces instead of commas between the capabilities?
|
||||
int igate_get_pkt_cnt (void) {
|
||||
return (stats_rf_xmit_packets - stats_msg_cnt);
|
||||
}
|
||||
|
||||
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
|
||||
int igate_get_upl_cnt (void) {
|
||||
return (stats_uplink_packets);
|
||||
}
|
||||
|
||||
IGate Design ( http://www.aprs-is.net/IGating.aspx ) barely mentions
|
||||
<IGATE,MSG_CNT=n,LOC_CNT=n
|
||||
int igate_get_dnl_cnt (void) {
|
||||
return (stats_downlink_packets);
|
||||
}
|
||||
|
||||
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?
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
|
@ -424,11 +430,12 @@ void igate_init (struct audio_s *p_audio_config, struct igate_config_s *p_igate_
|
|||
stats_connects = 0;
|
||||
stats_connect_at = 0;
|
||||
stats_rf_recv_packets = 0;
|
||||
stats_rx_igate_packets = 0;
|
||||
stats_uplink_packets = 0;
|
||||
stats_uplink_bytes = 0;
|
||||
stats_downlink_bytes = 0;
|
||||
stats_tx_igate_packets = 0;
|
||||
stats_downlink_packets = 0;
|
||||
stats_rf_xmit_packets = 0;
|
||||
stats_msg_cnt = 0;
|
||||
|
||||
rx_to_ig_init ();
|
||||
ig_to_tx_init ();
|
||||
|
@ -685,8 +692,11 @@ static void * connnect_thread (void *arg)
|
|||
// Try each address until we find one that is successful.
|
||||
|
||||
for (n=0; n<num_hosts; n++) {
|
||||
#if __WIN32__
|
||||
SOCKET is;
|
||||
#else
|
||||
int is;
|
||||
|
||||
#endif
|
||||
ai = hosts[n];
|
||||
|
||||
ia_to_text (ai->ai_family, ai->ai_addr, ipaddr_str, sizeof(ipaddr_str));
|
||||
|
@ -787,7 +797,7 @@ static void * connnect_thread (void *arg)
|
|||
strlcat (stemp, " filter ", sizeof(stemp));
|
||||
strlcat (stemp, save_igate_config_p->t2_filter, sizeof(stemp));
|
||||
}
|
||||
send_msg_to_server (stemp);
|
||||
send_msg_to_server (stemp, strlen(stemp));
|
||||
|
||||
/* Delay until it is ok to start sending packets. */
|
||||
|
||||
|
@ -817,10 +827,13 @@ static void * connnect_thread (void *arg)
|
|||
strlcpy (heartbeat, "#", sizeof(heartbeat));
|
||||
|
||||
/* This will close the socket if any error. */
|
||||
send_msg_to_server (heartbeat);
|
||||
send_msg_to_server (heartbeat, strlen(heartbeat));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
exit(0); // Unreachable but stops compiler from complaining
|
||||
// about function not returning a value.
|
||||
} /* end connnect_thread */
|
||||
|
||||
|
||||
|
@ -848,14 +861,15 @@ static void * connnect_thread (void *arg)
|
|||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
#define IGATE_MAX_MSG 520 /* Message to IGate max 512 characters. */
|
||||
#define IGATE_MAX_MSG 512 /* "All 'packets' sent to APRS-IS must be in the TNC2 format terminated */
|
||||
/* by a carriage return, line feed sequence. No line may exceed 512 bytes */
|
||||
/* including the CR/LF sequence." */
|
||||
|
||||
void igate_send_rec_packet (int chan, packet_t recv_pp)
|
||||
{
|
||||
packet_t pp;
|
||||
int n;
|
||||
unsigned char *pinfo;
|
||||
char *p;
|
||||
int info_len;
|
||||
|
||||
|
||||
|
@ -867,26 +881,34 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
|
|||
return; /* Login not complete. */
|
||||
}
|
||||
|
||||
/* Gather statistics. */
|
||||
|
||||
stats_rf_recv_packets++;
|
||||
|
||||
/*
|
||||
* Check for filtering from specified channel to the IGate server.
|
||||
*
|
||||
* Should we do this after unwrapping the payload from a third party packet?
|
||||
* In my experience, third party packets have only been seen coming from IGates.
|
||||
* In that case, the payload will have TCPIP in the path and it will be dropped.
|
||||
*/
|
||||
|
||||
if (save_digi_config_p->filter_str[chan][MAX_CHANS] != NULL) {
|
||||
|
||||
if (pfilter(chan, MAX_CHANS, save_digi_config_p->filter_str[chan][MAX_CHANS], recv_pp) != 1) {
|
||||
if (pfilter(chan, MAX_CHANS, save_digi_config_p->filter_str[chan][MAX_CHANS], recv_pp, 1) != 1) {
|
||||
|
||||
// Is this useful troubleshooting information or just distracting noise?
|
||||
// Originally this was always printed but there was a request to add a "quiet" option to suppress this.
|
||||
// version 1.4: Instead, make the default off and activate it only with the debug igate option.
|
||||
|
||||
if (s_debug >= 1) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Packet from channel %d to IGate was rejected by filter: %s\n", chan, save_digi_config_p->filter_str[chan][MAX_CHANS]);
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Gather statistics. */
|
||||
|
||||
stats_rf_recv_packets++;
|
||||
|
||||
/*
|
||||
* First make a copy of it because it might be modified in place.
|
||||
*/
|
||||
|
@ -972,32 +994,25 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
|
|||
|
||||
/*
|
||||
* Cut the information part at the first CR or LF.
|
||||
* This is required because CR/LF is used as record separator when sending to server.
|
||||
* Do NOT trim trailing spaces.
|
||||
* Starting in 1.4 we preserve any nul characters in the information part.
|
||||
*/
|
||||
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
(void)(info_len);
|
||||
|
||||
if ((p = strchr ((char*)pinfo, '\r')) != NULL) {
|
||||
if (ax25_cut_at_crlf (pp) > 0) {
|
||||
if (s_debug >= 1) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Rx IGate: Truncated information part at CR.\n");
|
||||
}
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
if ((p = strchr ((char*)pinfo, '\n')) != NULL) {
|
||||
if (s_debug >= 1) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Rx IGate: Truncated information part at LF.\n");
|
||||
}
|
||||
*p = '\0';
|
||||
}
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
|
||||
|
||||
/*
|
||||
* Someone around here occasionally sends a packet with no information part.
|
||||
*/
|
||||
if (strlen((char*)pinfo) == 0) {
|
||||
if (info_len == 0) {
|
||||
|
||||
if (s_debug >= 1) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
|
@ -1053,10 +1068,15 @@ static void send_packet_to_server (packet_t pp, int chan)
|
|||
|
||||
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
(void)(info_len);
|
||||
|
||||
/*
|
||||
* Do not relay if a duplicate of something sent recently.
|
||||
* We will often see the same packet multiple times close together due to digipeating.
|
||||
* The consensus seems to be that we should just send the first and drop the later duplicates.
|
||||
* There is some dissent on this issue. http://www.tapr.org/pipermail/aprssig/2016-July/045907.html
|
||||
* There could be some value to sending them all to provide information about digipeater paths.
|
||||
* However, the servers should drop all duplicates so we wasting everyone's time but sending duplicates.
|
||||
* If you feel strongly about this issue, you could remove the following section.
|
||||
* Currently rx_to_ig_allow only checks for recent duplicates.
|
||||
*/
|
||||
|
||||
if ( ! rx_to_ig_allow(pp)) {
|
||||
|
@ -1070,17 +1090,111 @@ static void send_packet_to_server (packet_t pp, int chan)
|
|||
|
||||
/*
|
||||
* Finally, append ",qAR," and my call to the path.
|
||||
*/
|
||||
|
||||
/*
|
||||
* It seems that the specification has changed recently.
|
||||
* http://www.tapr.org/pipermail/aprssig/2016-December/046456.html
|
||||
*
|
||||
* We can see the history at the Internet Archive Wayback Machine.
|
||||
*
|
||||
* http://www.aprs-is.net/Connecting.aspx
|
||||
* captured Oct 19, 2016:
|
||||
* ... Only the qAR construct may be generated by a client (IGate) on APRS-IS.
|
||||
* Captured Dec 1, 2016:
|
||||
* ... Only the qAR and qAO constructs may be generated by a client (IGate) on APRS-IS.
|
||||
*
|
||||
* http://www.aprs-is.net/q.aspx
|
||||
* Captured April 23, 2016:
|
||||
* (no mention of client generating qAO.)
|
||||
* Captured July 19, 2016:
|
||||
* qAO - (letter O) Packet is placed on APRS-IS by a receive-only IGate from RF.
|
||||
* The callSSID following the qAO is the callSSID of the IGate. Note that receive-only
|
||||
* IGates are discouraged on standard APRS frequencies. Please consider a bidirectional
|
||||
* IGate that only gates to RF messages for stations heard directly.
|
||||
*/
|
||||
|
||||
ax25_format_addrs (pp, msg);
|
||||
msg[strlen(msg)-1] = '\0'; /* Remove trailing ":" */
|
||||
|
||||
if (save_igate_config_p->tx_chan >= 0) {
|
||||
strlcat (msg, ",qAR,", sizeof(msg));
|
||||
}
|
||||
else {
|
||||
strlcat (msg, ",qAO,", sizeof(msg)); // new for version 1.4.
|
||||
}
|
||||
|
||||
strlcat (msg, save_audio_config_p->achan[chan].mycall, sizeof(msg));
|
||||
strlcat (msg, ":", sizeof(msg));
|
||||
strlcat (msg, (char*)pinfo, sizeof(msg));
|
||||
|
||||
send_msg_to_server (msg);
|
||||
stats_rx_igate_packets++;
|
||||
|
||||
|
||||
// It was reported that APRS packets, containing a nul byte in the information part,
|
||||
// are being truncated. https://github.com/wb2osz/direwolf/issues/84
|
||||
//
|
||||
// One might argue that the packets are invalid and the proper behavior would be
|
||||
// to simply discard them, the same way we do if the CRC is bad. One might argue
|
||||
// that we should simply pass along whatever we receive even if we don't like it.
|
||||
// We really shouldn't modify it and make the situation even worse.
|
||||
//
|
||||
// Chapter 5 of the APRS spec ( http://www.aprs.org/doc/APRS101.PDF ) says:
|
||||
//
|
||||
// "The comment may contain any printable ASCII characters (except | and ~,
|
||||
// which are reserved for TNC channel switching)."
|
||||
//
|
||||
// "Printable" would exclude character values less than space (00100000), e.g.
|
||||
// tab, carriage return, line feed, nul. Sometimes we see carriage return
|
||||
// (00001010) at the end of APRS packets. This would be in violation of the
|
||||
// specification.
|
||||
//
|
||||
// The MIC-E position format can have non printable characters (0x1c ... 0x1f, 0x7f)
|
||||
// in the information part. An unfortunate decision, but it is not in the comment part.
|
||||
//
|
||||
// The base 91 telemetry format (http://he.fi/doc/aprs-base91-comment-telemetry.txt ),
|
||||
// which is not part of the APRS spec, uses the | character in the comment to delimit encoded
|
||||
// telemetry data. This would be in violation of the original spec. No one cares.
|
||||
//
|
||||
// The APRS Spec Addendum 1.2 Proposals ( http://www.aprs.org/aprs12/datum.txt)
|
||||
// adds use of UTF-8 (https://en.wikipedia.org/wiki/UTF-8 )for the free form text in
|
||||
// messages and comments. It can't be used in the fixed width fields.
|
||||
//
|
||||
// Non-ASCII characters are represented by multi-byte sequences. All bytes in these
|
||||
// multi-byte sequences have the most significant bit set to 1. Using UTF-8 would not
|
||||
// add any nul (00000000) bytes to the stream.
|
||||
//
|
||||
// Based on all of that, we would not expect to see a nul character in the information part.
|
||||
//
|
||||
// There are two known cases where we can have a nul character value.
|
||||
//
|
||||
// * The Kenwood TM-D710A sometimes sends packets like this:
|
||||
//
|
||||
// VA3AJ-9>T2QU6X,VE3WRC,WIDE1,K8UNS,WIDE2*:4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00>`nW<0x1f>oS8>/]"6M}driving fast=
|
||||
// K4JH-9>S5UQ6X,WR4AGC-3*,WIDE1*:4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00>`jP}l"&>/]"47}QRV from the EV =
|
||||
//
|
||||
// Notice that the data type indicator of "4" is not valid. If we remove
|
||||
// 4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00> we are left with a good MIC-E format.
|
||||
// This same thing has been observed from others and is intermittent.
|
||||
//
|
||||
// * AGW Tracker can send UTF-16 if an option is selected. This can introduce nul bytes.
|
||||
// This is wrong, it should be using UTF-8.
|
||||
//
|
||||
// Rather than using strlcat here, we need to use memcpy and maintain our
|
||||
// own lengths, being careful to avoid buffer overflow.
|
||||
|
||||
int msg_len = strlen(msg); // What we have so far before info part.
|
||||
|
||||
if (info_len > IGATE_MAX_MSG - msg_len - 2) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Rx IGate: Too long. Truncating.\n");
|
||||
info_len = IGATE_MAX_MSG - msg_len - 2;
|
||||
}
|
||||
if (info_len > 0) {
|
||||
memcpy (msg + msg_len, pinfo, info_len);
|
||||
msg_len += info_len;
|
||||
}
|
||||
|
||||
send_msg_to_server (msg, msg_len);
|
||||
stats_uplink_packets++;
|
||||
|
||||
/*
|
||||
* Remember what was sent to avoid duplicates in near future.
|
||||
|
@ -1097,58 +1211,74 @@ static void send_packet_to_server (packet_t pp, int chan)
|
|||
*
|
||||
* Name: send_msg_to_server
|
||||
*
|
||||
* Purpose: Send to the IGate server.
|
||||
* Purpose: Send something to the IGate server.
|
||||
* This one function should be used for login, hearbeats,
|
||||
* and packets.
|
||||
*
|
||||
* Inputs: imsg - Message. We will add CR/LF.
|
||||
* Inputs: imsg - Message. We will add CR/LF here.
|
||||
*
|
||||
* imsg_len - Length of imsg in bytes.
|
||||
* It could contain nul characters so we can't
|
||||
* use the normal C string functions.
|
||||
*
|
||||
* Description: Send message to IGate Server if connected.
|
||||
* Disconnect from server, and notify user, if any error.
|
||||
* Should use a word other than message because that has
|
||||
* a specific meaning for APRS.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static void send_msg_to_server (const char *imsg)
|
||||
static void send_msg_to_server (const char *imsg, int imsg_len)
|
||||
{
|
||||
int err;
|
||||
char stemp[IGATE_MAX_MSG];
|
||||
char stemp[IGATE_MAX_MSG+1];
|
||||
int stemp_len;
|
||||
|
||||
if (igate_sock == -1) {
|
||||
return; /* Silently discard if not connected. */
|
||||
}
|
||||
|
||||
strlcpy(stemp, imsg, sizeof(stemp));
|
||||
stemp_len = imsg_len;
|
||||
if (stemp_len + 2 > IGATE_MAX_MSG) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Rx IGate: Too long. Truncating.\n");
|
||||
stemp_len = IGATE_MAX_MSG - 2;
|
||||
}
|
||||
|
||||
memcpy (stemp, imsg, stemp_len);
|
||||
|
||||
if (s_debug >= 1) {
|
||||
text_color_set(DW_COLOR_XMIT);
|
||||
dw_printf ("[rx>ig] ");
|
||||
ax25_safe_print (stemp, strlen(stemp), 0);
|
||||
ax25_safe_print (stemp, stemp_len, 0);
|
||||
dw_printf ("\n");
|
||||
}
|
||||
|
||||
strlcat (stemp, "\r\n", sizeof(stemp));
|
||||
stemp[stemp_len++] = '\r';
|
||||
stemp[stemp_len++] = '\n';
|
||||
stemp[stemp_len] = '\0';
|
||||
|
||||
stats_uplink_bytes += stemp_len;
|
||||
|
||||
stats_uplink_bytes += strlen(stemp);
|
||||
|
||||
#if __WIN32__
|
||||
err = send (igate_sock, stemp, strlen(stemp), 0);
|
||||
err = send (igate_sock, stemp, stemp_len, 0);
|
||||
if (err == SOCKET_ERROR)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nError %d sending message to IGate server. Closing connection.\n\n", WSAGetLastError());
|
||||
dw_printf ("\nError %d sending to IGate server. Closing connection.\n\n", WSAGetLastError());
|
||||
//dw_printf ("DEBUG: igate_sock=%d, line=%d\n", igate_sock, __LINE__);
|
||||
closesocket (igate_sock);
|
||||
igate_sock = -1;
|
||||
WSACleanup();
|
||||
}
|
||||
#else
|
||||
err = write (igate_sock, stemp, strlen(stemp));
|
||||
err = write (igate_sock, stemp, stemp_len);
|
||||
if (err <= 0)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nError sending message to IGate server. Closing connection.\n\n");
|
||||
dw_printf ("\nError sending to IGate server. Closing connection.\n\n");
|
||||
close (igate_sock);
|
||||
igate_sock = -1;
|
||||
}
|
||||
|
@ -1240,7 +1370,7 @@ static void * igate_recv_thread (void *arg)
|
|||
#endif
|
||||
{
|
||||
unsigned char ch;
|
||||
unsigned char message[1000]; // Spec says max 500 or so.
|
||||
unsigned char message[1000]; // Spec says max 512.
|
||||
int len;
|
||||
|
||||
|
||||
|
@ -1258,14 +1388,27 @@ static void * igate_recv_thread (void *arg)
|
|||
ch = get1ch();
|
||||
stats_downlink_bytes++;
|
||||
|
||||
if (len < sizeof(message))
|
||||
{
|
||||
message[len] = ch;
|
||||
// I never expected to see a nul character but it can happen.
|
||||
// If found, change it to <0x00> and ax25_from_text will change it back to a single byte.
|
||||
// Along the way we can use the normal C string handling.
|
||||
|
||||
if (ch == 0 && len < (int)(sizeof(message)) - 5) {
|
||||
message[len++] = '<';
|
||||
message[len++] = '0';
|
||||
message[len++] = 'x';
|
||||
message[len++] = '0';
|
||||
message[len++] = '0';
|
||||
message[len++] = '>';
|
||||
}
|
||||
else if (len < (int)(sizeof(message)))
|
||||
{
|
||||
message[len++] = ch;
|
||||
}
|
||||
len++;
|
||||
|
||||
} while (ch != '\n');
|
||||
|
||||
message[sizeof(message)-1] = '\0';
|
||||
|
||||
/*
|
||||
* We have a complete message terminated by LF.
|
||||
*
|
||||
|
@ -1280,10 +1423,13 @@ static void * igate_recv_thread (void *arg)
|
|||
* I've seen a case where the original RF packet had a trailing CR but
|
||||
* after someone else sent it to the server and it came back to me, that
|
||||
* CR was now a trailing space.
|
||||
*
|
||||
* At first I was tempted to trim a trailing space as well.
|
||||
* By fixing this one case it might corrupt the data in other cases.
|
||||
* We compensate for this by ignoring trailing spaces when performing
|
||||
* the duplicate detection and removal.
|
||||
*
|
||||
* We need to transmit exactly as we get it.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -1329,10 +1475,33 @@ static void * igate_recv_thread (void *arg)
|
|||
ax25_safe_print ((char *)message, len, 0);
|
||||
dw_printf ("\n");
|
||||
|
||||
if ((int)strlen((char*)message) != len) {
|
||||
|
||||
// Invalid. Either drop it or pass it along as-is. Don't change.
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("'nul' character found in packet from IS. This should never happen.\n");
|
||||
dw_printf("The source station is probably transmitting with defective software.\n");
|
||||
|
||||
//if (strcmp((char*)pinfo, "4P") == 0) {
|
||||
// dw_printf("The TM-D710 will do this intermittently. A firmware upgrade is needed to fix it.\n");
|
||||
//}
|
||||
}
|
||||
|
||||
/*
|
||||
* Record that we heard from the source address.
|
||||
*/
|
||||
mheard_save_is ((char *)message);
|
||||
|
||||
stats_downlink_packets++;
|
||||
|
||||
/*
|
||||
* Possibly transmit if so configured.
|
||||
*/
|
||||
int to_chan = save_igate_config_p->tx_chan;
|
||||
|
||||
if (to_chan >= 0) {
|
||||
xmit_packet ((char*)message, to_chan);
|
||||
maybe_xmit_packet_from_igate ((char*)message, to_chan);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1464,10 +1633,10 @@ static void * satgate_delay_thread (void *arg)
|
|||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: xmit_packet
|
||||
* Name: maybe_xmit_packet_from_igate
|
||||
*
|
||||
* Purpose: Convert text string, from IGate server, to third party
|
||||
* packet and send to transmit queue.
|
||||
* packet and send to transmit queue if appropriate.
|
||||
*
|
||||
* Inputs: message - As sent by the server.
|
||||
* Any trailing CRLF should have been removed.
|
||||
|
@ -1485,18 +1654,23 @@ static void * satgate_delay_thread (void *arg)
|
|||
* repackaging to go over the radio.
|
||||
*
|
||||
* The "q construct" ( http://www.aprs-is.net/q.aspx ) provides
|
||||
* a clue about the journey taken but I don't think we care here.
|
||||
* a clue about the journey taken. "qAX" means that the station sending
|
||||
* the packet to the server did not login properly as a ham radio
|
||||
* operator so we don't want to put this on to RF.
|
||||
*
|
||||
* to_chan - Radio channel for transmitting.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static void xmit_packet (char *message, int to_chan)
|
||||
static void maybe_xmit_packet_from_igate (char *message, int to_chan)
|
||||
{
|
||||
packet_t pp3;
|
||||
char payload[AX25_MAX_PACKET_LEN]; /* what is max len? */
|
||||
char src[AX25_MAX_ADDR_LEN]; /* Source address. */
|
||||
|
||||
char *pinfo = NULL;
|
||||
int info_len;
|
||||
int n;
|
||||
|
||||
assert (to_chan >= 0 && to_chan < MAX_CHANS);
|
||||
|
||||
|
@ -1518,6 +1692,32 @@ static void xmit_packet (char *message, int to_chan)
|
|||
return;
|
||||
}
|
||||
|
||||
ax25_get_addr_with_ssid (pp3, AX25_SOURCE, src);
|
||||
|
||||
/*
|
||||
* Drop if path contains:
|
||||
* NOGATE or RFONLY - means IGate should not pass them.
|
||||
* TCPXX or qAX - means it came from somewhere that did not identify itself correctly.
|
||||
*/
|
||||
for (n = 0; n < ax25_get_num_repeaters(pp3); n++) {
|
||||
char via[AX25_MAX_ADDR_LEN]; /* includes ssid. Do we want to ignore it? */
|
||||
|
||||
ax25_get_addr_with_ssid (pp3, n + AX25_REPEATER_1, via);
|
||||
|
||||
if (strcmp(via, "qAX") == 0 ||
|
||||
strcmp(via, "TCPXX") == 0 ||
|
||||
strcmp(via, "RFONLY") == 0 ||
|
||||
strcmp(via, "NOGATE") == 0) {
|
||||
|
||||
if (s_debug >= 1) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Tx IGate: Do not transmit with %s in path.\n", via);
|
||||
}
|
||||
|
||||
ax25_delete (pp3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply our own packet filtering if configured.
|
||||
|
@ -1528,17 +1728,60 @@ static void xmit_packet (char *message, int to_chan)
|
|||
|
||||
assert (to_chan >= 0 && to_chan < MAX_CHANS);
|
||||
|
||||
|
||||
/*
|
||||
* We have a rather strange special case here.
|
||||
* If we recently transmitted a 'message' from some station,
|
||||
* send the position of the message sender when it comes along later.
|
||||
*
|
||||
* If we have a position report, look up the sender and see if we should
|
||||
* bypass the normal filtering.
|
||||
*/
|
||||
|
||||
// TODO: Not quite this simple. Should have a function to check for position.
|
||||
// $ raw gps could be a position. @ could be weather data depending on symbol.
|
||||
|
||||
info_len = ax25_get_info (pp3, (unsigned char **)(&pinfo));
|
||||
|
||||
int msp_special_case = 0;
|
||||
|
||||
if (info_len >= 1 && strchr("!=/@'`", *pinfo) != NULL) {
|
||||
|
||||
int n = mheard_get_msp(src);
|
||||
|
||||
if (n > 0) {
|
||||
|
||||
msp_special_case = 1;
|
||||
|
||||
if (s_debug >= 1) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Special case, allow position from message sender %s, %d remaining.\n", src, n - 1);
|
||||
}
|
||||
|
||||
mheard_set_msp (src, n - 1);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! msp_special_case) {
|
||||
|
||||
if (save_digi_config_p->filter_str[MAX_CHANS][to_chan] != NULL) {
|
||||
|
||||
if (pfilter(MAX_CHANS, to_chan, save_digi_config_p->filter_str[MAX_CHANS][to_chan], pp3) != 1) {
|
||||
if (pfilter(MAX_CHANS, to_chan, save_digi_config_p->filter_str[MAX_CHANS][to_chan], pp3, 1) != 1) {
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Packet from IGate to channel %d was rejected by filter: %s\n", to_chan, save_digi_config_p->filter_str[MAX_CHANS][to_chan]);
|
||||
// Previously there was a debug message here about the packet being dropped by filtering.
|
||||
// This is now handled better by the "-df" command line option for filtering details.
|
||||
|
||||
// TODO: clean up - remove these lines.
|
||||
//if (s_debug >= 1) {
|
||||
// text_color_set(DW_COLOR_INFO);
|
||||
// dw_printf ("Packet from IGate to channel %d was rejected by filter: %s\n", to_chan, save_digi_config_p->filter_str[MAX_CHANS][to_chan]);
|
||||
//}
|
||||
|
||||
ax25_delete (pp3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
@ -1549,6 +1792,34 @@ static void xmit_packet (char *message, int to_chan)
|
|||
*
|
||||
* We want to reduce it to this before wrapping it as third party traffic.
|
||||
* K1USN-1>APWW10:T#479,100,048,002,500,000,10000000<0x0d><0x0a>
|
||||
*/
|
||||
|
||||
/*
|
||||
* These are typical examples where we see TCPIP*,qAC,<server>
|
||||
*
|
||||
* N3LLO-4>APRX28,TCPIP*,qAC,T2NUENGLD:T#474,21.4,0.3,114.0,4.0,0.0,00000000
|
||||
* N1WJO>APWW10,TCPIP*,qAC,T2MAINE:)147.120!4412.27N/07033.27WrW1OCA repeater136.5 Tone Norway Me
|
||||
* AB1OC-10>APWW10,TCPIP*,qAC,T2IAD2:=4242.70N/07135.41W#(Time 0:00:00)!INSERVICE!!W60!
|
||||
*
|
||||
* But sometimes we get a different form:
|
||||
*
|
||||
* N1YG-1>T1SY9P,WIDE1-1,WIDE2-2,qAR,W2DAN-15:'c&<0x7f>l <0x1c>-/>
|
||||
* W1HS-8>TSSP9T,WIDE1-1,WIDE2-1,qAR,N3LLO-2:`d^Vl"W>/'"85}|*&%_'[|!wLK!|3
|
||||
* N1RCW-1>APU25N,MA2-2,qAR,KA1VCQ-1:=4140.41N/07030.21W-Home Station/Fill-in Digi {UIV32N}
|
||||
* N1IEJ>T4PY3U,W1EMA-1,WIDE1*,WIDE2-2,qAR,KD1KE:`a5"l!<0x7f>-/]"4f}Retired & Busy=
|
||||
*
|
||||
* Oh! They have qAR rather than qAC. What does that mean?
|
||||
* From http://www.aprs-is.net/q.aspx
|
||||
*
|
||||
* qAC - Packet was received from the client directly via a verified connection (FROMCALL=login).
|
||||
* The callSSID following the qAC is the server's callsign-SSID.
|
||||
*
|
||||
* qAR - Packet was received directly (via a verified connection) from an IGate using the ,I construct.
|
||||
* The callSSID following the qAR it the callSSID of the IGate.
|
||||
*
|
||||
* What is the ",I" construct?
|
||||
* Do we care here?
|
||||
* Is is something new and improved that we should be using in the other direction?
|
||||
*/
|
||||
|
||||
while (ax25_get_num_repeaters(pp3) > 0) {
|
||||
|
@ -1581,6 +1852,16 @@ static void xmit_packet (char *message, int to_chan)
|
|||
|
||||
/*
|
||||
* Encapsulate for sending over radio if no reason to drop it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We don't want to suppress duplicate "messages" within a short time period.
|
||||
* Suppose we transmitted a "message" for some station and it did not respond with an ack.
|
||||
* 25 seconds later the sender retries. Wouldn't we want to pass along that retry?
|
||||
*
|
||||
* "Messages" get preferential treatment because they are high value and very rare.
|
||||
* -> Bypass the duplicate suppression.
|
||||
* -> Raise the rate limiting value.
|
||||
*/
|
||||
if (ig_to_tx_allow (pp3, to_chan)) {
|
||||
char radio [500];
|
||||
|
@ -1599,8 +1880,6 @@ static void xmit_packet (char *message, int to_chan)
|
|||
|
||||
if (pradio != NULL) {
|
||||
|
||||
stats_tx_igate_packets++;
|
||||
|
||||
#if ITEST
|
||||
text_color_set(DW_COLOR_XMIT);
|
||||
dw_printf ("Xmit: %s\n", radio);
|
||||
|
@ -1609,7 +1888,21 @@ static void xmit_packet (char *message, int to_chan)
|
|||
/* This consumes packet so don't reference it again! */
|
||||
tq_append (to_chan, TQ_PRIO_1_LO, pradio);
|
||||
#endif
|
||||
stats_rf_xmit_packets++;
|
||||
stats_rf_xmit_packets++; // Any type of packet.
|
||||
|
||||
// TEMP TEST: metadata temporarily allowed during testing.
|
||||
|
||||
if (*pinfo == ':' && ! is_telem_metadata(pinfo)) {
|
||||
// temp test // if (*pinfo == ':') {
|
||||
|
||||
// We transmitted a "message." Telemetry metadata is excluded.
|
||||
// Remember to pass along address of the sender later.
|
||||
|
||||
stats_msg_cnt++; // Update statistics.
|
||||
|
||||
mheard_set_msp (src, save_igate_config_p->igmsp);
|
||||
}
|
||||
|
||||
ig_to_tx_remember (pp3, save_igate_config_p->tx_chan, 0); // correct. version before encapsulating it.
|
||||
}
|
||||
else {
|
||||
|
@ -1624,7 +1917,7 @@ static void xmit_packet (char *message, int to_chan)
|
|||
|
||||
ax25_delete (pp3);
|
||||
|
||||
} /* end xmit_packet */
|
||||
} /* end maybe_xmit_packet_from_igate */
|
||||
|
||||
|
||||
|
||||
|
@ -1701,6 +1994,7 @@ static void rx_to_ig_remember (packet_t pp)
|
|||
ax25_get_addr_with_ssid(pp, AX25_SOURCE, src);
|
||||
ax25_get_addr_with_ssid(pp, AX25_DESTINATION, dest);
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
(void)info_len;
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("rx_to_ig_remember [%d] = %d %d \"%s>%s:%s\"\n",
|
||||
|
@ -1731,6 +2025,7 @@ static int rx_to_ig_allow (packet_t pp)
|
|||
ax25_get_addr_with_ssid(pp, AX25_SOURCE, src);
|
||||
ax25_get_addr_with_ssid(pp, AX25_DESTINATION, dest);
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
(void)info_len;
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("rx_to_ig_allow? %d \"%s>%s:%s\"\n", crc, src, dest, pinfo);
|
||||
|
@ -1798,7 +2093,7 @@ static int rx_to_ig_allow (packet_t pp)
|
|||
* duplicate of another sent recently.
|
||||
*
|
||||
* This is the essentially the same as the pair of functions
|
||||
* above with one addition restriction.
|
||||
* above, for RF to IS, with one additional restriction.
|
||||
*
|
||||
* The typical residential Internet connection is around 10,000
|
||||
* to 50,000 times faster than the radio links we are using. It would
|
||||
|
@ -1847,10 +2142,12 @@ static int rx_to_ig_allow (packet_t pp)
|
|||
* At first I thought duplicate removal was broken but it turns out they
|
||||
* are not exactly the same.
|
||||
*
|
||||
* The receive IGate spec says a packet should be cut at a CR.
|
||||
* >>> The receive IGate spec says a packet should be cut at a CR. <<<
|
||||
*
|
||||
* In one case it is removed as expected In another case, it is replaced by a trailing
|
||||
* space character. Maybe someone thought non printable characters should be
|
||||
* replaced by spaces???
|
||||
* replaced by spaces??? (I have since been told someone thought it would be a good
|
||||
* idea to replace unprintable characters with spaces. How's that working out for MIC-E position???)
|
||||
*
|
||||
* At first I was tempted to remove any trailing spaces to make up for the other
|
||||
* IGate adding it. Two wrongs don't make a right. Trailing spaces are not that
|
||||
|
@ -1986,6 +2283,7 @@ void ig_to_tx_remember (packet_t pp, int chan, int bydigi)
|
|||
ax25_get_addr_with_ssid(pp, AX25_SOURCE, src);
|
||||
ax25_get_addr_with_ssid(pp, AX25_DESTINATION, dest);
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
(void)info_len;
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("ig_to_tx_remember [%d] = ch%d d%d %d %d \"%s>%s:%s\"\n",
|
||||
|
@ -2012,16 +2310,20 @@ static int ig_to_tx_allow (packet_t pp, int chan)
|
|||
time_t now = time(NULL);
|
||||
int j;
|
||||
int count_1, count_5;
|
||||
int increase_limit;
|
||||
|
||||
unsigned char *pinfo;
|
||||
int info_len;
|
||||
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
(void)info_len;
|
||||
|
||||
if (s_debug >= 2) {
|
||||
char src[AX25_MAX_ADDR_LEN];
|
||||
char dest[AX25_MAX_ADDR_LEN];
|
||||
unsigned char *pinfo;
|
||||
int info_len;
|
||||
|
||||
ax25_get_addr_with_ssid(pp, AX25_SOURCE, src);
|
||||
ax25_get_addr_with_ssid(pp, AX25_DESTINATION, dest);
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("ig_to_tx_allow? ch%d %d \"%s>%s:%s\"\n", chan, crc, src, dest, pinfo);
|
||||
|
@ -2031,16 +2333,37 @@ static int ig_to_tx_allow (packet_t pp, int chan)
|
|||
|
||||
for (j=0; j<IG2TX_HISTORY_MAX; j++) {
|
||||
if (ig2tx_checksum[j] == crc && ig2tx_chan[j] == chan && ig2tx_time_stamp[j] >= now - IG2TX_DEDUPE_TIME) {
|
||||
|
||||
/* We have a duplicate within some time period. */
|
||||
|
||||
if (*pinfo == ':' && ! is_telem_metadata((char*)pinfo)) {
|
||||
|
||||
/* I think I want to avoid the duplicate suppression for "messages." */
|
||||
/* Suppose we transmit a message from station X and it doesn't get an ack back. */
|
||||
/* Station X then sends exactly the same thing 20 seconds later. */
|
||||
/* We don't want to suppress the retry. */
|
||||
|
||||
if (s_debug >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("ig_to_tx_allow? Yes for duplicate message sent %d seconds ago. bydigi=%d\n", (int)(now - ig2tx_time_stamp[j]), ig2tx_bydigi[j]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
/* Normal (non-message) case. */
|
||||
|
||||
if (s_debug >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
// could be multiple entries and this might not be the most recent.
|
||||
dw_printf ("ig_to_tx_allow? NO. Sent %d seconds ago. bydigi=%d\n", (int)(now - ig2tx_time_stamp[j]), ig2tx_bydigi[j]);
|
||||
dw_printf ("ig_to_tx_allow? NO. Duplicate sent %d seconds ago. bydigi=%d\n", (int)(now - ig2tx_time_stamp[j]), ig2tx_bydigi[j]);
|
||||
}
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Tx IGate: Drop duplicate packet transmitted recently.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* IGate transmit counts must not include digipeater transmissions. */
|
||||
|
||||
|
@ -2053,12 +2376,25 @@ static int ig_to_tx_allow (packet_t pp, int chan)
|
|||
}
|
||||
}
|
||||
|
||||
if (count_1 >= save_igate_config_p->tx_limit_1) {
|
||||
/* "Messages" (special APRS data type ":") are intentional and more */
|
||||
/* important than all of the other mostly repetitive useless junk */
|
||||
/* flowing thru here. */
|
||||
/* It would be unfortunate to discard a message because we already */
|
||||
/* hit our limit. I don't want to completely eliminate limiting for */
|
||||
/* messages, in case something goes terribly wrong, but we can triple */
|
||||
/* the normal limit for them. */
|
||||
|
||||
increase_limit = 1;
|
||||
if (*pinfo == ':' && ! is_telem_metadata((char*)pinfo)) {
|
||||
increase_limit = 3;
|
||||
}
|
||||
|
||||
if (count_1 >= save_igate_config_p->tx_limit_1 * increase_limit) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Tx IGate: Already transmitted maximum of %d packets in 1 minute.\n", save_igate_config_p->tx_limit_1);
|
||||
return 0;
|
||||
}
|
||||
if (count_5 >= save_igate_config_p->tx_limit_5) {
|
||||
if (count_5 >= save_igate_config_p->tx_limit_5 * increase_limit) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Tx IGate: Already transmitted maximum of %d packets in 5 minutes.\n", save_igate_config_p->tx_limit_5);
|
||||
return 0;
|
||||
|
|
29
igate.h
29
igate.h
|
@ -45,15 +45,30 @@ struct igate_config_s {
|
|||
*/
|
||||
int tx_chan; /* Radio channel for transmitting. */
|
||||
/* 0=first, etc. -1 for none. */
|
||||
/* Presently IGate can transmit on only a single channel. */
|
||||
/* A future version might generalize this. */
|
||||
/* Each transmit channel would have its own client side filtering. */
|
||||
|
||||
char tx_via[80]; /* VIA path for transmitting third party packets. */
|
||||
/* Usual text representation. */
|
||||
/* Must start with "," if not empty so it can */
|
||||
/* simply be inserted after the destination address. */
|
||||
|
||||
int max_digi_hops; /* Maximum number of digipeater hops possible for via path. */
|
||||
/* Derived from the SSID when last character of address is a digit. */
|
||||
/* e.g. "WIDE1-1,WIDE5-2" would be 3. */
|
||||
/* This is useful to know so we can determine how many */
|
||||
/* stations we might be able to reach. */
|
||||
|
||||
int tx_limit_1; /* Max. packets to transmit in 1 minute. */
|
||||
|
||||
int tx_limit_5; /* Max. packets to transmit in 5 minutes. */
|
||||
|
||||
int igmsp; /* Number of message sender position reports to allow. */
|
||||
/* Common practice is to default to 1. */
|
||||
/* We allow additional flexibility of 0 to disable feature */
|
||||
/* or a small number to allow more. */
|
||||
|
||||
/*
|
||||
* Special SATgate mode to delay packets heard directly.
|
||||
*/
|
||||
|
@ -84,4 +99,18 @@ void igate_send_rec_packet (int chan, packet_t recv_pp);
|
|||
|
||||
void ig_to_tx_remember (packet_t pp, int chan, int bydigi);
|
||||
|
||||
|
||||
|
||||
/* Get statistics for IGATE status beacon. */
|
||||
|
||||
int igate_get_msg_cnt (void);
|
||||
|
||||
int igate_get_pkt_cnt (void);
|
||||
|
||||
int igate_get_upl_cnt (void);
|
||||
|
||||
int igate_get_dnl_cnt (void);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
|
187
kiss.c
187
kiss.c
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2011, 2013, 2014 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2013, 2014, 2016 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
|
||||
|
@ -24,14 +24,13 @@
|
|||
* Module: kiss.c
|
||||
*
|
||||
* Purpose: Act as a virtual KISS TNC for use by other packet radio applications.
|
||||
* On Windows, it is a serial port. On Linux, a pseudo terminal.
|
||||
*
|
||||
* Input:
|
||||
*
|
||||
* Outputs:
|
||||
*
|
||||
* Description: This provides a pseudo terminal for communication with a client application.
|
||||
*
|
||||
* It implements the KISS TNC protocol as described in:
|
||||
* Description: It implements the KISS TNC protocol as described in:
|
||||
* http://www.ka9q.net/papers/kiss.html
|
||||
*
|
||||
* Briefly, a frame is composed of
|
||||
|
@ -113,20 +112,19 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if __WIN32__
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#define __USE_XOPEN2KXSI 1
|
||||
#define __USE_XOPEN 1
|
||||
//#define __USE_POSIX 1
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#ifdef __OpenBSD__
|
||||
|
@ -139,7 +137,7 @@
|
|||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include "tq.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
|
@ -242,17 +240,22 @@ void hex_dump (unsigned char *p, int len);
|
|||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static MYFDTYPE kiss_open_pt (void);
|
||||
#if __WIN32__
|
||||
static MYFDTYPE kiss_open_nullmodem (char *device);
|
||||
#else
|
||||
static MYFDTYPE kiss_open_pt (void);
|
||||
#endif
|
||||
|
||||
|
||||
void kiss_init (struct misc_config_s *mc)
|
||||
{
|
||||
int e;
|
||||
|
||||
#if __WIN32__
|
||||
HANDLE kiss_nullmodem_listen_th;
|
||||
#else
|
||||
pthread_t kiss_pterm_listen_tid;
|
||||
pthread_t kiss_nullmodem_listen_tid;
|
||||
//pthread_t kiss_nullmodem_listen_tid;
|
||||
int e;
|
||||
#endif
|
||||
|
||||
memset (&kf, 0, sizeof(kf));
|
||||
|
@ -355,7 +358,7 @@ static MYFDTYPE kiss_open_pt (void)
|
|||
char *pts;
|
||||
struct termios ts;
|
||||
int e;
|
||||
//int flags;
|
||||
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
|
@ -397,39 +400,31 @@ static MYFDTYPE kiss_open_pt (void)
|
|||
}
|
||||
|
||||
/*
|
||||
* After running for a while on Linux, the write eventually
|
||||
* blocks if no one is reading from the other side of
|
||||
* the pseudo terminal. We get stuck on the kiss data
|
||||
* write and reception stops.
|
||||
* We had a problem here since the beginning.
|
||||
* If no one was reading from the other end of the pseudo
|
||||
* terminal, the buffer space would eventually fill up,
|
||||
* the write here would block, and the receive decode
|
||||
* thread would get stuck.
|
||||
*
|
||||
* I tried using ioctl(,TIOCOUTQ,) to see how much was in
|
||||
* the queue but that always returned zero. (Ubuntu)
|
||||
*
|
||||
* Let's try using non-blocking writes and see if we get
|
||||
* the EWOULDBLOCK status instead of hanging.
|
||||
* March 2016 - A "select" was put before the read to
|
||||
* solve a different problem. With that in place, we can
|
||||
* now use non-blocking I/O and detect the buffer full
|
||||
* condition here.
|
||||
*/
|
||||
|
||||
#if 0 // this is worse. all writes fail. errno = 0 bad file descriptor
|
||||
flags = fcntl(fd, F_GETFL, 0);
|
||||
// text_color_set(DW_COLOR_DEBUG);
|
||||
// dw_printf("Debug: Try using non-blocking mode for pseudo terminal.\n");
|
||||
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
e = fcntl (fd, F_SETFL, flags | O_NONBLOCK);
|
||||
if (e != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Can't set pseudo terminal to nonblocking, fcntl returns %d, errno = %d\n", e, errno);
|
||||
perror ("pt fcntl");
|
||||
}
|
||||
#endif
|
||||
#if 0 // same
|
||||
flags = 1;
|
||||
e = ioctl (fd, FIONBIO, &flags);
|
||||
if (e != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Can't set pseudo terminal to nonblocking, ioctl returns %d, errno = %d\n", e, errno);
|
||||
perror ("pt ioctl");
|
||||
}
|
||||
#endif
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf("Virtual KISS TNC is available on %s\n", pt_slave_name);
|
||||
dw_printf("WARNING - Dire Wolf will hang eventually if nothing is reading from it.\n");
|
||||
|
||||
|
||||
#if 1
|
||||
|
@ -633,12 +628,10 @@ static MYFDTYPE kiss_open_nullmodem (char *devicename)
|
|||
* flen - Length of raw received frame not including the FCS
|
||||
* or -1 for a text string.
|
||||
*
|
||||
*
|
||||
* Description: Send message to client.
|
||||
* We really don't care if anyone is listening or not.
|
||||
* I don't even know if we can find out.
|
||||
*
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
@ -646,7 +639,6 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen)
|
|||
{
|
||||
unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN + 2];
|
||||
int kiss_len;
|
||||
int j;
|
||||
int err;
|
||||
|
||||
#if ! __WIN32__
|
||||
|
@ -675,7 +667,7 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen)
|
|||
|
||||
unsigned char stemp[AX25_MAX_PACKET_LEN + 1];
|
||||
|
||||
assert (flen < sizeof(stemp));
|
||||
assert (flen < (int)(sizeof(stemp)));
|
||||
|
||||
stemp[0] = (chan << 4) + 0;
|
||||
memcpy (stemp+1, fbuf, flen);
|
||||
|
@ -702,14 +694,12 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen)
|
|||
|
||||
/* Pseudo terminal for Cygwin and Linux. */
|
||||
|
||||
|
||||
err = write (pt_master_fd, kiss_buff, (size_t)kiss_len);
|
||||
|
||||
if (err == -1 && errno == EWOULDBLOCK) {
|
||||
#if DEBUG
|
||||
text_color_set (DW_COLOR_INFO);
|
||||
dw_printf ("KISS SEND - discarding message because write would block.\n");
|
||||
#endif
|
||||
dw_printf ("KISS SEND - Discarding message because no one is listening.\n");
|
||||
dw_printf ("This happens when you use the -p option and don't read from the pseudo terminal.\n");
|
||||
}
|
||||
else if (err != kiss_len)
|
||||
{
|
||||
|
@ -764,7 +754,7 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen)
|
|||
//nullmodem_fd = MYFDERROR;
|
||||
}
|
||||
}
|
||||
else if (nwritten != kiss_len)
|
||||
else if ((int)nwritten != kiss_len)
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nError sending KISS message to client application thru null modem. Only %d of %d written.\n\n", (int)nwritten, kiss_len);
|
||||
|
@ -772,13 +762,6 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen)
|
|||
//nullmodem_fd = MYFDERROR;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/* Could wait with GetOverlappedResult but we never */
|
||||
/* have an issues in this direction. */
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("KISS SEND completed. wrote %d / %d\n", nwritten, kiss_len);
|
||||
#endif
|
||||
|
||||
#else
|
||||
err = write (nullmodem_fd, kiss_buf, (size_t)kiss_len);
|
||||
if (err != len)
|
||||
|
@ -796,20 +779,25 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen)
|
|||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: kiss_listen_thread
|
||||
* Name: kiss_get
|
||||
*
|
||||
* Purpose: Wait for messages from an application.
|
||||
* Purpose: Read one byte from the KISS client app.
|
||||
*
|
||||
* Global In: nullmodem_fd or pt_master_fd
|
||||
* Global In: nullmodem_fd (Windows) or pt_master_fd (Linux)
|
||||
*
|
||||
* Description: Process messages from the client application.
|
||||
* Returns: one byte (value 0 - 255) or terminate thread on error.
|
||||
*
|
||||
* Description: There is room for improvment here. Reading one byte
|
||||
* at a time is inefficient. We could read a large block
|
||||
* into a local buffer and return a byte from that most of the time.
|
||||
* Is it worth the effort? I don't know. With GHz processors and
|
||||
* the low data rate here it might not make a noticable difference.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
/* Return one byte (value 0 - 255) or terminate thread on error. */
|
||||
|
||||
|
||||
static int kiss_get (/* MYFDTYPE fd*/ void )
|
||||
{
|
||||
|
@ -884,25 +872,76 @@ static int kiss_get (/* MYFDTYPE fd*/ void )
|
|||
#else /* Linux/Cygwin version */
|
||||
|
||||
int n = 0;
|
||||
fd_set fd_in, fd_ex;
|
||||
int rc;
|
||||
|
||||
while ( n == 0 ) {
|
||||
|
||||
n = read(pt_master_fd, &ch, (size_t)1);
|
||||
/*
|
||||
* Since the beginning we've always had a couple annoying problems with
|
||||
* the pseudo terminal KISS interface.
|
||||
* When using "kissattach" we would sometimes get the error message:
|
||||
*
|
||||
* kissattach: Error setting line discipline: TIOCSETD: Device or resource busy
|
||||
* Are you sure you have enabled MKISS support in the kernel
|
||||
* or, if you made it a module, that the module is loaded?
|
||||
*
|
||||
* martinhpedersen came up with the interesting idea of putting in a "select"
|
||||
* before the "read" and explained it like this:
|
||||
*
|
||||
* "Reading from master fd of the pty before the client has connected leads
|
||||
* to trouble with kissattach. Use select to check if the slave has sent
|
||||
* any data before trying to read from it."
|
||||
*
|
||||
* "This fix resolves the issue by not reading from the pty's master fd, until
|
||||
* kissattach has opened and configured the slave. This is implemented using
|
||||
* select() to wait for data before reading from the master fd."
|
||||
*
|
||||
* The submitted code looked like this:
|
||||
*
|
||||
* FD_ZERO(&fd_in);
|
||||
* rc = select(pt_master_fd + 1, &fd_in, NULL, &fd_in, NULL);
|
||||
*
|
||||
* That doesn't look right to me for a couple reasons.
|
||||
* First, I would expect to use FD_SET for the fd.
|
||||
* Second, using the same bit mask for two arguments doesn't seem
|
||||
* like a good idea because select modifies them.
|
||||
* When I tried running it, we don't get the failure message
|
||||
* anymore but the select never returns so we can't read data from
|
||||
* the KISS client app.
|
||||
*
|
||||
* I think this is what we want.
|
||||
*
|
||||
* Tested on Raspian (ARM) and Ubuntu (x86_64).
|
||||
* We don't get the error from kissattach anymore.
|
||||
*/
|
||||
|
||||
if (n != 1) {
|
||||
FD_ZERO(&fd_in);
|
||||
FD_SET(pt_master_fd, &fd_in);
|
||||
|
||||
FD_ZERO(&fd_ex);
|
||||
FD_SET(pt_master_fd, &fd_ex);
|
||||
|
||||
rc = select(pt_master_fd + 1, &fd_in, NULL, &fd_ex, NULL);
|
||||
|
||||
#if 0
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("select returns %d, errno=%d, fd=%d, fd_in=%08x, fd_ex=%08x\n", rc, errno, pt_master_fd, *((int*)(&fd_in)), *((int*)(&fd_in)));
|
||||
#endif
|
||||
|
||||
if (rc == 0)
|
||||
{
|
||||
continue; // When could we get a 0?
|
||||
}
|
||||
|
||||
if (rc == MYFDERROR
|
||||
|| (n = read(pt_master_fd, &ch, (size_t)1)) != 1)
|
||||
{
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nError receiving kiss message from client application. Closing %s.\n\n", pt_slave_name);
|
||||
dw_printf ("\nError receiving KISS message from client application. Closing %s.\n\n", pt_slave_name);
|
||||
perror ("");
|
||||
|
||||
/* Message added between 1.1 beta test and final version 1.1 */
|
||||
|
||||
/* TODO: Determine root cause and find proper solution. */
|
||||
|
||||
dw_printf ("This is a known problem that sometimes shows up when using with kissattach.\n");
|
||||
dw_printf ("There are a couple work-arounds described in the Dire Wolf User Guide\n");
|
||||
dw_printf ("and the Raspberry Pi APRS documents.\n");
|
||||
|
||||
close (pt_master_fd);
|
||||
|
||||
pt_master_fd = MYFDERROR;
|
||||
|
@ -935,6 +974,18 @@ static int kiss_get (/* MYFDTYPE fd*/ void )
|
|||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: kiss_listen_thread
|
||||
*
|
||||
* Purpose: Read messages from serial port KISS client application.
|
||||
*
|
||||
* Global In: nullmodem_fd (Windows) or pt_master_fd (Linux)
|
||||
*
|
||||
* Description: Reads bytes from the KISS client app and
|
||||
* sends them to kiss_rec_byte for processing.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static THREAD_F kiss_listen_thread (void *arg)
|
||||
|
|
19
kiss_frame.c
19
kiss_frame.c
|
@ -68,6 +68,8 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -75,7 +77,6 @@
|
|||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
#include "kiss_frame.h"
|
||||
|
@ -86,7 +87,7 @@
|
|||
void hex_dump (unsigned char *p, int len);
|
||||
|
||||
|
||||
static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug);
|
||||
|
||||
|
||||
|
||||
#if KISSTEST
|
||||
|
@ -98,6 +99,10 @@ void text_color_set (dw_color_t c)
|
|||
return;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -561,9 +566,15 @@ static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug)
|
|||
break;
|
||||
|
||||
default:
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("KISS Invalid command %d\n", cmd);
|
||||
kiss_debug_print (FROM_CLIENT, NULL, kiss_msg, kiss_len);
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Troubleshooting tip:\n");
|
||||
dw_printf ("Use \"-d kn\" option on direwolf command line to observe\n");
|
||||
dw_printf ("all communication with the client application.\n");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -630,7 +641,7 @@ void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int
|
|||
#if KISSTEST
|
||||
|
||||
|
||||
main ()
|
||||
int main ()
|
||||
{
|
||||
unsigned char din[512];
|
||||
unsigned char kissed[520];
|
||||
|
|
57
kissnet.c
57
kissnet.c
|
@ -95,10 +95,12 @@
|
|||
*/
|
||||
|
||||
|
||||
#include "direwolf.h" // Sets _WIN32_WINNT for XP API level needed by ws2tcpip.h
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
#include <winsock2.h>
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#include <ws2tcpip.h>
|
||||
#include <ws2tcpip.h> // _WIN32_WINNT must be set to 0x0501 before including this
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -115,11 +117,9 @@
|
|||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "tq.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
|
@ -128,6 +128,8 @@
|
|||
#include "kiss_frame.h"
|
||||
#include "xmit.h"
|
||||
|
||||
void hex_dump (unsigned char *p, int len); // This should be in a .h file.
|
||||
|
||||
|
||||
static kiss_frame_t kf; /* Accumulated KISS frame and state of decoder. */
|
||||
// TODO: multiple instances if multiple KISS network clients!
|
||||
|
@ -139,8 +141,15 @@ static int client_sock; /* File descriptor for socket for */
|
|||
/* (Don't use SOCKET type because it is unsigned.) */
|
||||
|
||||
|
||||
static void * connect_listen_thread (void *arg);
|
||||
static void * kissnet_listen_thread (void *arg);
|
||||
// TODO: define in one place, use everywhere.
|
||||
#if __WIN32__
|
||||
#define THREAD_F unsigned __stdcall
|
||||
#else
|
||||
#define THREAD_F void *
|
||||
#endif
|
||||
|
||||
static THREAD_F connect_listen_thread (void *arg);
|
||||
static THREAD_F kissnet_listen_thread (void *arg);
|
||||
|
||||
|
||||
|
||||
|
@ -184,8 +193,8 @@ void kissnet_init (struct misc_config_s *mc)
|
|||
#else
|
||||
pthread_t connect_listen_tid;
|
||||
pthread_t cmd_listen_tid;
|
||||
#endif
|
||||
int e;
|
||||
#endif
|
||||
int kiss_port = mc->kiss_port;
|
||||
|
||||
|
||||
|
@ -208,7 +217,7 @@ void kissnet_init (struct misc_config_s *mc)
|
|||
* This waits for a client to connect and sets client_sock.
|
||||
*/
|
||||
#if __WIN32__
|
||||
connect_listen_th = _beginthreadex (NULL, 0, connect_listen_thread, (void *)kiss_port, 0, NULL);
|
||||
connect_listen_th = (HANDLE)_beginthreadex (NULL, 0, connect_listen_thread, (void *)kiss_port, 0, NULL);
|
||||
if (connect_listen_th == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Could not create KISS socket connect listening thread\n");
|
||||
|
@ -227,7 +236,7 @@ void kissnet_init (struct misc_config_s *mc)
|
|||
* This reads messages from client when client_sock is valid.
|
||||
*/
|
||||
#if __WIN32__
|
||||
cmd_listen_th = _beginthreadex (NULL, 0, kissnet_listen_thread, NULL, 0, NULL);
|
||||
cmd_listen_th = (HANDLE)_beginthreadex (NULL, 0, kissnet_listen_thread, NULL, 0, NULL);
|
||||
if (cmd_listen_th == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Could not create KISS socket command listening thread\n");
|
||||
|
@ -263,7 +272,7 @@ void kissnet_init (struct misc_config_s *mc)
|
|||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static void * connect_listen_thread (void *arg)
|
||||
static THREAD_F connect_listen_thread (void *arg)
|
||||
{
|
||||
#if __WIN32__
|
||||
|
||||
|
@ -284,7 +293,7 @@ static void * connect_listen_thread (void *arg)
|
|||
if (err != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("WSAStartup failed: %d\n", err);
|
||||
return (NULL);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) {
|
||||
|
@ -292,7 +301,7 @@ static void * connect_listen_thread (void *arg)
|
|||
dw_printf("Could not find a usable version of Winsock.dll\n");
|
||||
WSACleanup();
|
||||
//sleep (1);
|
||||
return (NULL);
|
||||
return (0);
|
||||
}
|
||||
|
||||
memset (&hints, 0, sizeof(hints));
|
||||
|
@ -307,14 +316,14 @@ static void * connect_listen_thread (void *arg)
|
|||
dw_printf("getaddrinfo failed: %d\n", err);
|
||||
//sleep (1);
|
||||
WSACleanup();
|
||||
return (NULL);
|
||||
return (0);
|
||||
}
|
||||
|
||||
listen_sock= socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
||||
if (listen_sock == INVALID_SOCKET) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("connect_listen_thread: Socket creation failed, err=%d", WSAGetLastError());
|
||||
return (NULL);
|
||||
return (0);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
@ -331,7 +340,7 @@ static void * connect_listen_thread (void *arg)
|
|||
freeaddrinfo(ai);
|
||||
closesocket(listen_sock);
|
||||
WSACleanup();
|
||||
return (NULL);
|
||||
return (0);
|
||||
}
|
||||
|
||||
freeaddrinfo(ai);
|
||||
|
@ -353,7 +362,7 @@ static void * connect_listen_thread (void *arg)
|
|||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("Listen failed with error: %d\n", WSAGetLastError());
|
||||
return (NULL);
|
||||
return (0);
|
||||
}
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
|
@ -366,7 +375,7 @@ static void * connect_listen_thread (void *arg)
|
|||
dw_printf("Accept failed with error: %d\n", WSAGetLastError());
|
||||
closesocket(listen_sock);
|
||||
WSACleanup();
|
||||
return (NULL);
|
||||
return (0);
|
||||
}
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
|
@ -481,7 +490,6 @@ void kissnet_send_rec_packet (int chan, unsigned char *fbuf, int flen)
|
|||
{
|
||||
unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN];
|
||||
int kiss_len;
|
||||
int j;
|
||||
int err;
|
||||
|
||||
|
||||
|
@ -501,7 +509,7 @@ void kissnet_send_rec_packet (int chan, unsigned char *fbuf, int flen)
|
|||
|
||||
unsigned char stemp[AX25_MAX_PACKET_LEN + 1];
|
||||
|
||||
assert (flen < sizeof(stemp));
|
||||
assert (flen < (int)(sizeof(stemp)));
|
||||
|
||||
stemp[0] = (chan << 4) + 0;
|
||||
memcpy (stemp+1, fbuf, flen);
|
||||
|
@ -580,6 +588,7 @@ static int read_from_socket (int fd, char *ptr, int len)
|
|||
#if __WIN32__
|
||||
|
||||
//TODO: any flags for send/recv?
|
||||
//TODO: Would be useful to have more detailed explanation from the error code.
|
||||
|
||||
n = recv (fd, ptr + got_bytes, len - got_bytes, 0);
|
||||
#else
|
||||
|
@ -659,7 +668,7 @@ static int kiss_get (void)
|
|||
}
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nError reading KISS byte from clent application. Closing connection.\n\n");
|
||||
dw_printf ("\nError reading KISS byte from client application. Closing connection.\n\n");
|
||||
#if __WIN32__
|
||||
closesocket (client_sock);
|
||||
#else
|
||||
|
@ -671,7 +680,7 @@ static int kiss_get (void)
|
|||
|
||||
|
||||
|
||||
static void * kissnet_listen_thread (void *arg)
|
||||
static THREAD_F kissnet_listen_thread (void *arg)
|
||||
{
|
||||
unsigned char ch;
|
||||
|
||||
|
@ -685,7 +694,11 @@ static void * kissnet_listen_thread (void *arg)
|
|||
kiss_rec_byte (&kf, ch, kiss_debug, kissnet_send_rec_packet);
|
||||
}
|
||||
|
||||
return (NULL); /* to suppress compiler warning. */
|
||||
#if __WIN32__
|
||||
return(0);
|
||||
#else
|
||||
return (THREAD_F) 0; /* Unreachable but avoids compiler warning. */
|
||||
#endif
|
||||
|
||||
} /* end kissnet_listen_thread */
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -40,7 +41,6 @@
|
|||
#include <math.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "latlong.h"
|
||||
#include "textcolor.h"
|
||||
|
||||
|
|
2
ll2utm.c
2
ll2utm.c
|
@ -1,5 +1,7 @@
|
|||
/* Latitude / Longitude to UTM conversion */
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
|
87
log.c
87
log.c
|
@ -31,6 +31,8 @@
|
|||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
@ -42,8 +44,6 @@
|
|||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
#include "decode_aprs.h"
|
||||
|
@ -341,6 +341,89 @@ void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_
|
|||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: log_rr_bits
|
||||
*
|
||||
* Purpose: Quick hack to look at the C and RR bits just to see what is there.
|
||||
* This seems like a good place because it is a small subset of the function above.
|
||||
*
|
||||
* Inputs: A - Explode information from APRS packet.
|
||||
*
|
||||
* pp - Received packet object.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
void log_rr_bits (decode_aprs_t *A, packet_t pp)
|
||||
{
|
||||
|
||||
if (1) {
|
||||
|
||||
char heard[AX25_MAX_ADDR_LEN+1];
|
||||
char smfr[60];
|
||||
char *p;
|
||||
int src_c, dst_c;
|
||||
int src_rr, dst_rr;
|
||||
|
||||
// Sanitize system type (manufacturer) changing any comma to period.
|
||||
|
||||
strlcpy (smfr, A->g_mfr, sizeof(smfr));
|
||||
for (p=smfr; *p!='\0'; p++) {
|
||||
if (*p == ',') *p = '.';
|
||||
}
|
||||
|
||||
/* Who are we hearing? Original station or digipeater? */
|
||||
/* Similar code in direwolf.c. Combine into one function? */
|
||||
|
||||
strlcpy(heard, "", sizeof(heard));
|
||||
|
||||
if (pp != NULL) {
|
||||
int h;
|
||||
|
||||
if (ax25_get_num_addr(pp) == 0) {
|
||||
/* Not AX.25. No station to display below. */
|
||||
h = -1;
|
||||
strlcpy (heard, "", sizeof(heard));
|
||||
}
|
||||
else {
|
||||
h = ax25_get_heard(pp);
|
||||
ax25_get_addr_with_ssid(pp, h, heard);
|
||||
}
|
||||
|
||||
if (h >= AX25_REPEATER_2 &&
|
||||
strncmp(heard, "WIDE", 4) == 0 &&
|
||||
isdigit(heard[4]) &&
|
||||
heard[5] == '\0') {
|
||||
|
||||
ax25_get_addr_with_ssid(pp, h-1, heard);
|
||||
strlcat (heard, "?", sizeof(heard));
|
||||
}
|
||||
|
||||
src_c = ax25_get_h (pp, AX25_SOURCE);
|
||||
dst_c = ax25_get_h (pp, AX25_DESTINATION);
|
||||
src_rr = ax25_get_rr (pp, AX25_SOURCE);
|
||||
dst_rr = ax25_get_rr (pp, AX25_DESTINATION);
|
||||
|
||||
// C RR for source
|
||||
// C RR for destination
|
||||
// system type
|
||||
// source
|
||||
// station heard
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
|
||||
dw_printf ("%d %d%d %d %d%d,%s,%s,%s\n",
|
||||
src_c, (src_rr >> 1) & 1, src_rr & 1,
|
||||
dst_c, (dst_rr >> 1) & 1, dst_rr & 1,
|
||||
smfr, A->g_src, heard);
|
||||
}
|
||||
}
|
||||
|
||||
} /* end log_rr_bits */
|
||||
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: log_term
|
||||
|
|
2
log.h
2
log.h
|
@ -14,4 +14,6 @@ void log_init (char *path);
|
|||
|
||||
void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_t retries);
|
||||
|
||||
void log_rr_bits (decode_aprs_t *A, packet_t pp);
|
||||
|
||||
void log_term (void);
|
|
@ -18,17 +18,12 @@
|
|||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if __WIN32__
|
||||
char *strsep(char **stringp, const char *delim);
|
||||
#endif
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
|
||||
/*
|
||||
* Information we gather for each thing.
|
||||
|
|
|
@ -79,6 +79,8 @@ p = Packet dump in hexadecimal.
|
|||
.P
|
||||
g = GPS interface.
|
||||
.P
|
||||
W = Waypoints for position or object reports.
|
||||
.P
|
||||
t = Tracker beacon.
|
||||
.P
|
||||
o = Output controls such as PTT and DCD.
|
||||
|
|
23
mgn_icon.h
23
mgn_icon.h
|
@ -5,9 +5,18 @@
|
|||
*
|
||||
* 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, March 2003
|
||||
*
|
||||
* http://www.gpsinformation.org/mag-proto-2-11.pdf
|
||||
*
|
||||
*
|
||||
* That's 13 years ago. There should be something newer available but I can't find it.
|
||||
*
|
||||
* The is based on the newer models at the time. Earlier models had shorter incompatible icon lists.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#define MGN_crossed_square "a"
|
||||
#define MGN_box "b"
|
||||
#define MGN_house "c"
|
||||
|
@ -110,7 +119,7 @@ static const char mgn_primary_symtab[SYMTAB_SIZE][3] = {
|
|||
MGN_default, // E 37 EYEBALL (Eye catcher!)
|
||||
MGN_default, // F 38 Farm Vehicle (tractor)
|
||||
MGN_default, // G 39 Grid Square (6 digit)
|
||||
MGN_default, // H 40 HOTEL (blue bed symbol)
|
||||
MGN_hotel, // H 40 HOTEL (blue bed symbol)
|
||||
MGN_aerial, // I 41 TcpIp on air network stn
|
||||
MGN_default, // J 42
|
||||
MGN_default, // K 43 School
|
||||
|
@ -150,7 +159,7 @@ static const char mgn_primary_symtab[SYMTAB_SIZE][3] = {
|
|||
MGN_aerial, // m 77 Mic-E Repeater
|
||||
MGN_default, // n 78 Node (black bulls-eye)
|
||||
MGN_default, // o 79 EOC
|
||||
MGN_default, // p 80 ROVER (puppy, or dog)
|
||||
MGN_zoo, // p 80 ROVER (puppy, or dog)
|
||||
MGN_default, // q 81 GRID SQ shown above 128 m
|
||||
MGN_aerial, // r 82 Repeater
|
||||
MGN_default, // s 83 SHIP (pwr boat)
|
||||
|
@ -202,7 +211,7 @@ static const char mgn_alternate_symtab[SYMTAB_SIZE][3] = {
|
|||
MGN_default, // > 30 OVERLAYED CAR
|
||||
MGN_tourist_info, // ? 31 INFO Kiosk (Blue box with ?)
|
||||
MGN_default, // @ 32 HURICANE/Trop-Storm
|
||||
MGN_default, // A 33 overlayBOX DTMF & RFID & XO
|
||||
MGN_box, // A 33 overlayBOX DTMF & RFID & XO
|
||||
MGN_default, // B 34 Blwng Snow (& future codes)
|
||||
MGN_boating, // C 35 Coast Guard
|
||||
MGN_default, // D 36 Drizzle (proposed APRStt)
|
||||
|
@ -215,7 +224,7 @@ static const char mgn_alternate_symtab[SYMTAB_SIZE][3] = {
|
|||
MGN_default, // K 43 Kenwood HT (W)
|
||||
MGN_lighthouse, // L 44 Lighthouse
|
||||
MGN_default, // M 45 MARS (A=Army,N=Navy,F=AF)
|
||||
MGN_nav_aid, // N 46 Navigation Buoy
|
||||
MGN_buoy, // N 46 Navigation Buoy
|
||||
MGN_airport, // O 47 Rocket
|
||||
MGN_default, // P 48 Parking
|
||||
MGN_default, // Q 49 QUAKE
|
||||
|
@ -223,7 +232,7 @@ static const char mgn_alternate_symtab[SYMTAB_SIZE][3] = {
|
|||
MGN_aerial, // S 51 Satellite/Pacsat
|
||||
MGN_default, // T 52 Thunderstorm
|
||||
MGN_default, // U 53 SUNNY
|
||||
MGN_default, // V 54 VORTAC Nav Aid
|
||||
MGN_nav_aid, // V 54 VORTAC Nav Aid
|
||||
MGN_default, // W 55 # NWS site (NWS options)
|
||||
MGN_default, // X 56 Pharmacy Rx (Apothicary)
|
||||
MGN_aerial, // Y 57 Radios and devices
|
||||
|
@ -242,7 +251,7 @@ static const char mgn_alternate_symtab[SYMTAB_SIZE][3] = {
|
|||
MGN_default, // f 70 Funnel Cloud
|
||||
MGN_default, // g 71 Gale Flags
|
||||
MGN_default, // h 72 Store. or HAMFST Hh=HAM store
|
||||
MGN_default, // i 73 BOX or points of Interest
|
||||
MGN_box, // i 73 BOX or points of Interest
|
||||
MGN_default, // j 74 WorkZone (Steam Shovel)
|
||||
MGN_default, // k 75 Special Vehicle SUV,ATV,4x4
|
||||
MGN_default, // l 76 Areas (box,circles,etc)
|
||||
|
|
|
@ -0,0 +1,797 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2016 John Langner, WB2OSZ
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* File: mheard.c
|
||||
*
|
||||
* Purpose: Maintain a list of all stations heard.
|
||||
*
|
||||
* Description: This was added for IGate statistics and checking if a user is local
|
||||
* but would also be useful for the AGW network protocol 'H' request.
|
||||
*
|
||||
* This application has no GUI and is not interactive so
|
||||
* I'm not sure what else we might do with the information.
|
||||
*
|
||||
* Why mheard instead of just heard? The KPC-3+ has an MHEARD command
|
||||
* to list stations heard. I guess that stuck in my mind.
|
||||
* It should be noted that here "heard" refers to the AX.25 source station.
|
||||
* Before printing the received packet, the "heard" line refers to who
|
||||
* we heard over the radio. This would be the digipeater with "*" after
|
||||
* its name.
|
||||
*
|
||||
* Future Ideas: Someone suggested using SQLite to store the information
|
||||
* so other applications could access it.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "textcolor.h"
|
||||
#include "decode_aprs.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "hdlc_rec2.h" // for retry_t
|
||||
#include "mheard.h"
|
||||
#include "latlong.h"
|
||||
|
||||
|
||||
// This is getting updated from two different threads so we need a critical region
|
||||
// for adding new nodes.
|
||||
|
||||
static dw_mutex_t mheard_mutex;
|
||||
|
||||
|
||||
// I think we can get away without a critical region for reading if we follow these
|
||||
// rules:
|
||||
//
|
||||
// (1) When adding a new node, make sure it is complete, including next ptr,
|
||||
// before adding it to the list.
|
||||
// (2) Update the start of list pointer last.
|
||||
// (2) Nothing gets deleted.
|
||||
|
||||
// If we ever decide to start cleaning out very old data, all access would then
|
||||
// need to use the mutex.
|
||||
|
||||
|
||||
/*
|
||||
* Information for each station heard over the radio or from Internet Server.
|
||||
*/
|
||||
|
||||
typedef struct mheard_s {
|
||||
|
||||
struct mheard_s *pnext; // Pointer to next in list.
|
||||
|
||||
char callsign[AX25_MAX_ADDR_LEN]; // Callsign from the AX.25 source field.
|
||||
|
||||
int count; // Number of times heard.
|
||||
// We don't use this for anything.
|
||||
// Just something potentially interesting when looking at data dump.
|
||||
|
||||
int chan; // Most recent channel where heard.
|
||||
|
||||
int num_digi_hops; // Number of digipeater hops before we heard it.
|
||||
// over radio. Zero when heard directly.
|
||||
|
||||
time_t last_heard_rf; // Timestamp when last heard over the radio.
|
||||
|
||||
time_t last_heard_is; // Timestamp when last heard from Internet Server.
|
||||
|
||||
double dlat, dlon; // Last position. G_UNKNOWN for unknown.
|
||||
|
||||
int msp; // Allow message sender positon report.
|
||||
// When non zero, an IS>RF position report is allowed.
|
||||
// Then decremented.
|
||||
|
||||
// What else would be useful?
|
||||
// The AGW protocol is by channel and returns
|
||||
// first heard in addition to last heard.
|
||||
} mheard_t;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* The list could be quite long and we hit this a lot so use a hash table.
|
||||
*/
|
||||
|
||||
#define MHEARD_HASH_SIZE 73 // Best if prime number.
|
||||
|
||||
static mheard_t *mheard_hash[MHEARD_HASH_SIZE];
|
||||
|
||||
static inline int hash_index(char *callsign) {
|
||||
int n = 0;
|
||||
char *p = callsign;
|
||||
|
||||
while (*p != '\0') {
|
||||
n += *p++;
|
||||
}
|
||||
return (n % MHEARD_HASH_SIZE);
|
||||
}
|
||||
|
||||
static mheard_t *mheard_ptr(char *callsign) {
|
||||
int n = hash_index(callsign);
|
||||
mheard_t *p = mheard_hash[n];
|
||||
|
||||
while (p != NULL) {
|
||||
if (strcmp(callsign,p->callsign) == 0) return (p);
|
||||
p = p->pnext;
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
static int mheard_debug = 0;
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: mheard_init
|
||||
*
|
||||
* Purpose: Initialization at start of application.
|
||||
*
|
||||
* Inputs: debug - Debug level.
|
||||
*
|
||||
* Description: Clear pointer table.
|
||||
* Save debug level for later use.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
|
||||
void mheard_init (int debug)
|
||||
{
|
||||
int i;
|
||||
|
||||
mheard_debug = debug;
|
||||
|
||||
for (i = 0; i < MHEARD_HASH_SIZE; i++) {
|
||||
mheard_hash[i] = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mutex to coordinate adding new nodes.
|
||||
*/
|
||||
dw_mutex_init(&mheard_mutex);
|
||||
|
||||
} /* end mheard_init */
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: mheard_dump
|
||||
*
|
||||
* Purpose: Print list of stations heard for debugging.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
/* convert some time in past to hours:minutes text format. */
|
||||
|
||||
static void age(char *result, time_t now, time_t t)
|
||||
{
|
||||
int s, h, m;
|
||||
|
||||
if (t == 0) {
|
||||
strcpy (result, "- ");
|
||||
return;
|
||||
}
|
||||
|
||||
s = (int)(now - t);
|
||||
m = s / 60;
|
||||
h = m / 60;
|
||||
m -= h * 60;
|
||||
|
||||
sprintf (result, "%4d:%02d", h, m);
|
||||
}
|
||||
|
||||
/* Convert latitude, longitude to text or - if not defined. */
|
||||
|
||||
static void latlon (char * result, double dlat, double dlon)
|
||||
{
|
||||
if (dlat != G_UNKNOWN && dlon != G_UNKNOWN) {
|
||||
sprintf (result, "%6.2f %7.2f", dlat, dlon);
|
||||
}
|
||||
else {
|
||||
strcpy (result, " - - ");
|
||||
}
|
||||
}
|
||||
|
||||
/* Compare last heard time for use with qsort. */
|
||||
|
||||
#define MAXX(x,y) (((x)>(y))?(x):(y))
|
||||
static int compar(const void *a, const void *b)
|
||||
{
|
||||
mheard_t *ma = *((mheard_t **)a);
|
||||
mheard_t *mb = *((mheard_t **)b);
|
||||
|
||||
time_t ta = MAXX(ma->last_heard_rf, ma->last_heard_is);
|
||||
time_t tb = MAXX(mb->last_heard_rf, mb->last_heard_is);
|
||||
|
||||
return (tb - ta);
|
||||
}
|
||||
|
||||
#define MAXDUMP 1000
|
||||
|
||||
static void mheard_dump (void)
|
||||
{
|
||||
int i;
|
||||
mheard_t *mptr;
|
||||
time_t now = time(NULL);
|
||||
char stuff[80];
|
||||
char rf[16]; // hours:minutes
|
||||
char is[16];
|
||||
char position[40];
|
||||
mheard_t *station[MAXDUMP];
|
||||
int num_stations = 0;
|
||||
|
||||
|
||||
/* Get linear array of node pointers so they can be sorted easily. */
|
||||
|
||||
num_stations = 0;
|
||||
|
||||
for (i = 0; i < MHEARD_HASH_SIZE; i++) {
|
||||
for (mptr = mheard_hash[i]; mptr != NULL; mptr = mptr->pnext) {
|
||||
|
||||
if (num_stations < MAXDUMP) {
|
||||
station[num_stations] = mptr;
|
||||
num_stations++;
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("mheard_dump - max number of stations exceeded.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Sort most recently heard to the top then print. */
|
||||
|
||||
qsort (station, num_stations, sizeof(mheard_t *), compar);
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
|
||||
dw_printf ("callsign cnt chan hops RF IS lat long msp\n");
|
||||
|
||||
for (i = 0; i < num_stations; i++) {
|
||||
|
||||
mptr = station[i];
|
||||
|
||||
age (rf, now, mptr->last_heard_rf);
|
||||
age (is, now, mptr->last_heard_is);
|
||||
latlon (position, mptr->dlat, mptr->dlon);
|
||||
|
||||
snprintf (stuff, sizeof(stuff), "%-9s %3d %d %d %7s %7s %s %d\n",
|
||||
mptr->callsign, mptr->count, mptr->chan, mptr->num_digi_hops, rf, is, position, mptr->msp);
|
||||
dw_printf ("%s", stuff);
|
||||
}
|
||||
|
||||
} /* end mheard_dump */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: mheard_save_rf
|
||||
*
|
||||
* Purpose: Save information about station heard over the radio.
|
||||
*
|
||||
* Inputs: chan - Radio channel where heard.
|
||||
*
|
||||
* A - Exploded information from APRS packet.
|
||||
*
|
||||
* pp - Received packet object.
|
||||
*
|
||||
* alevel - audio level.
|
||||
*
|
||||
* retries - Amount of effort to get a good CRC.
|
||||
*
|
||||
* Description: Calling sequence was copied from "log_write."
|
||||
* It has a lot more than what we currently keep but the
|
||||
* hooks are there so it will be easy to capture additional
|
||||
* information when the need arises.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
void mheard_save_rf (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_t retries)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
char source[AX25_MAX_ADDR_LEN];
|
||||
int hops;
|
||||
mheard_t *mptr;
|
||||
|
||||
ax25_get_addr_with_ssid (pp, AX25_SOURCE, source);
|
||||
|
||||
/*
|
||||
* How many digipeaters has it gone thru before we hear it?
|
||||
* We can count the number of digi addresses that are marked as "has been used."
|
||||
* This is not always accurate because there is inconsistency in digipeater behavior.
|
||||
* The base AX.25 spec seems clear in this regard. The used digipeaters should
|
||||
* should accurately reflict the path taken by the packet. Sometimes we see excess
|
||||
* stuff in there. Even when you understand what is going on, it is still an ambiguous
|
||||
* situation. Look for my rant in the User Guide.
|
||||
*/
|
||||
|
||||
hops = ax25_get_heard(pp) - AX25_SOURCE;
|
||||
|
||||
mptr = mheard_ptr(source);
|
||||
if (mptr == NULL) {
|
||||
int i;
|
||||
/*
|
||||
* Not heard before. Add it.
|
||||
*/
|
||||
|
||||
if (mheard_debug) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("mheard_save_rf: %s %d - added new\n", source, hops);
|
||||
}
|
||||
|
||||
mptr = calloc(sizeof(mheard_t),1);
|
||||
strlcpy (mptr->callsign, source, sizeof(mptr->callsign));
|
||||
mptr->count = 1;
|
||||
mptr->chan = chan;
|
||||
mptr->num_digi_hops = hops;
|
||||
mptr->last_heard_rf = now;
|
||||
mptr->dlat = G_UNKNOWN;
|
||||
mptr->dlon = G_UNKNOWN;
|
||||
|
||||
i = hash_index(source);
|
||||
|
||||
dw_mutex_lock (&mheard_mutex);
|
||||
mptr->pnext = mheard_hash[i]; // before inserting into list.
|
||||
mheard_hash[i] = mptr;
|
||||
dw_mutex_unlock (&mheard_mutex);
|
||||
}
|
||||
else {
|
||||
|
||||
/*
|
||||
* Update existing entry.
|
||||
* The only tricky part here is that we might hear the same transmission
|
||||
* several times. First direct, then thru various digipeater paths.
|
||||
* We are interested in the shortest path if heard very recently.
|
||||
*/
|
||||
|
||||
if (hops > mptr->num_digi_hops && (int)(now - mptr->last_heard_rf) < 15) {
|
||||
|
||||
if (mheard_debug) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("mheard_save_rf: %s %d - skip because hops was %d %d seconds ago.\n", source, hops, mptr->num_digi_hops, (int)(now - mptr->last_heard_rf) );
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
if (mheard_debug) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("mheard_save_rf: %s %d - update time, was %d hops %d seconds ago.\n", source, hops, mptr->num_digi_hops, (int)(now - mptr->last_heard_rf));
|
||||
}
|
||||
|
||||
mptr->count++;
|
||||
mptr->chan = chan;
|
||||
mptr->num_digi_hops = hops;
|
||||
mptr->last_heard_rf = now;
|
||||
}
|
||||
}
|
||||
|
||||
if (A->g_lat != G_UNKNOWN && A->g_lon != G_UNKNOWN) {
|
||||
mptr->dlat = A->g_lat;
|
||||
mptr->dlon = A->g_lon;
|
||||
}
|
||||
|
||||
if (mheard_debug >= 2) {
|
||||
int limit = 10; // normally 30 or 60. more frequent when debugging.
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("mheard debug, %d min, DIR_CNT=%d,LOC_CNT=%d,RF_CNT=%d\n", limit, mheard_count(0,limit), mheard_count(2,limit), mheard_count(8,limit));
|
||||
}
|
||||
|
||||
if (mheard_debug) {
|
||||
mheard_dump ();
|
||||
}
|
||||
|
||||
} /* end mheard_save_rf */
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: mheard_save_is
|
||||
*
|
||||
* Purpose: Save information about station heard via Internet Server.
|
||||
*
|
||||
* Inputs: ptext - Packet in monitoring text form as sent by the Internet server.
|
||||
*
|
||||
* Any trailing CRLF should have been removed.
|
||||
* Typical examples:
|
||||
*
|
||||
* KA1BTK-5>APDR13,TCPIP*,qAC,T2IRELAND:=4237.62N/07040.68W$/A=-00054 http://aprsdroid.org/
|
||||
* N1HKO-10>APJI40,TCPIP*,qAC,N1HKO-JS:<IGATE,MSG_CNT=0,LOC_CNT=0
|
||||
* K1RI-2>APWW10,WIDE1-1,WIDE2-1,qAS,K1RI:/221700h/9AmA<Ct3_ sT010/002g005t045r000p023P020h97b10148
|
||||
* KC1BOS-2>T3PQ3S,WIDE1-1,WIDE2-1,qAR,W1TG-1:`c)@qh\>/"50}TinyTrak4 Mobile
|
||||
*
|
||||
* Notice how the final address in the header might not
|
||||
* be a valid AX.25 address. We see a 9 character address
|
||||
* (with no ssid) and an ssid of two letters.
|
||||
*
|
||||
* The "q construct" ( http://www.aprs-is.net/q.aspx ) provides
|
||||
* a clue about the journey taken but I don't think we care here.
|
||||
*
|
||||
* All we should care about here is the the source address.
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
void mheard_save_is (char *ptext)
|
||||
{
|
||||
packet_t pp;
|
||||
time_t now = time(NULL);
|
||||
char source[AX25_MAX_ADDR_LEN];
|
||||
mheard_t *mptr;
|
||||
|
||||
/*
|
||||
* Try to parse it into a packet object.
|
||||
* This will contain "q constructs" and we might see an address
|
||||
* with two alphnumeric characters in the SSID so we must use
|
||||
* the non-strict parsing.
|
||||
*
|
||||
* Bug: Up to 8 digipeaters are allowed in radio format.
|
||||
* There is a potential of finding a larger number here.
|
||||
*/
|
||||
pp = ax25_from_text(ptext, 0);
|
||||
|
||||
if (pp == NULL) {
|
||||
if (mheard_debug) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("mheard_save_is: Could not parse message from server.\n");
|
||||
dw_printf ("%s\n", ptext);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ax25_get_addr_with_ssid (pp, AX25_SOURCE, source);
|
||||
|
||||
mptr = mheard_ptr(source);
|
||||
if (mptr == NULL) {
|
||||
int i;
|
||||
/*
|
||||
* Not heard before. Add it.
|
||||
*/
|
||||
|
||||
if (mheard_debug) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("mheard_save_is: %s - added new\n", source);
|
||||
}
|
||||
|
||||
mptr = calloc(sizeof(mheard_t),1);
|
||||
strlcpy (mptr->callsign, source, sizeof(mptr->callsign));
|
||||
mptr->count = 1;
|
||||
mptr->last_heard_is = now;
|
||||
mptr->dlat = G_UNKNOWN;
|
||||
mptr->dlon = G_UNKNOWN;
|
||||
|
||||
i = hash_index(source);
|
||||
|
||||
dw_mutex_lock (&mheard_mutex);
|
||||
mptr->pnext = mheard_hash[i]; // before inserting into list.
|
||||
mheard_hash[i] = mptr;
|
||||
dw_mutex_unlock (&mheard_mutex);
|
||||
}
|
||||
else {
|
||||
|
||||
/* Already there. UPdate last heard from IS time. */
|
||||
|
||||
if (mheard_debug) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("mheard_save_is: %s - update time, was %d seconds ago.\n", source, (int)(now - mptr->last_heard_rf));
|
||||
}
|
||||
mptr->count++;
|
||||
mptr->last_heard_is = now;
|
||||
}
|
||||
|
||||
// Is is desirable to save any location in this case?
|
||||
// I don't think it would help.
|
||||
// The whole purpose of keeping the location is for message sending filter.
|
||||
// We wouldn't want to try sending a message to the station if we didn't hear it over the radio.
|
||||
// On the other hand, I don't think it would hurt.
|
||||
// The filter always includes a time since last heard over the radi.
|
||||
|
||||
if (mheard_debug >= 2) {
|
||||
int limit = 10; // normally 30 or 60
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("mheard debug, %d min, DIR_CNT=%d,LOC_CNT=%d,RF_CNT=%d\n", limit, mheard_count(0,limit), mheard_count(2,limit), mheard_count(8,limit));
|
||||
}
|
||||
|
||||
if (mheard_debug) {
|
||||
mheard_dump ();
|
||||
}
|
||||
|
||||
ax25_delete (pp);
|
||||
|
||||
} /* end mheard_save_is */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: mheard_count
|
||||
*
|
||||
* Purpose: Count local stations for IGate statistics report like this:
|
||||
*
|
||||
* <IGATE,MSG_CNT=1,LOC_CNT=25
|
||||
*
|
||||
* Inputs: max_hops - Include only stations heard with this number of
|
||||
* digipeater hops or less. For reporting, we might use:
|
||||
*
|
||||
* 0 for DIR_CNT (heard directly)
|
||||
* IGate transmit path for LOC_CNT.
|
||||
* e.g. 3 for WIDE1-1,WIDE2-2
|
||||
* 8 for RF_CNT.
|
||||
*
|
||||
* time_limit - Include only stations heard within this many minutes.
|
||||
* Typically 30 or 60.
|
||||
*
|
||||
* Returns: Number to be used in the statistics report.
|
||||
*
|
||||
* Description: Look for discussion here: http://www.tapr.org/pipermail/aprssig/2016-June/045837.html
|
||||
*
|
||||
* Lynn KJ4ERJ:
|
||||
*
|
||||
* For APRSISCE/32, "Local" is defined as those stations to which messages
|
||||
* would be gated if any are received from the APRS-IS. This currently
|
||||
* means unique stations heard within the past 30 minutes with at most two
|
||||
* used path hops.
|
||||
*
|
||||
* I added DIR_CNT and RF_CNT with comma delimiters to APRSISCE/32's IGate
|
||||
* status. DIR_CNT is the count of unique stations received on RF in the
|
||||
* past 30 minutes with no used hops. RF_CNT is the total count of unique
|
||||
* stations received on RF in the past 30 minutes.
|
||||
*
|
||||
* Steve K4HG:
|
||||
*
|
||||
* The number of hops defining local should match the number of hops of the
|
||||
* outgoing packets from the IGate. So if the path is only WIDE, then local
|
||||
* should only be stations heard direct or through one hop. From the beginning
|
||||
* I was very much against on a standardization of the outgoing IGate path,
|
||||
* hams should be free to manage their local RF network in a way that works
|
||||
* for them. Busy areas one hop may be best, I lived in an area where three was
|
||||
* a much better choice. I avoided as much as possible prescribing anything
|
||||
* that might change between locations.
|
||||
*
|
||||
* The intent was how many stations are there for which messages could be IGated.
|
||||
* IGate software keeps an internal list of the 'local' stations so it knows
|
||||
* when to IGate a message, and this number should be the length of that list.
|
||||
* Some IGates have a parameter for local timeout, 1 hour was the original default,
|
||||
* so if in an hour the IGate has not heard another local packet the station is
|
||||
* dropped from the local list. Messages will no longer be IGated to that station
|
||||
* and the station count would drop by one. The number should not just continue to rise.
|
||||
*
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
int mheard_count (int max_hops, int time_limit)
|
||||
{
|
||||
time_t since = time(NULL) - time_limit * 60;
|
||||
int count = 0;
|
||||
int i;
|
||||
mheard_t *p;
|
||||
|
||||
for (i = 0; i < MHEARD_HASH_SIZE; i++) {
|
||||
for (p = mheard_hash[i]; p != NULL; p = p->pnext) {
|
||||
if (p->last_heard_rf >= since && p->num_digi_hops <= max_hops) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mheard_debug == 1) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("mheard_count(<= %d digi hops, last %d minutes) returns %d\n", max_hops, time_limit, count);
|
||||
}
|
||||
|
||||
return (count);
|
||||
|
||||
} /* end mheard_count */
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: mheard_was_recently_nearby
|
||||
*
|
||||
* Purpose: Determine whether given station was heard recently on the radio.
|
||||
*
|
||||
* Inputs: role - "addressee" or "source" if debug out is desired.
|
||||
* Otherwise empty string.
|
||||
*
|
||||
* callsign - Callsign for station.
|
||||
*
|
||||
* time_limit - Include only stations heard within this many minutes.
|
||||
* Typically 30 or 60.
|
||||
*
|
||||
* max_hops - Include only stations heard with this number of
|
||||
* digipeater hops or less. For reporting, we might use:
|
||||
*
|
||||
* dlat, dlon, km - Include only stations within distance of location.
|
||||
* Not used if G_UNKNOWN is supplied.
|
||||
*
|
||||
* Returns: 1 for true, 0 for false.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
int mheard_was_recently_nearby (char *role, char *callsign, int time_limit, int max_hops, double dlat, double dlon, double km)
|
||||
{
|
||||
mheard_t *mptr;
|
||||
time_t now;
|
||||
int heard_ago;
|
||||
|
||||
|
||||
if (role != NULL && strlen(role) > 0) {
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
if (dlat != G_UNKNOWN && dlon != G_UNKNOWN && km != G_UNKNOWN) {
|
||||
dw_printf ("Was message %s %s heard in the past %d minutes, with %d or fewer digipeater hops, and within %.1f km of %.2f %.2f?\n", role, callsign, time_limit, max_hops, km, dlat, dlon);
|
||||
}
|
||||
else {
|
||||
dw_printf ("Was message %s %s heard in the past %d minutes, with %d or fewer digipeater hops?\n", role, callsign, time_limit, max_hops);
|
||||
}
|
||||
}
|
||||
|
||||
mptr = mheard_ptr(callsign);
|
||||
|
||||
if (mptr == NULL || mptr->last_heard_rf == 0) {
|
||||
|
||||
if (role != NULL && strlen(role) > 0) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("No, we have not heard %s over the radio.\n", callsign);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
now = time(NULL);
|
||||
heard_ago = (int)(now - mptr->last_heard_rf) / 60;
|
||||
|
||||
if (heard_ago > time_limit) {
|
||||
|
||||
if (role != NULL && strlen(role) > 0) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("No, %s was last heard over the radio %d minutes ago with %d digipeater hops.\n", callsign, heard_ago, mptr->num_digi_hops);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (mptr->num_digi_hops > max_hops) {
|
||||
|
||||
if (role != NULL && strlen(role) > 0) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("No, %s was last heard over the radio with %d digipeater hops %d minutes ago.\n", callsign, mptr->num_digi_hops, heard_ago);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
// Apply physical distance check?
|
||||
|
||||
if (dlat != G_UNKNOWN && dlon != G_UNKNOWN && km != G_UNKNOWN && mptr->dlat != G_UNKNOWN && mptr->dlon != G_UNKNOWN) {
|
||||
|
||||
double dist = ll_distance_km (mptr->dlat, mptr->dlon, dlat, dlon);
|
||||
|
||||
if (dist > km) {
|
||||
|
||||
if (role != NULL && strlen(role) > 0) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("No, %s was %.1f km away although it was %d digipeater hops %d minutes ago.\n", callsign, dist, mptr->num_digi_hops, heard_ago);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
else {
|
||||
if (role != NULL && strlen(role) > 0) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Yes, %s last heard over radio %d minutes ago, %d digipeater hops. Last location %.1f km away.\n", callsign, heard_ago, mptr->num_digi_hops, dist);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
// Passed all the tests.
|
||||
|
||||
if (role != NULL && strlen(role) > 0) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Yes, %s last heard over radio %d minutes ago, %d digipeater hops.\n", callsign, heard_ago, mptr->num_digi_hops);
|
||||
}
|
||||
|
||||
return (1);
|
||||
|
||||
} /* end mheard_was_recently_nearby */
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: mheard_set_msp
|
||||
*
|
||||
* Purpose: Set the "message sender position" count for specified station.
|
||||
*
|
||||
* Inputs: callsign - Callsign for station which sent the "message."
|
||||
*
|
||||
* num - Number of position reports to allow. Typically 1.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
void mheard_set_msp (char *callsign, int num)
|
||||
{
|
||||
mheard_t *mptr;
|
||||
|
||||
mptr = mheard_ptr(callsign);
|
||||
|
||||
if (mptr != NULL) {
|
||||
|
||||
mptr->msp = num;
|
||||
|
||||
if (mheard_debug) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("MSP for %s set to %d\n", callsign, num);
|
||||
}
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error: Can't find %s to set MSP.\n", callsign);
|
||||
}
|
||||
|
||||
} /* end mheard_set_msp */
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: mheard_get_msp
|
||||
*
|
||||
* Purpose: Get the "message sender position" count for specified station.
|
||||
*
|
||||
* Inputs: callsign - Callsign for station which sent the "message."
|
||||
*
|
||||
* Returns: The cound for the specified station.
|
||||
* 0 if not found.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
int mheard_get_msp (char *callsign)
|
||||
{
|
||||
mheard_t *mptr;
|
||||
|
||||
mptr = mheard_ptr(callsign);
|
||||
|
||||
if (mptr != NULL) {
|
||||
|
||||
if (mheard_debug) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("MSP for %s is %d\n", callsign, mptr->msp);
|
||||
}
|
||||
return (mptr->msp); // Should we have a time limit?
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
||||
} /* end mheard_get_msp */
|
||||
|
||||
|
||||
|
||||
/* end mheard.c */
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
|
||||
/* mheard.h */
|
||||
|
||||
#include "decode_aprs.h" // for decode_aprs_t
|
||||
|
||||
|
||||
void mheard_init (int debug);
|
||||
|
||||
void mheard_save_rf (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_t retries);
|
||||
|
||||
void mheard_save_is (char *ptext);
|
||||
|
||||
int mheard_count (int max_hops, int time_limit);
|
||||
|
||||
int mheard_was_recently_nearby (char *role, char *callsign, int time_limit, int max_hops, double dlat, double dlon, double km);
|
||||
|
||||
void mheard_set_msp (char *callsign, int num);
|
||||
|
||||
int mheard_get_msp (char *callsign);
|
|
@ -43,8 +43,7 @@
|
|||
* Find the first occurrence of find in s, ignore case.
|
||||
*/
|
||||
char *
|
||||
strcasestr(s, find)
|
||||
const char *s, *find;
|
||||
strcasestr(const char *s, const char *find)
|
||||
{
|
||||
char c, sc;
|
||||
size_t len;
|
||||
|
|
8
morse.c
8
morse.c
|
@ -30,6 +30,8 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -39,8 +41,6 @@
|
|||
#include <time.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "textcolor.h"
|
||||
#include "audio.h"
|
||||
#include "ptt.h"
|
||||
|
@ -122,7 +122,7 @@ static const struct morse_s {
|
|||
|
||||
};
|
||||
|
||||
#define NUM_MORSE (sizeof(morse) / sizeof(struct morse_s))
|
||||
#define NUM_MORSE ((int)(sizeof(morse) / sizeof(struct morse_s)))
|
||||
|
||||
static void morse_tone (int chan, int tu, int wpm);
|
||||
static void morse_quiet (int chan, int tu, int wpm);
|
||||
|
@ -272,6 +272,8 @@ int morse_send (int chan, char *str, int wpm, int txdelay, int txtail)
|
|||
time_units, morse_units_str(str));
|
||||
}
|
||||
|
||||
audio_flush(ACHAN2ADEV(chan));
|
||||
|
||||
return (txdelay +
|
||||
(int) (TIME_UNITS_TO_MS(time_units, wpm) + 0.5) +
|
||||
txtail);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2013, 2014, 2015 John Langner, WB2OSZ
|
||||
// Copyright (C) 2013, 2014, 2015, 2016 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
|
||||
|
@ -70,9 +70,11 @@
|
|||
* Set limit on number of packets in fix up later queue.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
//#define DEBUG 1
|
||||
#define DIGIPEATER_C
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -80,7 +82,6 @@
|
|||
#include <stdio.h>
|
||||
#include <sys/unistd.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
#include "multi_modem.h"
|
||||
|
@ -108,7 +109,10 @@ static struct {
|
|||
|
||||
|
||||
|
||||
#define PROCESS_AFTER_BITS 2
|
||||
//#define PROCESS_AFTER_BITS 2 // version 1.4. Was a little short for skew of PSK with different modem types, optional pre-filter
|
||||
|
||||
#define PROCESS_AFTER_BITS 3
|
||||
|
||||
|
||||
static int process_age[MAX_CHANS];
|
||||
|
||||
|
@ -154,7 +158,11 @@ void multi_modem_init (struct audio_s *pa)
|
|||
dw_printf("Internal error, chan=%d, %s, %d\n", chan, __FILE__, __LINE__);
|
||||
save_audio_config_p->achan[chan].baud = DEFAULT_BAUD;
|
||||
}
|
||||
process_age[chan] = PROCESS_AFTER_BITS * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].baud;
|
||||
int real_baud = save_audio_config_p->achan[chan].baud;
|
||||
if (save_audio_config_p->achan[chan].modem_type == MODEM_QPSK) real_baud = save_audio_config_p->achan[chan].baud / 2;
|
||||
if (save_audio_config_p->achan[chan].modem_type == MODEM_8PSK) real_baud = save_audio_config_p->achan[chan].baud / 3;
|
||||
|
||||
process_age[chan] = PROCESS_AFTER_BITS * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / real_baud ;
|
||||
//crc_queue_of_last_to_app[chan] = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -462,7 +470,27 @@ void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned c
|
|||
if (save_audio_config_p->achan[chan].num_subchan == 1 &&
|
||||
save_audio_config_p->achan[chan].num_slicers == 1) {
|
||||
|
||||
dlq_append (DLQ_REC_FRAME, chan, subchan, slice, pp, alevel, retries, "");
|
||||
|
||||
int drop_it = 0;
|
||||
if (save_audio_config_p->recv_error_rate != 0) {
|
||||
float r = (float)(rand()) / (float)RAND_MAX; // Random, 0.0 to 1.0
|
||||
|
||||
//text_color_set(DW_COLOR_INFO);
|
||||
//dw_printf ("TEMP DEBUG. recv error rate = %d\n", save_audio_config_p->recv_error_rate);
|
||||
|
||||
if (save_audio_config_p->recv_error_rate / 100.0 > r) {
|
||||
drop_it = 1;
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Intentionally dropping incoming frame. Recv Error rate = %d per cent.\n", save_audio_config_p->recv_error_rate);
|
||||
}
|
||||
}
|
||||
|
||||
if (drop_it ) {
|
||||
ax25_delete (pp);
|
||||
}
|
||||
else {
|
||||
dlq_rec_frame (chan, subchan, slice, pp, alevel, retries, "");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -641,7 +669,26 @@ static void pick_best_candidate (int chan)
|
|||
j = subchan_from_n(best_n);
|
||||
k = slice_from_n(best_n);
|
||||
|
||||
dlq_append (DLQ_REC_FRAME, chan, j, k,
|
||||
int drop_it = 0;
|
||||
if (save_audio_config_p->recv_error_rate != 0) {
|
||||
float r = (float)(rand()) / (float)RAND_MAX; // Random, 0.0 to 1.0
|
||||
|
||||
//text_color_set(DW_COLOR_INFO);
|
||||
//dw_printf ("TEMP DEBUG. recv error rate = %d\n", save_audio_config_p->recv_error_rate);
|
||||
|
||||
if (save_audio_config_p->recv_error_rate / 100.0 > r) {
|
||||
drop_it = 1;
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Intentionally dropping incoming frame. Recv Error rate = %d per cent.\n", save_audio_config_p->recv_error_rate);
|
||||
}
|
||||
}
|
||||
|
||||
if ( drop_it ) {
|
||||
ax25_delete (candidate[chan][j][k].packet_p);
|
||||
candidate[chan][j][k].packet_p = NULL;
|
||||
}
|
||||
else {
|
||||
dlq_rec_frame (chan, j, k,
|
||||
candidate[chan][j][k].packet_p,
|
||||
candidate[chan][j][k].alevel,
|
||||
(int)(candidate[chan][j][k].retries),
|
||||
|
@ -649,6 +696,7 @@ static void pick_best_candidate (int chan)
|
|||
|
||||
/* Someone else owns it now and will delete it later. */
|
||||
candidate[chan][j][k].packet_p = NULL;
|
||||
}
|
||||
|
||||
/* Clear in preparation for next time. */
|
||||
|
||||
|
|
465
nmea.c
465
nmea.c
|
@ -1,465 +0,0 @@
|
|||
//
|
||||
// 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
|
||||
|
||||
|
||||
// TODO: rename this to waypoint & integrate.
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Module: nmea.c
|
||||
*
|
||||
* Purpose: Receive NMEA sentences from a GPS receiver.
|
||||
* Send NMEA waypoint sentences to GPS display or mapping application.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if __WIN32__
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#define __USE_XOPEN2KXSI 1
|
||||
#define __USE_XOPEN 1
|
||||
//#define __USE_POSIX 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>
|
||||
|
||||
#if __WIN32__
|
||||
char *strsep(char **stringp, const char *delim);
|
||||
#endif
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "config.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
//#include "xmit.h"
|
||||
#include "latlong.h"
|
||||
#include "nmea.h"
|
||||
#include "grm_sym.h" /* Garmin symbols */
|
||||
#include "mgn_icon.h" /* Magellan icons */
|
||||
#include "serial_port.h"
|
||||
|
||||
|
||||
// TODO: receive buffer... static kiss_frame_t kf; /* Accumulated KISS frame and state of decoder. */
|
||||
|
||||
static MYFDTYPE nmea_port_fd = MYFDERROR;
|
||||
|
||||
static void nmea_send_sentence (char *sent);
|
||||
|
||||
|
||||
|
||||
//static void nmea_parse_gps (char *sentence);
|
||||
|
||||
|
||||
static int nmea_debug = 0; /* Print information flowing from and to attached device. */
|
||||
|
||||
void nmea_set_debug (int n)
|
||||
{
|
||||
nmea_debug = n;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: nmea_init
|
||||
*
|
||||
* Purpose: Initialization for NMEA communication port.
|
||||
*
|
||||
* Inputs: mc->nmea_port - name of serial port.
|
||||
*
|
||||
* Global output: nmea_port_fd
|
||||
*
|
||||
*
|
||||
* Description: (1) Open serial port device.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
|
||||
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) {
|
||||
|
||||
nmea_port_fd = serial_port_open (mc->nmea_port, 4800);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#if DEBUG
|
||||
text_color_set (DW_COLOR_DEBUG);
|
||||
|
||||
dw_printf ("end of nmea_init: nmea_port_fd = %d\n", nmea_port_fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: append_checksum
|
||||
*
|
||||
* Purpose: Append checksum to the sentence.
|
||||
*
|
||||
* In/out: sentence - NMEA sentence beginning with '$'.
|
||||
*
|
||||
* Description: Checksum is exclusive of characters except leading '$'.
|
||||
* We append '*' and an upper case two hexadecimal value.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static void append_checksum (char *sentence)
|
||||
{
|
||||
char *p;
|
||||
int cs;
|
||||
|
||||
assert (sentence[0] == '$');
|
||||
|
||||
cs = 0;
|
||||
for (p = sentence+1; *p != '\0'; p++) {
|
||||
cs ^= *p;
|
||||
}
|
||||
|
||||
sprintf (p, "*%02X", cs & 0xff);
|
||||
|
||||
// Add crlf too?
|
||||
|
||||
} /* end append_checksum */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: nema_send_waypoint
|
||||
*
|
||||
* Purpose: Convert APRS position or object into NMEA waypoint sentence
|
||||
* for use by a GPS display or other mapping application.
|
||||
*
|
||||
* Inputs: wname_in - Name of waypoint.
|
||||
* dlat - Latitude.
|
||||
* dlong - Longitude.
|
||||
* symtab - Symbol table or overlay character.
|
||||
* symbol - Symbol code.
|
||||
* alt - Altitude in meters or G_UNKOWN.
|
||||
* course - Course in degrees or ??? for unknown.
|
||||
* speed - Speed in knots ?? or ??
|
||||
* comment - Description or message.
|
||||
*
|
||||
*
|
||||
* Description: Currently we send multiple styles. Maybe someday there might
|
||||
* be an option to send a selected subset.
|
||||
*
|
||||
* $GPWPL - Generic with only location and name.
|
||||
* $PGRMW - Garmin, adds altitude, symbol, and comment
|
||||
* to previously named waypoint.
|
||||
* $PMGNWPL - Magellan, more complete for stationary objects.
|
||||
* $PKWDWPL - Kenwood with APRS style symbol but missing comment.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
void nmea_send_waypoint (char *wname_in, double dlat, double dlong, char symtab, char symbol,
|
||||
float alt, float course, float speed, char *comment)
|
||||
{
|
||||
char wname[12]; /* Waypoint name. Any , or * removed. */
|
||||
char slat[12]; /* DDMM.mmmm */
|
||||
char slat_ns[2]; /* N or S */
|
||||
char slong[12]; /* DDDMM.mmmm */
|
||||
char slong_ew[2]; /* E or W */
|
||||
char sentence[500];
|
||||
char salt[12]; /* altitude as string, empty if unknown */
|
||||
char sspeed[12]; /* speed as string, empty if unknown */
|
||||
char scourse[12]; /* course as string, empty if unknown */
|
||||
int grm_sym; /* Garmin symbol code. */
|
||||
char sicon[5]; /* Magellan icon string */
|
||||
char stime[8];
|
||||
char sdate[8];
|
||||
char *p;
|
||||
|
||||
|
||||
|
||||
// Remove any comma from name.
|
||||
|
||||
// TODO: remove any , or * from comment.
|
||||
|
||||
strcpy (wname, wname_in);
|
||||
for (p=wname; *p != '\0'; p++) {
|
||||
if (*p == ',') *p = ' ';
|
||||
if (*p == '*') *p = ' ';
|
||||
}
|
||||
|
||||
// Convert position to character form.
|
||||
|
||||
latitude_to_nmea (dlat, slat, slat_ns);
|
||||
longitude_to_nmea (dlong, slong, slong_ew);
|
||||
|
||||
/*
|
||||
* Generic.
|
||||
*
|
||||
* $GPWPL,ddmm.mmmm,ns,dddmm.mmmm,ew,wname*99
|
||||
*
|
||||
* Where,
|
||||
* ddmm.mmmm,ns is latitude
|
||||
* dddmm.mmmm,ew is longitude
|
||||
* wname is the waypoint name
|
||||
* *99 is checksum
|
||||
*/
|
||||
|
||||
snprintf (sentence, sizeof(sentence), "$GPWPL,%s,%s,%s,%s,%s", slat, slat_ns, slong, slong_ew, wname);
|
||||
append_checksum (sentence);
|
||||
nmea_send_sentence (sentence);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Garmin - https://www8.garmin.com/support/pdf/NMEA_0183.pdf
|
||||
* http://gpsinformation.net/mag-proto.htm
|
||||
*
|
||||
* $PGRMW,wname,alt,symbol,comment*99
|
||||
*
|
||||
* Where,
|
||||
*
|
||||
* wname is waypoint name. Must match existing waypoint.
|
||||
* alt is altitude in meters.
|
||||
* symbol is symbol code. Hexadecimal up to FFFF.
|
||||
* See Garmin Device Interface Specification 001-0063-00.
|
||||
* comment is comment for the waypoint.
|
||||
* *99 is checksum
|
||||
*/
|
||||
|
||||
if (alt == G_UNKNOWN) {
|
||||
strcpy (salt, "");
|
||||
}
|
||||
else {
|
||||
snprintf (salt, sizeof(salt), "%.1f", alt);
|
||||
}
|
||||
grm_sym = 0x1234; // TODO
|
||||
|
||||
snprintf (sentence, sizeof(sentence), "$PGRMW,%s,%s,%04X,%s", wname, salt, grm_sym, comment);
|
||||
append_checksum (sentence);
|
||||
nmea_send_sentence (sentence);
|
||||
|
||||
/*
|
||||
* Magellan - http://www.gpsinformation.org/mag-proto-2-11.pdf
|
||||
*
|
||||
* $PMGNWPL,ddmm.mmmm,ns,dddmm.mmmm,ew,alt,unit,wname,comment,icon,xx*99
|
||||
*
|
||||
* Where,
|
||||
* ddmm.mmmm,ns is latitude
|
||||
* dddmm.mmmm,ew is longitude
|
||||
* alt is altitude
|
||||
* unit is M for meters or F for feet
|
||||
* wname is the waypoint name
|
||||
* comment is message or comment
|
||||
* icon is one or two letters for icon code
|
||||
* xx is waypoint type which is optional, not well
|
||||
* defined, and not used in their example.
|
||||
* *99 is checksum
|
||||
*/
|
||||
|
||||
// TODO: icon
|
||||
|
||||
snprintf (sicon, sizeof(sicon), "??");
|
||||
snprintf (sentence, sizeof(sentence), "$PMGNWPL,%s,%s,%s,%s,%s,M,%s,%s,%s",
|
||||
slat, slat_ns, slong, slong_ew, salt, wname, comment, sicon);
|
||||
append_checksum (sentence);
|
||||
nmea_send_sentence (sentence);
|
||||
|
||||
/*
|
||||
* Kenwood - Speculation due to no official spec found so far.
|
||||
*
|
||||
* $PKWDWPL,hhmmss,v,ddmm.mmmm,ns,dddmm.mmmm,ew,speed,course,ddmmyy,alt,wname,ts*99
|
||||
*
|
||||
* Where,
|
||||
* hhmmss is time in UTC. Should we supply current
|
||||
* time or only pass along time from
|
||||
* received signal?
|
||||
* v indicates valid data ?????????????????
|
||||
* Why would we send if not valid?
|
||||
* ddmm.mmmm,ns is latitude
|
||||
* dddmm.mmmm,ew is longitude
|
||||
* speed is speed in UNITS ??? knots ?????
|
||||
* course is course in degrees
|
||||
* ddmmyy is date. Same question as time.
|
||||
* alt is altitude. in UNITS ??? meters ???
|
||||
* wname is the waypoint name
|
||||
* ts are the table and symbol.
|
||||
* Non-standard parsing would be required
|
||||
* to deal with these for symbol:
|
||||
* , Boy Scouts / Girl Scouts
|
||||
* * SnowMobile / Snow
|
||||
* *99 is checksum
|
||||
*
|
||||
* Oddly, there is not place for comment.
|
||||
*/
|
||||
|
||||
if (speed == G_UNKNOWN) {
|
||||
strcpy (sspeed, "");
|
||||
}
|
||||
else {
|
||||
snprintf (sspeed, sizeof(sspeed), "%.1f", speed);
|
||||
}
|
||||
if (course == G_UNKNOWN) {
|
||||
strcpy (scourse, "");
|
||||
}
|
||||
else {
|
||||
snprintf (scourse, sizeof(scourse), "%.1f", course);
|
||||
}
|
||||
|
||||
// TODO: how to handle time & date ???
|
||||
|
||||
strcpy (stime, "123456");
|
||||
strcpy (sdate, "123456");
|
||||
|
||||
snprintf (sentence, sizeof(sentence), "$PKWDWPL,%s,V,%s,%s,%s,%s,%s,%s,%s,%s,%s,%c%c",
|
||||
stime, slat, slat_ns, slong, slong_ew,
|
||||
sspeed, scourse, sdate, salt, wname, symtab, symbol);
|
||||
append_checksum (sentence);
|
||||
nmea_send_sentence (sentence);
|
||||
|
||||
/*
|
||||
* One application recognizes these. Not implemented at this time.
|
||||
*
|
||||
* $GPTLL,01,ddmm.mmmm,ns,dddmm.mmmm,ew,tname,000000.00,T,R*99
|
||||
*
|
||||
* Where,
|
||||
* ddmm.mmmm,ns is latitude
|
||||
* dddmm.mmmm,ew is longitude
|
||||
* tname is the target name
|
||||
* 000000.00 is timestamp ???
|
||||
* T is target status (S for need help)
|
||||
* R is reference target ???
|
||||
* *99 is checksum
|
||||
*
|
||||
*
|
||||
* $GPTXT,01,01,tname,message*99
|
||||
*
|
||||
* Where,
|
||||
*
|
||||
* 01 is total number of messages in transmission
|
||||
* 01 is message number in this transmission
|
||||
* tname is target name. Should match name in WPL or TTL.
|
||||
* message is the message.
|
||||
* *99 is checksum
|
||||
*
|
||||
*/
|
||||
|
||||
} /* end nmea_send_waypoint */
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
* d710a menu 603 $GPWPL $PMGNWPL $PKWDWPL
|
||||
|
||||
symbol mapping
|
||||
|
||||
https://freepository.com:444/50lItuLQ7fW6s-web/browser/Tracker2/trunk/sources/waypoint.c?rev=108
|
||||
|
||||
|
||||
|
||||
Data Transmission Protocol For Magellan Products - version 2.11
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$PMGNWPL,4651.529,N,07111.425,W,0000000,M,GC5A5F, GC. The straight line,a*13
|
||||
$PMGNWPL,3549.499,N,08650.827,W,0000257,M,HOME,HOME,c*4D
|
||||
|
||||
http://gpsbabel.sourcearchive.com/documentation/1.3.7~cvs1/magproto_8c-source.html
|
||||
|
||||
sscanf(trkmsg,"$PMGNWPL,%lf,%c,%lf,%c,%d,%c,%[^,],%[^,]",
|
||||
&latdeg,&latdir,
|
||||
&lngdeg,&lngdir,
|
||||
&alt,&altunits,shortname,descr); then icon
|
||||
|
||||
snprintf(obuf, sizeof(), "PMGNWPL,%4.3f,%c,%09.3f,%c,%07.0f,M,%-.*s,%-.46s,%s",
|
||||
lat, ilat < 0 ? 'S' : 'N',
|
||||
lon, ilon < 0 ? 'W' : 'E',
|
||||
waypointp->altitude == unknown_alt ?
|
||||
0 : waypointp->altitude,
|
||||
wpt_len,
|
||||
owpt,
|
||||
odesc,
|
||||
icon_token);
|
||||
|
||||
https://freepository.com:444/50lItuLQ7fW6s-web/changeset/108/Tracker2/trunk/sources/waypoint.c
|
||||
|
||||
$PMGNWPL,4106.003,S,14640.214,E,0000069,M,KISSING SPOT,,a*3C
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
*
|
||||
https://freepository.com:444/50lItuLQ7fW6s-web/changeset/325
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static void nmea_send_sentence (char *sent)
|
||||
{
|
||||
|
||||
int err;
|
||||
int len = strlen(sent);
|
||||
|
||||
|
||||
if (nmea_port_fd == MYFDERROR) {
|
||||
return;
|
||||
}
|
||||
|
||||
text_color_set(DW_COLOR_XMIT);
|
||||
dw_printf ("%s\n", sent);
|
||||
|
||||
if (nmea_debug) {
|
||||
// TODO: debugg out... nmea_debug_print (TO_CLIENT, NULL, nmea_buff+1, nmea_len-2);
|
||||
}
|
||||
|
||||
// TODO: need to append CR LF.
|
||||
|
||||
serial_port_write (nmea_port_fd, sent, len);
|
||||
|
||||
} /* nmea_send_sentence */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* end nmea.c */
|
19
nmea.h
19
nmea.h
|
@ -1,19 +0,0 @@
|
|||
|
||||
/*
|
||||
* Name: nmea.h
|
||||
*/
|
||||
|
||||
|
||||
#include "ax25_pad.h" /* for packet_t */
|
||||
|
||||
#include "config.h" /* for struct misc_config_s */
|
||||
|
||||
|
||||
void nmea_init (struct misc_config_s *misc_config);
|
||||
|
||||
void nmea_set_debug (int n);
|
||||
|
||||
void nmea_send_waypoint (char *wname_in, double dlat, double dlong, char symtab, char symbol,
|
||||
float alt, float course, float speed, char *comment);
|
||||
|
||||
/* end nmea.h */
|
524
pfilter.c
524
pfilter.c
|
@ -37,6 +37,7 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
@ -45,16 +46,48 @@
|
|||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#if __WIN32__
|
||||
char *strsep(char **stringp, const char *delim);
|
||||
#endif
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
#include "decode_aprs.h"
|
||||
#include "latlong.h"
|
||||
#include "pfilter.h"
|
||||
#include "mheard.h"
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Global stuff (to this file)
|
||||
*
|
||||
* These are set by init function.
|
||||
*/
|
||||
|
||||
static struct igate_config_s *save_igate_config_p;
|
||||
static int s_debug = 0;
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: pfilter_init
|
||||
*
|
||||
* Purpose: One time initialization when main application starts up.
|
||||
*
|
||||
* Inputs: p_igate_config - IGate configuration.
|
||||
*
|
||||
* debug_level - 0 no debug output.
|
||||
* 1 single summary line with final result. Indent by 1.
|
||||
* 2 details from each filter specification. Indent by 3.
|
||||
* 3 Logical operators. Indent by 2.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
void pfilter_init (struct igate_config_s *p_igate_config, int debug_level)
|
||||
{
|
||||
s_debug = debug_level;
|
||||
save_igate_config_p = p_igate_config;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -69,9 +102,6 @@ typedef struct pfstate_s {
|
|||
int from_chan; /* From and to channels. MAX_CHANS is used for IGate. */
|
||||
int to_chan; /* Used only for debug and error messages. */
|
||||
|
||||
|
||||
// TODO: might want to put channels and packet here so we only pass one thing around.
|
||||
|
||||
/*
|
||||
* Original filter string from config file.
|
||||
* All control characters should be replaced by spaces.
|
||||
|
@ -85,9 +115,15 @@ typedef struct pfstate_s {
|
|||
packet_t pp;
|
||||
|
||||
/*
|
||||
* Packet split into separate parts.
|
||||
* Are we processing APRS or connected mode?
|
||||
* This determines whch types of filters are available.
|
||||
*/
|
||||
int is_aprs;
|
||||
|
||||
/*
|
||||
* Packet split into separate parts if APRS.
|
||||
* Most interesting fields are:
|
||||
* g_src - source address
|
||||
*
|
||||
* g_symbol_table - / \ or overlay
|
||||
* g_symbol_code
|
||||
* g_lat, g_lon - Location
|
||||
|
@ -118,8 +154,17 @@ static void print_error (pfstate_t *pf, char *msg);
|
|||
|
||||
static int filt_bodgu (pfstate_t *pf, char *pattern);
|
||||
static int filt_t (pfstate_t *pf);
|
||||
static int filt_r (pfstate_t *pf);
|
||||
static int filt_r (pfstate_t *pf, char *sdist);
|
||||
static int filt_s (pfstate_t *pf);
|
||||
static int filt_i (pfstate_t *pf);
|
||||
|
||||
static char *bool2text (int val)
|
||||
{
|
||||
if (val == 1) return "TRUE";
|
||||
if (val == 0) return "FALSE";
|
||||
if (val == -1) return "ERROR";
|
||||
return "OOPS!";
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
|
@ -137,6 +182,10 @@ static int filt_s (pfstate_t *pf);
|
|||
*
|
||||
* pp - Packet object handle.
|
||||
*
|
||||
* is_aprs - True for APRS, false for connected mode digipeater.
|
||||
* Connected mode allows a subset of the filter types, only
|
||||
* looking at the addresses, not information part contents.
|
||||
*
|
||||
* Returns: 1 = yes
|
||||
* 0 = no
|
||||
* -1 = error detected
|
||||
|
@ -146,7 +195,7 @@ static int filt_s (pfstate_t *pf);
|
|||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
int pfilter (int from_chan, int to_chan, char *filter, packet_t pp)
|
||||
int pfilter (int from_chan, int to_chan, char *filter, packet_t pp, int is_aprs)
|
||||
{
|
||||
pfstate_t pfstate;
|
||||
char *p;
|
||||
|
@ -155,13 +204,15 @@ int pfilter (int from_chan, int to_chan, char *filter, packet_t pp)
|
|||
assert (from_chan >= 0 && from_chan <= MAX_CHANS);
|
||||
assert (to_chan >= 0 && to_chan <= MAX_CHANS);
|
||||
|
||||
memset (&pfstate, 0, sizeof(pfstate));
|
||||
|
||||
if (pp == NULL) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("INTERNAL ERROR in pfilter: NULL packet pointer. Please report this!\n");
|
||||
return (-1);
|
||||
}
|
||||
if (filter == NULL) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("INTERNAL ERROR in pfilter: NULL filter string pointer. Please report this!\n");
|
||||
return (-1);
|
||||
}
|
||||
|
@ -169,10 +220,9 @@ int pfilter (int from_chan, int to_chan, char *filter, packet_t pp)
|
|||
pfstate.from_chan = from_chan;
|
||||
pfstate.to_chan = to_chan;
|
||||
|
||||
/* Copy filter string, removing any control characters. */
|
||||
/* Copy filter string, changing any control characers to spaces. */
|
||||
|
||||
memset (pfstate.filter_str, 0, sizeof(pfstate.filter_str));
|
||||
strncpy (pfstate.filter_str, filter, MAX_FILTER_LEN-1);
|
||||
strlcpy (pfstate.filter_str, filter, sizeof(pfstate.filter_str));
|
||||
|
||||
pfstate.nexti = 0;
|
||||
for (p = pfstate.filter_str; *p != '\0'; p++) {
|
||||
|
@ -182,7 +232,11 @@ int pfilter (int from_chan, int to_chan, char *filter, packet_t pp)
|
|||
}
|
||||
|
||||
pfstate.pp = pp;
|
||||
pfstate.is_aprs = is_aprs;
|
||||
|
||||
if (is_aprs) {
|
||||
decode_aprs (&pfstate.decoded, pp, 1);
|
||||
}
|
||||
|
||||
next_token(&pfstate);
|
||||
|
||||
|
@ -201,6 +255,23 @@ int pfilter (int from_chan, int to_chan, char *filter, packet_t pp)
|
|||
result = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (s_debug >= 1) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
if (from_chan == MAX_CHANS) {
|
||||
dw_printf (" Packet filter from IGate to radio channel %d returns %s\n", to_chan, bool2text(result));
|
||||
}
|
||||
else if (to_chan == MAX_CHANS) {
|
||||
dw_printf (" Packet filter from radio channel %d to IGate returns %s\n", from_chan, bool2text(result));
|
||||
}
|
||||
else if (is_aprs) {
|
||||
dw_printf (" Packet filter for APRS digipeater from radio channel %d to %d returns %s\n", from_chan, to_chan, bool2text(result));
|
||||
}
|
||||
else {
|
||||
dw_printf (" Packet filter for traditional digipeater from radio channel %d to %d returns %s\n", from_chan, to_chan, bool2text(result));
|
||||
}
|
||||
}
|
||||
|
||||
return (result);
|
||||
|
||||
} /* end pfilter */
|
||||
|
@ -339,6 +410,12 @@ static int parse_or_expr (pfstate_t *pf)
|
|||
|
||||
next_token (pf);
|
||||
e = parse_and_expr (pf);
|
||||
|
||||
if (s_debug >= 3) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf (" %s | %s\n", bool2text(result), bool2text(e));
|
||||
}
|
||||
|
||||
if (e < 0) return (-1);
|
||||
result |= e;
|
||||
}
|
||||
|
@ -360,6 +437,12 @@ static int parse_and_expr (pfstate_t *pf)
|
|||
|
||||
next_token (pf);
|
||||
e = parse_primary (pf);
|
||||
|
||||
if (s_debug >= 3) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf (" %s & %s\n", bool2text(result), bool2text(e));
|
||||
}
|
||||
|
||||
if (e < 0) return (-1);
|
||||
result &= e;
|
||||
}
|
||||
|
@ -393,6 +476,12 @@ static int parse_primary (pfstate_t *pf)
|
|||
|
||||
next_token (pf);
|
||||
e = parse_primary (pf);
|
||||
|
||||
if (s_debug >= 3) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf (" ! %s\n", bool2text(e));
|
||||
}
|
||||
|
||||
if (e < 0) result = -1;
|
||||
else result = ! e;
|
||||
}
|
||||
|
@ -419,14 +508,29 @@ static int parse_primary (pfstate_t *pf)
|
|||
* 0 = no
|
||||
* -1 = error detected
|
||||
*
|
||||
* Description: All filter specifications are allowed for APRS.
|
||||
* Only those dealing with addresses are allowed for connected digipeater.
|
||||
*
|
||||
* b - budlist (source)
|
||||
* d - digipeaters used
|
||||
* v - digipeaters not used
|
||||
* u - unproto (destination)
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
||||
static int parse_filter_spec (pfstate_t *pf)
|
||||
{
|
||||
int result = -1;
|
||||
char addr[AX25_MAX_ADDR_LEN];
|
||||
|
||||
|
||||
if ( ( ! pf->is_aprs) && strchr ("01bdvu", pf->token_str[0]) == NULL) {
|
||||
|
||||
print_error (pf, "Only b, d, v, and u specifications are allowed for connected mode digipeater filtering.");
|
||||
result = -1;
|
||||
next_token (pf);
|
||||
return (result);
|
||||
}
|
||||
|
||||
|
||||
/* undocumented: can use 0 or 1 for testing. */
|
||||
|
||||
|
@ -439,14 +543,33 @@ static int parse_filter_spec (pfstate_t *pf)
|
|||
|
||||
/* simple string matching */
|
||||
|
||||
/* b - budlist */
|
||||
|
||||
else if (pf->token_str[0] == 'b' && ispunct(pf->token_str[1])) {
|
||||
/* Budlist - source address */
|
||||
result = filt_bodgu (pf, pf->decoded.g_src);
|
||||
char addr[AX25_MAX_ADDR_LEN];
|
||||
ax25_get_addr_with_ssid (pf->pp, AX25_SOURCE, addr);
|
||||
result = filt_bodgu (pf, addr);
|
||||
|
||||
if (s_debug >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), addr);
|
||||
}
|
||||
}
|
||||
|
||||
/* o - object or item name */
|
||||
|
||||
else if (pf->token_str[0] == 'o' && ispunct(pf->token_str[1])) {
|
||||
/* Object or item name */
|
||||
result = filt_bodgu (pf, pf->decoded.g_name);
|
||||
|
||||
if (s_debug >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), pf->decoded.g_name);
|
||||
}
|
||||
}
|
||||
|
||||
/* d - was digipeated by */
|
||||
|
||||
else if (pf->token_str[0] == 'd' && ispunct(pf->token_str[1])) {
|
||||
int n;
|
||||
// loop on all digipeaters
|
||||
|
@ -454,11 +577,26 @@ static int parse_filter_spec (pfstate_t *pf)
|
|||
for (n = AX25_REPEATER_1; result == 0 && n < ax25_get_num_addr (pf->pp); n++) {
|
||||
// Consider only those with the H (has-been-used) bit set.
|
||||
if (ax25_get_h (pf->pp, n)) {
|
||||
char addr[AX25_MAX_ADDR_LEN];
|
||||
ax25_get_addr_with_ssid (pf->pp, n, addr);
|
||||
result = filt_bodgu (pf, addr);
|
||||
}
|
||||
}
|
||||
|
||||
if (s_debug >= 2) {
|
||||
char path[100];
|
||||
|
||||
ax25_format_via_path (pf->pp, path, sizeof(path));
|
||||
if (strlen(path) == 0) {
|
||||
strcpy (path, "no digipeater path");
|
||||
}
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), path);
|
||||
}
|
||||
}
|
||||
|
||||
/* v - via not used */
|
||||
|
||||
else if (pf->token_str[0] == 'v' && ispunct(pf->token_str[1])) {
|
||||
int n;
|
||||
// loop on all digipeaters (mnemonic Via)
|
||||
|
@ -467,54 +605,139 @@ static int parse_filter_spec (pfstate_t *pf)
|
|||
// This is different than the previous "d" filter.
|
||||
// Consider only those where the the H (has-been-used) bit is NOT set.
|
||||
if ( ! ax25_get_h (pf->pp, n)) {
|
||||
char addr[AX25_MAX_ADDR_LEN];
|
||||
ax25_get_addr_with_ssid (pf->pp, n, addr);
|
||||
result = filt_bodgu (pf, addr);
|
||||
}
|
||||
}
|
||||
|
||||
if (s_debug >= 2) {
|
||||
char path[100];
|
||||
|
||||
ax25_format_via_path (pf->pp, path, sizeof(path));
|
||||
if (strlen(path) == 0) {
|
||||
strcpy (path, "no digipeater path");
|
||||
}
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), path);
|
||||
}
|
||||
}
|
||||
|
||||
/* g - Addressee of message. */
|
||||
|
||||
else if (pf->token_str[0] == 'g' && ispunct(pf->token_str[1])) {
|
||||
/* Addressee of message. */
|
||||
if (ax25_get_dti(pf->pp) == ':') {
|
||||
result = filt_bodgu (pf, pf->decoded.g_addressee);
|
||||
|
||||
if (s_debug >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), pf->decoded.g_addressee);
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = 0;
|
||||
if (s_debug >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), "not a message");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* u - unproto (destination) */
|
||||
|
||||
else if (pf->token_str[0] == 'u' && ispunct(pf->token_str[1])) {
|
||||
/* Unproto (destination) - probably want to exclude mic-e types */
|
||||
/* because destintation is used for part of location. */
|
||||
/* Probably want to exclude mic-e types */
|
||||
/* because destination is used for part of location. */
|
||||
|
||||
if (ax25_get_dti(pf->pp) != '\'' && ax25_get_dti(pf->pp) != '`') {
|
||||
char addr[AX25_MAX_ADDR_LEN];
|
||||
ax25_get_addr_with_ssid (pf->pp, AX25_DESTINATION, addr);
|
||||
result = filt_bodgu (pf, addr);
|
||||
|
||||
if (s_debug >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), addr);
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = 0;
|
||||
if (s_debug >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), "MIC-E packet type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* type: position, weather, etc. */
|
||||
/* t - type: position, weather, etc. */
|
||||
|
||||
else if (pf->token_str[0] == 't' && ispunct(pf->token_str[1])) {
|
||||
|
||||
ax25_get_addr_with_ssid (pf->pp, AX25_DESTINATION, addr);
|
||||
result = filt_t (pf);
|
||||
|
||||
if (s_debug >= 2) {
|
||||
char *infop = NULL;
|
||||
(void) ax25_get_info (pf->pp, (unsigned char **)(&infop));
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf (" %s returns %s for %c data type indicator\n", pf->token_str, bool2text(result), *infop);
|
||||
}
|
||||
}
|
||||
|
||||
/* range */
|
||||
/* r - range */
|
||||
|
||||
else if (pf->token_str[0] == 'r' && ispunct(pf->token_str[1])) {
|
||||
/* range */
|
||||
result = filt_r (pf);
|
||||
char sdist[30];
|
||||
strcpy (sdist, "unknown distance");
|
||||
result = filt_r (pf, sdist);
|
||||
|
||||
if (s_debug >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), sdist);
|
||||
}
|
||||
}
|
||||
|
||||
/* symbol */
|
||||
/* s - symbol */
|
||||
|
||||
else if (pf->token_str[0] == 's' && ispunct(pf->token_str[1])) {
|
||||
/* symbol */
|
||||
result = filt_s (pf);
|
||||
|
||||
if (s_debug >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
if (pf->decoded.g_symbol_table == '/') {
|
||||
dw_printf (" %s returns %s for symbol %c in primary table\n", pf->token_str, bool2text(result), pf->decoded.g_symbol_code);
|
||||
}
|
||||
else if (pf->decoded.g_symbol_table == '\\') {
|
||||
dw_printf (" %s returns %s for symbol %c in alternate table\n", pf->token_str, bool2text(result), pf->decoded.g_symbol_code);
|
||||
}
|
||||
else {
|
||||
dw_printf (" %s returns %s for symbol %c with overlay %c\n", pf->token_str, bool2text(result), pf->decoded.g_symbol_code, pf->decoded.g_symbol_table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* i - IGate messaging default */
|
||||
|
||||
else if (pf->token_str[0] == 'i' && ispunct(pf->token_str[1])) {
|
||||
/* IGatge messaging */
|
||||
result = filt_i (pf);
|
||||
|
||||
if (s_debug >= 2) {
|
||||
char *infop = NULL;
|
||||
(void) ax25_get_info (pf->pp, (unsigned char **)(&infop));
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
if (*infop == ':' && ! is_telem_metadata(infop)) {
|
||||
dw_printf (" %s returns %s for message to %s\n", pf->token_str, bool2text(result), pf->decoded.g_addressee);
|
||||
}
|
||||
else {
|
||||
dw_printf (" %s returns %s for not an APRS 'message'\n", pf->token_str, bool2text(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* unrecognized filter type */
|
||||
|
||||
else {
|
||||
char stemp[80];
|
||||
|
@ -543,7 +766,7 @@ static int parse_filter_spec (pfstate_t *pf)
|
|||
* Digipeater d/digi1/digi2...
|
||||
* Group Msg g/call1/call2...
|
||||
* Unproto u/unproto1/unproto2...
|
||||
* Via-not-yet v/digi1/digi2...
|
||||
* Via-not-yet v/digi1/digi2...noteapd
|
||||
*
|
||||
* arg - Value to match from source addr, destination,
|
||||
* used digipeater, object name, etc.
|
||||
|
@ -580,7 +803,7 @@ static int filt_bodgu (pfstate_t *pf, char *arg)
|
|||
/* Wildcarding. Should have single * on end. */
|
||||
|
||||
mlen = w - v;
|
||||
if (mlen != strlen(v) - 1) {
|
||||
if (mlen != (int)(strlen(v) - 1)) {
|
||||
print_error (pf, "Any wildcard * must be at the end of pattern.\n");
|
||||
return (-1);
|
||||
}
|
||||
|
@ -622,7 +845,7 @@ static int filt_bodgu (pfstate_t *pf, char *arg)
|
|||
/* Telemetry metadata is a special case of message. */
|
||||
/* We want to categorize it as telemetry rather than message. */
|
||||
|
||||
static int is_telem_metadata (char *infop)
|
||||
int is_telem_metadata (char *infop)
|
||||
{
|
||||
if (*infop != ':') return (0);
|
||||
if (strlen(infop) < 16) return (0);
|
||||
|
@ -757,6 +980,8 @@ static int filt_t (pfstate_t *pf)
|
|||
*
|
||||
* decoded.g_lat & decoded.g_lon
|
||||
*
|
||||
* Outputs: sdist - Distance as a string for troubleshooting.
|
||||
*
|
||||
* Returns: 1 = yes
|
||||
* 0 = no
|
||||
* -1 = error detected
|
||||
|
@ -765,7 +990,7 @@ static int filt_t (pfstate_t *pf)
|
|||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
static int filt_r (pfstate_t *pf)
|
||||
static int filt_r (pfstate_t *pf, char *sdist)
|
||||
{
|
||||
char str[MAX_TOKEN_LEN];
|
||||
char *cp;
|
||||
|
@ -806,10 +1031,7 @@ static int filt_r (pfstate_t *pf)
|
|||
|
||||
km = ll_distance_km (dlat, dlon, pf->decoded.g_lat, pf->decoded.g_lon);
|
||||
|
||||
|
||||
text_color_set (DW_COLOR_DEBUG);
|
||||
|
||||
dw_printf ("Calculated distance = %.3f km\n", km);
|
||||
sprintf (sdist, "%.2f km", km);
|
||||
|
||||
if (km <= ddist) {
|
||||
return (1);
|
||||
|
@ -862,7 +1084,10 @@ static int filt_s (pfstate_t *pf)
|
|||
sep[1] = '\0';
|
||||
cp = str + 2;
|
||||
|
||||
// TODO: check here.
|
||||
|
||||
pri = strsep (&cp, sep);
|
||||
|
||||
if (pri == NULL) {
|
||||
print_error (pf, "Missing arguments for Symbol filter.");
|
||||
return (-1);
|
||||
|
@ -906,6 +1131,212 @@ static int filt_s (pfstate_t *pf)
|
|||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: filt_i
|
||||
*
|
||||
* Purpose: IGate messaging default behavior.
|
||||
*
|
||||
* Inputs: pf - Pointer to current state information.
|
||||
* token_str should contain something of format:
|
||||
*
|
||||
* i/time/hops/lat/lon/km
|
||||
*
|
||||
* Returns: 1 = yes
|
||||
* 0 = no
|
||||
* -1 = error detected
|
||||
*
|
||||
* Description: Selection is based on time since last heard on RF, and distance
|
||||
* in terms of digipeater hops and/or phyiscal location.
|
||||
*
|
||||
* i/time
|
||||
* i/time/hops
|
||||
* i/time/hops/lat/lon/km
|
||||
*
|
||||
*
|
||||
* "time" is maximum number of minutes since message addressee was last heard.
|
||||
* This is required.
|
||||
*
|
||||
* "hops" is maximum number of digpeater hops. (i.e. 0 for heard directly).
|
||||
* If hops is not specified, the maximum transmit digipeater hop count,
|
||||
* from the IGTXVIA configuration will be used.
|
||||
|
||||
* The rest is distanced, in kilometers, from given point.
|
||||
*
|
||||
* Examples:
|
||||
* i/60/0 Heard in past 60 minutes directly.
|
||||
* i/45 Past 45 minutes, default max digi hops.
|
||||
* i/30/3 Default time, max 3 digi hops.
|
||||
* i/30/8/42.6/-71.3/50.
|
||||
*
|
||||
*
|
||||
* It only makes sense to use this for the IS>RF direction.
|
||||
* The basic idea is that we want to transmit a "message" only if the
|
||||
* addressee has been heard recently and is not too far away.
|
||||
*
|
||||
* After passing along a "message" we will also allow the next
|
||||
* position report from the sender of the "message."
|
||||
* That is done somewhere else. We are not concerned with it here.
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
static int filt_i (pfstate_t *pf)
|
||||
{
|
||||
char str[MAX_TOKEN_LEN];
|
||||
char *cp;
|
||||
char sep[2];
|
||||
char *v;
|
||||
int heardtime = 30;
|
||||
#if PFTEST
|
||||
int maxhops = 2;
|
||||
#else
|
||||
int maxhops = save_igate_config_p->max_digi_hops; // from IGTXVIA config.
|
||||
#endif
|
||||
double dlat = G_UNKNOWN;
|
||||
double dlon = G_UNKNOWN;
|
||||
double km = G_UNKNOWN;
|
||||
|
||||
|
||||
char src[AX25_MAX_ADDR_LEN];
|
||||
char *infop = NULL;
|
||||
int info_len;
|
||||
//char *f;
|
||||
//char addressee[AX25_MAX_ADDR_LEN];
|
||||
|
||||
|
||||
strlcpy (str, pf->token_str, sizeof(str));
|
||||
sep[0] = str[1];
|
||||
sep[1] = '\0';
|
||||
cp = str + 2;
|
||||
|
||||
// Get parameters or defaults.
|
||||
|
||||
v = strsep (&cp, sep);
|
||||
|
||||
if (v != NULL && strlen(v) > 0) {
|
||||
heardtime = atoi(v);
|
||||
}
|
||||
else {
|
||||
print_error (pf, "Missing time limit for IGate message filter.");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
v = strsep (&cp, sep);
|
||||
|
||||
if (v != NULL) {
|
||||
if (strlen(v) > 0) {
|
||||
maxhops = atoi(v);
|
||||
}
|
||||
else {
|
||||
print_error (pf, "Missing max digipeater hops for IGate message filter.");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
v = strsep (&cp, sep);
|
||||
if (v != NULL && strlen(v) > 0) {
|
||||
dlat = atof(v);
|
||||
|
||||
v = strsep (&cp, sep);
|
||||
if (v != NULL && strlen(v) > 0) {
|
||||
dlon = atof(v);
|
||||
}
|
||||
else {
|
||||
print_error (pf, "Missing longitude for IGate message filter.");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
v = strsep (&cp, sep);
|
||||
if (v != NULL && strlen(v) > 0) {
|
||||
km = atof(v);
|
||||
}
|
||||
else {
|
||||
print_error (pf, "Missing distance, in km, for IGate message filter.");
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
v = strsep (&cp, sep);
|
||||
if (v != NULL) {
|
||||
print_error (pf, "Something unexpected after distance for IGate message filter.");
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
#if PFTEST
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("debug: IGate message filter, %d minutes, %d hops, %.2f %.2f %.2f km\n",
|
||||
heardtime, maxhops, dlat, dlon, km);
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Get source address and info part.
|
||||
* Addressee has already been extracted into pf->decoded.g_addressee.
|
||||
*/
|
||||
|
||||
memset (src, 0, sizeof(src));
|
||||
ax25_get_addr_with_ssid (pf->pp, AX25_SOURCE, src);
|
||||
info_len = ax25_get_info (pf->pp, (unsigned char **)(&infop));
|
||||
|
||||
if (infop == NULL) return (0);
|
||||
if (info_len < 1) return (0);
|
||||
|
||||
// Determine packet type. We are interested only in "message."
|
||||
// Telemetry metadata is not considered a message.
|
||||
|
||||
if (*infop != ':') return (0);
|
||||
if (is_telem_metadata(infop)) return (0);
|
||||
|
||||
|
||||
#if defined(PFTEST) || defined(DIGITEST) // TODO: test functionality too, not just syntax.
|
||||
|
||||
(void)dlat; // Suppress set and not used warning.
|
||||
(void)dlon;
|
||||
(void)km;
|
||||
(void)maxhops;
|
||||
(void)heardtime;
|
||||
|
||||
return (1);
|
||||
#else
|
||||
|
||||
/*
|
||||
* Condition 1:
|
||||
* "the receiving station has been heard within range within a predefined time
|
||||
* period (range defined as digi hops, distance, or both)."
|
||||
*/
|
||||
|
||||
int was_heard = mheard_was_recently_nearby ("addressee", pf->decoded.g_addressee, heardtime, maxhops, dlat, dlon, km);
|
||||
|
||||
if ( ! was_heard) return (0);
|
||||
|
||||
/*
|
||||
* Condition 2:
|
||||
* "the sending station has not been heard via RF within a predefined time period
|
||||
* (packets gated from the Internet by other stations are excluded from this test)."
|
||||
*
|
||||
* This is the part I'm not so sure about.
|
||||
* I guess the intention is that if the sender can be heard over RF, then the addressee
|
||||
* might hear the sender without the help of Igate stations.
|
||||
* Suppose the sender was 1 digipeater hop to the west and the addressee was 1 digipeater hop to the east.
|
||||
* I can communicate with each of them with 1 digipeater hop but for them to reach each other, they
|
||||
* might need 3 hops and using that many is generally frowned upon and rare.
|
||||
*
|
||||
* Maybe we could compromise here and say the sender must have been heard directly.
|
||||
* It sent the message currently being processed so we must have heard it very recently, i.e. in
|
||||
* the past minute, rather than the usual 30 or 60 minutes for the addressee.
|
||||
*/
|
||||
|
||||
was_heard = mheard_was_recently_nearby ("source", src, 1, 0, G_UNKNOWN, G_UNKNOWN, G_UNKNOWN);
|
||||
|
||||
if (was_heard) return (0);
|
||||
|
||||
return (1);
|
||||
|
||||
#endif
|
||||
|
||||
} /* end filt_i */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: print_error
|
||||
|
@ -1101,6 +1532,21 @@ int main ()
|
|||
pftest (203, "t/w t/w", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", -1);
|
||||
pftest (204, "r/42.6/-71.3", "WA1PLE-5>APWW10,W1MHL,N8VIM,WIDE2*:@022301h4208.75N/07115.16WoAPRS-IS for Win32", -1);
|
||||
|
||||
pftest (220, "i/30/8/42.6/-71.3/50", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
|
||||
pftest (222, "i/30/8/42.6/-71.3/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
|
||||
pftest (223, "i/30/8/42.6/-71.3", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
|
||||
pftest (224, "i/30/8/42.6/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
|
||||
pftest (225, "i/30/8/42.6", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
|
||||
pftest (226, "i/30/8/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
|
||||
pftest (227, "i/30/8", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
|
||||
|
||||
// FIXME: behaves differently on Windows and Linux
|
||||
//pftest (228, "i/30/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
|
||||
|
||||
pftest (229, "i/30", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
|
||||
pftest (230, "i/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
|
||||
|
||||
// TODO: to be continued...
|
||||
|
||||
if (error_count > 0) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
|
@ -1124,7 +1570,7 @@ static void pftest (int test_num, char *filter, char *monitor, int expected)
|
|||
pp = ax25_from_text (monitor, 1);
|
||||
assert (pp != NULL);
|
||||
|
||||
result = pfilter (0, 0, filter, pp);
|
||||
result = pfilter (0, 0, filter, pp, 1);
|
||||
if (result != expected) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Unexpected result for test number %d\n", test_num);
|
||||
|
|
11
pfilter.h
11
pfilter.h
|
@ -1,4 +1,13 @@
|
|||
|
||||
/* pfilter.h */
|
||||
|
||||
int pfilter (int from_chan, int to_chan, char *filter, packet_t pp);
|
||||
|
||||
#include "igate.h" // for igate_config_s
|
||||
|
||||
|
||||
|
||||
void pfilter_init (struct igate_config_s *p_igate_config, int debug_level);
|
||||
|
||||
int pfilter (int from_chan, int to_chan, char *filter, packet_t pp, int is_aprs);
|
||||
|
||||
int is_telem_metadata (char *infop);
|
508
ptt.c
508
ptt.c
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2011, 2013, 2014, 2015, 2016 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2013, 2014, 2015, 2016, 2017 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
|
||||
|
@ -46,6 +46,12 @@
|
|||
*
|
||||
* Version 1.3: HAMLIB support.
|
||||
*
|
||||
* Version 1.4: The spare "future" indicator is now used when connected to another station.
|
||||
*
|
||||
* Take advantage of the new 'gpio' group and new /sys/class/gpio protections in Raspbian Jessie.
|
||||
*
|
||||
* Handle more complicated gpio node names for CubieBoard, etc.
|
||||
*
|
||||
* References: http://www.robbayer.com/files/serial-win.pdf
|
||||
*
|
||||
* https://www.kernel.org/doc/Documentation/gpio.txt
|
||||
|
@ -94,6 +100,9 @@
|
|||
Maybe even for Windows. ;-)
|
||||
*/
|
||||
|
||||
|
||||
#include "direwolf.h" // should be first. This includes windows.h.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -102,7 +111,6 @@
|
|||
#include <time.h>
|
||||
|
||||
#if __WIN32__
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
@ -111,6 +119,8 @@
|
|||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#ifdef USE_HAMLIB
|
||||
#include <hamlib/rig.h>
|
||||
|
@ -122,10 +132,10 @@ typedef int HANDLE;
|
|||
|
||||
#endif
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "textcolor.h"
|
||||
#include "audio.h"
|
||||
#include "ptt.h"
|
||||
#include "dlq.h"
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
|
@ -152,6 +162,8 @@ typedef int HANDLE;
|
|||
#endif
|
||||
|
||||
|
||||
static struct audio_s *save_audio_config_p; /* Save config information for later use. */
|
||||
|
||||
static int ptt_debug_level = 0;
|
||||
|
||||
void ptt_set_debug(int debug)
|
||||
|
@ -159,6 +171,198 @@ void ptt_set_debug(int debug)
|
|||
ptt_debug_level = debug;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: get_access_to_gpio
|
||||
*
|
||||
* Purpose: Try to get access to the GPIO device.
|
||||
*
|
||||
* Inputs: path - Path to device node.
|
||||
* /sys/class/gpio/export
|
||||
* /sys/class/gpio/unexport
|
||||
* /sys/class/gpio/gpio??/direction
|
||||
* /sys/class/gpio/gpio??/value
|
||||
*
|
||||
* Description: First see if we have access thru the usual uid/gid/mode method.
|
||||
* If that fails, we try a hack where we use "sudo chmod ..." to open up access.
|
||||
* That requires that sudo be configured to work without a password.
|
||||
* That's the case for 'pi' user in Raspbian but not not be for other boards / operating systems.
|
||||
*
|
||||
* Debug: Use the "-doo" command line option.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
#ifndef __WIN32__
|
||||
|
||||
#define MAX_GROUPS 50
|
||||
|
||||
|
||||
static void get_access_to_gpio (const char *path)
|
||||
{
|
||||
|
||||
static int my_uid = -1;
|
||||
static int my_gid = -1;
|
||||
static gid_t my_groups[MAX_GROUPS];
|
||||
static int num_groups = 0;
|
||||
static int first_time = 1;
|
||||
|
||||
struct stat finfo;
|
||||
int i;
|
||||
char cmd[80];
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Does path even exist?
|
||||
*/
|
||||
|
||||
if (stat(path, &finfo) < 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Can't get properties of %s.\n", path);
|
||||
dw_printf ("This system is not configured with the GPIO user interface.\n");
|
||||
dw_printf ("Use a different method for PTT control.\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (first_time) {
|
||||
|
||||
// No need to fetch same information each time. Cache it.
|
||||
my_uid = geteuid();
|
||||
my_gid = getegid();
|
||||
num_groups = getgroups (MAX_GROUPS, my_groups);
|
||||
|
||||
if (num_groups < 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("getgroups() failed to get supplementary groups, errno=%d\n", errno);
|
||||
num_groups = 0;
|
||||
}
|
||||
first_time = 0;
|
||||
}
|
||||
|
||||
if (ptt_debug_level >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("%s: uid=%d, gid=%d, mode=o%o\n", path, finfo.st_uid, finfo.st_gid, finfo.st_mode);
|
||||
dw_printf ("my uid=%d, gid=%d, supplementary groups=", my_uid, my_gid);
|
||||
for (i = 0; i < num_groups; i++) {
|
||||
dw_printf (" %d", my_groups[i]);
|
||||
}
|
||||
dw_printf ("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Do we have permission to access it?
|
||||
*
|
||||
* On Debian 7 (Wheezy) we see this:
|
||||
*
|
||||
* $ ls -l /sys/class/gpio/export
|
||||
* --w------- 1 root root 4096 Feb 27 12:31 /sys/class/gpio/export
|
||||
*
|
||||
*
|
||||
* Only root can write to it.
|
||||
* Our work-around is change the protection so that everyone can write.
|
||||
* This requires that the current user can use sudo without a password.
|
||||
* This has been the case for the predefined "pi" user but can be a problem
|
||||
* when people add new user names.
|
||||
* Other operating systems could have different default configurations.
|
||||
*
|
||||
* A better solution is available in Debian 8 (Jessie). The group is now "gpio"
|
||||
* so anyone in that group can now write to it.
|
||||
*
|
||||
* $ ls -l /sys/class/gpio/export
|
||||
* -rwxrwx--- 1 root gpio 4096 Mar 4 21:12 /sys/class/gpio/export
|
||||
*
|
||||
*
|
||||
* First see if we can access it by the usual file protection rules.
|
||||
* If not, we will try the "sudo chmod go+rw ..." hack.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Do I have access?
|
||||
* We could just try to open for write but this gives us more debugging information.
|
||||
*/
|
||||
|
||||
if ((my_uid == finfo.st_uid) && (finfo.st_mode & S_IWUSR)) { // user write 00200
|
||||
if (ptt_debug_level >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("My uid matches and we have user write permission.\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ((my_gid == finfo.st_gid) && (finfo.st_mode & S_IWGRP)) { // group write 00020
|
||||
if (ptt_debug_level >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("My primary gid matches and we have group write permission.\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_groups; i++) {
|
||||
if ((my_groups[i] == finfo.st_gid) && (finfo.st_mode & S_IWGRP)) { // group write 00020
|
||||
if (ptt_debug_level >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("My supplemental group %d matches and we have group write permission.\n", my_groups[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (finfo.st_mode & S_IWOTH) { // other write 00002
|
||||
if (ptt_debug_level >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("We have other write permission.\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't have permission.
|
||||
* Try a hack which requires that the user be set up to use sudo without a password.
|
||||
*/
|
||||
|
||||
if (ptt_debug_level >= 2) {
|
||||
text_color_set(DW_COLOR_ERROR); // debug message but different color so it stands out.
|
||||
dw_printf ("Trying 'sudo chmod go+rw %s' hack.\n", path);
|
||||
}
|
||||
|
||||
snprintf (cmd, sizeof(cmd), "sudo chmod go+rw %s", path);
|
||||
err = system (cmd);
|
||||
(void)err; // suppress warning about not using result.
|
||||
|
||||
/*
|
||||
* I don't trust status coming back from system() so we will check the mode again.
|
||||
*/
|
||||
|
||||
if (stat(path, &finfo) < 0) {
|
||||
/* Unexpected because we could do it before. */
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("This system is not configured with the GPIO user interface.\n");
|
||||
dw_printf ("Use a different method for PTT control.\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Did we succeed in changing the protection? */
|
||||
|
||||
if ( (finfo.st_mode & 0266) != 0266) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("You don't have the necessary permission to access GPIO.\n");
|
||||
dw_printf ("There are three different solutions: \n");
|
||||
dw_printf (" 1. Run as root. (not recommended)\n");
|
||||
dw_printf (" 2. If operating system has 'gpio' group, add your user id to it.\n");
|
||||
dw_printf (" 3. Configure your user id for sudo without a password.\n");
|
||||
dw_printf ("\n");
|
||||
dw_printf ("Read the documentation and try -doo command line option for debugging details.\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: export_gpio
|
||||
|
@ -166,39 +370,63 @@ void ptt_set_debug(int debug)
|
|||
* Purpose: Tell the GPIO subsystem to export a GPIO line for
|
||||
* us to use, and set the initial state of the GPIO.
|
||||
*
|
||||
* Inputs: gpio - GPIO line to export
|
||||
* Inputs: ch - Radio Channel.
|
||||
* ot - Output type.
|
||||
* invert: - Is the GPIO active low?
|
||||
* direction: - 0 for input, 1 for output
|
||||
*
|
||||
* Outputs: None.
|
||||
* Outputs: out_gpio_name - in the audio configuration structure.
|
||||
* in_gpio_name
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
#ifndef __WIN32__
|
||||
|
||||
void export_gpio(int gpio, int invert, int direction)
|
||||
|
||||
void export_gpio(int ch, int ot, int invert, int direction)
|
||||
{
|
||||
HANDLE fd;
|
||||
char stemp[80];
|
||||
struct stat finfo;
|
||||
int err;
|
||||
const char gpio_export_path[] = "/sys/class/gpio/export";
|
||||
char gpio_direction_path[80];
|
||||
char gpio_value_path[80];
|
||||
char stemp[16];
|
||||
int gpio_num;
|
||||
char *gpio_name;
|
||||
|
||||
fd = open("/sys/class/gpio/export", O_WRONLY);
|
||||
// Raspberry Pi was easy. GPIO 24 has the name gpio24.
|
||||
// Others, such as the Cubieboard, take a little more effort.
|
||||
// The name might be gpio24_ph11 meaning connector H, pin 11.
|
||||
// When we "export" GPIO number, we will store the corresponding
|
||||
// device name for future use when we want to access it.
|
||||
|
||||
if (direction) {
|
||||
gpio_num = save_audio_config_p->achan[ch].octrl[ot].out_gpio_num;
|
||||
gpio_name = save_audio_config_p->achan[ch].octrl[ot].out_gpio_name;
|
||||
}
|
||||
else {
|
||||
gpio_num = save_audio_config_p->achan[ch].ictrl[ot].in_gpio_num;
|
||||
gpio_name = save_audio_config_p->achan[ch].ictrl[ot].in_gpio_name;
|
||||
}
|
||||
|
||||
|
||||
get_access_to_gpio (gpio_export_path);
|
||||
|
||||
fd = open(gpio_export_path, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
// Not expected. Above should have obtained permission or exited.
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Permissions do not allow ordinary users to access GPIO.\n");
|
||||
dw_printf ("Log in as root and type this command:\n");
|
||||
dw_printf (" chmod go+w /sys/class/gpio/export /sys/class/gpio/unexport\n");
|
||||
dw_printf ("Permissions do not allow access to GPIO.\n");
|
||||
exit (1);
|
||||
}
|
||||
snprintf (stemp, sizeof(stemp), "%d", gpio);
|
||||
|
||||
snprintf (stemp, sizeof(stemp), "%d", gpio_num);
|
||||
if (write (fd, stemp, strlen(stemp)) != strlen(stemp)) {
|
||||
int e = errno;
|
||||
/* Ignore EBUSY error which seems to mean */
|
||||
/* the device node already exists. */
|
||||
if (e != EBUSY) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Error writing \"%s\" to /sys/class/gpio/export, errno=%d\n", stemp, e);
|
||||
dw_printf ("Error writing \"%s\" to %s, errno=%d\n", stemp, gpio_export_path, e);
|
||||
dw_printf ("%s\n", strerror(e));
|
||||
exit (1);
|
||||
}
|
||||
|
@ -206,62 +434,118 @@ void export_gpio(int gpio, int invert, int direction)
|
|||
close (fd);
|
||||
|
||||
/*
|
||||
Idea for future:
|
||||
* Added in release 1.4.
|
||||
*
|
||||
* On the RPi, the device path for GPIO number XX is simply /sys/class/gpio/gpioXX.
|
||||
*
|
||||
* There was a report that it is different for the CubieBoard. For instance
|
||||
* GPIO 61 has gpio61_pi13 in the path. This indicates connector "i" pin 13.
|
||||
* https://github.com/cubieplayer/Cubian/wiki/GPIO-Introduction
|
||||
*
|
||||
* For another similar single board computer, we find the same thing:
|
||||
* https://www.olimex.com/wiki/A20-OLinuXino-LIME#GPIO_under_Linux
|
||||
*
|
||||
* How should we deal with this? Some possibilities:
|
||||
*
|
||||
* (1) The user might explicitly mention the name in direwolf.conf.
|
||||
* (2) We might be able to find the names in some system device config file.
|
||||
* (3) Get a directory listing of /sys/class/gpio then search for a
|
||||
* matching name. Suppose we wanted GPIO 61. First look for an exact
|
||||
* match to "gpio61". If that is not found, look for something
|
||||
* matching the pattern "gpio61_*".
|
||||
*
|
||||
* We are finally implementing the third choice.
|
||||
*/
|
||||
|
||||
On the RPi, the device path for GPIO number XX is /sys/class/gpio/gpioXX.
|
||||
There was a report that it is different for the Cubieboard. For instance
|
||||
GPIO 61 has gpio61_pi13 in the path. This indicates connector "i" pin 13.
|
||||
struct dirent **file_list;
|
||||
int num_files;
|
||||
int i;
|
||||
int ok = 0;
|
||||
|
||||
For another similar single board computer, we find the same thing:
|
||||
https://www.olimex.com/wiki/A20-OLinuXino-LIME#GPIO_under_Linux
|
||||
if (ptt_debug_level >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Contents of /sys/class/gpio:\n");
|
||||
}
|
||||
|
||||
How should we deal with this? Some possibilities:
|
||||
num_files = scandir ("/sys/class/gpio", &file_list, NULL, alphasort);
|
||||
|
||||
(1) The user might explicitly mention the name in direwolf.conf.
|
||||
(2) We might be able to find the names in some system device config file.
|
||||
(3) Get a directory listing of /sys/class/gpio then search for a
|
||||
matching name. Suppose we wanted GPIO 61. First look for an exact
|
||||
match to "gpio61". If that is not found, look for something
|
||||
matching the pattern "gpio61_*".
|
||||
*/
|
||||
if (num_files < 0) {
|
||||
// Something went wrong. Fill in the simple expected name and keep going.
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR! Could not get directory listing for /sys/class/gpio\n");
|
||||
|
||||
snprintf (gpio_name, MAX_GPIO_NAME_LEN, "gpio%d", gpio_num);
|
||||
num_files = 0;
|
||||
ok = 1;
|
||||
}
|
||||
else {
|
||||
|
||||
if (ptt_debug_level >= 2) {
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
|
||||
for (i = 0; i < num_files; i++) {
|
||||
dw_printf("\t%s\n", file_list[i]->d_name);
|
||||
}
|
||||
}
|
||||
|
||||
// Look for exact name gpioNN
|
||||
|
||||
char lookfor[16];
|
||||
snprintf (lookfor, sizeof(lookfor), "gpio%d", gpio_num);
|
||||
|
||||
for (i = 0; i < num_files && ! ok; i++) {
|
||||
if (strcmp(lookfor, file_list[i]->d_name) == 0) {
|
||||
strlcpy (gpio_name, file_list[i]->d_name, MAX_GPIO_NAME_LEN);
|
||||
ok = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If not found, Look for gpioNN_*
|
||||
|
||||
snprintf (lookfor, sizeof(lookfor), "gpio%d_", gpio_num);
|
||||
|
||||
for (i = 0; i < num_files && ! ok; i++) {
|
||||
if (strncmp(lookfor, file_list[i]->d_name, strlen(lookfor)) == 0) {
|
||||
strlcpy (gpio_name, file_list[i]->d_name, MAX_GPIO_NAME_LEN);
|
||||
ok = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Free the storage allocated by scandir().
|
||||
|
||||
for (i = 0; i < num_files; i++) {
|
||||
free (file_list[i]);
|
||||
}
|
||||
free (file_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* We will have the same permission problem if not root.
|
||||
* We only care about "direction" and "value".
|
||||
* We should now have the corresponding node name.
|
||||
*/
|
||||
snprintf (stemp, sizeof(stemp), "sudo chmod go+rw /sys/class/gpio/gpio%d/direction", gpio);
|
||||
err = system (stemp);
|
||||
snprintf (stemp, sizeof(stemp), "sudo chmod go+rw /sys/class/gpio/gpio%d/value", gpio);
|
||||
err = system (stemp);
|
||||
(void)err;
|
||||
if (ok) {
|
||||
|
||||
snprintf (stemp, sizeof(stemp), "/sys/class/gpio/gpio%d/value", gpio);
|
||||
|
||||
if (stat(stemp, &finfo) < 0) {
|
||||
int e = errno;
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Failed to get status for %s \n", stemp);
|
||||
dw_printf ("%s\n", strerror(e));
|
||||
exit (1);
|
||||
if (ptt_debug_level >= 2) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Path for gpio number %d is /sys/class/gpio/%s\n", gpio_num, gpio_name);
|
||||
}
|
||||
|
||||
if (geteuid() != 0) {
|
||||
if ( ! (finfo.st_mode & S_IWOTH)) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Permissions do not allow ordinary users to access GPIO.\n");
|
||||
dw_printf ("Log in as root and type these commands:\n");
|
||||
dw_printf (" chmod go+rw /sys/class/gpio/gpio%d/direction", gpio);
|
||||
dw_printf (" chmod go+rw /sys/class/gpio/gpio%d/value", gpio);
|
||||
exit (1);
|
||||
}
|
||||
else {
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR! Could not find Path for gpio number %d.n", gpio_num);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set output direction and initial state
|
||||
*/
|
||||
|
||||
snprintf (stemp, sizeof(stemp), "/sys/class/gpio/gpio%d/direction", gpio);
|
||||
fd = open(stemp, O_WRONLY);
|
||||
snprintf (gpio_direction_path, sizeof(gpio_direction_path), "/sys/class/gpio/%s/direction", gpio_name);
|
||||
get_access_to_gpio (gpio_direction_path);
|
||||
|
||||
fd = open(gpio_direction_path, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
int e = errno;
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
|
@ -290,6 +574,14 @@ void export_gpio(int gpio, int invert, int direction)
|
|||
exit (1);
|
||||
}
|
||||
close (fd);
|
||||
|
||||
/*
|
||||
* Make sure that we have access to 'value'.
|
||||
* Do it once here, rather than each time we want to use it.
|
||||
*/
|
||||
|
||||
snprintf (gpio_value_path, sizeof(gpio_value_path), "/sys/class/gpio/%s/value", gpio_name);
|
||||
get_access_to_gpio (gpio_value_path);
|
||||
}
|
||||
|
||||
#endif /* not __WIN32__ */
|
||||
|
@ -318,7 +610,7 @@ void export_gpio(int gpio, int invert, int direction)
|
|||
*
|
||||
* ptt_line RTS or DTR when using serial port.
|
||||
*
|
||||
* ptt_gpio GPIO number. Only used for Linux.
|
||||
* out_gpio_num GPIO number. Only used for Linux.
|
||||
* Valid only when ptt_method is PTT_METHOD_GPIO.
|
||||
*
|
||||
* ptt_lpt_bit Bit number for parallel printer port.
|
||||
|
@ -341,7 +633,6 @@ void export_gpio(int gpio, int invert, int direction)
|
|||
|
||||
|
||||
|
||||
static struct audio_s *save_audio_config_p; /* Save config information for later use. */
|
||||
|
||||
static HANDLE ptt_fd[MAX_CHANS][NUM_OCTYPES];
|
||||
/* Serial port handle or fd. */
|
||||
|
@ -371,7 +662,7 @@ void ptt_init (struct audio_s *audio_config_p)
|
|||
|
||||
strlcpy (otnames[OCTYPE_PTT], "PTT", sizeof(otnames[OCTYPE_PTT]));
|
||||
strlcpy (otnames[OCTYPE_DCD], "DCD", sizeof(otnames[OCTYPE_DCD]));
|
||||
strlcpy (otnames[OCTYPE_FUTURE], "FUTURE", sizeof(otnames[OCTYPE_FUTURE]));
|
||||
strlcpy (otnames[OCTYPE_CON], "CON", sizeof(otnames[OCTYPE_CON]));
|
||||
|
||||
|
||||
for (ch = 0; ch < MAX_CHANS; ch++) {
|
||||
|
@ -392,7 +683,7 @@ void ptt_init (struct audio_s *audio_config_p)
|
|||
audio_config_p->achan[ch].octrl[ot].ptt_method,
|
||||
audio_config_p->achan[ch].octrl[ot].ptt_device,
|
||||
audio_config_p->achan[ch].octrl[ot].ptt_line,
|
||||
audio_config_p->achan[ch].octrl[ot].ptt_gpio,
|
||||
audio_config_p->achan[ch].octrl[ot].out_gpio_num,
|
||||
audio_config_p->achan[ch].octrl[ot].ptt_lpt_bit,
|
||||
audio_config_p->achan[ch].octrl[ot].ptt_invert);
|
||||
}
|
||||
|
@ -535,58 +826,9 @@ void ptt_init (struct audio_s *audio_config_p)
|
|||
}
|
||||
|
||||
if (using_gpio) {
|
||||
|
||||
struct stat finfo;
|
||||
/*
|
||||
* Normally the device nodes are set up for access
|
||||
* only by root. Try to change it here so we don't
|
||||
* burden user with another configuration step.
|
||||
*
|
||||
* Does /sys/class/gpio/export even exist?
|
||||
*/
|
||||
|
||||
if (stat("/sys/class/gpio/export", &finfo) < 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("This system is not configured with the GPIO user interface.\n");
|
||||
dw_printf ("Use a different method for PTT control.\n");
|
||||
exit (1);
|
||||
get_access_to_gpio ("/sys/class/gpio/export");
|
||||
}
|
||||
|
||||
/*
|
||||
* Do we have permission to access it?
|
||||
*
|
||||
* pi@raspberrypi /sys/class/gpio $ ls -l
|
||||
* total 0
|
||||
* --w------- 1 root root 4096 Aug 20 07:59 export
|
||||
* lrwxrwxrwx 1 root root 0 Aug 20 07:59 gpiochip0 -> ../../devices/virtual/gpio/gpiochip0
|
||||
* --w------- 1 root root 4096 Aug 20 07:59 unexport
|
||||
*/
|
||||
if (geteuid() != 0) {
|
||||
if ( ! (finfo.st_mode & S_IWOTH)) {
|
||||
int err;
|
||||
|
||||
/* Try to change protection. */
|
||||
err = system ("sudo chmod go+w /sys/class/gpio/export /sys/class/gpio/unexport");
|
||||
|
||||
if (stat("/sys/class/gpio/export", &finfo) < 0) {
|
||||
/* Unexpected because we could do it before. */
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("This system is not configured with the GPIO user interface.\n");
|
||||
dw_printf ("Use a different method for PTT control.\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Did we succeed in changing the protection? */
|
||||
if ( ! (finfo.st_mode & S_IWOTH)) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Permissions do not allow ordinary users to access GPIO.\n");
|
||||
dw_printf ("Log in as root and type this command:\n");
|
||||
dw_printf (" chmod go+w /sys/class/gpio/export /sys/class/gpio/unexport\n");
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* We should now be able to create the device nodes for
|
||||
* the pins we want to use.
|
||||
|
@ -594,15 +836,18 @@ void ptt_init (struct audio_s *audio_config_p)
|
|||
|
||||
for (ch = 0; ch < MAX_CHANS; ch++) {
|
||||
if (save_audio_config_p->achan[ch].valid) {
|
||||
int ot;
|
||||
|
||||
int ot; // output control type, PTT, DCD, CON, ...
|
||||
int it; // input control type
|
||||
|
||||
for (ot = 0; ot < NUM_OCTYPES; ot++) {
|
||||
if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_GPIO) {
|
||||
export_gpio(audio_config_p->achan[ch].octrl[ot].ptt_gpio, audio_config_p->achan[ch].octrl[ot].ptt_invert, 1);
|
||||
export_gpio(ch, ot, audio_config_p->achan[ch].octrl[ot].ptt_invert, 1);
|
||||
}
|
||||
}
|
||||
for (ot = 0; ot < NUM_ICTYPES; ot++) {
|
||||
if (audio_config_p->achan[ch].ictrl[ot].method == PTT_METHOD_GPIO) {
|
||||
export_gpio(audio_config_p->achan[ch].ictrl[ot].gpio, audio_config_p->achan[ch].ictrl[ot].invert, 0);
|
||||
for (it = 0; it < NUM_ICTYPES; it++) {
|
||||
if (audio_config_p->achan[ch].ictrl[it].method == PTT_METHOD_GPIO) {
|
||||
export_gpio(ch, it, audio_config_p->achan[ch].ictrl[it].invert, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -772,7 +1017,7 @@ void ptt_init (struct audio_s *audio_config_p)
|
|||
* probably be renamed something like octrl_set.
|
||||
*
|
||||
* Inputs: ot - Output control type:
|
||||
* OCTYPE_PTT, OCTYPE_DCD, OCTYPE_HAMLIB, OCTYPE_FUTURE
|
||||
* OCTYPE_PTT, OCTYPE_DCD, OCTYPE_FUTURE
|
||||
*
|
||||
* chan - channel, 0 .. (number of channels)-1
|
||||
*
|
||||
|
@ -809,6 +1054,13 @@ void ptt_set (int ot, int chan, int ptt_signal)
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The data link state machine has an interest in activity on the radio channel.
|
||||
* This is a very convenient place to get that information.
|
||||
*/
|
||||
|
||||
dlq_channel_busy (chan, ot, ptt_signal);
|
||||
|
||||
/*
|
||||
* Inverted output?
|
||||
*/
|
||||
|
@ -880,11 +1132,12 @@ void ptt_set (int ot, int chan, int ptt_signal)
|
|||
|
||||
if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_GPIO) {
|
||||
int fd;
|
||||
char stemp[80];
|
||||
char gpio_value_path[80];
|
||||
char stemp[16];
|
||||
|
||||
snprintf (stemp, sizeof(stemp), "/sys/class/gpio/gpio%d/value", save_audio_config_p->achan[chan].octrl[ot].ptt_gpio);
|
||||
snprintf (gpio_value_path, sizeof(gpio_value_path), "/sys/class/gpio/%s/value", save_audio_config_p->achan[chan].octrl[ot].out_gpio_name);
|
||||
|
||||
fd = open(stemp, O_WRONLY);
|
||||
fd = open(gpio_value_path, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
int e = errno;
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
|
@ -898,7 +1151,7 @@ void ptt_set (int ot, int chan, int ptt_signal)
|
|||
if (write (fd, stemp, 1) != 1) {
|
||||
int e = errno;
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Error setting GPIO %d for %s\n", save_audio_config_p->achan[chan].octrl[ot].ptt_gpio, otnames[ot]);
|
||||
dw_printf ("Error setting GPIO %d for %s\n", save_audio_config_p->achan[chan].octrl[ot].out_gpio_num, otnames[ot]);
|
||||
dw_printf ("%s\n", strerror(e));
|
||||
}
|
||||
close (fd);
|
||||
|
@ -999,15 +1252,17 @@ int get_input (int it, int chan)
|
|||
#else
|
||||
if (save_audio_config_p->achan[chan].ictrl[it].method == PTT_METHOD_GPIO) {
|
||||
int fd;
|
||||
char stemp[80];
|
||||
char gpio_value_path[80];
|
||||
|
||||
snprintf (stemp, sizeof(stemp), "/sys/class/gpio/gpio%d/value", save_audio_config_p->achan[chan].ictrl[it].gpio);
|
||||
snprintf (gpio_value_path, sizeof(gpio_value_path), "/sys/class/gpio/%s/value", save_audio_config_p->achan[chan].ictrl[it].in_gpio_name);
|
||||
|
||||
fd = open(stemp, O_RDONLY);
|
||||
get_access_to_gpio (gpio_value_path);
|
||||
|
||||
fd = open(gpio_value_path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
int e = errno;
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Error opening %s to check input.\n", stemp);
|
||||
dw_printf ("Error opening %s to check input.\n", gpio_value_path);
|
||||
dw_printf ("%s\n", strerror(e));
|
||||
return -1;
|
||||
}
|
||||
|
@ -1016,11 +1271,12 @@ int get_input (int it, int chan)
|
|||
if (read (fd, vtemp, 1) != 1) {
|
||||
int e = errno;
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Error getting GPIO %d value\n", save_audio_config_p->achan[chan].ictrl[it].gpio);
|
||||
dw_printf ("Error getting GPIO %d value\n", save_audio_config_p->achan[chan].ictrl[it].in_gpio_num);
|
||||
dw_printf ("%s\n", strerror(e));
|
||||
}
|
||||
close (fd);
|
||||
|
||||
vtemp[1] = '\0';
|
||||
if (atoi(vtemp) != save_audio_config_p->achan[chan].ictrl[it].invert) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -1201,9 +1457,9 @@ main ()
|
|||
my_audio_config.adev[0].num_channels = 1;
|
||||
my_audio_config.valid[0] = 1;
|
||||
my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_GPIO;
|
||||
my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_gpio = 25;
|
||||
my_audio_config.adev[0].octrl[OCTYPE_PTT].out_gpio_num = 25;
|
||||
|
||||
dw_printf ("Try GPIO %d a few times...\n", my_audio_config.ptt_gpio[0]);
|
||||
dw_printf ("Try GPIO %d a few times...\n", my_audio_config.out_gpio_num[0]);
|
||||
|
||||
ptt_init (&my_audio_config);
|
||||
|
||||
|
|
6
rdq.c
6
rdq.c
|
@ -29,13 +29,14 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
#include "audio.h"
|
||||
|
@ -336,9 +337,6 @@ rrbb_t rdq_remove (void)
|
|||
{
|
||||
|
||||
rrbb_t result_p;
|
||||
#ifndef __WIN32__
|
||||
int err;
|
||||
#endif
|
||||
|
||||
|
||||
#if DEBUG
|
||||
|
|
124
recv.c
124
recv.c
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2014, 2015 John Langner, WB2OSZ
|
||||
// Copyright (C) 2014, 2015, 2016 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,7 +68,7 @@
|
|||
*
|
||||
* The difference is that app_process_rec_frame
|
||||
* is no longer called directly. Instead
|
||||
* the frame is appended to a queue with dlq_append.
|
||||
* the frame is appended to a queue with dlq_rec_frame.
|
||||
*
|
||||
* Received frames can now be processed one at
|
||||
* a time and we don't need to worry about later
|
||||
|
@ -80,6 +80,9 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
//#define DEBUG 1
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
@ -95,7 +98,6 @@
|
|||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "audio.h"
|
||||
#include "demod.h"
|
||||
#include "multi_modem.h"
|
||||
|
@ -104,6 +106,8 @@
|
|||
#include "recv.h"
|
||||
#include "dtmf.h"
|
||||
#include "aprs_tt.h"
|
||||
#include "dtime_now.h"
|
||||
#include "ax25_link.h"
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
|
@ -261,7 +265,7 @@ static void * recv_adev_thread (void *arg)
|
|||
}
|
||||
|
||||
/* When a complete frame is accumulated, */
|
||||
/* dlq_append, is called. */
|
||||
/* dlq_rec_frame, is called. */
|
||||
|
||||
/* recv_process, below, drains the queue. */
|
||||
|
||||
|
@ -283,33 +287,105 @@ static void * recv_adev_thread (void *arg)
|
|||
void recv_process (void)
|
||||
{
|
||||
|
||||
int ok;
|
||||
dlq_type_t type;
|
||||
int chan;
|
||||
int subchan;
|
||||
int slice;
|
||||
packet_t pp;
|
||||
alevel_t alevel;
|
||||
retry_t retries;
|
||||
char spectrum[MAX_SUBCHANS*MAX_SLICERS+1];
|
||||
struct dlq_item_s *pitem;
|
||||
|
||||
while (1) {
|
||||
|
||||
dlq_wait_while_empty ();
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("recv_process: woke up\n");
|
||||
#endif
|
||||
int timed_out;
|
||||
|
||||
double timeout_value = ax25_link_get_next_timer_expiry();
|
||||
|
||||
timed_out = dlq_wait_while_empty (timeout_value);
|
||||
|
||||
ok = dlq_remove (&type, &chan, &subchan, &slice, &pp, &alevel, &retries, spectrum, sizeof(spectrum));
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("recv_process: dlq_remove() returned ok=%d, type=%d, chan=%d, pp=%p\n",
|
||||
ok, (int)type, chan, pp);
|
||||
dw_printf ("recv_process: woke up, timed_out=%d\n", timed_out);
|
||||
#endif
|
||||
if (ok) {
|
||||
app_process_rec_packet (chan, subchan, slice, pp, alevel, retries, spectrum);
|
||||
|
||||
if (timed_out) {
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("recv_process: time waiting on dlq. call dl_timer_expiry.\n");
|
||||
#endif
|
||||
|
||||
dl_timer_expiry ();
|
||||
}
|
||||
else {
|
||||
|
||||
pitem = dlq_remove ();
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("recv_process: dlq_remove() returned pitem=%p\n", pitem);
|
||||
#endif
|
||||
|
||||
if (pitem != NULL) {
|
||||
switch (pitem->type) {
|
||||
|
||||
case DLQ_REC_FRAME:
|
||||
/*
|
||||
* This is the traditional processing.
|
||||
* For all frames:
|
||||
* - Print in standard monitoring format.
|
||||
* - Send to KISS client applications.
|
||||
* - Send to AGw client applications in raw mode.
|
||||
* For APRS frames:
|
||||
* - Explain what it means.
|
||||
* - Send to Igate.
|
||||
* - Digipeater.
|
||||
*/
|
||||
|
||||
app_process_rec_packet (pitem->chan, pitem->subchan, pitem->slice, pitem->pp, pitem->alevel, pitem->retries, pitem->spectrum);
|
||||
|
||||
|
||||
/*
|
||||
* Link processing.
|
||||
*/
|
||||
lm_data_indication(pitem);
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case DLQ_CONNECT_REQUEST:
|
||||
|
||||
dl_connect_request (pitem);
|
||||
break;
|
||||
|
||||
case DLQ_DISCONNECT_REQUEST:
|
||||
|
||||
dl_disconnect_request (pitem);
|
||||
break;
|
||||
|
||||
case DLQ_XMIT_DATA_REQUEST:
|
||||
|
||||
dl_data_request (pitem);
|
||||
break;
|
||||
|
||||
case DLQ_REGISTER_CALLSIGN:
|
||||
|
||||
dl_register_callsign (pitem);
|
||||
break;
|
||||
|
||||
case DLQ_UNREGISTER_CALLSIGN:
|
||||
|
||||
dl_unregister_callsign (pitem);
|
||||
break;
|
||||
|
||||
case DLQ_CHANNEL_BUSY:
|
||||
|
||||
lm_channel_busy (pitem);
|
||||
break;
|
||||
|
||||
case DLQ_CLIENT_CLEANUP:
|
||||
|
||||
dl_client_cleanup (pitem);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
dlq_delete (pitem);
|
||||
}
|
||||
#if DEBUG
|
||||
else {
|
||||
|
@ -319,6 +395,8 @@ void recv_process (void)
|
|||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} /* end recv_process */
|
||||
|
||||
|
||||
|
|
255
redecode.c
255
redecode.c
|
@ -1,255 +0,0 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2011, 2012, 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
|
||||
// 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: redecode.c
|
||||
*
|
||||
* Purpose: Retry decoding frames that have a bad FCS.
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
*
|
||||
* Usage: (1) The main application calls redecode_init.
|
||||
*
|
||||
* This will initialize the retry decoding queue
|
||||
* and create a thread to work on contents of the queue.
|
||||
*
|
||||
* (2) The application queues up frames by calling rdq_append.
|
||||
*
|
||||
*
|
||||
* (3) redecode_thread removes raw frames from the queue and
|
||||
* tries to recover from errors.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#if __WIN32__
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
#include "audio.h"
|
||||
#include "rdq.h"
|
||||
#include "redecode.h"
|
||||
#include "hdlc_send.h"
|
||||
#include "hdlc_rec2.h"
|
||||
#include "ptt.h"
|
||||
|
||||
|
||||
/* Audio configuration for the fix_bits / passall optiions. */
|
||||
|
||||
static struct audio_s *save_audio_config_p;
|
||||
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
static unsigned redecode_thread (void *arg);
|
||||
#else
|
||||
static void * redecode_thread (void *arg);
|
||||
#endif
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: redecode_init
|
||||
*
|
||||
* Purpose: Initialize the process to try fixing bits in frames with bad FCS.
|
||||
*
|
||||
* Inputs: none.
|
||||
*
|
||||
* Outputs: none.
|
||||
*
|
||||
* Description: Initialize the queue to be empty and set up other
|
||||
* mechanisms for sharing it between different threads.
|
||||
*
|
||||
* Start up redecode_thread to actually process the
|
||||
* raw frames from the queue.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
void redecode_init (struct audio_s *p_audio_config)
|
||||
{
|
||||
|
||||
#if 0
|
||||
|
||||
#if __WIN32__
|
||||
HANDLE redecode_th;
|
||||
#else
|
||||
pthread_t redecode_tid;
|
||||
int e;
|
||||
#endif
|
||||
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("redecode_init ( ... )\n");
|
||||
#endif
|
||||
|
||||
save_audio_config_p = p_audio_config;
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("redecode_init: about to call rdq_init \n");
|
||||
#endif
|
||||
rdq_init ();
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("redecode_init: about to create thread \n");
|
||||
#endif
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
redecode_th = _beginthreadex (NULL, 0, redecode_thread, NULL, 0, NULL);
|
||||
if (redecode_th == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Could not create redecode thread\n");
|
||||
return;
|
||||
}
|
||||
#else
|
||||
|
||||
//TODO: Give thread lower priority.
|
||||
|
||||
e = pthread_create (&redecode_tid, NULL, redecode_thread, (void *)0);
|
||||
if (e != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
perror("Could not create redecode thread");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("redecode_init: finished \n");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
} /* end redecode_init */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: redecode_thread
|
||||
*
|
||||
* Purpose: Try to decode frames with a bad FCS.
|
||||
*
|
||||
* Inputs: None.
|
||||
*
|
||||
* Outputs:
|
||||
*
|
||||
* Description: Initialize the queue to be empty and set up other
|
||||
* mechanisms for sharing it between different threads.
|
||||
*
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
#if 0
|
||||
|
||||
#if __WIN32__
|
||||
static unsigned redecode_thread (void *arg)
|
||||
#else
|
||||
static void * redecode_thread (void *arg)
|
||||
#endif
|
||||
{
|
||||
#if __WIN32__
|
||||
HANDLE tid = GetCurrentThread();
|
||||
//int tp;
|
||||
|
||||
//tp = GetThreadPriority (tid);
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("Starting redecode thread priority=%d\n", tp);
|
||||
SetThreadPriority (tid, THREAD_PRIORITY_LOWEST);
|
||||
//tp = GetThreadPriority (tid);
|
||||
//dw_printf ("New redecode thread priority=%d\n", tp);
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
rrbb_t block;
|
||||
|
||||
rdq_wait_while_empty ();
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("redecode_thread: woke up\n");
|
||||
#endif
|
||||
|
||||
block = rdq_remove ();
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("redecode_thread: rdq_remove() returned %p\n", block);
|
||||
#endif
|
||||
|
||||
/* Don't expect null ever but be safe. */
|
||||
|
||||
if (block != NULL) {
|
||||
|
||||
int chan = rrbb_get_chan(block);
|
||||
int subchan = rrbb_get_subchan(block);
|
||||
int blen = rrbb_get_len(block);
|
||||
alevel_t alevel = rrbb_get_audio_level(block);
|
||||
//retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits;
|
||||
//int passall = save_audio_config_p->achan[chan].passall;
|
||||
|
||||
int ok;
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("redecode_thread: begin processing %p, from channel %d, blen=%d\n", block, chan, blen);
|
||||
#endif
|
||||
|
||||
ok = hdlc_rec2_try_to_fix_later (block, chan, subchan, alevel);
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("redecode_thread: finished processing %p\n", block);
|
||||
#endif
|
||||
rrbb_delete (block);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
} /* end redecode_thread */
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* end redecode.c */
|
||||
|
||||
|
||||
|
|
@ -5,3 +5,7 @@ For the Windows version, we need to include our own version.
|
|||
The source was obtained from:
|
||||
|
||||
http://gnuwin32.sourceforge.net/packages/regex.htm
|
||||
|
||||
That is very old with loads of compile warnings. Should we upgrade from here?
|
||||
|
||||
https://www.gnu.org/software/libc/sources.html
|
|
@ -3036,7 +3036,9 @@ parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token,
|
|||
|
||||
while (1)
|
||||
{
|
||||
bracket_elem_t start_elem, end_elem;
|
||||
// Got warnings about being used uninitialized.
|
||||
// bracket_elem_t start_elem, end_elem;
|
||||
bracket_elem_t start_elem = {.type=0, .opr.name=NULL}, end_elem = {.type=0, .opr.name=NULL};
|
||||
unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE];
|
||||
unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE];
|
||||
reg_errcode_t ret;
|
||||
|
|
|
@ -675,9 +675,9 @@ re_string_reconstruct (re_string_t *pstr, int idx, int eflags)
|
|||
else
|
||||
{
|
||||
/* No, skip all characters until IDX. */
|
||||
#ifdef RE_ENABLE_I18N
|
||||
int prev_valid_len = pstr->valid_len;
|
||||
|
||||
#ifdef RE_ENABLE_I18N
|
||||
if (BE (pstr->offsets_needed, 0))
|
||||
{
|
||||
pstr->len = pstr->raw_len - idx + offset;
|
||||
|
@ -1396,7 +1396,9 @@ static int
|
|||
internal_function
|
||||
re_dfa_add_node (re_dfa_t *dfa, re_token_t token)
|
||||
{
|
||||
#ifdef RE_ENABLE_I18N
|
||||
int type = token.type;
|
||||
#endif
|
||||
if (BE (dfa->nodes_len >= dfa->nodes_alloc, 0))
|
||||
{
|
||||
size_t new_nodes_alloc = dfa->nodes_alloc * 2;
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
|
||||
/* this is very old and has massive numbers of compiler warnings. */
|
||||
/* Maybe try upgrading to a newer version such as */
|
||||
/* https://fossies.org/dox/glibc-2.24/regexec_8c_source.html */
|
||||
|
||||
|
||||
/* Extended regular expression matching and search library.
|
||||
Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
@ -18,6 +24,14 @@
|
|||
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA. */
|
||||
|
||||
/* Added 12/2016 to remove warning: */
|
||||
/* incompatible implicit declaration of built-in function 'alloca' */
|
||||
#if __WIN32__
|
||||
#include <malloc.h>
|
||||
#else
|
||||
#include <alloca.h>
|
||||
#endif
|
||||
|
||||
static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags,
|
||||
int n) internal_function;
|
||||
static void match_ctx_clean (re_match_context_t *mctx) internal_function;
|
||||
|
@ -228,6 +242,7 @@ regexec (preg, string, nmatch, pmatch, eflags)
|
|||
reg_errcode_t err;
|
||||
int start, length;
|
||||
re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
|
||||
(void)dfa;
|
||||
|
||||
if (eflags & ~(REG_NOTBOL | REG_NOTEOL | REG_STARTEND))
|
||||
return REG_BADPAT;
|
||||
|
@ -419,6 +434,7 @@ re_search_stub (bufp, string, length, start, range, stop, regs, ret_len)
|
|||
int nregs, rval;
|
||||
int eflags = 0;
|
||||
re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
|
||||
(void)dfa;
|
||||
|
||||
/* Check for out-of-range. */
|
||||
if (BE (start < 0 || start > length, 0))
|
||||
|
@ -2894,7 +2910,10 @@ check_arrival (re_match_context_t *mctx, state_array_t *path, int top_node,
|
|||
sizeof (re_dfastate_t *) * (path->alloc - old_alloc));
|
||||
}
|
||||
|
||||
str_idx = path->next_idx ?: top_str;
|
||||
// Original:
|
||||
// str_idx = path->next_idx ?: top_str;
|
||||
// Copied following from another version when cleaning up compiler warnings.
|
||||
str_idx = path->next_idx ? path->next_idx : top_str;
|
||||
|
||||
/* Temporary modify MCTX. */
|
||||
backup_state_log = mctx->state_log;
|
||||
|
@ -3032,7 +3051,9 @@ check_arrival_add_next_nodes (re_match_context_t *mctx, int str_idx,
|
|||
const re_dfa_t *const dfa = mctx->dfa;
|
||||
int result;
|
||||
int cur_idx;
|
||||
#ifdef RE_ENABLE_I18N
|
||||
reg_errcode_t err = REG_NOERROR;
|
||||
#endif
|
||||
re_node_set union_set;
|
||||
re_node_set_init_empty (&union_set);
|
||||
for (cur_idx = 0; cur_idx < cur_nodes->nelem; ++cur_idx)
|
||||
|
|
3
rrbb.c
3
rrbb.c
|
@ -35,12 +35,13 @@
|
|||
|
||||
#define RRBB_C
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "textcolor.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "rrbb.h"
|
||||
|
|
|
@ -34,17 +34,16 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#include "direwolf.h" // should be first
|
||||
|
||||
#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>
|
||||
|
@ -58,7 +57,7 @@
|
|||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include "textcolor.h"
|
||||
#include "serial_port.h"
|
||||
|
||||
|
@ -183,6 +182,10 @@ MYFDTYPE serial_port_open (char *devicename, int baud)
|
|||
//text_color_set(DW_COLOR_INFO);
|
||||
//dw_printf("Successful serial port open on %s.\n", devicename);
|
||||
|
||||
// Some devices, e.g. KPC-3+, can't turn off hardware flow control and need RTS.
|
||||
|
||||
EscapeCommFunction(fd,SETRTS);
|
||||
EscapeCommFunction(fd,SETDTR);
|
||||
#else
|
||||
|
||||
/* Linux version. */
|
||||
|
@ -300,7 +303,7 @@ int serial_port_write (MYFDTYPE fd, char *str, int len)
|
|||
dw_printf ("Error writing to serial port. Error %d.\n\n", err);
|
||||
}
|
||||
}
|
||||
else if (nwritten != len)
|
||||
else if ((int)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);
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
/* serial_port.h */
|
||||
|
||||
|
||||
#ifndef SERIAL_PORT_H
|
||||
#define SERIAL_PORT_H 1
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
|
||||
typedef HANDLE MYFDTYPE;
|
||||
#define MYFDERROR INVALID_HANDLE_VALUE
|
||||
|
@ -25,3 +27,6 @@ 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);
|
||||
|
||||
|
||||
#endif
|
300
server.c
300
server.c
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016 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
|
||||
|
@ -59,6 +59,17 @@
|
|||
*
|
||||
* 'y' Ask Outstanding frames waiting on a Port (new in 1.2)
|
||||
*
|
||||
* 'C' Connect, Start an AX.25 Connection (new in 1.4)
|
||||
*
|
||||
* 'v' Connect VIA, Start an AX.25 circuit thru digipeaters (new in 1.4)
|
||||
*
|
||||
* 'c' Connection with non-standard PID (new in 1.4)
|
||||
*
|
||||
* 'D' Send Connected Data (new in 1.4)
|
||||
*
|
||||
* 'd' Disconnect, Terminate an AX.25 Connection (new in 1.4)
|
||||
*
|
||||
*
|
||||
* A message is printed if any others are received.
|
||||
*
|
||||
* TODO: Should others be implemented?
|
||||
|
@ -81,6 +92,12 @@
|
|||
*
|
||||
* 'y' Outstanding frames waiting on a Port (new in 1.2)
|
||||
*
|
||||
* 'C' AX.25 Connection Received (new in 1.4)
|
||||
*
|
||||
* 'D' Connected AX.25 Data (new in 1.4)
|
||||
*
|
||||
* 'd' Disconnected (new in 1.4)
|
||||
*
|
||||
*
|
||||
*
|
||||
* References: AGWPE TCP/IP API Tutorial
|
||||
|
@ -104,11 +121,11 @@
|
|||
* Cygwin: Can use either one.
|
||||
*/
|
||||
|
||||
#include "direwolf.h" // Sets _WIN32_WINNT for XP API level needed by ws2tcpip.h
|
||||
|
||||
#if __WIN32__
|
||||
#include <winsock2.h>
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#include <ws2tcpip.h>
|
||||
#include <ws2tcpip.h> // _WIN32_WINNT must be set to 0x0501 before including this
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -129,18 +146,21 @@
|
|||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "direwolf.h"
|
||||
#include "tq.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
#include "audio.h"
|
||||
#include "server.h"
|
||||
#include "dlq.h"
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Previously, we allowed only one network connection at a time to each port.
|
||||
* In version 1.1, we allow multiple concurrent client apps to connect.
|
||||
* In version 1.1, we allow multiple concurrent client apps to attach with the AGW network protocol.
|
||||
* The default is a limit of 3 client applications at the same time.
|
||||
* You can increase the limit by changing the line below.
|
||||
* A larger number consumes more resources so don't go crazy by making it larger than needed.
|
||||
*/
|
||||
|
||||
#define MAX_NET_CLIENTS 3
|
||||
|
@ -162,23 +182,6 @@ static int enable_send_monitor_to_client[MAX_NET_CLIENTS];
|
|||
/* the client app must send a command to enable this. */
|
||||
|
||||
|
||||
/*
|
||||
* Registered callsigns from 'X' command.
|
||||
* For simplicity just use a fixed size array until there
|
||||
* is evidence that a larger number would be needed.
|
||||
*
|
||||
* Also keep track of which client did the registration.
|
||||
* For example client 0 might register the callsign ABC
|
||||
* and client 1 register DEF. If something comes addressed
|
||||
* to DEF, we would want it going only to client 1.
|
||||
*/
|
||||
|
||||
#define MAX_REG_CALLSIGNS 20
|
||||
|
||||
static char registered_callsigns[MAX_REG_CALLSIGNS][AX25_MAX_ADDR_LEN];
|
||||
static int registered_by_client[MAX_REG_CALLSIGNS];
|
||||
|
||||
|
||||
// TODO: define in one place, use everywhere.
|
||||
// TODO: Macro to terminate thread when no point to go on.
|
||||
|
||||
|
@ -226,7 +229,7 @@ static THREAD_F cmd_listen_thread (void *arg);
|
|||
|
||||
#if defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||
|
||||
// gcc >= 4.2 has __builtin_swap32() but be compatible with older versions.
|
||||
// gcc >= 4.2 has __builtin_swap32() but might not be compatible with older versions or other compilers.
|
||||
|
||||
#define host2netle(x) ( (((x)>>24)&0x000000ff) | (((x)>>8)&0x0000ff00) | (((x)<<8)&0x00ff0000) | (((x)<<24)&0xff000000) )
|
||||
#define netle2host(x) ( (((x)>>24)&0x000000ff) | (((x)>>8)&0x0000ff00) | (((x)<<8)&0x00ff0000) | (((x)<<24)&0xff000000) )
|
||||
|
@ -447,8 +450,6 @@ void server_init (struct audio_s *audio_config_p, struct misc_config_s *mc)
|
|||
enable_send_monitor_to_client[client] = 0;
|
||||
}
|
||||
|
||||
memset (registered_callsigns, 0, sizeof(registered_callsigns));
|
||||
|
||||
if (server_port == 0) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Disabled AGW network client port.\n");
|
||||
|
@ -636,6 +637,7 @@ static THREAD_F connect_listen_thread (void *arg)
|
|||
}
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
// TODO: "attached" or some other term would be better because "connected" has a different meaning for AX.25.
|
||||
dw_printf("\nConnected to AGW client application %d ...\n\n", client);
|
||||
|
||||
/*
|
||||
|
@ -819,6 +821,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
|
|||
closesocket (client_sock[client]);
|
||||
client_sock[client] = -1;
|
||||
WSACleanup();
|
||||
dlq_client_cleanup (client);
|
||||
}
|
||||
#else
|
||||
err = write (client_sock[client], &agwpe_msg, sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE));
|
||||
|
@ -828,6 +831,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
|
|||
dw_printf ("\nError sending message to AGW client application. Closing connection.\n\n");
|
||||
close (client_sock[client]);
|
||||
client_sock[client] = -1;
|
||||
dlq_client_cleanup (client);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -843,6 +847,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
|
|||
|
||||
time_t clock;
|
||||
struct tm *tm;
|
||||
int num_digi;
|
||||
|
||||
clock = time(NULL);
|
||||
tm = localtime(&clock); // TODO: should use localtime_r
|
||||
|
@ -867,13 +872,46 @@ 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. */
|
||||
|
||||
// Documentation doesn't mention anything about including the via path.
|
||||
// In version 1.4, we add that to match observed behaviour.
|
||||
|
||||
// This inconsistency was reported:
|
||||
// Direwolf:
|
||||
// [AGWE-IN] 1:Fm ZL4FOX-8 To Q7P2U2 [08:25:07]`I1*l V>/"9<}[:Barts Tracker 3.83V X
|
||||
// AGWPE:
|
||||
// [AGWE-IN] 1:Fm ZL4FOX-8 To Q7P2U2 Via WIDE3-3 [08:32:14]`I0*l V>/"98}[:Barts Tracker 3.83V X
|
||||
|
||||
num_digi = ax25_get_num_repeaters(pp);
|
||||
|
||||
if (num_digi > 0) {
|
||||
|
||||
char via[AX25_MAX_REPEATERS*(AX25_MAX_ADDR_LEN+1)];
|
||||
char stemp[AX25_MAX_ADDR_LEN+1];
|
||||
int j;
|
||||
|
||||
ax25_get_addr_with_ssid (pp, AX25_REPEATER_1, via);
|
||||
for (j = 1; j < num_digi; j++) {
|
||||
ax25_get_addr_with_ssid (pp, AX25_REPEATER_1 + j, stemp);
|
||||
strlcat (via, ",", sizeof(via));
|
||||
strlcat (via, stemp, sizeof(via));
|
||||
}
|
||||
|
||||
snprintf (agwpe_msg.data, sizeof(agwpe_msg.data), " %d:Fm %s To %s Via %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, via,
|
||||
ax25_get_pid(pp), info_len,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec,
|
||||
pinfo);
|
||||
}
|
||||
else {
|
||||
|
||||
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,
|
||||
pinfo);
|
||||
}
|
||||
|
||||
agwpe_msg.hdr.data_len_NETLE = host2netle(strlen(agwpe_msg.data) + 1) /* include null */ ;
|
||||
agwpe_msg.hdr.data_len_NETLE = host2netle(strlen(agwpe_msg.data) + 1) /* +1 to include terminating null */ ;
|
||||
|
||||
if (debug_client) {
|
||||
debug_print (TO_CLIENT, client, &agwpe_msg.hdr, sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE));
|
||||
|
@ -888,6 +926,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
|
|||
closesocket (client_sock[client]);
|
||||
client_sock[client] = -1;
|
||||
WSACleanup();
|
||||
dlq_client_cleanup (client);
|
||||
}
|
||||
#else
|
||||
err = write (client_sock[client], &agwpe_msg, sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE));
|
||||
|
@ -897,6 +936,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
|
|||
dw_printf ("\nError sending message to AGW client application %d. Closing connection.\n\n", client);
|
||||
close (client_sock[client]);
|
||||
client_sock[client] = -1;
|
||||
dlq_client_cleanup (client);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -913,6 +953,8 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
|
|||
* Purpose: Send notification to client app when a link has
|
||||
* been established with another station.
|
||||
*
|
||||
* DL-CONNECT Confirm or DL-CONNECT Indication in the protocol spec.
|
||||
*
|
||||
* Inputs: chan - Which radio channel.
|
||||
*
|
||||
* client - Which one of potentially several clients.
|
||||
|
@ -942,6 +984,8 @@ void server_link_established (int chan, int client, char *remote_call, char *own
|
|||
strlcpy (reply.hdr.call_from, remote_call, sizeof(reply.hdr.call_from));
|
||||
strlcpy (reply.hdr.call_to, own_call, sizeof(reply.hdr.call_to));
|
||||
|
||||
// Question: Should the via path be provided too?
|
||||
|
||||
if (incoming) {
|
||||
// Other end initiated the connection.
|
||||
snprintf (reply.info, sizeof(reply.info), "*** CONNECTED To Station %s\r", remote_call);
|
||||
|
@ -966,6 +1010,8 @@ void server_link_established (int chan, int client, char *remote_call, char *own
|
|||
* another station has been terminated or a connection
|
||||
* attempt failed.
|
||||
*
|
||||
* DL-DISCONNECT Confirm or DL-DISCONNECT Indication in the protocol spec.
|
||||
*
|
||||
* Inputs: chan - Which radio channel.
|
||||
*
|
||||
* client - Which one of potentially several clients.
|
||||
|
@ -976,7 +1022,7 @@ void server_link_established (int chan, int client, char *remote_call, char *own
|
|||
*
|
||||
* timeout - true when no answer from other station.
|
||||
* How do we distinguish who asked for the
|
||||
* termination of an existing linkn?
|
||||
* termination of an existing link?
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
@ -1009,6 +1055,66 @@ void server_link_terminated (int chan, int client, char *remote_call, char *own_
|
|||
} /* end server_link_terminated */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: server_rec_conn_data
|
||||
*
|
||||
* Purpose: Send received connected data to the application.
|
||||
*
|
||||
* DL-DATA Indication in the protocol spec.
|
||||
*
|
||||
* Inputs: chan - Which radio channel.
|
||||
*
|
||||
* client - Which one of potentially several clients.
|
||||
*
|
||||
* remote_call - Callsign[-ssid] of remote station.
|
||||
*
|
||||
* own_call - Callsign[-ssid] of my end.
|
||||
*
|
||||
* pid - Protocol ID from I frame.
|
||||
*
|
||||
* data_ptr - Pointer to a block of bytes.
|
||||
*
|
||||
* data_len - Number of bytes. Could be zero.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
void server_rec_conn_data (int chan, int client, char *remote_call, char *own_call, int pid, char *data_ptr, int data_len)
|
||||
{
|
||||
|
||||
struct {
|
||||
struct agwpe_s hdr;
|
||||
char info[AX25_MAX_INFO_LEN]; // I suppose there is potential for something larger.
|
||||
// We'll cross that bridge if we ever come to it.
|
||||
} reply;
|
||||
|
||||
|
||||
memset (&reply.hdr, 0, sizeof(reply.hdr));
|
||||
reply.hdr.portx = chan;
|
||||
reply.hdr.datakind = 'D';
|
||||
reply.hdr.pid = pid;
|
||||
|
||||
strlcpy (reply.hdr.call_from, remote_call, sizeof(reply.hdr.call_from));
|
||||
strlcpy (reply.hdr.call_to, own_call, sizeof(reply.hdr.call_to));
|
||||
|
||||
if (data_len < 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Invalid length %d for connected data to client %d.\n", data_len, client);
|
||||
data_len = 0;
|
||||
}
|
||||
else if (data_len > AX25_MAX_INFO_LEN) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Invalid length %d for connected data to client %d.\n", data_len, client);
|
||||
data_len = AX25_MAX_INFO_LEN;
|
||||
}
|
||||
|
||||
memcpy (reply.info, data_ptr, data_len);
|
||||
reply.hdr.data_len_NETLE = host2netle(data_len);
|
||||
|
||||
send_to_client (client, &reply);
|
||||
|
||||
} /* end server_rec_conn_data */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
|
@ -1111,6 +1217,7 @@ static void send_to_client (int client, void *reply_p)
|
|||
send (client_sock[client], (char*)(ph), len, 0);
|
||||
#else
|
||||
err = write (client_sock[client], ph, len);
|
||||
(void)err;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1148,6 +1255,7 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
close (client_sock[client]);
|
||||
#endif
|
||||
client_sock[client] = -1;
|
||||
dlq_client_cleanup (client);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1172,7 +1280,7 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
|
||||
int data_len = netle2host(cmd.hdr.data_len_NETLE);
|
||||
|
||||
if (data_len < 0 || data_len > sizeof(cmd.data) - 1) {
|
||||
if (data_len < 0 || data_len > (int)(sizeof(cmd.data) - 1)) {
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\nInvalid message from AGW client application %d.\n", client);
|
||||
|
@ -1189,6 +1297,7 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
close (client_sock[client]);
|
||||
#endif
|
||||
client_sock[client] = -1;
|
||||
dlq_client_cleanup (client);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -1207,6 +1316,7 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
close (client_sock[client]);
|
||||
#endif
|
||||
client_sock[client] = -1;
|
||||
dlq_client_cleanup (client);
|
||||
return (0);
|
||||
}
|
||||
if (n >= 0) {
|
||||
|
@ -1326,7 +1436,7 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
{
|
||||
struct {
|
||||
struct agwpe_s hdr;
|
||||
unsigned char on_air_baud_rate; /* 0=1200, 3=9600 */
|
||||
unsigned char on_air_baud_rate; /* 0=1200, 1=2400, 2=4800, 3=9600, ... */
|
||||
unsigned char traffic_level; /* 0xff if not in autoupdate mode */
|
||||
unsigned char tx_delay;
|
||||
unsigned char tx_tail;
|
||||
|
@ -1365,10 +1475,14 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
break;
|
||||
|
||||
|
||||
case 'H': /* Ask about recently heard stations. */
|
||||
case 'H': /* Ask about recently heard stations on given port. */
|
||||
|
||||
/* This should send back 20 'H' frames for the most recently heard stations. */
|
||||
/* If there are less available, empty frames are sent to make a total of 20. */
|
||||
/* Each contains the first and last heard times. */
|
||||
|
||||
{
|
||||
#if 0 /* This information is not being collected. */
|
||||
#if 0 /* Currently, this information is not being collected. */
|
||||
struct {
|
||||
struct agwpe_s hdr;
|
||||
char info[100];
|
||||
|
@ -1382,7 +1496,8 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
|
||||
reply.hdr.portx = cmd.hdr.portx
|
||||
|
||||
strlcpy (reply.hdr.call_from, "WB2OSZ-15", sizeof(reply.hdr.call_from));
|
||||
strlcpy (reply.hdr.call_from, "WB2OSZ-15 Mon,01Jan2000 01:02:03 Tue,31Dec2099 23:45:56", sizeof(reply.hdr.call_from));
|
||||
// or 00:00:00 00:00:00
|
||||
|
||||
strlcpy (agwpe_msg.data, ..., sizeof(agwpe_msg.data));
|
||||
|
||||
|
@ -1525,38 +1640,34 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
{
|
||||
struct {
|
||||
struct agwpe_s hdr;
|
||||
char data;
|
||||
char data; /* 1 = success, 0 = failure */
|
||||
} reply;
|
||||
|
||||
int j, ok;
|
||||
int ok = 1;
|
||||
|
||||
// The protocol spec says it is an error to register the same one more than once.
|
||||
// Too much trouble. Report success if the channel is valid.
|
||||
|
||||
|
||||
int chan = cmd.hdr.portx;
|
||||
|
||||
if (chan >= 0 && chan < MAX_CHANS && save_audio_config_p->achan[chan].valid) {
|
||||
ok = 1;
|
||||
dlq_register_callsign (cmd.hdr.call_from, chan, client);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("AGW protocol error. Register callsign for invalid channel %d.\n", chan);
|
||||
ok = 0;
|
||||
}
|
||||
|
||||
|
||||
memset (&reply, 0, sizeof(reply));
|
||||
reply.hdr.datakind = 'X';
|
||||
reply.hdr.portx = cmd.hdr.portx;
|
||||
memcpy (reply.hdr.call_from, cmd.hdr.call_from, sizeof(reply.hdr.call_from));
|
||||
reply.hdr.data_len_NETLE = host2netle(1);
|
||||
|
||||
// Version 1.0.
|
||||
// Previously used sizeof(reply) but compiler rounded it up to next byte boundary.
|
||||
// That's why more cumbersome size expression is used.
|
||||
|
||||
// The protocol spec says it is an error to register the same one more than once.
|
||||
// First make sure is it not already in there. Add if space available.
|
||||
|
||||
if (server_callsign_registered_by_client(cmd.hdr.call_from) >= 0) {
|
||||
ok = 0;
|
||||
}
|
||||
else {
|
||||
ok = 0;
|
||||
for (j = 0; j < MAX_REG_CALLSIGNS && ok == 0; j++) {
|
||||
if (registered_callsigns[j][0] == '\0') {
|
||||
strlcpy (registered_callsigns[j], cmd.hdr.call_from, sizeof(registered_callsigns[j]));
|
||||
registered_by_client[j] = client;
|
||||
ok = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reply.data = ok; /* 1 = success, 0 = failure */
|
||||
reply.data = ok;
|
||||
send_to_client (client, &reply);
|
||||
}
|
||||
break;
|
||||
|
@ -1564,13 +1675,15 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
case 'x': /* Unregister CallSign */
|
||||
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = 0; j < MAX_REG_CALLSIGNS; j++) {
|
||||
if (strcmp(registered_callsigns[j], cmd.hdr.call_from) == 0) {
|
||||
registered_callsigns[j][0] = '\0';
|
||||
registered_by_client[j] = -1;
|
||||
int chan = cmd.hdr.portx;
|
||||
|
||||
if (chan >= 0 && chan < MAX_CHANS && save_audio_config_p->achan[chan].valid) {
|
||||
dlq_unregister_callsign (cmd.hdr.call_from, chan, client);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("AGW protocol error. Unregister callsign for invalid channel %d.\n", chan);
|
||||
}
|
||||
}
|
||||
/* No reponse is expected. */
|
||||
|
@ -1590,10 +1703,9 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
int num_calls = 2; /* 2 plus any digipeaters. */
|
||||
int pid = 0xf0; /* normal for AX.25 I frames. */
|
||||
int j;
|
||||
char stemp[256];
|
||||
|
||||
strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
|
||||
strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE]));
|
||||
strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_DESTINATION]));
|
||||
|
||||
if (cmd.hdr.datakind == 'c') {
|
||||
pid = cmd.hdr.pid; /* non standard for NETROM, TCP/IP, etc. */
|
||||
|
@ -1620,28 +1732,38 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
}
|
||||
}
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\n");
|
||||
dw_printf ("Can't process command '%c' from AGW client app %d.\n", cmd.hdr.datakind, client);
|
||||
dw_printf ("Connected packet mode is not implemented.\n");
|
||||
|
||||
dlq_connect_request (callsigns, num_calls, cmd.hdr.portx, client, pid);
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'D': /* Send Connected Data */
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\n");
|
||||
dw_printf ("Can't process command '%c' from AGW client app %d.\n", cmd.hdr.datakind, client);
|
||||
dw_printf ("Connected packet mode is not implemented.\n");
|
||||
{
|
||||
char callsigns[2][AX25_MAX_ADDR_LEN];
|
||||
const int num_calls = 2;
|
||||
|
||||
strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
|
||||
strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE]));
|
||||
|
||||
dlq_xmit_data_request (callsigns, num_calls, cmd.hdr.portx, client, cmd.hdr.pid, cmd.data, netle2host(cmd.hdr.data_len_NETLE));
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case 'd': /* Disconnect, Terminate an AX.25 Connection */
|
||||
|
||||
{
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\n");
|
||||
dw_printf ("Can't process command '%c' from AGW client app %d.\n", cmd.hdr.datakind, client);
|
||||
dw_printf ("Connected packet mode is not implemented.\n");
|
||||
char callsigns[2][AX25_MAX_ADDR_LEN];
|
||||
const int num_calls = 2;
|
||||
|
||||
strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
|
||||
strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE]));
|
||||
|
||||
dlq_disconnect_request (callsigns, num_calls, cmd.hdr.portx, client);
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1756,30 +1878,4 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
} /* end send_to_client */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: server_callsign_registered_by_client
|
||||
*
|
||||
* Purpose: See if given callsign was registered.
|
||||
*
|
||||
* Inputs: callsign
|
||||
*
|
||||
* Returns: >= 0 for the client number.
|
||||
* -1 for not found.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
int server_callsign_registered_by_client (char *callsign)
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = 0; j < MAX_REG_CALLSIGNS; j++) {
|
||||
if (strcmp(registered_callsigns[j], callsign) == 0) {
|
||||
return (registered_by_client[j]);
|
||||
}
|
||||
}
|
||||
return (-1);
|
||||
|
||||
} /* end server_callsign_registered_by_client */
|
||||
|
||||
/* end server.c */
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue