mirror of https://github.com/wb2osz/direwolf.git
Add 2400 & 4800 PSK modems. New functions to handle frames besides UI.
This commit is contained in:
parent
ccae7529bf
commit
4f1918c3af
35
CHANGES.md
35
CHANGES.md
|
@ -1,6 +1,36 @@
|
||||||
|
|
||||||
# Revision History #
|
# 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 ##
|
## Version 1.3 -- Beta Test -- March 2016 ##
|
||||||
|
@ -61,7 +91,7 @@ such as PowerPC or MIPS.
|
||||||
|
|
||||||
- Improved decoder performance.
|
- Improved decoder performance.
|
||||||
Over 1000 error-free frames decoded from WA8LMF TNC Test CD.
|
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.
|
- 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: ###
|
### New Features: ###
|
||||||
|
|
||||||
- Added APRStt gateway capability. For details, see:
|
- Added APRStt gateway capability. For details, see ***APRStt-Implementation-Notes.pdf***
|
||||||
**APRStt-Implementation-Notes.pdf**
|
|
||||||
|
|
||||||
|
|
||||||
-----------
|
-----------
|
||||||
|
|
|
@ -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 \
|
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 \
|
||||||
decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
|
decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
|
||||||
|
@ -248,15 +248,18 @@ endif
|
||||||
|
|
||||||
# Optimization for slow processors.
|
# Optimization for slow processors.
|
||||||
|
|
||||||
demod.o : fsk_fast_filter.h
|
demod.o : fsk_fast_filter.h
|
||||||
|
|
||||||
demod_afsk.o : fsk_fast_filter.h
|
demod_afsk.o : fsk_fast_filter.h
|
||||||
|
|
||||||
|
|
||||||
fsk_fast_filter.h : demod_afsk.c
|
fsk_fast_filter.h : gen_fff
|
||||||
$(CC) $(CFLAGS) -o gen_fff -DGEN_FFF demod_afsk.c dsp.c textcolor.c $(LDFLAGS)
|
|
||||||
./gen_fff > fsk_fast_filter.h
|
./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.
|
# 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
|
# 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 \
|
dsp.o hdlc_rec.o hdlc_rec2.o multi_modem.o rrbb.o \
|
||||||
fcs_calc.o ax25_pad.o decode_aprs.o dwgpsnmea.o \
|
fcs_calc.o ax25_pad.o decode_aprs.o dwgpsnmea.o \
|
||||||
dwgps.o dwgpsd.o serial_port.o telemetry.o latlong.o symbols.o tt_text.o textcolor.o \
|
dwgps.o dwgpsd.o serial_port.o telemetry.o 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.
|
# 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?
|
# Can we encode and decode at popular data rates?
|
||||||
|
|
||||||
check-modem1200 : gen_packets atest
|
check-modem1200 : gen_packets atest
|
||||||
./gen_packets -n 100 -o /tmp/test1.wav
|
./gen_packets -n 100 -o /tmp/test12.wav
|
||||||
./atest -F0 -PE -L70 -G71 /tmp/test1.wav
|
./atest -F0 -PE -L63 -G71 /tmp/test12.wav
|
||||||
./atest -F1 -PE -L73 -G75 /tmp/test1.wav
|
./atest -F1 -PE -L70 -G75 /tmp/test12.wav
|
||||||
#rm /tmp/test1.wav
|
rm /tmp/test12.wav
|
||||||
|
|
||||||
check-modem300 : gen_packets atest
|
check-modem300 : gen_packets atest
|
||||||
./gen_packets -B300 -n 100 -o /tmp/test3.wav
|
./gen_packets -B300 -n 100 -o /tmp/test3.wav
|
||||||
|
@ -602,11 +605,28 @@ check-modem300 : gen_packets atest
|
||||||
rm /tmp/test3.wav
|
rm /tmp/test3.wav
|
||||||
|
|
||||||
check-modem9600 : gen_packets atest
|
check-modem9600 : gen_packets atest
|
||||||
./gen_packets -B9600 -n 100 -o /tmp/test9.wav
|
./gen_packets -B9600 -n 100 -o /tmp/test96.wav
|
||||||
./atest -B9600 -F0 -L57 -G59 /tmp/test9.wav
|
./atest -B9600 -F0 -L57 -G61 /tmp/test96.wav
|
||||||
./atest -B9600 -F1 -L66 -G67 /tmp/test9.wav
|
./atest -B9600 -F1 -L64 -G67 /tmp/test96.wav
|
||||||
rm /tmp/test9.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
|
# Unit test for inner digipeater algorithm
|
||||||
|
@ -679,6 +699,14 @@ kisstest : kiss_frame.c
|
||||||
./kisstest
|
./kisstest
|
||||||
rm 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 ---------------------------
|
# ----------------------------- 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.
|
# Unit test for UDP reception with AFSK demodulator.
|
||||||
# Temporary during development. Might not be useful anymore.
|
# 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
|
fcs_calc.o ax25_pad.o decode_aprs.o symbols.o textcolor.o misc.a
|
||||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
./udptest
|
./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.
|
# For demodulator tweaking experiments.
|
||||||
# Dependencies of demod*.c, rather than .o, are intentional.
|
# Dependencies of demod*.c, rather than .o, are intentional.
|
||||||
|
|
||||||
demod.o : tune.h
|
demod.o : tune.h
|
||||||
|
|
||||||
demod_afsk.o : tune.h
|
demod_afsk.o : tune.h
|
||||||
|
|
||||||
demod_9600.o : tune.h
|
demod_9600.o : tune.h
|
||||||
|
|
||||||
testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.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
|
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)
|
$(CC) $(CFLAGS) -o atest $^ $(LDFLAGS)
|
||||||
./atest 02_Track_2.wav | grep "packets decoded in" > atest.out
|
./atest 02_Track_2.wav | grep "packets decoded in" > atest.out
|
||||||
|
@ -757,8 +793,7 @@ dist-src : README.md CHANGES.md
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean :
|
clean :
|
||||||
rm -f $(APPS) fsk_fast_filter.h *.o *.a direwolf.desktop
|
rm -f $(APPS) gen_fff tune.h fsk_fast_filter.h *.o *.a direwolf.desktop
|
||||||
echo " " > tune.h
|
|
||||||
|
|
||||||
|
|
||||||
depend : $(wildcard *.c)
|
depend : $(wildcard *.c)
|
||||||
|
|
|
@ -24,8 +24,9 @@
|
||||||
# 3. Removed fsk_fast_filter.h from atest receipe, clang compiler was having
|
# 3. Removed fsk_fast_filter.h from atest receipe, clang compiler was having
|
||||||
# a hissy fit. Not check with GCC.
|
# a hissy fit. Not check with GCC.
|
||||||
|
|
||||||
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc direwolf.conf
|
APPS := direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc
|
||||||
@echo " "
|
|
||||||
|
all : $(APPS) direwolf.desktop direwolf.conf @echo " "
|
||||||
@echo "Next step install with: "
|
@echo "Next step install with: "
|
||||||
@echo " "
|
@echo " "
|
||||||
@echo " sudo make install"
|
@echo " sudo make install"
|
||||||
|
@ -221,7 +222,7 @@ z := $(notdir ${CURDIR})
|
||||||
# Main application.
|
# Main application.
|
||||||
|
|
||||||
direwolf : direwolf.o aprs_tt.o audio_portaudio.o audio_stats.o ax25_pad.o beacon.o \
|
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 \
|
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 \
|
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 \
|
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.
|
# Optimization for slow processors.
|
||||||
|
|
||||||
demod.o : fsk_fast_filter.h
|
demod.o : fsk_fast_filter.h
|
||||||
|
|
||||||
demod_afsk.o : fsk_fast_filter.h
|
demod_afsk.o : fsk_fast_filter.h
|
||||||
|
|
||||||
|
|
||||||
fsk_fast_filter.h : demod_afsk.c
|
fsk_fast_filter.h : gen_fff
|
||||||
$(CC) $(CFLAGS) -o gen_fff -DGEN_FFF demod_afsk.c dsp.c textcolor.c -lm
|
|
||||||
./gen_fff > fsk_fast_filter.h
|
./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.
|
# UTM, USNG, MGRS conversions.
|
||||||
|
|
||||||
geotranz.a : error_string.o mgrs.o polarst.o tranmerc.o ups.o usng.o utm.o
|
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.
|
# These would be done as ordinary user.
|
||||||
|
|
||||||
# The Raspberry Pi has ~/Desktop but Ubuntu does not.
|
|
||||||
|
|
||||||
# TODO: Handle Linux variations correctly.
|
|
||||||
|
|
||||||
|
|
||||||
.PHONY: install-conf
|
.PHONY: install-conf
|
||||||
install-conf : direwolf.conf
|
install-conf : direwolf.conf
|
||||||
|
@ -406,7 +408,7 @@ install-conf : direwolf.conf
|
||||||
|
|
||||||
# Separate application to decode raw data.
|
# 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
|
$(CC) $(CFLAGS) -DDECAMAIN -o $@ $^ -lm
|
||||||
|
|
||||||
# Convert between text and touch tone representation.
|
# 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
|
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
|
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -lm
|
||||||
|
|
||||||
demod.o : tune.h
|
demod.o : tune.h
|
||||||
|
|
||||||
demod_afsk.o : tune.h
|
demod_afsk.o : tune.h
|
||||||
|
|
||||||
demod_9600.o : tune.h
|
demod_9600.o : tune.h
|
||||||
|
|
||||||
|
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 \
|
testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.o multi_modem.o rrbb.o \
|
||||||
fcs_calc.c ax25_pad.c decode_aprs.c telemetry.c latlong.c symbols.c tune.h textcolor.c
|
fcs_calc.c ax25_pad.c decode_aprs.c telemetry.c latlong.c symbols.c tune.h textcolor.c
|
||||||
$(CC) $(CFLAGS) -o atest $^ -lm
|
$(CC) $(CFLAGS) -o atest $^ -lm
|
||||||
./atest 02_Track_2.wav | grep "packets decoded in" > atest.out
|
./atest 02_Track_2.wav | grep "packets decoded in" > atest.out
|
||||||
|
|
||||||
|
|
||||||
# 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
|
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
|
$(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
|
# 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
|
# $(CC) $(CFLAGS) -o $@ $^ -lm
|
||||||
|
|
||||||
|
@ -513,7 +523,7 @@ depend : $(wildcard *.c)
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
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
|
fsk_fast_filter.h *.o *.a use_this_sdk
|
||||||
echo " " > tune.h
|
echo " " > tune.h
|
||||||
|
|
||||||
|
|
138
Makefile.win
138
Makefile.win
|
@ -28,6 +28,9 @@ AR := ar
|
||||||
|
|
||||||
CFLAGS += -g
|
CFLAGS += -g
|
||||||
|
|
||||||
|
# TODO: Development in progress. Don't try using yet.
|
||||||
|
#CFLAGS += -DNEW14
|
||||||
|
|
||||||
#
|
#
|
||||||
# Let's see impact of various optimization levels.
|
# Let's see impact of various optimization levels.
|
||||||
# Benchmark results with MinGW gcc version 4.6.2.
|
# Benchmark results with MinGW gcc version 4.6.2.
|
||||||
|
@ -61,14 +64,22 @@ CFLAGS += -g
|
||||||
|
|
||||||
# -------------------------------------- Main application --------------------------------
|
# -------------------------------------- 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_9600.o : fsk_demod_state.h
|
||||||
|
|
||||||
demod_afsk.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 \
|
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 \
|
decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
|
||||||
gen_tone.o morse.o audio_win.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o \
|
gen_tone.o morse.o audio_win.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o \
|
||||||
ptt.o beacon.o dwgps.o encode_aprs.o latlong.o textcolor.o \
|
ptt.o beacon.o dwgps.o encode_aprs.o latlong.o textcolor.o \
|
||||||
|
@ -88,10 +99,13 @@ demod.o : fsk_fast_filter.h
|
||||||
demod_afsk.o : fsk_fast_filter.h
|
demod_afsk.o : fsk_fast_filter.h
|
||||||
|
|
||||||
|
|
||||||
fsk_fast_filter.h : demod_afsk.c
|
fsk_fast_filter.h : gen_fff
|
||||||
$(CC) $(CFLAGS) -o gen_fff -DGEN_FFF demod_afsk.c dsp.c textcolor.c
|
|
||||||
./gen_fff > fsk_fast_filter.h
|
./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.
|
# 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.
|
# 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?
|
# Can we encode and decode at popular data rates?
|
||||||
# Verify that single bit fixup increases the count.
|
# Verify that single bit fixup increases the count.
|
||||||
|
|
||||||
check-modem1200 : gen_packets atest
|
check-modem1200 : gen_packets atest
|
||||||
gen_packets -n 100 -o test1.wav
|
gen_packets -n 100 -o test12.wav
|
||||||
atest -F0 -PE -L70 -G71 test1.wav
|
atest -F0 -PE -L64 -G72 test12.wav
|
||||||
atest -F1 -PE -L73 -G75 test1.wav
|
atest -F1 -PE -L70 -G75 test12.wav
|
||||||
#rm test1.wav
|
rm test12.wav
|
||||||
|
|
||||||
check-modem300 : gen_packets atest
|
check-modem300 : gen_packets atest
|
||||||
gen_packets -B300 -n 100 -o test3.wav
|
gen_packets -B300 -n 100 -o test3.wav
|
||||||
atest -B300 -F0 -L68 -G69 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
|
rm test3.wav
|
||||||
|
|
||||||
check-modem9600 : gen_packets atest
|
check-modem9600 : gen_packets atest
|
||||||
gen_packets -B9600 -n 100 -o test9.wav
|
gen_packets -B9600 -n 100 -o test96.wav
|
||||||
atest -B9600 -F0 -L57 -G59 test9.wav
|
sleep 1
|
||||||
atest -B9600 -F1 -L66 -G67 test9.wav
|
atest -B9600 -F0 -L57 -G62 test96.wav
|
||||||
rm test9.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 \
|
dsp.o hdlc_rec.o hdlc_rec2.o multi_modem.o \
|
||||||
rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o \
|
rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o \
|
||||||
dwgpsnmea.o dwgps.o serial_port.o latlong.c \
|
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 -B 9600 z9.wav
|
||||||
#atest za100.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 \
|
rrbb.c fcs_calc.c ax25_pad.c decode_aprs.c latlong.c symbols.c textcolor.c telemetry.c misc.a regex.a \
|
||||||
fsk_fast_filter.h
|
fsk_fast_filter.h
|
||||||
echo " " > tune.h
|
echo " " > tune.h
|
||||||
|
@ -352,17 +393,26 @@ kisstest : kiss_frame.c
|
||||||
rm kisstest.exe
|
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 -------------------------------
|
# ------------------------------ Other manual testing & experimenting -------------------------------
|
||||||
|
|
||||||
|
|
||||||
# For tweaking the demodulator.
|
# For tweaking the demodulator.
|
||||||
|
|
||||||
demod.o : tune.h
|
demod.o : tune.h
|
||||||
demod_9600.o : tune.h
|
demod_9600.o : tune.h
|
||||||
demod_afsk.o : tune.h
|
demod_afsk.o : tune.h
|
||||||
|
demod_psk.o : tune.h
|
||||||
|
|
||||||
|
testagc : atest.c demod.c dsp.c demod_afsk.c demod_psk.c demod_9600.o fsk_demod_agc.h \
|
||||||
testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.o fsk_demod_agc.h \
|
|
||||||
hdlc_rec.o hdlc_rec2.o multi_modem.o \
|
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 \
|
rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o latlong.o symbols.o textcolor.o telemetry.o \
|
||||||
dwgpsnmea.o dwgps.o serial_port.o tt_text.o regex.a misc.a
|
dwgpsnmea.o dwgps.o serial_port.o tt_text.o 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
|
noisy3.wav : gen_packets
|
||||||
./gen_packets -B 300 -n 100 -o noisy3.wav
|
./gen_packets -B 300 -n 100 -o noisy3.wav
|
||||||
|
|
||||||
testagc3 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \
|
testagc3 : atest.c demod.c dsp.c demod_afsk.c demod_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 \
|
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
|
tune.h
|
||||||
rm -f atest.exe
|
rm -f atest3.exe
|
||||||
$(CC) $(CFLAGS) -o atest $^
|
$(CC) $(CFLAGS) -o atest3 $^
|
||||||
./atest -B 300 -P D -D 3 noisy3.wav | grep "packets decoded in" >atest.out
|
./atest3 -B 300 -P D -D 3 noisy3.wav | grep "packets decoded in" >atest.out
|
||||||
echo " " > tune.h
|
echo " " > tune.h
|
||||||
|
|
||||||
|
|
||||||
noisy96.wav : gen_packets
|
noisy96.wav : gen_packets
|
||||||
./gen_packets -B 9600 -n 100 -o noisy96.wav
|
./gen_packets -B 9600 -n 100 -o noisy96.wav
|
||||||
|
|
||||||
testagc9 : atest.c demod.c dsp.c demod_afsk.c demod_9600.c hdlc_rec.c hdlc_rec2.c multi_modem.c \
|
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 \
|
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
|
tune.h
|
||||||
rm -f atest.exe
|
rm -f atest9.exe
|
||||||
$(CC) $(CFLAGS) -o atest $^
|
$(CC) $(CFLAGS) -o atest9 $^
|
||||||
./atest -B 9600 ../walkabout9600.wav | grep "packets decoded in" >atest.out
|
./atest9 -B 9600 ../walkabout9600.wav | grep "packets decoded in" >atest.out
|
||||||
#./atest -B 9600 noisy96.wav | grep "packets decoded in" >atest.out
|
#./atest9 -B 9600 noisy96.wav | grep "packets decoded in" >atest.out
|
||||||
echo " " > tune.h
|
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
|
# Unit test for IGate
|
||||||
|
|
||||||
itest : igate.c textcolor.c ax25_pad.c fcs_calc.c misc.a regex.a
|
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 \
|
ax25_pad.o fcs_calc.o \
|
||||||
xmit.o hdlc_send.o gen_tone.o ptt.o tq.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 \
|
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 \
|
server.o morse.o audio_stats.o dtime_now.o dlq.o \
|
||||||
regex.a misc.a
|
regex.a misc.a
|
||||||
$(CC) $(CFLAGS) -DWALK96 -o $@ $^ -lwinmm -lws2_32
|
$(CC) $(CFLAGS) -DWALK96 -o $@ $^ -lwinmm -lws2_32
|
||||||
|
|
|
@ -1706,7 +1706,7 @@ static void raw_tt_data_to_app (int chan, char *msg)
|
||||||
alevel.mark = -2;
|
alevel.mark = -2;
|
||||||
alevel.space = -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 {
|
else {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
|
84
atest.c
84
atest.c
|
@ -2,7 +2,7 @@
|
||||||
//
|
//
|
||||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2011, 2012, 2013, 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
|
// This program is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
@ -291,35 +291,55 @@ int main (int argc, char *argv[])
|
||||||
case 'B': /* -B for data Bit rate */
|
case 'B': /* -B for data Bit rate */
|
||||||
/* 300 implies 1600/1800 AFSK. */
|
/* 300 implies 1600/1800 AFSK. */
|
||||||
/* 1200 implies 1200/2200 AFSK. */
|
/* 1200 implies 1200/2200 AFSK. */
|
||||||
|
/* 2400 implies V.26 */
|
||||||
/* 9600 implies scrambled. */
|
/* 9600 implies scrambled. */
|
||||||
|
|
||||||
my_audio_config.achan[0].baud = atoi(optarg);
|
my_audio_config.achan[0].baud = atoi(optarg);
|
||||||
|
|
||||||
dw_printf ("Data rate set to %d bits / second.\n", my_audio_config.achan[0].baud);
|
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);
|
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);
|
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) {
|
if (my_audio_config.achan[0].baud < 600) {
|
||||||
my_audio_config.achan[0].modem_type = MODEM_AFSK;
|
my_audio_config.achan[0].modem_type = MODEM_AFSK;
|
||||||
my_audio_config.achan[0].mark_freq = 1600;
|
my_audio_config.achan[0].mark_freq = 1600;
|
||||||
my_audio_config.achan[0].space_freq = 1800;
|
my_audio_config.achan[0].space_freq = 1800;
|
||||||
strlcpy (my_audio_config.achan[0].profiles, "D", sizeof(my_audio_config.achan[0].profiles));
|
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].modem_type = MODEM_SCRAMBLE;
|
||||||
my_audio_config.achan[0].mark_freq = 0;
|
my_audio_config.achan[0].mark_freq = 0;
|
||||||
my_audio_config.achan[0].space_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.
|
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");
|
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;
|
break;
|
||||||
|
|
||||||
case 'P': /* -P for modem profile. */
|
case 'P': /* -P for modem profile. */
|
||||||
|
@ -336,7 +356,7 @@ int main (int argc, char *argv[])
|
||||||
if (decimate < 1 || decimate > 8) {
|
if (decimate < 1 || decimate > 8) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("Unreasonable value for -D.\n");
|
dw_printf ("Unreasonable value for -D.\n");
|
||||||
exit (1);
|
exit (EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
dw_printf ("Divide audio sample rate by %d\n", decimate);
|
dw_printf ("Divide audio sample rate by %d\n", decimate);
|
||||||
my_audio_config.achan[0].decimate = 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) {
|
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);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("Invalid Fix Bits level.\n");
|
dw_printf ("Invalid Fix Bits level.\n");
|
||||||
exit (1);
|
exit (EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -407,7 +427,7 @@ int main (int argc, char *argv[])
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("Couldn't open file for read: %s\n", argv[optind]);
|
dw_printf ("Couldn't open file for read: %s\n", argv[optind]);
|
||||||
//perror ("more info?");
|
//perror ("more info?");
|
||||||
exit (1);
|
exit (EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
start_time = time(NULL);
|
start_time = time(NULL);
|
||||||
|
@ -437,12 +457,12 @@ int main (int argc, char *argv[])
|
||||||
if (strncmp(chunk.id, "fmt ", 4) != 0) {
|
if (strncmp(chunk.id, "fmt ", 4) != 0) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("WAV file error: Found \"%4.4s\" where \"fmt \" was expected.\n", chunk.id);
|
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) {
|
if (chunk.datasize != 16 && chunk.datasize != 18) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("WAV file error: Need fmt chunk datasize of 16 or 18. Found %d.\n", chunk.datasize);
|
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);
|
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) {
|
if (strncmp(wav_data.data, "data", 4) != 0) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("WAV file error: Found \"%4.4s\" where \"data\" was expected.\n", wav_data.data);
|
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.
|
if (format.wformattag != 1) {
|
||||||
assert (format.nchannels == 1 || format.nchannels == 2);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
assert (format.wbitspersample == 8 || format.wbitspersample == 16);
|
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].samples_per_sec = format.nsamplespersec;
|
||||||
my_audio_config.adev[0].bits_per_sample = format.wbitspersample;
|
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));
|
audio_sample = demod_get_sample (ACHAN2ADEV(c));
|
||||||
|
|
||||||
if (audio_sample >= 256 * 256)
|
if (audio_sample >= 256 * 256) {
|
||||||
e_o_f = 1;
|
e_o_f = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (c == 0) sample_number++;
|
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) {
|
if (error_if_less_than != -1 && packets_decoded < error_if_less_than) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("\n * * * TEST FAILED: number decoded is less than %d * * * \n", error_if_less_than);
|
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) {
|
if (error_if_greater_than != -1 && packets_decoded > error_if_greater_than) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("\n * * * TEST FAILED: number decoded is greater than %d * * * \n", error_if_greater_than);
|
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.
|
* 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];
|
char stemp[500];
|
||||||
|
@ -740,7 +776,7 @@ static void usage (void) {
|
||||||
dw_printf ("\n");
|
dw_printf ("\n");
|
||||||
dw_printf (" -0 Use channel 0 (left) of stereo audio (default).\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 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 ("\n");
|
||||||
dw_printf (" wav-file-in is a WAV format audio file.\n");
|
dw_printf (" wav-file-in is a WAV format audio file.\n");
|
||||||
dw_printf ("\n");
|
dw_printf ("\n");
|
||||||
|
|
6
audio.h
6
audio.h
|
@ -101,11 +101,12 @@ struct audio_s {
|
||||||
/* Could all be the same or different. */
|
/* 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. */
|
/* Usual AFSK. */
|
||||||
/* Baseband signal. Not used yet. */
|
/* Baseband signal. Not used yet. */
|
||||||
/* Scrambled http://www.amsat.org/amsat/articles/g3ruh/109/fig03.gif */
|
/* 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. */
|
/* 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 mark_freq; /* Two tones for AFSK modulation, in Hz. */
|
||||||
int space_freq; /* Standard tones are 1200 and 2200 for 1200 baud. */
|
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. */
|
/* 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. */
|
/* Next 3 come from config file or command line. */
|
||||||
|
|
||||||
|
|
72
ax25_pad.c
72
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);
|
(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);
|
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.
|
* 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.
|
* required to know if control is 1 or 2 bytes.
|
||||||
*
|
*
|
||||||
* Outputs: desc - Text description such as "I frame" or
|
* Outputs: desc - Text description such as "I frame" or
|
||||||
* "U frame SABME".
|
* "U frame SABME".
|
||||||
* Supply 16 bytes to be safe.
|
* Supply 16 bytes to be safe.
|
||||||
*
|
*
|
||||||
|
* cr - Command or response?
|
||||||
|
*
|
||||||
* pf - P/F - Poll/Final or -1 if not applicable
|
* pf - P/F - Poll/Final or -1 if not applicable
|
||||||
*
|
*
|
||||||
* nr - N(R) - receive sequence 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.
|
// TODO: need someway to ensure caller allocated enough space.
|
||||||
#define DESC_SIZ 32
|
#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 c; // U frames are always one control byte.
|
||||||
int c2; // I & S frames can have second 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);
|
assert (this_p->magic2 == MAGIC);
|
||||||
|
|
||||||
strlcpy (desc, "????", DESC_SIZ);
|
strlcpy (desc, "????", DESC_SIZ);
|
||||||
|
*cr = cr_11;
|
||||||
*pf = -1;
|
*pf = -1;
|
||||||
*nr = -1;
|
*nr = -1;
|
||||||
*ns = -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);
|
strlcpy (desc, "Not AX.25", DESC_SIZ);
|
||||||
return (frame_not_AX25);
|
return (frame_not_AX25);
|
||||||
}
|
}
|
||||||
if (modulo == modulo_128) {
|
if (this_p->modulo == modulo_128) {
|
||||||
c2 = ax25_get_c2 (this_p);
|
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) {
|
if ((c & 1) == 0) {
|
||||||
|
|
||||||
// Information rrr p sss 0 or sssssss 0 rrrrrrr p
|
// Information rrr p sss 0 or sssssss 0 rrrrrrr p
|
||||||
|
|
||||||
if (modulo == modulo_128) {
|
if (this_p->modulo == modulo_128) {
|
||||||
*ns = (c >> 1) & 0x7f;
|
*ns = (c >> 1) & 0x7f;
|
||||||
*pf = c2 & 1;
|
*pf = c2 & 1;
|
||||||
*nr = (c2 >> 1) & 0x7f;
|
*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;
|
*pf = (c >> 4) & 1;
|
||||||
*nr = (c >> 5) & 7;
|
*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);
|
return (frame_type_I);
|
||||||
}
|
}
|
||||||
else if ((c & 2) == 0) {
|
else if ((c & 2) == 0) {
|
||||||
|
|
||||||
// Supervisory rrr p/f ss 0 1 or 0000 ss 0 1 rrrrrrr p/f
|
// 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;
|
*pf = c2 & 1;
|
||||||
*nr = (c2 >> 1) & 0x7f;
|
*nr = (c2 >> 1) & 0x7f;
|
||||||
}
|
}
|
||||||
|
@ -1862,11 +1883,12 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *
|
||||||
*nr = (c >> 5) & 7;
|
*nr = (c >> 5) & 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
switch ((c >> 2) & 3) {
|
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 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, "S frame RNR, n(r)=%d, p/f=%d", *nr, *pf); return (frame_type_S_RNR); 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, "S frame REJ, n(r)=%d, p/f=%d", *nr, *pf); return (frame_type_S_REJ); 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, "S frame SREJ, n(r)=%d, p/f=%d", *nr, *pf); return (frame_type_S_SREJ); 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 {
|
else {
|
||||||
|
@ -1877,16 +1899,16 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *
|
||||||
|
|
||||||
switch (c & 0xef) {
|
switch (c & 0xef) {
|
||||||
|
|
||||||
case 0x6f: snprintf (desc, DESC_SIZ, "U frame SABME, p=%d", *pf); return (frame_type_U_SABME); 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, "U frame SABM, p=%d", *pf); return (frame_type_U_SABM); 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, "U frame DISC, p=%d", *pf); return (frame_type_U_DISC); 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, "U frame DM, f=%d", *pf); return (frame_type_U_DM); 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, "U frame UA, f=%d", *pf); return (frame_type_U_UA); 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, "U frame FRMR, f=%d", *pf); return (frame_type_U_FRMR); 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, "U frame UI, pf=%d", *pf); return (frame_type_U_UI); 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, "U frame XID, pf=%d", *pf); return (frame_type_U_XID); 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, "U frame TEST, pf=%d", *pf); return (frame_type_U_TEST); 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 frame ???"); return (frame_type_U); 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 */
|
} /* end ax25_frame_type */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------
|
/*------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* Function: ax25_hex_dump
|
* 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);
|
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);
|
snprintf (text, AX25_ALEVEL_TO_TEXT_SIZE, "%d", alevel.rec);
|
||||||
}
|
}
|
||||||
|
|
24
ax25_pad.h
24
ax25_pad.h
|
@ -100,7 +100,8 @@ struct packet_s {
|
||||||
* Changed to 1 when position has been used.
|
* Changed to 1 when position has been used.
|
||||||
*
|
*
|
||||||
* for source & destination it is called
|
* 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.
|
* R R Reserved. Normally set to 1 1.
|
||||||
*
|
*
|
||||||
|
@ -109,6 +110,7 @@ struct packet_s {
|
||||||
* 0 Usually 0 but 1 for last address.
|
* 0 Usually 0 but 1 for last address.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#define SSID_H_MASK 0x80
|
#define SSID_H_MASK 0x80
|
||||||
#define SSID_H_SHIFT 7
|
#define SSID_H_SHIFT 7
|
||||||
|
|
||||||
|
@ -123,6 +125,15 @@ struct packet_s {
|
||||||
|
|
||||||
int frame_len; /* Frame length without CRC. */
|
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];
|
unsigned char frame_data[AX25_MAX_PACKET_LEN+1];
|
||||||
/* Raw frame contents, without the CRC. */
|
/* Raw frame contents, without the CRC. */
|
||||||
|
@ -145,6 +156,7 @@ struct packet_s {
|
||||||
|
|
||||||
typedef struct packet_s *packet_t;
|
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. */
|
#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
|
* APRS always has one control octet of 0x03 but the more
|
||||||
* general AX.25 case is one or two control bytes depending on
|
* general AX.25 case is one or two control bytes depending on
|
||||||
* "modulo 128 operation" is in effect. Unfortunately, it seems
|
* whether "modulo 128 operation" is in effect.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline int ax25_get_control_offset (packet_t this_p)
|
static inline int ax25_get_control_offset (packet_t this_p)
|
||||||
{
|
{
|
||||||
//return (0);
|
|
||||||
return (this_p->num_addr*7);
|
return (this_p->num_addr*7);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int ax25_get_num_control (packet_t this_p)
|
static inline int ax25_get_num_control (packet_t this_p)
|
||||||
{
|
{
|
||||||
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 int ax25_pack (packet_t pp, unsigned char result[AX25_MAX_PACKET_LEN]);
|
||||||
|
|
||||||
extern ax25_frame_type_t ax25_frame_type (packet_t this_p, ax25_modulo_t modulo, char *desc, int *pf, int *nr, int *ns);
|
extern ax25_frame_type_t ax25_frame_type (packet_t this_p, cmdres_t *cr, char *desc, int *pf, int *nr, int *ns);
|
||||||
|
|
||||||
extern void ax25_hex_dump (packet_t this_p);
|
extern void ax25_hex_dump (packet_t this_p);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,889 @@
|
||||||
|
//
|
||||||
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2016 John Langner, WB2OSZ
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: ax25_pad2.c
|
||||||
|
*
|
||||||
|
* Purpose: Packet assembler and disasembler, part 2.
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
*
|
||||||
|
* The original ax25_pad.c was written with APRS in mind.
|
||||||
|
* It handles UI frames and transparency for a KISS TNC.
|
||||||
|
* Here we add new functions that can handle the
|
||||||
|
* more general cases of AX.25 frames.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* * Destination Address (note: opposite order in printed format)
|
||||||
|
*
|
||||||
|
* * Source Address
|
||||||
|
*
|
||||||
|
* * 0-8 Digipeater Addresses
|
||||||
|
* (The AX.25 v2.2 spec reduced this number to
|
||||||
|
* a maximum of 2 but I allow the original 8.)
|
||||||
|
*
|
||||||
|
* Each address is composed of:
|
||||||
|
*
|
||||||
|
* * 6 upper case letters or digits, blank padded.
|
||||||
|
* These are shifted left one bit, leaving the LSB always 0.
|
||||||
|
*
|
||||||
|
* * a 7th octet containing the SSID and flags.
|
||||||
|
* The LSB is always 0 except for the last octet of the address field.
|
||||||
|
*
|
||||||
|
* The final octet of the Destination has the form:
|
||||||
|
*
|
||||||
|
* C R R SSID 0, where,
|
||||||
|
*
|
||||||
|
* C = command/response. Set to 1 for command.
|
||||||
|
* R R = Reserved = 1 1 (See RR note, below)
|
||||||
|
* SSID = substation ID
|
||||||
|
* 0 = zero
|
||||||
|
*
|
||||||
|
* The final octet of the Source has the form:
|
||||||
|
*
|
||||||
|
* C R R SSID 0, where,
|
||||||
|
*
|
||||||
|
* C = command/response. Must be inverse of destination C bit.
|
||||||
|
* R R = Reserved = 1 1 (See RR note, below)
|
||||||
|
* SSID = substation ID
|
||||||
|
* 0 = zero (or 1 if no repeaters)
|
||||||
|
*
|
||||||
|
* The final octet of each repeater has the form:
|
||||||
|
*
|
||||||
|
* H R R SSID 0, where,
|
||||||
|
*
|
||||||
|
* H = has-been-repeated = 0 initially.
|
||||||
|
* Set to 1 after this address has been used.
|
||||||
|
* R R = Reserved = 1 1
|
||||||
|
* SSID = substation ID
|
||||||
|
* 0 = zero (or 1 if last repeater in list)
|
||||||
|
*
|
||||||
|
* A digipeater would repeat this frame if it finds its address
|
||||||
|
* with the "H" bit set to 0 and all earlier repeater addresses
|
||||||
|
* have the "H" bit set to 1.
|
||||||
|
* The "H" bit would be set to 1 in the repeated frame.
|
||||||
|
*
|
||||||
|
* In standard monitoring format, an asterisk is displayed after the last
|
||||||
|
* digipeater with the "H" bit set. That indicates who you are hearing
|
||||||
|
* over the radio.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Next we have:
|
||||||
|
*
|
||||||
|
* * One or two byte Control Field - A U frame always has one control byte.
|
||||||
|
* When using modulo 128 sequence numbers, the
|
||||||
|
* I and S frames can have a second byte allowing
|
||||||
|
* 7 bit fields instead of 3 bit fields.
|
||||||
|
* Unfortunately, we can't tell which we have by looking
|
||||||
|
* at a frame out of context. :-(
|
||||||
|
* If we are one end of the link, we would know this
|
||||||
|
* from SABM/SABME and possible later negotiation
|
||||||
|
* with XID. But if we start monitoring two other
|
||||||
|
* stations that are already conversing, we don't know.
|
||||||
|
*
|
||||||
|
* RR note: It seems that some implementations put a hint
|
||||||
|
* in the "RR" reserved bits.
|
||||||
|
* http://www.tapr.org/pipermail/ax25-layer2/2005-October/000297.html
|
||||||
|
* The RR bits can also be used for "DAMA" which is
|
||||||
|
* some sort of channel access coordination scheme.
|
||||||
|
* http://internet.freepage.de/cgi-bin/feets/freepage_ext/41030x030A/rewrite/hennig/afu/afudoc/afudama.html
|
||||||
|
* Neither is part of the official protocol spec.
|
||||||
|
*
|
||||||
|
* * One byte Protocol ID - Only for I and UI frames.
|
||||||
|
* Normally we would use 0xf0 for no layer 3.
|
||||||
|
*
|
||||||
|
* Finally the Information Field. The initial max size is 256 but it
|
||||||
|
* can be negotiated higher if both ends agree.
|
||||||
|
*
|
||||||
|
* Only these types of frames can have an information part:
|
||||||
|
* - I
|
||||||
|
* - UI
|
||||||
|
* - XID
|
||||||
|
* - TEST
|
||||||
|
* - FRMR
|
||||||
|
*
|
||||||
|
* The 2 byte CRC is not stored here.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Constructors:
|
||||||
|
* ax25_u_frame - Construct a U frame.
|
||||||
|
* ax25_s_frame - Construct a S frame.
|
||||||
|
* ax25_i_frame - Construct a I frame.
|
||||||
|
*
|
||||||
|
* Get methods: .... ???
|
||||||
|
*
|
||||||
|
*------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define AX25_PAD_C /* this will affect behavior of ax25_pad.h */
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
|
||||||
|
#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 */
|
|
@ -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 */
|
||||||
|
|
||||||
|
|
2
beacon.c
2
beacon.c
|
@ -914,7 +914,7 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
|
||||||
/* Simulated reception. */
|
/* Simulated reception. */
|
||||||
|
|
||||||
memset (&alevel, 0xff, sizeof(alevel));
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
29
config.c
29
config.c
|
@ -1116,15 +1116,15 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
||||||
n = atoi(t);
|
n = atoi(t);
|
||||||
if (n >= MIN_BAUD && n <= MAX_BAUD) {
|
if (n >= MIN_BAUD && n <= MAX_BAUD) {
|
||||||
p_audio_config->achan[channel].baud = n;
|
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);
|
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 {
|
else {
|
||||||
p_audio_config->achan[channel].baud = DEFAULT_BAUD;
|
p_audio_config->achan[channel].baud = DEFAULT_BAUD;
|
||||||
text_color_set(DW_COLOR_ERROR);
|
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);
|
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. */
|
/* Set defaults based on speed. */
|
||||||
/* Should be same as -B command line option in direwolf.c. */
|
/* 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) {
|
if (p_audio_config->achan[channel].baud < 600) {
|
||||||
p_audio_config->achan[channel].modem_type = MODEM_AFSK;
|
p_audio_config->achan[channel].modem_type = MODEM_AFSK;
|
||||||
p_audio_config->achan[channel].mark_freq = 1600;
|
p_audio_config->achan[channel].mark_freq = 1600;
|
||||||
p_audio_config->achan[channel].space_freq = 1800;
|
p_audio_config->achan[channel].space_freq = 1800;
|
||||||
}
|
}
|
||||||
else if (p_audio_config->achan[channel].baud > 2400) {
|
else if (p_audio_config->achan[channel].baud < 1800) {
|
||||||
p_audio_config->achan[channel].modem_type = MODEM_SCRAMBLE;
|
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].mark_freq = 0;
|
||||||
p_audio_config->achan[channel].space_freq = 0;
|
p_audio_config->achan[channel].space_freq = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
p_audio_config->achan[channel].modem_type = MODEM_AFSK;
|
p_audio_config->achan[channel].modem_type = MODEM_SCRAMBLE;
|
||||||
p_audio_config->achan[channel].mark_freq = 1200;
|
p_audio_config->achan[channel].mark_freq = 0;
|
||||||
p_audio_config->achan[channel].space_freq = 2200;
|
p_audio_config->achan[channel].space_freq = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get mark frequency. */
|
/* Get mark frequency. */
|
||||||
|
|
|
@ -166,7 +166,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet)
|
||||||
|
|
||||||
A->g_quiet = 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_table = '/'; /* Default to primary table. */
|
||||||
A->g_symbol_code = ' '; /* What should we have for default symbol? */
|
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;
|
break;
|
||||||
|
|
||||||
|
|
||||||
case ':': /* Message */
|
case ':': /* Message: for one person, a group, or a bulletin. */
|
||||||
/* Directed Station Query */
|
/* Directed Station Query */
|
||||||
|
/* Telemetry metadata. */
|
||||||
|
|
||||||
aprs_message (A, pinfo, info_len, quiet);
|
aprs_message (A, pinfo, info_len, quiet);
|
||||||
break;
|
break;
|
||||||
|
@ -1409,22 +1410,45 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
|
||||||
*
|
*
|
||||||
* Function: aprs_message
|
* 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.
|
* Inputs: info - Pointer to Information field.
|
||||||
* ilen - Information field length.
|
* ilen - Information field length.
|
||||||
* quiet - supress error messages.
|
* 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.
|
* Description: An APRS message is a text string with a specifed addressee.
|
||||||
*
|
*
|
||||||
* It's a lot more complicated with different types of addressees
|
* It's a lot more complicated with different types of addressees
|
||||||
* and replies with acknowledgement or rejection.
|
* 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 addressee[9];
|
||||||
char colon; /* : */
|
char colon; /* : */
|
||||||
char message[73]; /* 0-67 characters for message */
|
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. */
|
/* If the first chracter is '?' it is a Directed Station Query. */
|
||||||
} *p;
|
} *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;
|
p = (struct aprs_message_s *)info;
|
||||||
|
|
||||||
strlcpy (A->g_msg_type, "APRS Message", sizeof(A->g_msg_type));
|
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 (ilen < 11) {
|
||||||
if (! quiet) {
|
if (! quiet) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p->colon != ':') {
|
if (p->colon != ':') {
|
||||||
if (! quiet) {
|
if (! quiet) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
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;
|
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));
|
strlcpy (A->g_addressee, addressee, sizeof(A->g_addressee));
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Special message formats contain telemetry metadata.
|
* Special message formats contain telemetry metadata.
|
||||||
* It applies to the addressee, not the sender.
|
* 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) {
|
if (strncmp(p->message,"PARM.",5) == 0) {
|
||||||
snprintf (A->g_msg_type, sizeof(A->g_msg_type), "Telemetry Parameter Name Message for \"%s\"", addressee);
|
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);
|
telemetry_name_message (addressee, p->message+5);
|
||||||
}
|
}
|
||||||
else if (strncmp(p->message,"UNIT.",5) == 0) {
|
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);
|
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);
|
telemetry_unit_label_message (addressee, p->message+5);
|
||||||
}
|
}
|
||||||
else if (strncmp(p->message,"EQNS.",5) == 0) {
|
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);
|
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);
|
telemetry_coefficents_message (addressee, p->message+5, quiet);
|
||||||
}
|
}
|
||||||
else if (strncmp(p->message,"BITS.",5) == 0) {
|
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);
|
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);
|
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] == '?') {
|
else if (p->message[0] == '?') {
|
||||||
|
|
||||||
strlcpy (A->g_msg_type, "Directed Station Query", sizeof(A->g_msg_type));
|
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);
|
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 {
|
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 () */
|
/* No location so don't use process_comment () */
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,9 @@ typedef struct decode_aprs_s {
|
||||||
|
|
||||||
char g_src[AX25_MAX_ADDR_LEN];
|
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 */
|
char g_symbol_table; /* The Symbol Table Identifier character selects one */
|
||||||
/* of the two Symbol Tables, or it may be used as */
|
/* 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 */
|
/* Also for Directed Station Query which is a */
|
||||||
/* special case of message. */
|
/* 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_speed_mph; /* Speed in MPH. */
|
||||||
|
|
||||||
float g_course; /* 0 = North, 90 = East, etc. */
|
float g_course; /* 0 = North, 90 = East, etc. */
|
||||||
|
|
160
demod.c
160
demod.c
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2011, 2012, 2013, 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
|
// This program is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
@ -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 "textcolor.h"
|
||||||
#include "demod_9600.h"
|
#include "demod_9600.h"
|
||||||
#include "demod_afsk.h"
|
#include "demod_afsk.h"
|
||||||
|
#include "demod_psk.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -225,6 +218,7 @@ int demod_init (struct audio_s *pa)
|
||||||
num_letters = 1;
|
num_letters = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
assert (num_letters == strlen(just_letters));
|
assert (num_letters == strlen(just_letters));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -403,7 +397,7 @@ int demod_init (struct audio_s *pa)
|
||||||
D->num_slicers = MAX_SLICERS;
|
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.
|
// TODO: Should probably move this into the init functions.
|
||||||
|
|
||||||
D->quick_attack = D->agc_fast_attack * 0.2;
|
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;
|
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->quick_attack = D->agc_fast_attack * 0.2;
|
||||||
D->sluggish_decay = D->agc_slow_decay * 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;
|
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->quick_attack = D->agc_fast_attack * 0.2;
|
||||||
D->sluggish_decay = D->agc_slow_decay * 0.2;
|
D->sluggish_decay = D->agc_slow_decay * 0.2;
|
||||||
|
@ -521,6 +515,114 @@ int demod_init (struct audio_s *pa)
|
||||||
}
|
}
|
||||||
break;
|
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?
|
//TODO: how about MODEM_OFF case?
|
||||||
|
|
||||||
case MODEM_BASEBAND:
|
case MODEM_BASEBAND:
|
||||||
|
@ -602,7 +704,7 @@ int demod_init (struct audio_s *pa)
|
||||||
D->num_slicers = MAX_SLICERS;
|
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->quick_attack = D->agc_fast_attack * 0.2;
|
||||||
D->sluggish_decay = D->agc_slow_decay * 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)
|
void demod_process_sample (int chan, int subchan, int sam)
|
||||||
{
|
{
|
||||||
float fsam;
|
float fsam;
|
||||||
//float abs_fsam;
|
|
||||||
int k;
|
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;
|
struct demodulator_state_s *D;
|
||||||
|
|
||||||
assert (chan >= 0 && chan < MAX_CHANS);
|
assert (chan >= 0 && chan < MAX_CHANS);
|
||||||
|
@ -803,7 +897,22 @@ void demod_process_sample (int chan, int subchan, int sam)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
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;
|
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.mark = (int) ((D->alevel_mark_peak ) * 100.0f + 0.5f);
|
||||||
alevel.space = (int) ((D->alevel_space_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 {
|
else {
|
||||||
|
|
||||||
|
|
|
@ -756,7 +756,7 @@ int main (void)
|
||||||
emit_macro ("CALC_S_SUM1", ds.ms_filter_size, ds.s_sin_table);
|
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);
|
emit_macro ("CALC_S_SUM2", ds.ms_filter_size, ds.s_cos_table);
|
||||||
|
|
||||||
exit(0);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//#define DEBUG1 1 /* display debugging info */
|
||||||
|
|
||||||
|
//#define DEBUG3 1 /* print carrier detect changes. */
|
||||||
|
|
||||||
|
//#define DEBUG4 1 /* capture PSK demodulator output to log files */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Module: demod_psk.c
|
||||||
|
*
|
||||||
|
* Purpose: Demodulator for Phase Shift Keying (PSK).
|
||||||
|
*
|
||||||
|
* This is my initial attempt at implementing a 2400 bps mode.
|
||||||
|
* The MFJ-2400 & AEA PK232-2400 used V.26 / Bell 201 so I will follow that precedent.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Input: Audio samples from either a file or the "sound card."
|
||||||
|
*
|
||||||
|
* Outputs: Calls hdlc_rec_bit() for each bit demodulated.
|
||||||
|
*
|
||||||
|
* Current Status: New for Version 1.4.
|
||||||
|
*
|
||||||
|
* Don't know if this is correct and/or compatible with
|
||||||
|
* other implementations.
|
||||||
|
* There is a lot of stuff going on here with phase
|
||||||
|
* shifting, gray code, bit order for the dibit, NRZI and
|
||||||
|
* bit-stuffing for HDLC. Plenty of opportunity for
|
||||||
|
* misinterpreting a protocol spec or just stupid mistakes.
|
||||||
|
*
|
||||||
|
* References: MFJ-2400 Product description and manual:
|
||||||
|
*
|
||||||
|
* http://www.mfjenterprises.com/Product.php?productid=MFJ-2400
|
||||||
|
* http://www.mfjenterprises.com/Downloads/index.php?productid=MFJ-2400&filename=MFJ-2400.pdf&company=mfj
|
||||||
|
*
|
||||||
|
* AEA had a 2400 bps packet modem, PK232-2400.
|
||||||
|
*
|
||||||
|
* http://www.repeater-builder.com/aea/pk232/pk232-2400-baud-dpsk-modem.pdf
|
||||||
|
*
|
||||||
|
* There was also a Kantronics KPC-2400 that had 2400 bps.
|
||||||
|
*
|
||||||
|
* http://www.brazoriacountyares.org/winlink-collection/TNC%20manuals/Kantronics/2400_modem_operators_guide@rgf.pdf
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* The MFJ and AEA both use the EXAR XR-2123 PSK modem chip.
|
||||||
|
* The Kantronics has a P423 ???
|
||||||
|
*
|
||||||
|
* Can't find the chip specs on the EXAR website so Google it.
|
||||||
|
*
|
||||||
|
* http://www.komponenten.es.aau.dk/fileadmin/komponenten/Data_Sheet/Linear/XR2123.pdf
|
||||||
|
*
|
||||||
|
* The XR-2123 implements the V.26 / Bell 201 standard:
|
||||||
|
*
|
||||||
|
* https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-V.26-198811-I!!PDF-E&type=items
|
||||||
|
* https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-V.26bis-198811-I!!PDF-E&type=items
|
||||||
|
* https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-V.26ter-198811-I!!PDF-E&type=items
|
||||||
|
*
|
||||||
|
* "bis" and "ter" are from Latin for second and third.
|
||||||
|
* I used the "ter" version which has phase shifts of 0, 90, 180, and 270 degrees.
|
||||||
|
*
|
||||||
|
* There are other references to an alternative B which uses other multiples of 45.
|
||||||
|
* The XR-2123 data sheet mentions only multiples of 90. That's what I went with.
|
||||||
|
*
|
||||||
|
* The XR-2123 does not perform the scrambling as specified in V.26 so I wonder if
|
||||||
|
* the vendors implemented it in software or just left it out.
|
||||||
|
* I left out scrambling for now. Eventually, I'd like to get my hands on an old
|
||||||
|
* 2400 bps TNC for compatibility testing.
|
||||||
|
*
|
||||||
|
* After getting QPSK working, it was not much more effort to add V.27 with 8 phases.
|
||||||
|
*
|
||||||
|
* https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-V.27bis-198811-I!!PDF-E&type=items
|
||||||
|
* https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-V.27ter-198811-I!!PDF-E&type=items
|
||||||
|
*
|
||||||
|
*---------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "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; j<filter_size; j++) {
|
||||||
|
sum += filter[j] * data[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Might replace this with faster, lower precision version someday. */
|
||||||
|
|
||||||
|
static inline float my_atan2f (float y, float x)
|
||||||
|
{
|
||||||
|
if ( y == 0 && x == 0) return (0.0); // different atan2 implementations behave differently.
|
||||||
|
|
||||||
|
return (atan2f(y,x));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: demod_psk_init
|
||||||
|
*
|
||||||
|
* Purpose: Initialization for an psk demodulator.
|
||||||
|
* Select appropriate parameters and set up filters.
|
||||||
|
*
|
||||||
|
* Inputs: modem_type - MODEM_QPSK or MODEM_8PSK.
|
||||||
|
*
|
||||||
|
* samples_per_sec - Audio sample rate.
|
||||||
|
*
|
||||||
|
* bps - Bits per second.
|
||||||
|
* Should be 2400 for V.26 but we don't enforce it.
|
||||||
|
* The carrier frequency will be proportional.
|
||||||
|
*
|
||||||
|
* profile - Select different variations. For QPSK:
|
||||||
|
*
|
||||||
|
* P - Using self-correlation technique.
|
||||||
|
* Q - Same preceded by bandpass filter.
|
||||||
|
* R - Using local oscillator to derive phase.
|
||||||
|
* S - Same with bandpass filter.
|
||||||
|
*
|
||||||
|
* For 8-PSK:
|
||||||
|
*
|
||||||
|
* T, U, V, W same as above.
|
||||||
|
*
|
||||||
|
* D - Pointer to demodulator state for given channel.
|
||||||
|
*
|
||||||
|
* Outputs: D->ms_filter_size
|
||||||
|
*
|
||||||
|
* Returns: None.
|
||||||
|
*
|
||||||
|
* Bugs: This doesn't do much error checking so don't give it
|
||||||
|
* anything crazy.
|
||||||
|
*
|
||||||
|
*----------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void demod_psk_init (enum modem_t modem_type, int samples_per_sec, int bps, char profile, struct demodulator_state_s *D)
|
||||||
|
{
|
||||||
|
int correct_baud; // baud is not same as bits/sec here!
|
||||||
|
int carrier_freq;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
|
||||||
|
memset (D, 0, sizeof(struct demodulator_state_s));
|
||||||
|
|
||||||
|
D->modem_type = modem_type;
|
||||||
|
D->num_slicers = 1; // Haven't thought about this yet. Is it even applicable?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef TUNE_PROFILE
|
||||||
|
profile = TUNE_PROFILE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (modem_type == MODEM_QPSK) {
|
||||||
|
|
||||||
|
correct_baud = bps / 2;
|
||||||
|
// Originally I thought of scaling it to the data rate,
|
||||||
|
// e.g. 2400 bps -> 1800 Hz, but decided to make it a
|
||||||
|
// constant since it is the same for V.26 and V.27.
|
||||||
|
carrier_freq = 1800;
|
||||||
|
|
||||||
|
#if DEBUG1
|
||||||
|
dw_printf ("demod_psk_init QPSK (sample rate=%d, bps=%d, baud=%d, carrier=%d, profile=%c\n",
|
||||||
|
samples_per_sec, bps, correct_baud, carrier_freq, profile);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch (toupper(profile)) {
|
||||||
|
|
||||||
|
case 'P': /* Self correlation technique. */
|
||||||
|
|
||||||
|
D->use_prefilter = 0; /* No bandpass filter. */
|
||||||
|
|
||||||
|
D->lpf_baud = 0.60;
|
||||||
|
D->lp_filter_len_bits = 39. * 1200. / 44100.;
|
||||||
|
D->lp_window = BP_WINDOW_COSINE;
|
||||||
|
|
||||||
|
D->pll_locked_inertia = 0.95;
|
||||||
|
D->pll_searching_inertia = 0.50;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Q': /* Self correlation technique. */
|
||||||
|
|
||||||
|
D->use_prefilter = 1; /* Add a bandpass filter. */
|
||||||
|
D->prefilter_baud = 1.3;
|
||||||
|
D->pre_filter_len_bits = 55. * 1200. / 44100.;
|
||||||
|
D->pre_window = BP_WINDOW_COSINE;
|
||||||
|
|
||||||
|
D->lpf_baud = 0.60;
|
||||||
|
D->lp_filter_len_bits = 39. * 1200. / 44100.;
|
||||||
|
D->lp_window = BP_WINDOW_COSINE;
|
||||||
|
|
||||||
|
D->pll_locked_inertia = 0.87;
|
||||||
|
D->pll_searching_inertia = 0.50;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
text_color_set (DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Invalid demodulator profile %c for v.26 QPSK. Valid choices are P, Q, R, S. Using default.\n", profile);
|
||||||
|
// fall thru.
|
||||||
|
|
||||||
|
case 'R': /* Mix with local oscillator. */
|
||||||
|
|
||||||
|
D->psk_use_lo = 1;
|
||||||
|
|
||||||
|
D->use_prefilter = 0; /* No bandpass filter. */
|
||||||
|
|
||||||
|
D->lpf_baud = 0.70;
|
||||||
|
D->lp_filter_len_bits = 37. * 1200. / 44100.;
|
||||||
|
D->lp_window = BP_WINDOW_TRUNCATED;
|
||||||
|
|
||||||
|
D->pll_locked_inertia = 0.925;
|
||||||
|
D->pll_searching_inertia = 0.50;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'S': /* Mix with local oscillator. */
|
||||||
|
|
||||||
|
D->psk_use_lo = 1;
|
||||||
|
|
||||||
|
D->use_prefilter = 1; /* Add a bandpass filter. */
|
||||||
|
D->prefilter_baud = 0.55;
|
||||||
|
D->pre_filter_len_bits = 74. * 1200. / 44100.;
|
||||||
|
D->pre_window = BP_WINDOW_FLATTOP;
|
||||||
|
|
||||||
|
D->lpf_baud = 0.60;
|
||||||
|
D->lp_filter_len_bits = 39. * 1200. / 44100.;
|
||||||
|
D->lp_window = BP_WINDOW_COSINE;
|
||||||
|
|
||||||
|
D->pll_locked_inertia = 0.925;
|
||||||
|
D->pll_searching_inertia = 0.50;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
D->ms_filter_len_bits = 1.25; // Delay line > 13/12 * symbol period
|
||||||
|
|
||||||
|
D->coffs = (int) round( (11. / 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 */
|
|
@ -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);
|
67
direwolf.c
67
direwolf.c
|
@ -239,7 +239,7 @@ int main (int argc, char *argv[])
|
||||||
text_color_init(t_opt);
|
text_color_init(t_opt);
|
||||||
text_color_set(DW_COLOR_INFO);
|
text_color_set(DW_COLOR_INFO);
|
||||||
//dw_printf ("Dire Wolf version %d.%d (%s) Beta Test\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
|
//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);
|
//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
|
||||||
|
|
||||||
#if defined(ENABLE_GPSD) || defined(USE_HAMLIB)
|
#if defined(ENABLE_GPSD) || defined(USE_HAMLIB)
|
||||||
|
@ -561,22 +561,43 @@ int main (int argc, char *argv[])
|
||||||
if (B_opt != 0) {
|
if (B_opt != 0) {
|
||||||
audio_config.achan[0].baud = B_opt;
|
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) {
|
if (audio_config.achan[0].baud < 600) {
|
||||||
audio_config.achan[0].modem_type = MODEM_AFSK;
|
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].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].modem_type = MODEM_SCRAMBLE;
|
||||||
audio_config.achan[0].mark_freq = 0;
|
audio_config.achan[0].mark_freq = 0;
|
||||||
audio_config.achan[0].space_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;
|
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);
|
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 : */
|
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 APRS we generally want to display non-ASCII to see UTF-8.
|
||||||
// for other, probably want to restrict to ASCII only because we are
|
// for other, probably want to restrict to ASCII only because we are
|
||||||
// more likely to have compressed data than UTF-8 text.
|
// more likely to have compressed data than UTF-8 text.
|
||||||
|
|
||||||
// TODO: Might want to use d_u_opt for transmitted frames too.
|
// 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) );
|
ax25_safe_print ((char *)pinfo, info_len, ( ! ax25_is_aprs(pp)) && ( ! d_u_opt) );
|
||||||
dw_printf ("\n");
|
dw_printf ("\n");
|
||||||
|
|
||||||
|
@ -1068,10 +1105,12 @@ static void usage (char **argv)
|
||||||
dw_printf (" -r n Audio sample rate, per sec.\n");
|
dw_printf (" -r n Audio sample rate, per sec.\n");
|
||||||
dw_printf (" -n n Number of audio channels, 1 or 2.\n");
|
dw_printf (" -n n Number of audio channels, 1 or 2.\n");
|
||||||
dw_printf (" -b n Bits per audio sample, 8 or 16.\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 (" -B n Data rate in bits/sec for channel 0. Standard values are 300, 1200, 2400, 4800, 9600.\n");
|
||||||
dw_printf (" If < 600, AFSK tones are set to 1600 & 1800.\n");
|
dw_printf (" 300 bps defaults to AFSK tones of 1600 & 1800.\n");
|
||||||
dw_printf (" If > 2400, K9NG/G3RUH style encoding is used.\n");
|
dw_printf (" 1200 bps uses AFSK tones of 1200 & 2200.\n");
|
||||||
dw_printf (" Otherwise, AFSK tones are set to 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 n Divide audio sample rate by n for channel 0.\n");
|
||||||
dw_printf (" -d Debug options:\n");
|
dw_printf (" -d Debug options:\n");
|
||||||
dw_printf (" a a = AGWPE network protocol client.\n");
|
dw_printf (" a a = AGWPE network protocol client.\n");
|
||||||
|
|
485
dlq.c
485
dlq.c
|
@ -1,8 +1,7 @@
|
||||||
|
|
||||||
//
|
//
|
||||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
//
|
//
|
||||||
// Copyright (C) 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
|
// This program is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
@ -25,13 +24,17 @@
|
||||||
*
|
*
|
||||||
* Purpose: Received frame queue.
|
* 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.
|
* 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
|
* for each audio device. This queue is used to collect
|
||||||
* received frames from all channels and process them
|
* received frames from all channels and process them
|
||||||
* serially.
|
* 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 <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -39,6 +42,10 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#if __WIN32__
|
||||||
|
#else
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "direwolf.h"
|
#include "direwolf.h"
|
||||||
#include "ax25_pad.h"
|
#include "ax25_pad.h"
|
||||||
|
@ -46,36 +53,11 @@
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
#include "dlq.h"
|
#include "dlq.h"
|
||||||
#include "dedupe.h"
|
#include "dedupe.h"
|
||||||
|
#include "dtime_now.h"
|
||||||
|
|
||||||
|
|
||||||
/* The queue is a linked list of these. */
|
/* 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. */
|
static struct dlq_item_s *queue_head = NULL; /* Head of linked list for queue. */
|
||||||
|
|
||||||
#if __WIN32__
|
#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 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
|
#endif
|
||||||
|
|
||||||
static int dlq_is_empty (void);
|
|
||||||
|
|
||||||
static int was_init = 0; /* was initialization performed? */
|
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)
|
void dlq_init (void)
|
||||||
{
|
{
|
||||||
int c, p;
|
//int c, p;
|
||||||
int err;
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
@ -140,6 +126,7 @@ void dlq_init (void)
|
||||||
#if __WIN32__
|
#if __WIN32__
|
||||||
InitializeCriticalSection (&dlq_cs);
|
InitializeCriticalSection (&dlq_cs);
|
||||||
#else
|
#else
|
||||||
|
int err;
|
||||||
err = pthread_mutex_init (&wake_up_mutex, NULL);
|
err = pthread_mutex_init (&wake_up_mutex, NULL);
|
||||||
err = pthread_mutex_init (&dlq_mutex, NULL);
|
err = pthread_mutex_init (&dlq_mutex, NULL);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
|
@ -192,17 +179,17 @@ void dlq_init (void)
|
||||||
} /* end dlq_init */
|
} /* 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.
|
* Inputs: chan - Channel, 0 is first.
|
||||||
*
|
|
||||||
* chan - Channel, 0 is first.
|
|
||||||
*
|
*
|
||||||
* subchan - Which modem caught it.
|
* subchan - Which modem caught it.
|
||||||
* Special case -1 for APRStt gateway.
|
* Special case -1 for APRStt gateway.
|
||||||
|
@ -224,38 +211,27 @@ void dlq_init (void)
|
||||||
* spectrum - Display of how well multiple decoders did.
|
* 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
|
* IMPORTANT! Don't make an further references to the packet object after
|
||||||
* giving it to dlq_append.
|
* 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 *pnew;
|
||||||
struct dlq_item_s *plast;
|
|
||||||
int err;
|
|
||||||
int queue_length = 0;
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_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
|
#endif
|
||||||
|
|
||||||
if ( ! was_init) {
|
|
||||||
dlq_init ();
|
|
||||||
}
|
|
||||||
|
|
||||||
assert (chan >= 0 && chan < MAX_CHANS);
|
assert (chan >= 0 && chan < MAX_CHANS);
|
||||||
|
|
||||||
if (pp == NULL) {
|
if (pp == NULL) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,16 +239,17 @@ void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp,
|
||||||
|
|
||||||
if (ax25memdebug_get()) {
|
if (ax25memdebug_get()) {
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
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
|
#endif
|
||||||
|
|
||||||
/* Allocate a new queue item. */
|
/* Allocate a new queue item. */
|
||||||
|
|
||||||
pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
|
pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
|
||||||
|
s_new_count++;
|
||||||
|
|
||||||
pnew->nextp = NULL;
|
pnew->nextp = NULL;
|
||||||
pnew->type = type;
|
pnew->type = DLQ_REC_FRAME;
|
||||||
pnew->chan = chan;
|
pnew->chan = chan;
|
||||||
pnew->slice = slice;
|
pnew->slice = slice;
|
||||||
pnew->subchan = subchan;
|
pnew->subchan = subchan;
|
||||||
|
@ -284,17 +261,56 @@ void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp,
|
||||||
else
|
else
|
||||||
strlcpy(pnew->spectrum, spectrum, sizeof(pnew->spectrum));
|
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
|
#if DEBUG1
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
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
|
#endif
|
||||||
#if __WIN32__
|
#if __WIN32__
|
||||||
EnterCriticalSection (&dlq_cs);
|
EnterCriticalSection (&dlq_cs);
|
||||||
#else
|
#else
|
||||||
|
int err;
|
||||||
err = pthread_mutex_lock (&dlq_mutex);
|
err = pthread_mutex_lock (&dlq_mutex);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
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 ("");
|
perror ("");
|
||||||
exit (1);
|
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);
|
err = pthread_mutex_unlock (&dlq_mutex);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
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 ("");
|
perror ("");
|
||||||
exit (1);
|
exit (1);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if DEBUG1
|
#if DEBUG1
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
dw_printf ("dlq_append: left critical section\n");
|
dw_printf ("dlq append_to_queue: left critical section\n");
|
||||||
dw_printf ("dlq_append (): about to wake up recv processing thread.\n");
|
dw_printf ("dlq append_to_queue (): about to wake up recv processing thread.\n");
|
||||||
#endif
|
#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);
|
err = pthread_mutex_lock (&wake_up_mutex);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
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 ("");
|
perror ("");
|
||||||
exit (1);
|
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);
|
err = pthread_cond_signal (&wake_up_cond);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
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 ("");
|
perror ("");
|
||||||
exit (1);
|
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);
|
err = pthread_mutex_unlock (&wake_up_mutex);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
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 ("");
|
perror ("");
|
||||||
exit (1);
|
exit (1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
* Purpose: Sleep while the received data queue is empty rather than
|
||||||
* polling periodically.
|
* 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
|
#if DEBUG1
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
dw_printf ("dlq_wait_while_empty () \n");
|
dw_printf ("dlq_wait_while_empty (%.3f)\n", timeout);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ( ! was_init) {
|
if ( ! was_init) {
|
||||||
|
@ -448,21 +646,34 @@ void dlq_wait_while_empty (void)
|
||||||
|
|
||||||
|
|
||||||
if (queue_head == NULL) {
|
if (queue_head == NULL) {
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_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
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if __WIN32__
|
#if __WIN32__
|
||||||
WaitForSingleObject (wake_up_event, INFINITE);
|
|
||||||
|
|
||||||
|
if (timeout != 0.0) {
|
||||||
|
|
||||||
|
DWORD ms = (timeout - dtime_now()) * 1000;
|
||||||
|
if (ms <= 0) ms = 1;
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
dw_printf ("dlq_wait_while_empty (): returned from wait\n");
|
dw_printf ("WaitForSingleObject: timeout after %d ms\n", ms);
|
||||||
#endif
|
#endif
|
||||||
|
if (WaitForSingleObject (wake_up_event, ms) == WAIT_TIMEOUT) {
|
||||||
|
timed_out_result = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
WaitForSingleObject (wake_up_event, INFINITE);
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
int err;
|
||||||
|
|
||||||
err = pthread_mutex_lock (&wake_up_mutex);
|
err = pthread_mutex_lock (&wake_up_mutex);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
@ -472,20 +683,21 @@ void dlq_wait_while_empty (void)
|
||||||
}
|
}
|
||||||
|
|
||||||
recv_thread_is_waiting = 1;
|
recv_thread_is_waiting = 1;
|
||||||
err = pthread_cond_wait (&wake_up_cond, &wake_up_mutex);
|
if (timeout != 0.0) {
|
||||||
recv_thread_is_waiting = 0;
|
struct timespec abstime;
|
||||||
|
|
||||||
#if DEBUG
|
abstime.tv_sec = (time_t)(long)timeout;
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
abstime.tv_nsec = (long)((timeout - (long)abstime.tv_sec) * 1000000000.0);
|
||||||
dw_printf ("dlq_wait_while_empty (): WOKE UP - returned from cond wait, err = %d\n", err);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (err != 0) {
|
err = pthread_cond_timedwait (&wake_up_cond, &wake_up_mutex, &abstime);
|
||||||
text_color_set(DW_COLOR_ERROR);
|
if (err == ETIMEDOUT) {
|
||||||
dw_printf ("dlq_wait_while_empty: pthread_cond_wait err=%d", err);
|
timed_out_result = 1;
|
||||||
perror ("");
|
}
|
||||||
exit (1);
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
err = pthread_cond_wait (&wake_up_cond, &wake_up_mutex);
|
||||||
|
}
|
||||||
|
recv_thread_is_waiting = 0;
|
||||||
|
|
||||||
err = pthread_mutex_unlock (&wake_up_mutex);
|
err = pthread_mutex_unlock (&wake_up_mutex);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
|
@ -494,17 +706,18 @@ void dlq_wait_while_empty (void)
|
||||||
perror ("");
|
perror ("");
|
||||||
exit (1);
|
exit (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_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
|
#endif
|
||||||
|
return (timed_out_result);
|
||||||
|
|
||||||
|
} /* end dlq_wait_while_empty */
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------
|
/*-------------------------------------------------------------------
|
||||||
|
@ -515,27 +728,17 @@ void dlq_wait_while_empty (void)
|
||||||
*
|
*
|
||||||
* Inputs: None.
|
* Inputs: None.
|
||||||
*
|
*
|
||||||
* Outputs: type - type of queue entry.
|
* Returns: Pointer to a queue item. Caller is responsible for deleting it.
|
||||||
*
|
* NULL if queue is empty.
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
*--------------------------------------------------------------------*/
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
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;
|
struct dlq_item_s *result = NULL;
|
||||||
int result;
|
//int err;
|
||||||
int err;
|
|
||||||
|
|
||||||
#if DEBUG1
|
#if DEBUG1
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
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__
|
#if __WIN32__
|
||||||
EnterCriticalSection (&dlq_cs);
|
EnterCriticalSection (&dlq_cs);
|
||||||
#else
|
#else
|
||||||
|
int err;
|
||||||
|
|
||||||
err = pthread_mutex_lock (&dlq_mutex);
|
err = pthread_mutex_lock (&dlq_mutex);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
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
|
#endif
|
||||||
|
|
||||||
if (queue_head == NULL) {
|
if (queue_head != NULL) {
|
||||||
|
result = queue_head;
|
||||||
*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;
|
|
||||||
queue_head = queue_head->nextp;
|
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__
|
#if __WIN32__
|
||||||
|
@ -602,19 +782,22 @@ int dlq_remove (dlq_type_t *type, int *chan, int *subchan, int *slice, packet_t
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_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
|
#endif
|
||||||
|
|
||||||
#if AX25MEMDEBUG
|
#if AX25MEMDEBUG
|
||||||
|
|
||||||
if (ax25memdebug_get() && result) {
|
if (ax25memdebug_get() && result != NULL) {
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
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
|
#endif
|
||||||
if (result) {
|
|
||||||
free (phead);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (result);
|
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
|
* Inputs: pitem - Pointer to a queue item.
|
||||||
*
|
|
||||||
* Returns: True if nothing in the queue.
|
|
||||||
*
|
*
|
||||||
*--------------------------------------------------------------------*/
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
#if 0
|
|
||||||
static int dlq_is_empty (void)
|
|
||||||
{
|
|
||||||
if (queue_head == NULL) {
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
|
|
||||||
} /* end dlq_is_empty */
|
void dlq_delete (struct dlq_item_s *pitem)
|
||||||
#endif
|
{
|
||||||
|
s_delete_count++;
|
||||||
|
if (pitem->pp != NULL) ax25_delete (pitem->pp)
|
||||||
|
free (pitem);
|
||||||
|
|
||||||
|
} /* end dlq_delete */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* end dlq.c */
|
/* end dlq.c */
|
||||||
|
|
70
dlq.h
70
dlq.h
|
@ -12,17 +12,77 @@
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
|
|
||||||
|
|
||||||
void dlq_init (void);
|
|
||||||
|
|
||||||
/* Types of things that can be in queue. */
|
/* 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
|
#endif
|
||||||
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -45,6 +45,17 @@ These dive into more detail for specialized topics or typical usage scenarios.
|
||||||
digital converter to a Raspberry Pi and transmitting
|
digital converter to a Raspberry Pi and transmitting
|
||||||
a measured voltage.
|
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 ##
|
## Miscellaneous ##
|
||||||
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -4,9 +4,11 @@
|
||||||
|
|
||||||
#include "rpack.h"
|
#include "rpack.h"
|
||||||
|
|
||||||
|
#include "audio.h" // for enum modem_t
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Demodulator state.
|
* 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.
|
* 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.
|
* These are set once during initialization.
|
||||||
*/
|
*/
|
||||||
|
enum modem_t modem_type; // MODEM_AFSK, MODEM_8PSK, etc.
|
||||||
|
|
||||||
char profile; // 'A', 'B', etc. Upper case.
|
char profile; // 'A', 'B', etc. Upper case.
|
||||||
// Only needed to see if we are using 'F' to take fast path.
|
// 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_sin_table[MAX_FILTER_SIZE] __attribute__((aligned(16)));
|
||||||
float s_cos_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.
|
* The rest are continuously updated.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
unsigned int lo_phase; /* Local oscillator for PSK. */
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Most recent raw audio samples, before/after prefiltering.
|
* Most recent raw audio samples, before/after prefiltering.
|
||||||
*/
|
*/
|
||||||
|
|
198
gen_packets.c
198
gen_packets.c
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2011, 2013, 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
|
// This program is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
@ -76,6 +76,17 @@
|
||||||
#include "morse.h"
|
#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 void usage (char **argv);
|
||||||
static int audio_file_open (char *fname, struct audio_s *pa);
|
static int audio_file_open (char *fname, struct audio_s *pa);
|
||||||
static int audio_file_close (void);
|
static int audio_file_close (void);
|
||||||
|
@ -95,7 +106,6 @@ static void send_packet (char *str)
|
||||||
int flen;
|
int flen;
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
|
|
||||||
if (g_morse_wpm > 0) {
|
if (g_morse_wpm > 0) {
|
||||||
|
|
||||||
morse_send (0, str, g_morse_wpm, 100, 100);
|
morse_send (0, str, g_morse_wpm, 100, 100);
|
||||||
|
@ -105,6 +115,31 @@ static void send_packet (char *str)
|
||||||
flen = ax25_pack (pp, fbuf);
|
flen = ax25_pack (pp, fbuf);
|
||||||
for (c=0; c<modem.adev[0].num_channels; c++)
|
for (c=0; c<modem.adev[0].num_channels; c++)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
int samples_per_symbol, n, j;
|
||||||
|
|
||||||
|
/* Insert random amount of quiet time, approx. 0 to 10 symbol times, to test */
|
||||||
|
/* how well the clock recovery PLL can regain lock after random phase shifts. */
|
||||||
|
|
||||||
|
if (modem.achan[c].modem_type == MODEM_QPSK) {
|
||||||
|
samples_per_symbol = modem.adev[0].samples_per_sec / (modem.achan[c].baud / 2);
|
||||||
|
}
|
||||||
|
else if (modem.achan[c].modem_type == MODEM_8PSK) {
|
||||||
|
samples_per_symbol = modem.adev[0].samples_per_sec / (modem.achan[c].baud / 3);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
samples_per_symbol = modem.adev[0].samples_per_sec / modem.achan[c].baud;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for 1200 baud, 44100/sec, this should be 0 to 360.
|
||||||
|
n = samples_per_symbol * 10 * (float)my_rand() / (float)MY_RAND_MAX;
|
||||||
|
|
||||||
|
//dw_printf ("Random 0-360 = %d\n", n);
|
||||||
|
for (j=0; j<n; j++) {
|
||||||
|
gen_tone_put_sample (c, 0, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
hdlc_send_flags (c, 8, 0);
|
hdlc_send_flags (c, 8, 0);
|
||||||
hdlc_send_frame (c, fbuf, flen);
|
hdlc_send_frame (c, fbuf, flen);
|
||||||
hdlc_send_flags (c, 2, 1);
|
hdlc_send_flags (c, 2, 1);
|
||||||
|
@ -123,6 +158,8 @@ int main(int argc, char **argv)
|
||||||
int packet_count = 0;
|
int packet_count = 0;
|
||||||
int i;
|
int i;
|
||||||
int chan;
|
int chan;
|
||||||
|
int experiment = 0;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up default values for the modem.
|
* Set up default values for the modem.
|
||||||
|
@ -174,7 +211,7 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
/* ':' following option character means arg is required. */
|
/* ':' following option character means arg is required. */
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "gm:s:a:b:B:r:n:o:z:82M:",
|
c = getopt_long(argc, argv, "gm:s:a:b:B:r:n:o:z:82M:X",
|
||||||
long_options, &option_index);
|
long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
@ -220,13 +257,38 @@ int main(int argc, char **argv)
|
||||||
exit (EXIT_FAILURE);
|
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) {
|
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;
|
modem.achan[0].space_freq = 1800;
|
||||||
}
|
}
|
||||||
else if (modem.achan[0].baud <= 2400) {
|
else if (modem.achan[0].baud < 1800) {
|
||||||
modem.achan[0].mark_freq = 1200;
|
modem.achan[0].modem_type = MODEM_AFSK;
|
||||||
modem.achan[0].space_freq = 2200;
|
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 {
|
else {
|
||||||
modem.achan[0].modem_type = MODEM_SCRAMBLE;
|
modem.achan[0].modem_type = MODEM_SCRAMBLE;
|
||||||
|
@ -353,6 +415,11 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'X':
|
||||||
|
|
||||||
|
experiment = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
|
|
||||||
/* Unknown option message was already printed. */
|
/* 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);
|
gen_tone_init (&modem, amplitude/2);
|
||||||
morse_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].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);
|
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.
|
* Get user packets(s) from file or stdin if specified.
|
||||||
* "-n" option is ignored in this case.
|
* "-n" option is ignored in this case.
|
||||||
|
@ -467,17 +616,26 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
char stemp[80];
|
char stemp[80];
|
||||||
|
|
||||||
if (modem.achan[0].modem_type == MODEM_SCRAMBLE) {
|
if (modem.achan[0].baud < 600) {
|
||||||
g_noise_level = 0.33 * (amplitude / 200.0) * ((float)i / packet_count);
|
/* e.g. 300 bps AFSK - About 2/3 should be decoded properly. */
|
||||||
}
|
|
||||||
else if (modem.achan[0].baud < 600) {
|
|
||||||
/* About 2/3 should be decoded properly. */
|
|
||||||
g_noise_level = amplitude *.0048 * ((float)i / packet_count);
|
g_noise_level = amplitude *.0048 * ((float)i / packet_count);
|
||||||
}
|
}
|
||||||
else {
|
else if (modem.achan[0].baud < 1800) {
|
||||||
/* About 2/3 should be decoded properly. */
|
/* e.g. 1200 bps AFSK - About 2/3 should be decoded properly. */
|
||||||
g_noise_level = amplitude *.0023 * ((float)i / packet_count);
|
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);
|
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 ("Options:\n");
|
||||||
dw_printf (" -a <number> Signal amplitude in range of 0 - 200%%. Default 50.\n");
|
dw_printf (" -a <number> Signal amplitude in range of 0 - 200%%. Default 50.\n");
|
||||||
dw_printf (" -b <number> Bits / second for data. Default is %d.\n", DEFAULT_BAUD);
|
dw_printf (" -b <number> Bits / second for data. Default is %d.\n", DEFAULT_BAUD);
|
||||||
dw_printf (" -B <number> Bits / second for data. Proper modem selected for 300, 1200, 9600.\n");
|
dw_printf (" -B <number> Bits / second for data. Proper modem selected for 300, 1200, 2400, 4800, 9600.\n");
|
||||||
dw_printf (" -g Scrambled baseband rather than AFSK.\n");
|
dw_printf (" -g Scrambled baseband rather than AFSK.\n");
|
||||||
dw_printf (" -m <number> Mark frequency. Default is %d.\n", DEFAULT_MARK_FREQ);
|
dw_printf (" -m <number> Mark frequency. Default is %d.\n", DEFAULT_MARK_FREQ);
|
||||||
dw_printf (" -s <number> Space frequency. Default is %d.\n", DEFAULT_SPACE_FREQ);
|
dw_printf (" -s <number> 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)
|
int audio_put (int a, int c)
|
||||||
{
|
{
|
||||||
|
|
146
gen_tone.c
146
gen_tone.c
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2011, 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
|
// This program is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
@ -68,18 +68,40 @@ static int ticks_per_bit[MAX_CHANS];
|
||||||
static int f1_change_per_sample[MAX_CHANS];
|
static int f1_change_per_sample[MAX_CHANS];
|
||||||
static int f2_change_per_sample[MAX_CHANS];
|
static int f2_change_per_sample[MAX_CHANS];
|
||||||
|
|
||||||
|
|
||||||
static short sine_table[256];
|
static short sine_table[256];
|
||||||
|
|
||||||
|
|
||||||
/* Accumulators. */
|
/* Accumulators. */
|
||||||
|
|
||||||
static unsigned int tone_phase[MAX_CHANS]; // Phase accumulator for tone generation.
|
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 bit_len_acc[MAX_CHANS]; // To accumulate fractional samples per bit.
|
||||||
|
|
||||||
static int lfsr[MAX_CHANS]; // Shift register for scrambler.
|
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.
|
* 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);
|
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_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;
|
audio_config_p->achan[chan].mark_freq = 1800;
|
||||||
|
audio_config_p->achan[chan].space_freq = audio_config_p->achan[chan].mark_freq; // Not Used.
|
||||||
|
|
||||||
bit_len_acc[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.
|
||||||
|
|
||||||
lfsr[chan] = 0;
|
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.
|
* 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)
|
void tone_gen_put_bit (int chan, int dat)
|
||||||
{
|
{
|
||||||
int a = ACHAN2ADEV(chan); /* device for channel. */
|
int a = ACHAN2ADEV(chan); /* device for channel. */
|
||||||
|
|
||||||
|
|
||||||
assert (save_audio_config_p->achan[chan].valid);
|
assert (save_audio_config_p->achan[chan].valid);
|
||||||
|
|
||||||
|
|
||||||
|
@ -317,6 +373,70 @@ void tone_gen_put_bit (int chan, int dat)
|
||||||
dat = 0;
|
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) {
|
if (save_audio_config_p->achan[chan].modem_type == MODEM_SCRAMBLE) {
|
||||||
int x;
|
int x;
|
||||||
|
|
||||||
|
@ -334,6 +454,14 @@ void tone_gen_put_bit (int chan, int dat)
|
||||||
sam = sine_table[(tone_phase[chan] >> 24) & 0xff];
|
sam = sine_table[(tone_phase[chan] >> 24) & 0xff];
|
||||||
gen_tone_put_sample (chan, a, sam);
|
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 {
|
else {
|
||||||
|
|
||||||
float fsam = dat ? amp16bit : (-amp16bit);
|
float fsam = dat ? amp16bit : (-amp16bit);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2013, 2014, 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
|
// This program is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
@ -70,6 +70,7 @@
|
||||||
* Set limit on number of packets in fix up later queue.
|
* Set limit on number of packets in fix up later queue.
|
||||||
*
|
*
|
||||||
*------------------------------------------------------------------*/
|
*------------------------------------------------------------------*/
|
||||||
|
|
||||||
//#define DEBUG 1
|
//#define DEBUG 1
|
||||||
#define DIGIPEATER_C
|
#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];
|
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__);
|
dw_printf("Internal error, chan=%d, %s, %d\n", chan, __FILE__, __LINE__);
|
||||||
save_audio_config_p->achan[chan].baud = DEFAULT_BAUD;
|
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;
|
//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 &&
|
if (save_audio_config_p->achan[chan].num_subchan == 1 &&
|
||||||
save_audio_config_p->achan[chan].num_slicers == 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -641,7 +649,7 @@ static void pick_best_candidate (int chan)
|
||||||
j = subchan_from_n(best_n);
|
j = subchan_from_n(best_n);
|
||||||
k = slice_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].packet_p,
|
||||||
candidate[chan][j][k].alevel,
|
candidate[chan][j][k].alevel,
|
||||||
(int)(candidate[chan][j][k].retries),
|
(int)(candidate[chan][j][k].retries),
|
||||||
|
|
114
recv.c
114
recv.c
|
@ -2,7 +2,7 @@
|
||||||
//
|
//
|
||||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
//
|
//
|
||||||
// Copyright (C) 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
|
// This program is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
@ -80,6 +80,8 @@
|
||||||
*
|
*
|
||||||
*---------------------------------------------------------------*/
|
*---------------------------------------------------------------*/
|
||||||
|
|
||||||
|
//#define DEBUG 1
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
@ -104,7 +106,10 @@
|
||||||
#include "recv.h"
|
#include "recv.h"
|
||||||
#include "dtmf.h"
|
#include "dtmf.h"
|
||||||
#include "aprs_tt.h"
|
#include "aprs_tt.h"
|
||||||
|
#include "dtime_now.h"
|
||||||
|
#if NEW14
|
||||||
|
#include "ax25_link.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if __WIN32__
|
#if __WIN32__
|
||||||
static unsigned __stdcall recv_adev_thread (void *arg);
|
static unsigned __stdcall recv_adev_thread (void *arg);
|
||||||
|
@ -283,40 +288,101 @@ static void * recv_adev_thread (void *arg)
|
||||||
void recv_process (void)
|
void recv_process (void)
|
||||||
{
|
{
|
||||||
|
|
||||||
int ok;
|
struct dlq_item_s *pitem;
|
||||||
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];
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
dlq_wait_while_empty ();
|
int timed_out;
|
||||||
#if DEBUG
|
#if NEW14
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
double timeout_value = ax25_link_get_next_timer_expiry();
|
||||||
dw_printf ("recv_process: woke up\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ok = dlq_remove (&type, &chan, &subchan, &slice, &pp, &alevel, &retries, spectrum, sizeof(spectrum));
|
|
||||||
|
timed_out = dlq_wait_while_empty (timeout_value);
|
||||||
|
#else
|
||||||
|
dlq_wait_while_empty (0.0);
|
||||||
|
timed_out = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
dw_printf ("recv_process: dlq_remove() returned ok=%d, type=%d, chan=%d, pp=%p\n",
|
dw_printf ("recv_process: woke up, timed_out=%d\n", timed_out);
|
||||||
ok, (int)type, chan, pp);
|
#endif
|
||||||
|
|
||||||
|
if (timed_out) {
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("recv_process: time waiting on dlq. call dl_timer_expiry.\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if NEW14
|
||||||
|
dl_timer_expiry ();
|
||||||
#endif
|
#endif
|
||||||
if (ok) {
|
|
||||||
app_process_rec_packet (chan, subchan, slice, pp, alevel, retries, spectrum);
|
|
||||||
}
|
}
|
||||||
#if DEBUG
|
|
||||||
else {
|
else {
|
||||||
|
|
||||||
|
pitem = dlq_remove ();
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_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
|
#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 */
|
} /* end recv_process */
|
||||||
|
|
54
server.c
54
server.c
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2011, 2012, 2013, 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
|
// This program is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
@ -59,6 +59,17 @@
|
||||||
*
|
*
|
||||||
* 'y' Ask Outstanding frames waiting on a Port (new in 1.2)
|
* '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.
|
* A message is printed if any others are received.
|
||||||
*
|
*
|
||||||
* TODO: Should others be implemented?
|
* TODO: Should others be implemented?
|
||||||
|
@ -81,6 +92,12 @@
|
||||||
*
|
*
|
||||||
* 'y' Outstanding frames waiting on a Port (new in 1.2)
|
* '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
|
* References: AGWPE TCP/IP API Tutorial
|
||||||
|
@ -135,6 +152,7 @@
|
||||||
#include "textcolor.h"
|
#include "textcolor.h"
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
#include "server.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 num_calls = 2; /* 2 plus any digipeaters. */
|
||||||
int pid = 0xf0; /* normal for AX.25 I frames. */
|
int pid = 0xf0; /* normal for AX.25 I frames. */
|
||||||
int j;
|
int j;
|
||||||
char stemp[256];
|
|
||||||
|
|
||||||
strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
|
strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
|
||||||
strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE]));
|
strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_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);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("\n");
|
dw_printf ("\n");
|
||||||
dw_printf ("Can't process command '%c' from AGW client app %d.\n", cmd.hdr.datakind, client);
|
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");
|
dw_printf ("Connected packet mode is not implemented.\n");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
case 'D': /* Send Connected Data */
|
case 'D': /* Send Connected Data */
|
||||||
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
{
|
||||||
dw_printf ("\n");
|
char callsigns[2][AX25_MAX_ADDR_LEN];
|
||||||
dw_printf ("Can't process command '%c' from AGW client app %d.\n", cmd.hdr.datakind, client);
|
const int num_calls = 2;
|
||||||
dw_printf ("Connected packet mode is not implemented.\n");
|
|
||||||
|
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;
|
break;
|
||||||
|
|
||||||
case 'd': /* Disconnect, Terminate an AX.25 Connection */
|
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);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("\n");
|
dw_printf ("\n");
|
||||||
dw_printf ("Can't process command '%c' from AGW client app %d.\n", cmd.hdr.datakind, client);
|
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");
|
dw_printf ("Connected packet mode is not implemented.\n");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
4
server.h
4
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);
|
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 */
|
/* end server.h */
|
||||||
|
|
184
xid.c
184
xid.c
|
@ -23,13 +23,24 @@
|
||||||
*
|
*
|
||||||
* Module: xid.c
|
* 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 <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "direwolf.h"
|
||||||
#include "textcolor.h"
|
#include "textcolor.h"
|
||||||
//#include "xid.h"
|
//#include "xid.h"
|
||||||
|
|
||||||
|
@ -47,17 +59,17 @@ struct ax25_param_s {
|
||||||
int full_duplex;
|
int full_duplex;
|
||||||
|
|
||||||
// Order is important because negotiation keeps the lower.
|
// 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;
|
enum modulo_e {modulo_8 = 8, modulo_128 = 128} modulo;
|
||||||
|
|
||||||
int i_field_length_rx; /* In bytes. XID has it in bits. */
|
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 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: ...
|
* 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)
|
int xid_parse (unsigned char *info, int info_len, struct ax25_param_s *result)
|
||||||
{
|
{
|
||||||
unsigned char *p;
|
unsigned char *p;
|
||||||
int group_len;
|
int group_len;
|
||||||
|
char stemp[64];
|
||||||
|
char debug_msg[256];
|
||||||
|
|
||||||
|
|
||||||
result->full_duplex = 0;
|
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->rej = selective_reject;
|
||||||
|
|
||||||
result->modulo = modulo_8;
|
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->ack_timer = 3000;
|
||||||
result->retries = 10;
|
result->retries = 10;
|
||||||
|
|
||||||
p = info;
|
p = info;
|
||||||
|
|
||||||
if (*p != FI_Format_Indicator) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
p++;
|
p++;
|
||||||
|
|
||||||
if (*p != GI_Group_Identifier) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
p++;
|
p++;
|
||||||
|
@ -141,6 +171,11 @@ int xid_parse (unsigned char *info, int info_len, struct ax25_param_s *result)
|
||||||
|
|
||||||
pind = *p++;
|
pind = *p++;
|
||||||
plen = *p++; // should have sanity checking
|
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;
|
pval = 0;
|
||||||
for (j=0; j<plen; j++) {
|
for (j=0; j<plen; j++) {
|
||||||
pval = (pval << 8) + *p++;
|
pval = (pval << 8) + *p++;
|
||||||
|
@ -223,21 +258,20 @@ int xid_parse (unsigned char *info, int info_len, struct ax25_param_s *result)
|
||||||
|
|
||||||
if (pval & 0x7) {
|
if (pval & 0x7) {
|
||||||
text_color_set (DW_COLOR_ERROR);
|
text_color_set (DW_COLOR_ERROR);
|
||||||
dw_printf ("XID error: I Field Length Rx is not a whole number of bytes.\n");
|
dw_printf ("XID error: I Field Length Rx, %d, is not a whole number of bytes.\n", pval);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PI_Window_Size_Rx:
|
case PI_Window_Size_Rx:
|
||||||
|
|
||||||
result->window_size = pval;
|
result->window_size_rx = pval;
|
||||||
|
|
||||||
// TODO must be 1-7 for modulo 8 or 1-127 for modulo 128;
|
if (pval < 1 || pval >= result->modulo) {
|
||||||
|
text_color_set (DW_COLOR_ERROR);
|
||||||
// if (pval & 0x7) {
|
dw_printf ("XID error: Window Size Rx, %d, is not in range of 1 thru %d.\n", pval, result->modulo-1);
|
||||||
// text_color_set (DW_COLOR_ERROR);
|
result->window_size_rx = result->modulo == modulo_128 ? 32 : 7;
|
||||||
// dw_printf ("XID error: Window Size Rx is not in range of 1 thru ???\n");
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
//continue here.
|
//continue here.
|
||||||
|
|
||||||
|
@ -252,7 +286,7 @@ int xid_parse (unsigned char *info, int info_len, struct ax25_param_s *result)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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");
|
dw_printf ("XID error: Frame / Group Length mismatch.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return (1);
|
||||||
|
|
||||||
} /* end xid_parse */
|
} /* end xid_parse */
|
||||||
|
|
||||||
|
@ -270,16 +304,60 @@ int xid_parse (unsigned char *info, int info_len, struct ax25_param_s *result)
|
||||||
*
|
*
|
||||||
* Name: xid_encode
|
* 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.
|
* Outputs: info - Information part of XID frame.
|
||||||
* Does not include the control byte.
|
* 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 >> 8) & 0xff;
|
||||||
*p++ = x & 0xff;
|
*p++ = x & 0xff;
|
||||||
|
|
||||||
*p++ = PI_I_Field_Length_Rx;
|
if (param->i_field_length_rx != G_UNKNOWN) {
|
||||||
*p++ = 2;
|
*p++ = PI_I_Field_Length_Rx;
|
||||||
x = param->i_field_length_rx * 8;
|
*p++ = 2;
|
||||||
*p++ = (x >> 8) & 0xff;
|
x = param->i_field_length_rx * 8;
|
||||||
*p++ = x & 0xff;
|
*p++ = (x >> 8) & 0xff;
|
||||||
|
*p++ = x & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
*p++ = PI_Window_Size_Rx;
|
if (param->window_size_rx != G_UNKNOWN) {
|
||||||
*p++ = 1;
|
*p++ = PI_Window_Size_Rx;
|
||||||
*p++ = param->window_size;
|
*p++ = 1;
|
||||||
|
*p++ = param->window_size_rx;
|
||||||
|
}
|
||||||
|
|
||||||
*p++ = PI_Ack_Timer;
|
if (param->ack_timer != G_UNKNOWN) {
|
||||||
*p++ = 2;
|
*p++ = PI_Ack_Timer;
|
||||||
*p++ = param->ack_timer >> 8;
|
*p++ = 2;
|
||||||
*p++ = param->ack_timer & 0xff;
|
*p++ = (param->ack_timer >> 8) & 0xff;
|
||||||
|
*p++ = param->ack_timer & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
*p++ = PI_Retries;
|
if (param->retries != G_UNKNOWN) {
|
||||||
*p++ = 1;
|
*p++ = PI_Retries;
|
||||||
*p++ = param->retries;
|
*p++ = 1;
|
||||||
|
*p++ = param->retries;
|
||||||
|
}
|
||||||
|
|
||||||
len = p - info;
|
len = p - info;
|
||||||
assert (len == 27);
|
assert (len <= 27);
|
||||||
return (len);
|
return (len);
|
||||||
|
|
||||||
} /* end xid_encode */
|
} /* end xid_encode */
|
||||||
|
@ -368,7 +454,7 @@ int xid_encode (struct ax25_param_s *param, unsigned char *info)
|
||||||
*
|
*
|
||||||
* Description: Run with:
|
* Description: Run with:
|
||||||
*
|
*
|
||||||
* gcc -DTEST -g xid.c textcolor.c ; ./a
|
* gcc -DXIDTEST -g xid.c textcolor.o && ./a
|
||||||
*
|
*
|
||||||
* Result should be:
|
* 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 */
|
/* 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. */
|
/* This is the info part after a control byte of 0xAF. */
|
||||||
|
@ -417,7 +503,7 @@ static unsigned char example[27] = {
|
||||||
/* PV */ 0x00, /* */
|
/* PV */ 0x00, /* */
|
||||||
/* PI */ 0x0A, /* Parameter Indicator - Retries (N1) */
|
/* PI */ 0x0A, /* Parameter Indicator - Retries (N1) */
|
||||||
/* PL */ 0x01, /* Parameter Length */
|
/* PL */ 0x01, /* Parameter Length */
|
||||||
/* PV */ 0x03 /* Parameter Variable - 3 ret */
|
/* PV */ 0x03 /* Parameter Variable - 3 retries */
|
||||||
};
|
};
|
||||||
|
|
||||||
int main (int argc, char *argv[]) {
|
int main (int argc, char *argv[]) {
|
||||||
|
@ -438,7 +524,7 @@ int main (int argc, char *argv[]) {
|
||||||
assert (param.rej == selective_reject_reject);
|
assert (param.rej == selective_reject_reject);
|
||||||
assert (param.modulo == modulo_128);
|
assert (param.modulo == modulo_128);
|
||||||
assert (param.i_field_length_rx == 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.ack_timer == 4096);
|
||||||
assert (param.retries == 3);
|
assert (param.retries == 3);
|
||||||
|
|
||||||
|
@ -461,7 +547,7 @@ int main (int argc, char *argv[]) {
|
||||||
param.rej = implicit_reject;
|
param.rej = implicit_reject;
|
||||||
param.modulo = modulo_8;
|
param.modulo = modulo_8;
|
||||||
param.i_field_length_rx = 2048;
|
param.i_field_length_rx = 2048;
|
||||||
param.window_size = 3;
|
param.window_size_rx = 3;
|
||||||
param.ack_timer = 3000;
|
param.ack_timer = 3000;
|
||||||
param.retries = 10;
|
param.retries = 10;
|
||||||
|
|
||||||
|
@ -472,7 +558,7 @@ int main (int argc, char *argv[]) {
|
||||||
assert (param2.rej == implicit_reject);
|
assert (param2.rej == implicit_reject);
|
||||||
assert (param2.modulo == modulo_8);
|
assert (param2.modulo == modulo_8);
|
||||||
assert (param2.i_field_length_rx == 2048);
|
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.ack_timer == 3000);
|
||||||
assert (param2.retries == 10);
|
assert (param2.retries == 10);
|
||||||
|
|
||||||
|
@ -482,7 +568,7 @@ int main (int argc, char *argv[]) {
|
||||||
param.rej = selective_reject;
|
param.rej = selective_reject;
|
||||||
param.modulo = modulo_8;
|
param.modulo = modulo_8;
|
||||||
param.i_field_length_rx = 256;
|
param.i_field_length_rx = 256;
|
||||||
param.window_size = 4;
|
param.window_size_rx = 4;
|
||||||
param.ack_timer = 3000;
|
param.ack_timer = 3000;
|
||||||
param.retries = 10;
|
param.retries = 10;
|
||||||
|
|
||||||
|
@ -493,11 +579,11 @@ int main (int argc, char *argv[]) {
|
||||||
assert (param2.rej == selective_reject);
|
assert (param2.rej == selective_reject);
|
||||||
assert (param2.modulo == modulo_8);
|
assert (param2.modulo == modulo_8);
|
||||||
assert (param2.i_field_length_rx == 256);
|
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.ack_timer == 3000);
|
||||||
assert (param2.retries == 10);
|
assert (param2.retries == 10);
|
||||||
|
|
||||||
text_color_set (DW_COLOR_INFO);
|
text_color_set (DW_COLOR_REC);
|
||||||
dw_printf ("XID test: Success.\n");
|
dw_printf ("XID test: Success.\n");
|
||||||
|
|
||||||
exit (0);
|
exit (0);
|
||||||
|
|
19
xmit.c
19
xmit.c
|
@ -603,6 +603,22 @@ static void xmit_ax25_frames (int c, int p, packet_t pp)
|
||||||
text_color_set(DW_COLOR_XMIT);
|
text_color_set(DW_COLOR_XMIT);
|
||||||
dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L');
|
dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L');
|
||||||
dw_printf ("%s", stemp); /* stations followed by : */
|
dw_printf ("%s", stemp); /* stations followed by : */
|
||||||
|
|
||||||
|
/* 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));
|
ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp));
|
||||||
dw_printf ("\n");
|
dw_printf ("\n");
|
||||||
(void)ax25_check_addresses (pp);
|
(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 ();
|
time_ptt = dtime_now ();
|
||||||
|
|
||||||
|
// TODO: This was written assuming bits/sec = baud.
|
||||||
|
// Does it is need to be scaled differently for PSK?
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_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]);
|
dw_printf ("xmit_thread: Turn on PTT now for channel %d. speed = %d\n", c, xmit_bits_per_sec[c]);
|
||||||
|
|
Loading…
Reference in New Issue