diff --git a/CHANGES.md b/CHANGES.md
index 3e64700..65279ef 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,36 @@
# Revision History #
+----------
+
+## Version 1.4 -- Development snapshot B -- April 2016 ##
+
+### New Features: ###
+
+- 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.
+
+
+### Bugs Fixed: ###
+
+- 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.*
+
+
----------
## Version 1.3 -- Beta Test -- March 2016 ##
@@ -61,7 +91,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.
@@ -272,8 +302,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***
-----------
diff --git a/Makefile.linux b/Makefile.linux
index db78d16..e32be77 100644
--- a/Makefile.linux
+++ b/Makefile.linux
@@ -228,7 +228,7 @@ z := $(notdir ${CURDIR})
-direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_9600.o hdlc_rec.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 redecode.o rdq.o rrbb.o dlq.o \
fcs_calc.o ax25_pad.o \
decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
@@ -248,15 +248,18 @@ endif
# Optimization for slow processors.
-demod.o : fsk_fast_filter.h
+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.
@@ -327,7 +330,7 @@ gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c morse.c
# 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 \
@@ -585,15 +588,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 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,11 +605,28 @@ 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 -L57 -G61 /tmp/test96.wav
+ ./atest -B9600 -F1 -L64 -G67 /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 -L62 -G64 /tmp/test19.wav
+ ./atest -B19200 -F1 -L69 -G71 /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
@@ -679,6 +699,14 @@ 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
+
# ----------------------------- Manual tests and experiments ---------------------------
@@ -694,7 +722,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
@@ -702,11 +730,19 @@ udptest : udp_test.c demod.o dsp.o demod_afsk.o demod_9600.o hdlc_rec.o hdlc_rec
# For demodulator tweaking experiments.
# Dependencies of demod*.c, rather than .o, are intentional.
-demod.o : tune.h
+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 \
+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 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
@@ -757,8 +793,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)
diff --git a/Makefile.macosx b/Makefile.macosx
index 12c94c1..ced3b20 100644
--- a/Makefile.macosx
+++ b/Makefile.macosx
@@ -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"
@@ -221,7 +222,7 @@ 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 \
+ config.o decode_aprs.o dedupe.o demod_9600.o demod_afsk.o demod_psk.o \
demod.o digipeater.o dlq.o dsp.o dtime_now.o dtmf.o dwgps.o \
encode_aprs.o encode_aprs.o fcs_calc.o fcs_calc.o gen_tone.o \
geotranz.a hdlc_rec.o hdlc_rec2.o hdlc_send.o igate.o kiss_frame.o \
@@ -234,15 +235,20 @@ direwolf : direwolf.o aprs_tt.o audio_portaudio.o audio_stats.o ax25_pad.o beaco
# Optimization for slow processors.
-demod.o : fsk_fast_filter.h
+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 +398,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 +408,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.
@@ -438,22 +440,30 @@ log2gpx : log2gpx.c
gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c morse.c textcolor.c dsp.c
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -lm
-demod.o : tune.h
+demod.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
$(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 \
+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 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 +523,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
diff --git a/Makefile.win b/Makefile.win
index d62e414..5007702 100644
--- a/Makefile.win
+++ b/Makefile.win
@@ -28,6 +28,9 @@ AR := ar
CFLAGS += -g
+# TODO: Development in progress. Don't try using yet.
+#CFLAGS += -DNEW14
+
#
# Let's see impact of various optimization levels.
# Benchmark results with MinGW gcc version 4.6.2.
@@ -61,14 +64,22 @@ CFLAGS += -g
# -------------------------------------- Main application --------------------------------
-demod.o : fsk_demod_state.h
+# 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 \
+
+# later ax25_link.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 redecode.o rdq.o rrbb.o dlq.o \
- fcs_calc.o ax25_pad.o \
+ fcs_calc.o ax25_pad.o ax25_pad2.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 \
ptt.o beacon.o dwgps.o encode_aprs.o latlong.o textcolor.o \
@@ -88,10 +99,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.
@@ -235,32 +249,59 @@ 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 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
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 -n 100 -o test96.wav
+ sleep 1
+ atest -B9600 -F0 -L57 -G62 test96.wav
+ atest -B9600 -F1 -L65 -G67 test96.wav
+ sleep 1
+ rm test96.wav
-# Unit test for AFSK demodulator
+check-modem19200 : gen_packets atest
+ gen_packets -r 96000 -B19200 -n 100 -o test19.wav
+ sleep 1
+ atest -B19200 -F0 -L63 -G64 test19.wav
+ atest -B19200 -F1 -L69 -G70 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 \
@@ -272,7 +313,7 @@ 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 \
+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 misc.a regex.a \
fsk_fast_filter.h
echo " " > tune.h
@@ -352,17 +393,26 @@ 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
+
+
# ------------------------------ Other manual testing & experimenting -------------------------------
# For tweaking the demodulator.
-demod.o : tune.h
+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
@@ -375,27 +425,53 @@ 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 \
+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 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 \
+testagc96 : 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 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
+ rm -f atest9.exe
+ $(CC) $(CFLAGS) -o atest9 $^
+ ./atest9 -B 9600 ../walkabout9600.wav | grep "packets decoded in" >atest.out
+ #./atest9 -B 9600 noisy96.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 \
+ 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 \
+ 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,7 +500,7 @@ 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 \
+ multi_modem.o demod.o demod_afsk.o demod_psk.c demod_9600.o rdq.o \
server.o morse.o audio_stats.o dtime_now.o dlq.o \
regex.a misc.a
$(CC) $(CFLAGS) -DWALK96 -o $@ $^ -lwinmm -lws2_32
diff --git a/aprs_tt.c b/aprs_tt.c
index cf4e1c7..6642cef 100644
--- a/aprs_tt.c
+++ b/aprs_tt.c
@@ -1706,7 +1706,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);
diff --git a/atest.c b/atest.c
index 7ab60a8..e3d47b6 100644
--- a/atest.c
+++ b/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
@@ -291,35 +291,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 +356,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 +369,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,7 +427,7 @@ 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);
@@ -437,12 +457,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 +472,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;
@@ -496,8 +530,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++;
@@ -532,15 +568,15 @@ int main (int argc, char *argv[])
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 +632,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];
@@ -740,7 +776,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");
diff --git a/audio.h b/audio.h
index 33a5d2d..746d96d 100644
--- a/audio.h
+++ b/audio.h
@@ -101,11 +101,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 +131,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. */
diff --git a/ax25_pad.c b/ax25_pad.c
index 37a9508..90fba0b 100644
--- a/ax25_pad.c
+++ b/ax25_pad.c
@@ -891,7 +891,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);
}
@@ -1789,13 +1792,15 @@ 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
+ * modulo - We often need to know this because context is // TODO: remove this - return cr instead.
* 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.
*
+ * cr - Command or response?
+ *
* pf - P/F - Poll/Final or -1 if not applicable
*
* nr - N(R) - receive sequence or -1 if not applicable.
@@ -1809,7 +1814,8 @@ int ax25_pack (packet_t this_p, unsigned char result[AX25_MAX_PACKET_LEN])
// TODO: need someway to ensure caller allocated enough space.
#define DESC_SIZ 32
-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.
@@ -1818,6 +1824,7 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *
assert (this_p->magic2 == MAGIC);
strlcpy (desc, "????", DESC_SIZ);
+ *cr = cr_11;
*pf = -1;
*nr = -1;
*ns = -1;
@@ -1827,16 +1834,30 @@ 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) {
+ 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 +1867,14 @@ 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);
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;
}
@@ -1861,12 +1882,13 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *
*pf = (c >> 4) & 1;
*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 +1899,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 +1919,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
@@ -2437,7 +2461,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);
}
diff --git a/ax25_pad.h b/ax25_pad.h
index 75ede36..a579a83 100644
--- a/ax25_pad.h
+++ b/ax25_pad.h
@@ -100,7 +100,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 +110,7 @@ struct packet_s {
* 0 Usually 0 but 1 for last address.
*/
+
#define SSID_H_MASK 0x80
#define SSID_H_SHIFT 7
@@ -123,6 +125,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,6 +156,7 @@ 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;
#ifdef AX25_PAD_C /* Keep this hidden - implementation could change. */
@@ -154,21 +166,17 @@ 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.
*/
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.
+ return (this_p->modulo == 128 ? 2 : 1);
}
@@ -361,7 +369,7 @@ extern void ax25_format_addrs (packet_t pp, char *);
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);
diff --git a/ax25_pad2.c b/ax25_pad2.c
new file mode 100644
index 0000000..60e0f1b
--- /dev/null
+++ b/ax25_pad2.c
@@ -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 .
+//
+
+
+
+/*------------------------------------------------------------------
+ *
+ * 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
+#include
+#include
+#include
+#include
+
+
+#include "direwolf.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;
+ int t = -1; // 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.\n", __func__, cr, t);
+ }
+ }
+
+ 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_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[32];
+ 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[32];
+ 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_ERROR);
+ dw_printf ("Internal error in %s: I frame, Invalid pid value 0x%02x.\n", __func__, pid);
+ pid = AX25_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[32];
+ 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 */
diff --git a/ax25_pad2.h b/ax25_pad2.h
new file mode 100644
index 0000000..4860941
--- /dev/null
+++ b/ax25_pad2.h
@@ -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 */
+
+
diff --git a/beacon.c b/beacon.c
index 89ef30d..9837676 100644
--- a/beacon.c
+++ b/beacon.c
@@ -914,7 +914,7 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
/* Simulated reception. */
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;
}
}
diff --git a/config.c b/config.c
index 3e35590..a1bd076 100644
--- a/config.c
+++ b/config.c
@@ -1116,15 +1116,15 @@ void config_init (char *fname, struct audio_s *p_audio_config,
n = atoi(t);
if (n >= MIN_BAUD && n <= MAX_BAUD) {
p_audio_config->achan[channel].baud = n;
- if (n != 300 && n != 1200 && n != 9600) {
+ if (n != 300 && n != 1200 && n != 2400 && n != 4800 && n != 9600 && n != 19200) {
text_color_set(DW_COLOR_ERROR);
- dw_printf ("Line %d: Warning: Non-standard baud rate. Are you sure?\n", line);
+ dw_printf ("Line %d: Warning: Non-standard data rate of %d bits per second. Are you sure?\n", line, n);
}
}
else {
p_audio_config->achan[channel].baud = DEFAULT_BAUD;
text_color_set(DW_COLOR_ERROR);
- dw_printf ("Line %d: Unreasonable baud rate. Using %d.\n",
+ dw_printf ("Line %d: Unreasonable data rate. Using %d bits per second.\n",
line, p_audio_config->achan[channel].baud);
}
@@ -1132,20 +1132,33 @@ void config_init (char *fname, struct audio_s *p_audio_config,
/* Set defaults based on speed. */
/* Should be same as -B command line option in direwolf.c. */
+ /* 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 (p_audio_config->achan[channel].baud < 600) {
p_audio_config->achan[channel].modem_type = MODEM_AFSK;
p_audio_config->achan[channel].mark_freq = 1600;
p_audio_config->achan[channel].space_freq = 1800;
}
- else if (p_audio_config->achan[channel].baud > 2400) {
- p_audio_config->achan[channel].modem_type = MODEM_SCRAMBLE;
+ else if (p_audio_config->achan[channel].baud < 1800) {
+ p_audio_config->achan[channel].modem_type = MODEM_AFSK;
+ p_audio_config->achan[channel].mark_freq = DEFAULT_MARK_FREQ;
+ p_audio_config->achan[channel].space_freq = DEFAULT_SPACE_FREQ;
+ }
+ else if (p_audio_config->achan[channel].baud < 3600) {
+ p_audio_config->achan[channel].modem_type = MODEM_QPSK;
+ p_audio_config->achan[channel].mark_freq = 0;
+ p_audio_config->achan[channel].space_freq = 0;
+ }
+ else if (p_audio_config->achan[channel].baud < 7200) {
+ p_audio_config->achan[channel].modem_type = MODEM_8PSK;
p_audio_config->achan[channel].mark_freq = 0;
p_audio_config->achan[channel].space_freq = 0;
}
else {
- p_audio_config->achan[channel].modem_type = MODEM_AFSK;
- p_audio_config->achan[channel].mark_freq = 1200;
- p_audio_config->achan[channel].space_freq = 2200;
+ p_audio_config->achan[channel].modem_type = MODEM_SCRAMBLE;
+ p_audio_config->achan[channel].mark_freq = 0;
+ p_audio_config->achan[channel].space_freq = 0;
}
/* Get mark frequency. */
diff --git a/decode_aprs.c b/decode_aprs.c
index b259aa4..075fc72 100644
--- a/decode_aprs.c
+++ b/decode_aprs.c
@@ -166,7 +166,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? */
@@ -254,8 +254,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;
@@ -1409,22 +1410,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 +1460,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 +1471,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 +1502,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 +1516,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 +1542,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 () */
diff --git a/decode_aprs.h b/decode_aprs.h
index a767ed2..cdbb167 100644
--- a/decode_aprs.h
+++ b/decode_aprs.h
@@ -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. */
diff --git a/demod.c b/demod.c
index 320cde0..bdea694 100644
--- a/demod.c
+++ b/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 */
-
/*------------------------------------------------------------------
*
@@ -60,6 +52,7 @@
#include "textcolor.h"
#include "demod_9600.h"
#include "demod_afsk.h"
+#include "demod_psk.h"
@@ -225,6 +218,7 @@ int demod_init (struct audio_s *pa)
num_letters = 1;
}
+
assert (num_letters == strlen(just_letters));
/*
@@ -403,7 +397,7 @@ 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;
@@ -459,7 +453,7 @@ 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;
@@ -512,7 +506,7 @@ 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;
@@ -521,6 +515,114 @@ int demod_init (struct audio_s *pa)
}
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:
@@ -602,7 +704,7 @@ 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;
@@ -728,17 +830,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);
@@ -803,7 +897,22 @@ void demod_process_sample (int chan, int subchan, int sam)
}
}
else {
- demod_afsk_process_sample (chan, subchan, sam, D);
+ demod_afsk_process_sample (chan, subchan, sam, D);
+ }
+ 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;
@@ -897,8 +1006,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 {
diff --git a/demod_afsk.c b/demod_afsk.c
index b769e21..dba8317 100644
--- a/demod_afsk.c
+++ b/demod_afsk.c
@@ -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
diff --git a/demod_psk.c b/demod_psk.c
new file mode 100644
index 0000000..d750ecc
--- /dev/null
+++ b/demod_psk.c
@@ -0,0 +1,853 @@
+//
+// 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 .
+//
+
+
+//#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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "direwolf.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; jms_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. / 12.) * (float)samples_per_sec / (float)correct_baud );
+ D->boffs = (int) round( (float)samples_per_sec / (float)correct_baud );
+ D->soffs = (int) round( (13. / 12.) * (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. / 9.) * (float)samples_per_sec / (float)correct_baud );
+ D->boffs = (int) round( (float)samples_per_sec / (float)correct_baud );
+ D->soffs = (int) round( (10. / 9.) * (float)samples_per_sec / (float)correct_baud );
+ }
+
+
+ if (D->psk_use_lo) {
+ D->lo_step = (int) round( 256. * 256. * 256. * 256. * carrier_freq / (float)samples_per_sec);
+
+ assert (MAX_FILTER_SIZE >= 256);
+ for (j = 0; j < 256; j++) {
+ D->m_sin_table[j] = sinf(2. * M_PI * j / 256.);
+ }
+ }
+
+#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", f1, f2);
+#endif
+ if (f1 <= 0) {
+ text_color_set (DW_COLOR_ERROR);
+ dw_printf ("Prefilter of %.0f to %.0f Hz doesn't make sense.\n", f1, 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.
+ *
+ *--------------------------------------------------------------------*/
+
+
+
+static void inline 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 * 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 * 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, delta;
+ int id;
+
+ a = my_atan2f(I,Q);
+ id = ((int)((a / (2.f * 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 = ((id - 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))
+static void inline 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.
+ */
+
+ if (demod_bits != D->slicer[slice].prev_demod_data) {
+
+ if (hdlc_rec_gathering (chan, subchan, slice)) {
+ D->slicer[slice].data_clock_pll = (int)floor((double)(D->slicer[slice].data_clock_pll) * D->pll_locked_inertia);
+ }
+ else {
+ D->slicer[slice].data_clock_pll = (int)floor((double)(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 */
diff --git a/demod_psk.h b/demod_psk.h
new file mode 100644
index 0000000..0f5830d
--- /dev/null
+++ b/demod_psk.h
@@ -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);
diff --git a/direwolf.c b/direwolf.c
index 743db90..280acba 100644
--- a/direwolf.c
+++ b/direwolf.c
@@ -239,7 +239,7 @@ int main (int argc, char *argv[])
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, "A", __DATE__);
+ dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "B", __DATE__);
//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
#if defined(ENABLE_GPSD) || defined(USE_HAMLIB)
@@ -561,22 +561,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;
@@ -623,7 +644,7 @@ 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);
@@ -884,12 +905,28 @@ 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[32];
+ int pf;
+ int nr;
+ int ns;
+
+ ftype = ax25_frame_type (pp, &cr, desc, &pf, &nr, &ns);
+
+ dw_printf ("(%s)", desc);
+ }
+
// for APRS we generally want to display non-ASCII to see UTF-8.
// for other, probably want to restrict to ASCII only because we are
// more likely to have compressed data than UTF-8 text.
// TODO: Might want to use d_u_opt for transmitted frames too.
+
ax25_safe_print ((char *)pinfo, info_len, ( ! ax25_is_aprs(pp)) && ( ! d_u_opt) );
dw_printf ("\n");
@@ -1068,10 +1105,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");
diff --git a/dlq.c b/dlq.c
index 06157c8..5f01ff9 100644
--- a/dlq.c
+++ b/dlq.c
@@ -1,8 +1,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
@@ -25,13 +24,17 @@
*
* Purpose: Received frame queue.
*
- * Description: In previous versions, the main thread read from the
+ * Description: In earlierversions, the main thread read from the
* audio device and performed the receive demodulation/decoding.
- * In version 1.2 we now have a seprate receive thread
+ *
+ * Since version 1.2 we have a separate receive thread
* for each audio device. This queue is used to collect
* received frames from all channels and process them
* serially.
*
+ * In version 1.4, other types of events go into this
+ * queue and we use it to drive the data link state machine.
+ *
*---------------------------------------------------------------*/
#include
@@ -39,6 +42,10 @@
#include
#include
#include
+#if __WIN32__
+#else
+#include
+#endif
#include "direwolf.h"
#include "ax25_pad.h"
@@ -46,36 +53,11 @@
#include "audio.h"
#include "dlq.h"
#include "dedupe.h"
+#include "dtime_now.h"
/* The queue is a linked list of these. */
-struct dlq_item_s {
-
- struct dlq_item_s *nextp; /* Next item in queue. */
-
- dlq_type_t type; /* Type of item. */
- /* Only received frames at this time. */
-
- int chan; /* Radio channel of origin. */
-
- 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. */
-};
-
-
static struct dlq_item_s *queue_head = NULL; /* Head of linked list for queue. */
#if __WIN32__
@@ -94,14 +76,19 @@ static pthread_cond_t wake_up_cond; /* Notify received packet processing thread
static pthread_mutex_t wake_up_mutex; /* Required by cond_wait. */
-static int recv_thread_is_waiting = 0;
+static volatile int recv_thread_is_waiting = 0;
#endif
-static int dlq_is_empty (void);
-
static int was_init = 0; /* was initialization performed? */
+static void append_to_queue (struct dlq_item_s *pnew);
+
+static volatile int s_new_count = 0; /* To detect memory leak. */
+static volatile int s_delete_count = 0; // TODO: need to test.
+
+
+
/*-------------------------------------------------------------------
*
@@ -121,8 +108,7 @@ static int was_init = 0; /* was initialization performed? */
void dlq_init (void)
{
- int c, p;
- int err;
+ //int c, p;
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
@@ -140,6 +126,7 @@ void dlq_init (void)
#if __WIN32__
InitializeCriticalSection (&dlq_cs);
#else
+ int err;
err = pthread_mutex_init (&wake_up_mutex, NULL);
err = pthread_mutex_init (&dlq_mutex, NULL);
if (err != 0) {
@@ -192,17 +179,17 @@ void dlq_init (void)
} /* end dlq_init */
+
/*-------------------------------------------------------------------
*
- * Name: dlq_append
+ * Name: dlq_rec_frame
*
- * Purpose: Add a packet to the end of the specified receive queue.
+ * Purpose: Add a received packet to the end of the queue.
+ * Normally this was received over the radio but we can create
+ * our own from APRStt or beaconing.
*
- * Inputs: type - One of the following:
*
- * DLQ_REC_FRAME - Frame received from radio.
- *
- * chan - Channel, 0 is first.
+ * Inputs: chan - Channel, 0 is first.
*
* subchan - Which modem caught it.
* Special case -1 for APRStt gateway.
@@ -224,38 +211,27 @@ void dlq_init (void)
* spectrum - Display of how well multiple decoders did.
*
*
- * Outputs: Information is appended to queue.
- *
- * Description: Add item to end of linked list.
- * Signal the receive processing thread if the queue was formerly empty.
- *
* IMPORTANT! Don't make an further references to the packet object after
* giving it to dlq_append.
*
*--------------------------------------------------------------------*/
-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)
{
struct dlq_item_s *pnew;
- struct dlq_item_s *plast;
- int err;
- int queue_length = 0;
+
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
- dw_printf ("dlq_append (type=%d, chan=%d, pp=%p, ...)\n", type, chan, pp);
+ dw_printf ("dlq_rec_frame (chan=%d, pp=%p, ...)\n", chan, pp);
#endif
- if ( ! was_init) {
- dlq_init ();
- }
-
assert (chan >= 0 && chan < MAX_CHANS);
if (pp == NULL) {
text_color_set(DW_COLOR_ERROR);
- dw_printf ("INTERNAL ERROR: dlq_append NULL packet pointer. Please report this!\n");
+ dw_printf ("INTERNAL ERROR: dlq_rec_frame NULL packet pointer. Please report this!\n");
return;
}
@@ -263,16 +239,17 @@ void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp,
if (ax25memdebug_get()) {
text_color_set(DW_COLOR_DEBUG);
- dw_printf ("dlq_append (type=%d, chan=%d.%d, seq=%d, ...)\n", type, chan, subchan, ax25memdebug_seq(pp));
+ dw_printf ("dlq_rec_frame (chan=%d.%d, seq=%d, ...)\n", chan, subchan, ax25memdebug_seq(pp));
}
#endif
/* Allocate a new queue item. */
pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
+ s_new_count++;
pnew->nextp = NULL;
- pnew->type = type;
+ pnew->type = DLQ_REC_FRAME;
pnew->chan = chan;
pnew->slice = slice;
pnew->subchan = subchan;
@@ -284,17 +261,56 @@ void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp,
else
strlcpy(pnew->spectrum, spectrum, sizeof(pnew->spectrum));
+/* Put it into queue. */
+
+ append_to_queue (pnew);
+
+} /* end dlq_rec_frame */
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name: append_to_queue
+ *
+ * Purpose: Append some type of event to queue.
+ * This includes frames received over the radio,
+ * requests from client applications, and notifications
+ * from the frame transmission process.
+ *
+ *
+ * Inputs: pnew - Pointer to queue element structure.
+ *
+ * Outputs: Information is appended to queue.
+ *
+ * Description: Add item to end of linked list.
+ * Signal the receive processing thread if the queue was formerly empty.
+ *
+ *--------------------------------------------------------------------*/
+
+static void append_to_queue (struct dlq_item_s *pnew)
+{
+ struct dlq_item_s *plast;
+ int queue_length = 0;
+
+ if ( ! was_init) {
+ dlq_init ();
+ }
+
+ pnew->nextp = NULL;
+
#if DEBUG1
text_color_set(DW_COLOR_DEBUG);
- dw_printf ("dlq_append: enter critical section\n");
+ dw_printf ("dlq append_to_queue: enter critical section\n");
#endif
#if __WIN32__
EnterCriticalSection (&dlq_cs);
#else
+ int err;
err = pthread_mutex_lock (&dlq_mutex);
if (err != 0) {
text_color_set(DW_COLOR_ERROR);
- dw_printf ("dlq_append: pthread_mutex_lock err=%d", err);
+ dw_printf ("dlq append_to_queue: pthread_mutex_lock err=%d", err);
perror ("");
exit (1);
}
@@ -321,15 +337,15 @@ void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp,
err = pthread_mutex_unlock (&dlq_mutex);
if (err != 0) {
text_color_set(DW_COLOR_ERROR);
- dw_printf ("dlq_append: pthread_mutex_unlock err=%d", err);
+ dw_printf ("dlq append_to_queue: pthread_mutex_unlock err=%d", err);
perror ("");
exit (1);
}
#endif
#if DEBUG1
text_color_set(DW_COLOR_DEBUG);
- dw_printf ("dlq_append: left critical section\n");
- dw_printf ("dlq_append (): about to wake up recv processing thread.\n");
+ dw_printf ("dlq append_to_queue: left critical section\n");
+ dw_printf ("dlq append_to_queue (): about to wake up recv processing thread.\n");
#endif
@@ -394,7 +410,7 @@ void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp,
err = pthread_mutex_lock (&wake_up_mutex);
if (err != 0) {
text_color_set(DW_COLOR_ERROR);
- dw_printf ("dlq_append: pthread_mutex_lock wu err=%d", err);
+ dw_printf ("dlq append_to_queue: pthread_mutex_lock wu err=%d", err);
perror ("");
exit (1);
}
@@ -402,7 +418,7 @@ void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp,
err = pthread_cond_signal (&wake_up_cond);
if (err != 0) {
text_color_set(DW_COLOR_ERROR);
- dw_printf ("dlq_append: pthread_cond_signal err=%d", err);
+ dw_printf ("dlq append_to_queue: pthread_cond_signal err=%d", err);
perror ("");
exit (1);
}
@@ -410,14 +426,191 @@ void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp,
err = pthread_mutex_unlock (&wake_up_mutex);
if (err != 0) {
text_color_set(DW_COLOR_ERROR);
- dw_printf ("dlq_append: pthread_mutex_unlock wu err=%d", err);
+ dw_printf ("dlq append_to_queue: pthread_mutex_unlock wu err=%d", err);
perror ("");
exit (1);
}
}
#endif
-}
+} /* end append_to_queue */
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name: dlq_connect_request
+ *
+ * Purpose: Client application has requested connection to another station.
+ *
+ * Inputs: addrs - Source (owncall), destination (peercall),
+ * and possibly digipeaters.
+ *
+ * num_addr - Number of addresses. 2 to 10.
+ *
+ * chan - Channel, 0 is first.
+ *
+ * client - Client application instance. We could have multiple
+ * applications, all on the same channel, connecting
+ * to different stations. We need to know which one
+ * should get the results.
+ *
+ * pid - Protocol ID for data. Normally 0xf0 but the API
+ * allows the client app to use something non-standard
+ * for special situations.
+ *
+ * Outputs: Request is appended to queue for processing by
+ * the data link state machine.
+ *
+ *--------------------------------------------------------------------*/
+
+void dlq_connect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client, int pid)
+{
+ struct dlq_item_s *pnew;
+
+
+#if DEBUG
+ text_color_set(DW_COLOR_DEBUG);
+ dw_printf ("dlq_connect_request (...)\n");
+#endif
+
+ assert (chan >= 0 && chan < MAX_CHANS);
+
+/* Allocate a new queue item. */
+
+ pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
+ s_new_count++;
+
+ pnew->type = DLQ_CONNECT_REQUEST;
+ pnew->chan = chan;
+ memcpy (pnew->addrs, addrs, sizeof(pnew->addrs));
+ pnew->num_addr = num_addr;
+ pnew->client = client;
+ pnew->pid = pid;
+
+/* Put it into queue. */
+
+ append_to_queue (pnew);
+
+} /* end dlq_connect_request */
+
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name: dlq_disconnect_request
+ *
+ * Purpose: Client application has requested to disconnect.
+ *
+ * Inputs: addrs - Source (owncall), destination (peercall),
+ * and possibly digipeaters.
+ *
+ * num_addr - Number of addresses. 2 to 10.
+ * Only first two matter in this case.
+ *
+ * chan - Channel, 0 is first.
+ *
+ * client - Client application instance. We could have multiple
+ * applications, all on the same channel, connecting
+ * to different stations. We need to know which one
+ * should get the results.
+ *
+ * Outputs: Request is appended to queue for processing by
+ * the data link state machine.
+ *
+ *--------------------------------------------------------------------*/
+
+void dlq_disconnect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client)
+{
+ struct dlq_item_s *pnew;
+#if DEBUG
+ text_color_set(DW_COLOR_DEBUG);
+ dw_printf ("dlq_disconnect_request (...)\n");
+#endif
+
+ assert (chan >= 0 && chan < MAX_CHANS);
+
+/* Allocate a new queue item. */
+
+ pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
+ s_new_count++;
+
+ pnew->type = DLQ_DISCONNECT_REQUEST;
+ pnew->chan = chan;
+ memcpy (pnew->addrs, addrs, sizeof(pnew->addrs));
+ pnew->num_addr = num_addr;
+ pnew->client = client;
+
+/* Put it into queue. */
+
+ append_to_queue (pnew);
+
+} /* end dlq_connect_request */
+
+
+/*-------------------------------------------------------------------
+ *
+ * Name: dlq_xmit_data_request
+ *
+ * Purpose: Client application has requested transmission of connected
+ * data over an established link.
+ *
+ * Inputs: addrs - Source (owncall), destination (peercall),
+ * and possibly digipeaters.
+ *
+ * num_addr - Number of addresses. 2 to 10.
+ * First two are used to uniquely identify link.
+ * Any digipeaters involved are remembered
+ * from when the link was established.
+ *
+ * chan - Channel, 0 is first.
+ *
+ * client - Client application instance.
+ *
+ * pid - Protocol ID for data. Normally 0xf0 but the API
+ * allows the client app to use something non-standard
+ * for special situations.
+ *
+ * xdata_ptr - Pointer to block of data.
+ *
+ * xdata_len - Length of data in bytes.
+ *
+ * Outputs: Request is appended to queue for processing by
+ * the data link state machine.
+ *
+ *--------------------------------------------------------------------*/
+
+
+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)
+{
+ struct dlq_item_s *pnew;
+
+
+#if DEBUG
+ text_color_set(DW_COLOR_DEBUG);
+ dw_printf ("dlq_xmit_data_request (...)\n");
+#endif
+
+ assert (chan >= 0 && chan < MAX_CHANS);
+
+/* Allocate a new queue item. */
+
+ pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
+ s_new_count++;
+
+ pnew->type = DLQ_XMIT_DATA_REQUEST;
+ pnew->chan = chan;
+ memcpy (pnew->addrs, addrs, sizeof(pnew->addrs));
+ pnew->num_addr = num_addr;
+ pnew->client = client;
+ pnew->pid = pid;
+
+/* TODO: haven't thought about user data yet. */
+
+/* Put it into queue. */
+
+ append_to_queue (pnew);
+
+} /* end dlq_xmit_data_request */
/*-------------------------------------------------------------------
@@ -427,19 +620,24 @@ void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp,
* Purpose: Sleep while the received data queue is empty rather than
* polling periodically.
*
- * Inputs: None.
+ * Inputs: timeout - Return at this time even if queue is empty.
+ * Zero for no timeout.
+ *
+ * Returns: True if timed out before any event arrived.
+ *
+ * Description: In version 1.4, we add timeout option so we can continue after
+ * some amount of time even if no events are in the queue.
*
*--------------------------------------------------------------------*/
-void dlq_wait_while_empty (void)
+int dlq_wait_while_empty (double timeout)
{
- int err;
-
+ int timed_out_result = 0;
#if DEBUG1
text_color_set(DW_COLOR_DEBUG);
- dw_printf ("dlq_wait_while_empty () \n");
+ dw_printf ("dlq_wait_while_empty (%.3f)\n", timeout);
#endif
if ( ! was_init) {
@@ -448,21 +646,34 @@ void dlq_wait_while_empty (void)
if (queue_head == NULL) {
+
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
- dw_printf ("dlq_wait_while_empty (): prepare to SLEEP - about to call cond wait\n");
+ dw_printf ("dlq_wait_while_empty (): prepare to SLEEP...\n");
#endif
#if __WIN32__
- WaitForSingleObject (wake_up_event, INFINITE);
+ if (timeout != 0.0) {
+
+ DWORD ms = (timeout - dtime_now()) * 1000;
+ if (ms <= 0) ms = 1;
#if DEBUG
- text_color_set(DW_COLOR_DEBUG);
- dw_printf ("dlq_wait_while_empty (): returned from wait\n");
+ text_color_set(DW_COLOR_DEBUG);
+ dw_printf ("WaitForSingleObject: timeout after %d ms\n", ms);
#endif
+ if (WaitForSingleObject (wake_up_event, ms) == WAIT_TIMEOUT) {
+ timed_out_result = 1;
+ }
+ }
+ else {
+ WaitForSingleObject (wake_up_event, INFINITE);
+ }
#else
+ int err;
+
err = pthread_mutex_lock (&wake_up_mutex);
if (err != 0) {
text_color_set(DW_COLOR_ERROR);
@@ -472,20 +683,21 @@ void dlq_wait_while_empty (void)
}
recv_thread_is_waiting = 1;
- err = pthread_cond_wait (&wake_up_cond, &wake_up_mutex);
- recv_thread_is_waiting = 0;
+ if (timeout != 0.0) {
+ struct timespec abstime;
-#if DEBUG
- text_color_set(DW_COLOR_DEBUG);
- dw_printf ("dlq_wait_while_empty (): WOKE UP - returned from cond wait, err = %d\n", err);
-#endif
+ abstime.tv_sec = (time_t)(long)timeout;
+ abstime.tv_nsec = (long)((timeout - (long)abstime.tv_sec) * 1000000000.0);
- if (err != 0) {
- text_color_set(DW_COLOR_ERROR);
- dw_printf ("dlq_wait_while_empty: pthread_cond_wait err=%d", err);
- perror ("");
- exit (1);
+ err = pthread_cond_timedwait (&wake_up_cond, &wake_up_mutex, &abstime);
+ if (err == ETIMEDOUT) {
+ timed_out_result = 1;
+ }
}
+ else {
+ err = pthread_cond_wait (&wake_up_cond, &wake_up_mutex);
+ }
+ recv_thread_is_waiting = 0;
err = pthread_mutex_unlock (&wake_up_mutex);
if (err != 0) {
@@ -494,17 +706,18 @@ void dlq_wait_while_empty (void)
perror ("");
exit (1);
}
-
#endif
}
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
- dw_printf ("dlq_wait_while_empty () returns\n");
+ dw_printf ("dlq_wait_while_empty () returns timedout=%d\n", timed_out_result);
#endif
+ return (timed_out_result);
+
+} /* end dlq_wait_while_empty */
-}
/*-------------------------------------------------------------------
@@ -515,27 +728,17 @@ void dlq_wait_while_empty (void)
*
* Inputs: None.
*
- * Outputs: type - type of queue entry.
- *
- * chan - channel of received frame.
- * subchan - which demodulator caught it.
- * slice - which slicer caught it.
- *
- * pp - pointer to packet object when type is DLQ_REC_FRAME.
- * Caller should destroy it with ax25_delete when finished with it.
- *
- * Returns: 1 for success.
- * 0 if queue is empty.
+ * Returns: Pointer to a queue item. Caller is responsible for deleting it.
+ * NULL if queue is empty.
*
*--------------------------------------------------------------------*/
-int dlq_remove (dlq_type_t *type, int *chan, int *subchan, int *slice, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum, size_t spectrumsize)
+struct dlq_item_s *dlq_remove (void)
{
- struct dlq_item_s *phead;
- int result;
- int err;
+ struct dlq_item_s *result = NULL;
+ //int err;
#if DEBUG1
text_color_set(DW_COLOR_DEBUG);
@@ -549,6 +752,8 @@ int dlq_remove (dlq_type_t *type, int *chan, int *subchan, int *slice, packet_t
#if __WIN32__
EnterCriticalSection (&dlq_cs);
#else
+ int err;
+
err = pthread_mutex_lock (&dlq_mutex);
if (err != 0) {
text_color_set(DW_COLOR_ERROR);
@@ -558,34 +763,9 @@ int dlq_remove (dlq_type_t *type, int *chan, int *subchan, int *slice, packet_t
}
#endif
- if (queue_head == NULL) {
-
- *type = -1;
- *chan = -1;
- *subchan = -1;
- *slice = -1;
- *pp = NULL;
-
- memset (alevel, 0xff, sizeof(*alevel));
-
- *retries = -1;
- strlcpy(spectrum, "", spectrumsize);
- result = 0;
- }
- else {
-
- phead = queue_head;
+ if (queue_head != NULL) {
+ result = queue_head;
queue_head = queue_head->nextp;
-
- *type = phead->type;
- *chan = phead->chan;
- *subchan = phead->subchan;
- *slice = phead->slice;
- *pp = phead->pp;
- *alevel = phead->alevel;
- *retries = phead->retries;
- strlcpy (spectrum, phead->spectrum, spectrumsize);
- result = 1;
}
#if __WIN32__
@@ -602,19 +782,22 @@ int dlq_remove (dlq_type_t *type, int *chan, int *subchan, int *slice, packet_t
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
- dw_printf ("dlq_remove() returns type=%d, chan=%d\n", (int)(*type), *chan);
+ dw_printf ("dlq_remove() returns \n");
#endif
#if AX25MEMDEBUG
- if (ax25memdebug_get() && result) {
+ if (ax25memdebug_get() && result != NULL) {
text_color_set(DW_COLOR_DEBUG);
- dw_printf ("dlq_remove (type=%d, chan=%d.%d, seq=%d, ...)\n", *type, *chan, *subchan, ax25memdebug_seq(*pp));
+ if (result->pp != NULL) {
+// TODO: mnemonics for type.
+ dw_printf ("dlq_remove (type=%d, chan=%d.%d, seq=%d, ...)\n", result->type, result->chan, result->subchan, ax25memdebug_seq(result->pp));
+ }
+ else {
+ dw_printf ("dlq_remove (type=%d, chan=%d, ...)\n", result->type, result->chan);
+ }
}
#endif
- if (result) {
- free (phead);
- }
return (result);
}
@@ -622,25 +805,23 @@ int dlq_remove (dlq_type_t *type, int *chan, int *subchan, int *slice, packet_t
/*-------------------------------------------------------------------
*
- * Name: dlq_is_empty
+ * Name: dlq_delete
*
- * Purpose: Test whether queue is empty.
+ * Purpose: Release storage used by a queue item.
*
- * Inputs: None
- *
- * Returns: True if nothing in the queue.
+ * Inputs: pitem - Pointer to a queue item.
*
*--------------------------------------------------------------------*/
-#if 0
-static int dlq_is_empty (void)
-{
- if (queue_head == NULL) {
- return (1);
- }
- return (0);
-} /* end dlq_is_empty */
-#endif
+void dlq_delete (struct dlq_item_s *pitem)
+{
+ s_delete_count++;
+ if (pitem->pp != NULL) ax25_delete (pitem->pp)
+ free (pitem);
+
+} /* end dlq_delete */
+
+
/* end dlq.c */
diff --git a/dlq.h b/dlq.h
index acfbce3..f4f0a90 100644
--- a/dlq.h
+++ b/dlq.h
@@ -12,17 +12,77 @@
#include "audio.h"
-void dlq_init (void);
/* 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_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. */
-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);
+// 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. */
+ /* DLQ_REC_FRAME, DLQ_CONNECT_REQUEST, DLQ_DISCONNECT_REQUEST, DLQ_XMIT_DATA_REQUEST */
+
+ int chan; /* Radio channel of origin. */
+
+// 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.
+
+ char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN];
+
+ int num_addr; /* Range 2 .. 10. */
+
+ int client;
+
+ int pid;
+
+ /* TODO: xmit data */
+
+} 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 clienti, int pid, char *xdata_ptr, int xdata_len);
+
+
+int dlq_wait_while_empty (double timeout_val);
+
+struct dlq_item_s *dlq_remove (void);
+
+void dlq_delete (struct dlq_item_s *pitem);
#endif
diff --git a/doc/2400-4800-PSK-for-APRS-Packet-Radio.pdf b/doc/2400-4800-PSK-for-APRS-Packet-Radio.pdf
new file mode 100644
index 0000000..6fb09be
Binary files /dev/null and b/doc/2400-4800-PSK-for-APRS-Packet-Radio.pdf differ
diff --git a/doc/APRS-Telemetry-Toolkit.pdf b/doc/APRS-Telemetry-Toolkit.pdf
index 68568bf..1fa97bc 100644
Binary files a/doc/APRS-Telemetry-Toolkit.pdf and b/doc/APRS-Telemetry-Toolkit.pdf differ
diff --git a/doc/Going-beyond-9600-baud.pdf b/doc/Going-beyond-9600-baud.pdf
new file mode 100644
index 0000000..ce3545f
Binary files /dev/null and b/doc/Going-beyond-9600-baud.pdf differ
diff --git a/doc/README.md b/doc/README.md
index 67527f2..3355f64 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -45,6 +45,17 @@ These dive into more detail for specialized topics or typical usage scenarios.
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)
+
+ Double or quadruple your data rate by sending multiple bits at the same time.
+
+- [Going beyond 9600 baud](Going-beyond-9600-baud.pdf)
+
+ Why stop at 9600 baud? Go faster if your soundcard and radio can handle it.
+
+
## Miscellaneous ##
diff --git a/doc/Raspberry-Pi-APRS-Tracker.pdf b/doc/Raspberry-Pi-APRS-Tracker.pdf
index 3cb84e2..0d61dab 100644
Binary files a/doc/Raspberry-Pi-APRS-Tracker.pdf and b/doc/Raspberry-Pi-APRS-Tracker.pdf differ
diff --git a/doc/Raspberry-Pi-APRS.pdf b/doc/Raspberry-Pi-APRS.pdf
index dc6120f..61cecd6 100644
Binary files a/doc/Raspberry-Pi-APRS.pdf and b/doc/Raspberry-Pi-APRS.pdf differ
diff --git a/doc/WA8LMF-TNC-Test-CD-Results.pdf b/doc/WA8LMF-TNC-Test-CD-Results.pdf
index cf7c523..7f883ac 100644
Binary files a/doc/WA8LMF-TNC-Test-CD-Results.pdf and b/doc/WA8LMF-TNC-Test-CD-Results.pdf differ
diff --git a/fsk_demod_state.h b/fsk_demod_state.h
index 74a723c..85b970a 100644
--- a/fsk_demod_state.h
+++ b/fsk_demod_state.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.
*/
diff --git a/gen_packets.c b/gen_packets.c
index c042802..bee4de8 100644
--- a/gen_packets.c
+++ b/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
@@ -76,6 +76,17 @@
#include "morse.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,7 +106,6 @@ static void send_packet (char *str)
int flen;
int c;
-
if (g_morse_wpm > 0) {
morse_send (0, str, g_morse_wpm, 100, 100);
@@ -105,6 +115,31 @@ static void send_packet (char *str)
flen = ax25_pack (pp, fbuf);
for (c=0; c MAX_BAUD) {
- text_color_set(DW_COLOR_ERROR);
+ text_color_set(DW_COLOR_ERROR);
dw_printf ("Use a more reasonable bit rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD);
exit (EXIT_FAILURE);
}
@@ -215,18 +252,43 @@ int main(int argc, char **argv)
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 < MIN_BAUD || modem.achan[0].baud > MAX_BAUD) {
- text_color_set(DW_COLOR_ERROR);
+ text_color_set(DW_COLOR_ERROR);
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 (modem.achan[0].baud < 600) {
- modem.achan[0].mark_freq = 1600;
+ modem.achan[0].modem_type = MODEM_AFSK;
+ modem.achan[0].mark_freq = 1600; // Typical for HF SSB
modem.achan[0].space_freq = 1800;
}
- else if (modem.achan[0].baud <= 2400) {
- modem.achan[0].mark_freq = 1200;
- modem.achan[0].space_freq = 2200;
+ 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;
@@ -353,6 +415,11 @@ int main(int argc, char **argv)
}
break;
+ case 'X':
+
+ experiment = 1;
+ break;
+
case '?':
/* Unknown option message was already printed. */
@@ -390,7 +457,11 @@ 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);
morse_init (&modem, amplitude/2);
@@ -400,6 +471,84 @@ int main(int argc, char **argv)
assert (modem.adev[0].num_channels == 1 || modem.adev[0].num_channels == 2);
assert (modem.adev[0].samples_per_sec >= MIN_SAMPLES_PER_SEC && modem.adev[0].samples_per_sec <= MAX_SAMPLES_PER_SEC);
+
+ 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.
@@ -467,17 +616,26 @@ 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);
+ }
snprintf (stemp, sizeof(stemp), "WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog! %04d of %04d", i, packet_count);
@@ -512,7 +670,7 @@ static void usage (char **argv)
dw_printf ("Options:\n");
dw_printf (" -a Signal amplitude in range of 0 - 200%%. Default 50.\n");
dw_printf (" -b Bits / second for data. Default is %d.\n", DEFAULT_BAUD);
- dw_printf (" -B Bits / second for data. Proper modem selected for 300, 1200, 9600.\n");
+ dw_printf (" -B 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 Mark frequency. Default is %d.\n", DEFAULT_MARK_FREQ);
dw_printf (" -s Space frequency. Default is %d.\n", DEFAULT_SPACE_FREQ);
@@ -691,14 +849,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)
{
diff --git a/gen_tone.c b/gen_tone.c
index 5860911..d81a669 100644
--- a/gen_tone.c
+++ b/gen_tone.c
@@ -1,7 +1,7 @@
//
// 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
@@ -68,18 +68,40 @@ 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];
/* Accumulators. */
static unsigned int tone_phase[MAX_CHANS]; // Phase accumulator for tone generation.
- // Upper bits are used as index into sine table.
+ // 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];
+
+static int prev_symbol[MAX_CHANS]; // Data is conveyed by phase relative to the
+ // previous symbol. So we need to keep it.
+
/*
* The K9NG/G3RUH output originally took a very simple and lazy approach.
@@ -187,19 +209,49 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp)
int a = ACHAN2ADEV(chan);
+ 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);
- ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->achan[chan].baud ) + 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.
- 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);
+ switch (save_audio_config_p->achan[chan].modem_type) {
- 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);
+ case MODEM_QPSK:
- tone_phase[chan] = 0;
-
- bit_len_acc[chan] = 0;
+ audio_config_p->achan[chan].mark_freq = 1800;
+ audio_config_p->achan[chan].space_freq = audio_config_p->achan[chan].mark_freq; // Not Used.
- lfsr[chan] = 0;
+ // 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;
+ }
}
}
@@ -300,14 +352,18 @@ 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->achan[chan].valid);
@@ -317,6 +373,70 @@ 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;
+ }
+#define REV2 1
+#if REV2
+#else
+ tone_phase[chan] = PHASE_SHIFT_45;
+ if (bit_count[chan] & 2) {
+ tone_phase[chan] += (unsigned)PHASE_SHIFT_180;
+ }
+#endif
+ // 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;
+#if REV2
+ symbol = gray2phase_v26[dibit];
+ tone_phase[chan] += symbol * PHASE_SHIFT_90;
+#else
+ symbol = (prev_symbol[chan] + gray2phase_v26[dibit]) & 0x3;
+ tone_phase[chan] += symbol * PHASE_SHIFT_90;
+ prev_symbol[chan] = symbol;
+#endif
+ 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;
+#if 1
+ symbol = gray2phase_v27[tribit];
+ tone_phase[chan] += symbol * PHASE_SHIFT_45;
+#else
+ symbol = (prev_symbol[chan] + gray2phase_v27[tribit]) & 0x7;
+ tone_phase[chan] = symbol * PHASE_SHIFT_45;
+ prev_symbol[chan] = symbol;
+#endif
+ save_bit[chan] = 0;
+ bit_count[chan] = 0;
+ }
+
if (save_audio_config_p->achan[chan].modem_type == MODEM_SCRAMBLE) {
int x;
@@ -334,6 +454,14 @@ void tone_gen_put_bit (int chan, int dat)
sam = sine_table[(tone_phase[chan] >> 24) & 0xff];
gen_tone_put_sample (chan, a, sam);
}
+ else if (save_audio_config_p->achan[chan].modem_type == MODEM_QPSK ||
+ save_audio_config_p->achan[chan].modem_type == MODEM_8PSK) {
+ int sam;
+
+ tone_phase[chan] += f1_change_per_sample[chan];
+ sam = sine_table[(tone_phase[chan] >> 24) & 0xff];
+ gen_tone_put_sample (chan, a, sam);
+ }
else {
float fsam = dat ? amp16bit : (-amp16bit);
diff --git a/multi_modem.c b/multi_modem.c
index f9d57fd..e1d9f55 100644
--- a/multi_modem.c
+++ b/multi_modem.c
@@ -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,6 +70,7 @@
* Set limit on number of packets in fix up later queue.
*
*------------------------------------------------------------------*/
+
//#define DEBUG 1
#define DIGIPEATER_C
@@ -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,7 @@ 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, "");
+ dlq_rec_frame (chan, subchan, slice, pp, alevel, retries, "");
return;
}
@@ -641,7 +649,7 @@ 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,
+ dlq_rec_frame (chan, j, k,
candidate[chan][j][k].packet_p,
candidate[chan][j][k].alevel,
(int)(candidate[chan][j][k].retries),
diff --git a/recv.c b/recv.c
index a2dba4d..05c90b7 100644
--- a/recv.c
+++ b/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
@@ -80,6 +80,8 @@
*
*---------------------------------------------------------------*/
+//#define DEBUG 1
+
#include
#include
@@ -104,7 +106,10 @@
#include "recv.h"
#include "dtmf.h"
#include "aprs_tt.h"
-
+#include "dtime_now.h"
+#if NEW14
+#include "ax25_link.h"
+#endif
#if __WIN32__
static unsigned __stdcall recv_adev_thread (void *arg);
@@ -283,40 +288,101 @@ 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 ();
+ int timed_out;
+#if NEW14
+ double timeout_value = ax25_link_get_next_timer_expiry();
+
+
+ timed_out = dlq_wait_while_empty (timeout_value);
+#else
+ dlq_wait_while_empty (0.0);
+ timed_out = 0;
+#endif
+
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
- dw_printf ("recv_process: woke up\n");
+ dw_printf ("recv_process: woke up, timed_out=%d\n", timed_out);
#endif
- ok = dlq_remove (&type, &chan, &subchan, &slice, &pp, &alevel, &retries, spectrum, sizeof(spectrum));
+ if (timed_out) {
#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);
+ text_color_set(DW_COLOR_ERROR);
+ dw_printf ("recv_process: time waiting on dlq. call dl_timer_expiry.\n");
+#endif
+
+#if NEW14
+ dl_timer_expiry ();
#endif
- if (ok) {
- app_process_rec_packet (chan, subchan, slice, pp, alevel, retries, spectrum);
}
-#if DEBUG
else {
+
+ pitem = dlq_remove ();
+
+#if DEBUG
text_color_set(DW_COLOR_DEBUG);
- dw_printf ("recv_process: spurious wakeup. (Temp debugging message - not a problem if only occasional.)\n");
- }
+ 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);
+ pitem->pp = NULL; // Was consumed by above. Don't try to use or free again.
+
+
+/*
+ * Link processing - Can ignore UI frames.
+ */
+ // TODO - ax25_link_rec_frame & delete & remove delete from above.
+
+ break;
+
+#if NEW14
+ 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;
+#endif
+ }
+
+ dlq_delete (pitem);
+ }
+#if DEBUG
+ else {
+ text_color_set(DW_COLOR_DEBUG);
+ dw_printf ("recv_process: spurious wakeup. (Temp debugging message - not a problem if only occasional.)\n");
+ }
+#endif
+ }
+
}
} /* end recv_process */
diff --git a/server.c b/server.c
index 13e7980..f4cf5d7 100644
--- a/server.c
+++ b/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
@@ -58,6 +58,17 @@
* 'x' Unregister CallSign
*
* '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.
*
@@ -80,7 +91,13 @@
* (Enabled with 'm' command.)
*
* '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
@@ -135,6 +152,7 @@
#include "textcolor.h"
#include "audio.h"
#include "server.h"
+#include "dlq.h"
@@ -1590,7 +1608,6 @@ 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]));
@@ -1620,28 +1637,53 @@ static THREAD_F cmd_listen_thread (void *arg)
}
}
+#if NEW14
+ dlq_connect_request (callsigns, num_calls, cmd.hdr.portx, client, pid);
+#else
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");
+#endif
}
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]));
+#if NEW14
+ dlq_xmit_data_request (callsigns, num_calls, cmd.hdr.portx, client, cmd.hdr.pid, cmd.data, netle2host(cmd.hdr.data_len_NETLE));
+#else
+ 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");
+#endif
+ }
break;
case 'd': /* Disconnect, Terminate an AX.25 Connection */
{
+ 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]));
+#if NEW14
+ dlq_disconnect_request (callsigns, num_calls, cmd.hdr.portx, client);
+#else
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");
+#endif
}
break;
diff --git a/server.h b/server.h
index 94ad068..dda80f3 100644
--- a/server.h
+++ b/server.h
@@ -18,5 +18,9 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
int server_callsign_registered_by_client (char *callsign);
+void server_link_established (int chan, int client, char *remote_call, char *own_call, int incoming);
+
+void server_link_terminated (int chan, int client, char *remote_call, char *own_call, int timeout);
+
/* end server.h */
diff --git a/xid.c b/xid.c
index 7580c94..9db66f8 100644
--- a/xid.c
+++ b/xid.c
@@ -23,13 +23,24 @@
*
* Module: xid.c
*
- * Purpose: ....
+ * Purpose: Encode and decode the info field of XID frames.
*
- * Description:
+ * Description: If we originate the connection, and the other end is
+ * capable of AX.25 version 2.2,
*
- * References: ...
+ * - We send an XID command frame with our capabilities.
+ * - the other sends back an XID response, possibly
+ * reducing some values to be acceptable there.
+ * - Both ends use the values in that response.
*
- *
+ * If the other end originates the connection,
+ *
+ * - It sends XID command frame with its capabilities.
+ * - We might decrease some of them to be acceptable.
+ * - Send XID response.
+ * - Both ends use values in my response.
+ *
+ * References: AX.25 Protocol Spec, sections 4.3.3.7 & 6.3.2.
*
*---------------------------------------------------------------*/
@@ -38,6 +49,7 @@
#include
#include
+#include "direwolf.h"
#include "textcolor.h"
//#include "xid.h"
@@ -47,17 +59,17 @@ struct ax25_param_s {
int full_duplex;
// Order is important because negotiation keeps the lower.
- enum rej_e {implicit_reject, selective_reject, selective_reject_reject } rej;
+ enum rej_e {implicit_reject=1, selective_reject=2, selective_reject_reject=3 } rej;
enum modulo_e {modulo_8 = 8, modulo_128 = 128} modulo;
int i_field_length_rx; /* In bytes. XID has it in bits. */
- int window_size;
+ int window_size_rx;
int ack_timer; /* "T1" in mSec. */
- int retries; /* Inconsistently refered to as "N1" or "N2" */
+ int retries; /* "N1" */
};
@@ -98,36 +110,54 @@ struct ax25_param_s {
*
* Outputs: ...
*
- * Description:
+ * Returns: 1 for mostly successful (with possible error messages), 0 for failure.
+ *
+ * Description: 6.3.2 "The receipt of an XID response from the other station
+ * establishes that both stations are using AX.25 version
+ * 2.2 or higher and enables the use of the segmenter/reassembler
+ * and selective reject."
*
*--------------------------------------------------------------------*/
-//Returns: 1 for mostly successful (with possible error messages), 0 for failure.
+
int xid_parse (unsigned char *info, int info_len, struct ax25_param_s *result)
{
unsigned char *p;
int group_len;
+ char stemp[64];
+ char debug_msg[256];
result->full_duplex = 0;
+
+ // Default is implicit reject for pre version 2.2 but we wouldn't be here in that case.
result->rej = selective_reject;
+
result->modulo = modulo_8;
- result->i_field_length_rx = 256;
- result->window_size = result->modulo == modulo_128 ? 32 : 4;
+
+ result->i_field_length_rx = 256; // bytes here but converted to bits during encoding.
+
+ // Default is 4 for pre version 2.2 but we wouldn't be here in that case.
+ result->window_size_rx = result->modulo == modulo_128 ? 32 : 7;
+
result->ack_timer = 3000;
result->retries = 10;
p = info;
if (*p != FI_Format_Indicator) {
+ text_color_set (DW_COLOR_ERROR);
+ dw_printf ("XID error: First byte of info field should be Format Indicator, %d.\n", FI_Format_Indicator);
return 0;
}
p++;
if (*p != GI_Group_Identifier) {
+ text_color_set (DW_COLOR_ERROR);
+ dw_printf ("XID error: Second byte of info field should be Group Indicator, %d.\n", GI_Group_Identifier);
return 0;
}
p++;
@@ -141,6 +171,11 @@ int xid_parse (unsigned char *info, int info_len, struct ax25_param_s *result)
pind = *p++;
plen = *p++; // should have sanity checking
+ if (plen < 1 || plen > 4) {
+ text_color_set (DW_COLOR_ERROR);
+ dw_printf ("XID error: Length ????? TODO ???? %d.\n", plen);
+ return (1); // got this far.
+ }
pval = 0;
for (j=0; jwindow_size = pval;
+ result->window_size_rx = pval;
-// TODO must be 1-7 for modulo 8 or 1-127 for modulo 128;
-
-// if (pval & 0x7) {
-// text_color_set (DW_COLOR_ERROR);
-// dw_printf ("XID error: Window Size Rx is not in range of 1 thru ???\n");
-// }
+ if (pval < 1 || pval >= result->modulo) {
+ text_color_set (DW_COLOR_ERROR);
+ dw_printf ("XID error: Window Size Rx, %d, is not in range of 1 thru %d.\n", pval, result->modulo-1);
+ result->window_size_rx = result->modulo == modulo_128 ? 32 : 7;
+ }
//continue here.
@@ -252,7 +286,7 @@ int xid_parse (unsigned char *info, int info_len, struct ax25_param_s *result)
break;
default:
- break;
+ break; // Ignore anything we don't recognize.
}
}
@@ -261,7 +295,7 @@ int xid_parse (unsigned char *info, int info_len, struct ax25_param_s *result)
dw_printf ("XID error: Frame / Group Length mismatch.\n");
}
- return 1;
+ return (1);
} /* end xid_parse */
@@ -270,16 +304,60 @@ int xid_parse (unsigned char *info, int info_len, struct ax25_param_s *result)
*
* Name: xid_encode
*
- * Purpose: ...
+ * Purpose: Encode the information part of an XID frame.
*
- * Inputs: param -
+ * Inputs: param->
+ * full_duplex - As command, am I capable of full duplex operation?
+ * When a response, are we both?
+ * 0 = half duplex.
+ * 1 = full duplex.
+ *
+ * rej - One of: implicit_reject, selective_reject, selective_reject_reject.
+ * As command, what am I capable of processing?
+ * As response, take minimum of
+ *
+ *
+ * modulo - 8 or 128.
+ *
+ * i_field_length_rx - Maximum number of bytes I can handle in info part.
+ * Default is 256.
+ * Up to 8191 will fit into the field.
+ * Use G_UNKNOWN to omit this.
+ *
+ * window_size_rx - Maximum window size ("k") that I can handle.
+ * Defaults are are 4 for modulo 8 and 32 for modulo 128.
+ *
+ * ack_timer - Acknowledge timer in milliseconds.
+ * *** describe meaning. ***
+ * Default is 3000.
+ * Use G_UNKNOWN to omit this.
+ *
+ * retries - Allows negotiation of retries.
+ * Default is 10.
+ * Use G_UNKNOWN to omit this.
*
* Outputs: info - Information part of XID frame.
* Does not include the control byte.
+ * Supply 32 bytes to be safe.
*
- * Returns: Number of bytes in the info part.
+ * Returns: Number of bytes in the info part. Should be at most 27.
*
- * Description:
+ * Description: 6.3.2 "Parameter negotiation occurs at any time. It is accomplished by sending
+ * the XID command frame and receiving the XID response frame. Implementations of
+ * AX.25 prior to version 2.2 respond to an XID command frame with a FRMR response
+ * frame. The TNC receiving the FRMR uses a default set of parameters compatible
+ * with previous versions of AX.25."
+ *
+ * "This version of AX.25 implements the negotiation or notification of six AX.25
+ * parameters. Notification simply tells the distant TNC some limit that cannot be exceeded.
+ * The distant TNC can choose to use the limit or some other value that is within the
+ * limits. Notification is used with the Window Size Receive (k) and Information
+ * Field Length Receive (N1) parameters. Negotiation involves both TNCs choosing a
+ * value that is mutually acceptable. The XID command frame contains a set of values
+ * acceptable to the originating TNC. The distant TNC chooses to accept the values
+ * offered, or other acceptable values, and places these values in the XID response.
+ * Both TNCs set themselves up based on the values used in the XID response. Negotiation
+ * is used by Classes of Procedures, HDLC Optional Functions, Acknowledge Timer and Retries."
*
*--------------------------------------------------------------------*/
@@ -333,27 +411,35 @@ int xid_encode (struct ax25_param_s *param, unsigned char *info)
*p++ = (x >> 8) & 0xff;
*p++ = x & 0xff;
- *p++ = PI_I_Field_Length_Rx;
- *p++ = 2;
- x = param->i_field_length_rx * 8;
- *p++ = (x >> 8) & 0xff;
- *p++ = x & 0xff;
+ if (param->i_field_length_rx != G_UNKNOWN) {
+ *p++ = PI_I_Field_Length_Rx;
+ *p++ = 2;
+ x = param->i_field_length_rx * 8;
+ *p++ = (x >> 8) & 0xff;
+ *p++ = x & 0xff;
+ }
- *p++ = PI_Window_Size_Rx;
- *p++ = 1;
- *p++ = param->window_size;
+ if (param->window_size_rx != G_UNKNOWN) {
+ *p++ = PI_Window_Size_Rx;
+ *p++ = 1;
+ *p++ = param->window_size_rx;
+ }
- *p++ = PI_Ack_Timer;
- *p++ = 2;
- *p++ = param->ack_timer >> 8;
- *p++ = param->ack_timer & 0xff;
+ if (param->ack_timer != G_UNKNOWN) {
+ *p++ = PI_Ack_Timer;
+ *p++ = 2;
+ *p++ = (param->ack_timer >> 8) & 0xff;
+ *p++ = param->ack_timer & 0xff;
+ }
- *p++ = PI_Retries;
- *p++ = 1;
- *p++ = param->retries;
+ if (param->retries != G_UNKNOWN) {
+ *p++ = PI_Retries;
+ *p++ = 1;
+ *p++ = param->retries;
+ }
len = p - info;
- assert (len == 27);
+ assert (len <= 27);
return (len);
} /* end xid_encode */
@@ -368,7 +454,7 @@ int xid_encode (struct ax25_param_s *param, unsigned char *info)
*
* Description: Run with:
*
- * gcc -DTEST -g xid.c textcolor.c ; ./a
+ * gcc -DXIDTEST -g xid.c textcolor.o && ./a
*
* Result should be:
*
@@ -379,7 +465,7 @@ int xid_encode (struct ax25_param_s *param, unsigned char *info)
*--------------------------------------------------------------------*/
-#if TEST
+#if XIDTEST
/* From Figure 4.6. Typical XID frame, from AX.25 protocol spec, v. 2.2 */
/* This is the info part after a control byte of 0xAF. */
@@ -417,7 +503,7 @@ static unsigned char example[27] = {
/* PV */ 0x00, /* */
/* PI */ 0x0A, /* Parameter Indicator - Retries (N1) */
/* PL */ 0x01, /* Parameter Length */
- /* PV */ 0x03 /* Parameter Variable - 3 ret */
+ /* PV */ 0x03 /* Parameter Variable - 3 retries */
};
int main (int argc, char *argv[]) {
@@ -438,7 +524,7 @@ int main (int argc, char *argv[]) {
assert (param.rej == selective_reject_reject);
assert (param.modulo == modulo_128);
assert (param.i_field_length_rx == 128);
- assert (param.window_size == 2);
+ assert (param.window_size_rx == 2);
assert (param.ack_timer == 4096);
assert (param.retries == 3);
@@ -461,7 +547,7 @@ int main (int argc, char *argv[]) {
param.rej = implicit_reject;
param.modulo = modulo_8;
param.i_field_length_rx = 2048;
- param.window_size = 3;
+ param.window_size_rx = 3;
param.ack_timer = 3000;
param.retries = 10;
@@ -472,7 +558,7 @@ int main (int argc, char *argv[]) {
assert (param2.rej == implicit_reject);
assert (param2.modulo == modulo_8);
assert (param2.i_field_length_rx == 2048);
- assert (param2.window_size == 3);
+ assert (param2.window_size_rx == 3);
assert (param2.ack_timer == 3000);
assert (param2.retries == 10);
@@ -482,7 +568,7 @@ int main (int argc, char *argv[]) {
param.rej = selective_reject;
param.modulo = modulo_8;
param.i_field_length_rx = 256;
- param.window_size = 4;
+ param.window_size_rx = 4;
param.ack_timer = 3000;
param.retries = 10;
@@ -493,11 +579,11 @@ int main (int argc, char *argv[]) {
assert (param2.rej == selective_reject);
assert (param2.modulo == modulo_8);
assert (param2.i_field_length_rx == 256);
- assert (param2.window_size == 4);
+ assert (param2.window_size_rx == 4);
assert (param2.ack_timer == 3000);
assert (param2.retries == 10);
- text_color_set (DW_COLOR_INFO);
+ text_color_set (DW_COLOR_REC);
dw_printf ("XID test: Success.\n");
exit (0);
diff --git a/xmit.c b/xmit.c
index 21d4b67..b80d844 100644
--- a/xmit.c
+++ b/xmit.c
@@ -603,6 +603,22 @@ static void xmit_ax25_frames (int c, int p, packet_t pp)
text_color_set(DW_COLOR_XMIT);
dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L');
dw_printf ("%s", stemp); /* stations followed by : */
+
+/* Demystify non-APRS. Use same format for received frames in direwolf.c. */
+
+ if ( ! ax25_is_aprs(pp)) {
+ ax25_frame_type_t ftype;
+ cmdres_t cr;
+ char desc[32];
+ int pf;
+ int nr;
+ int ns;
+
+ ftype = ax25_frame_type (pp, &cr, desc, &pf, &nr, &ns);
+
+ dw_printf ("(%s)", desc);
+ }
+
ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp));
dw_printf ("\n");
(void)ax25_check_addresses (pp);
@@ -623,6 +639,9 @@ static void xmit_ax25_frames (int c, int p, packet_t pp)
*/
time_ptt = dtime_now ();
+// TODO: This was written assuming bits/sec = baud.
+// Does it is need to be scaled differently for PSK?
+
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("xmit_thread: Turn on PTT now for channel %d. speed = %d\n", c, xmit_bits_per_sec[c]);