mirror of https://github.com/wb2osz/direwolf.git
New "-g" option for direwolf and atest to force G3RUH modem and override
default for the speed. atest -h will display frame as hexadecimal bytes.
This commit is contained in:
parent
c7dcfd141e
commit
e3dc8bbf1b
|
@ -735,7 +735,7 @@ install-rpi :
|
||||||
# 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 pad2test xidtest dtmftest check-modem1200 check-modem300 check-modem9600 check-modem19200 check-modem2400 check-modem4800
|
check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest pad2test xidtest dtmftest check-modem1200 check-modem300 check-modem9600 check-modem19200 check-modem2400 check-modem2400-g check-modem4800
|
||||||
|
|
||||||
# Can we encode and decode at popular data rates?
|
# Can we encode and decode at popular data rates?
|
||||||
|
|
||||||
|
@ -753,14 +753,14 @@ check-modem300 : gen_packets atest
|
||||||
|
|
||||||
check-modem9600 : gen_packets atest
|
check-modem9600 : gen_packets atest
|
||||||
./gen_packets -B9600 -n 100 -o /tmp/test96.wav
|
./gen_packets -B9600 -n 100 -o /tmp/test96.wav
|
||||||
./atest -B9600 -F0 -L50 -G54 /tmp/test96.wav
|
./atest -B9600 -F0 -L61 -G65 /tmp/test96.wav
|
||||||
./atest -B9600 -F1 -L55 -G59 /tmp/test96.wav
|
./atest -B9600 -F1 -L62 -G66 /tmp/test96.wav
|
||||||
rm /tmp/test96.wav
|
rm /tmp/test96.wav
|
||||||
|
|
||||||
check-modem19200 : gen_packets atest
|
check-modem19200 : gen_packets atest
|
||||||
./gen_packets -r 96000 -B19200 -n 100 -o /tmp/test19.wav
|
./gen_packets -r 96000 -B19200 -n 100 -o /tmp/test19.wav
|
||||||
./atest -B19200 -F0 -L55 -G59 /tmp/test19.wav
|
./atest -B19200 -F0 -L60 -G64 /tmp/test19.wav
|
||||||
./atest -B19200 -F1 -L60 -G64 /tmp/test19.wav
|
./atest -B19200 -F1 -L64 -G68 /tmp/test19.wav
|
||||||
rm /tmp/test19.wav
|
rm /tmp/test19.wav
|
||||||
|
|
||||||
check-modem2400 : gen_packets atest
|
check-modem2400 : gen_packets atest
|
||||||
|
@ -769,6 +769,11 @@ check-modem2400 : gen_packets atest
|
||||||
./atest -B2400 -F1 -L80 -G87 /tmp/test24.wav
|
./atest -B2400 -F1 -L80 -G87 /tmp/test24.wav
|
||||||
rm /tmp/test24.wav
|
rm /tmp/test24.wav
|
||||||
|
|
||||||
|
check-modem2400-g : gen_packets atest
|
||||||
|
./gen_packets -B2400 -g -n 100 -o /tmp/test24-g.wav
|
||||||
|
./atest -B2400 -g -F0 -L99 -G100 /tmp/test24-g.wav
|
||||||
|
rm /tmp/test24-g.wav
|
||||||
|
|
||||||
check-modem4800 : gen_packets atest
|
check-modem4800 : gen_packets atest
|
||||||
./gen_packets -B2400 -n 100 -o /tmp/test48.wav
|
./gen_packets -B2400 -n 100 -o /tmp/test48.wav
|
||||||
./atest -B2400 -F0 -L70 -G79 /tmp/test48.wav
|
./atest -B2400 -F0 -L70 -G79 /tmp/test48.wav
|
||||||
|
|
40
Makefile.win
40
Makefile.win
|
@ -16,7 +16,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets atest ttcalc tnctest kissutil
|
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets atest ttcalc tnctest tnctest-issue-132 kissutil
|
||||||
|
|
||||||
|
|
||||||
# People say we need -mthreads option for threads to work properly.
|
# People say we need -mthreads option for threads to work properly.
|
||||||
|
@ -197,13 +197,13 @@ gen_packets : gen_packets.o ax25_pad.o hdlc_send.o fcs_calc.o gen_tone.o morse.
|
||||||
$(CC) $(CFLAGS) -o $@ $^
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
|
||||||
# Connected mode packet application server.
|
|
||||||
|
|
||||||
appserver : appserver.o textcolor.o ax25_pad.o fcs_calc.o misc.a
|
# Connected mode sample applications for talking to network TNC with AGW protocol.
|
||||||
|
|
||||||
|
appserver : appserver.o agwlib.o dwsock.o textcolor.o dtime_now.o misc.a
|
||||||
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
|
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------- Libraries --------------------------------------------
|
# ------------------------------------------- Libraries --------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@ -277,7 +277,7 @@ 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 pad2test xidtest dtmftest check-modem1200 check-modem300 check-modem9600 check-modem19200 check-modem2400 check-modem4800
|
check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest pad2test xidtest dtmftest check-modem1200 check-modem300 check-modem9600 check-modem19200 check-modem2400 check-modem2400-g 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.
|
||||||
|
@ -305,8 +305,8 @@ check-modem9600 : gen_packets atest
|
||||||
sleep 1
|
sleep 1
|
||||||
gen_packets -B9600 -n 100 -o test96.wav
|
gen_packets -B9600 -n 100 -o test96.wav
|
||||||
sleep 1
|
sleep 1
|
||||||
atest -B9600 -F0 -L50 -G54 test96.wav
|
atest -B9600 -F0 -L61 -G65 test96.wav
|
||||||
atest -B9600 -F1 -L55 -G59 test96.wav
|
atest -B9600 -F1 -L62 -G66 test96.wav
|
||||||
sleep 1
|
sleep 1
|
||||||
rm test96.wav
|
rm test96.wav
|
||||||
|
|
||||||
|
@ -319,8 +319,8 @@ check-modem19200 : gen_packets atest
|
||||||
sleep 1
|
sleep 1
|
||||||
gen_packets -r 96000 -B19200 -n 100 -o test19.wav
|
gen_packets -r 96000 -B19200 -n 100 -o test19.wav
|
||||||
sleep 1
|
sleep 1
|
||||||
atest -B19200 -F0 -L55 -G59 test19.wav
|
atest -B19200 -F0 -L60 -G64 test19.wav
|
||||||
atest -B19200 -F1 -L60 -G64 test19.wav
|
atest -B19200 -F1 -L64 -G68 test19.wav
|
||||||
sleep 1
|
sleep 1
|
||||||
rm test19.wav
|
rm test19.wav
|
||||||
|
|
||||||
|
@ -332,6 +332,13 @@ check-modem2400 : gen_packets atest
|
||||||
sleep 1
|
sleep 1
|
||||||
rm test24.wav
|
rm test24.wav
|
||||||
|
|
||||||
|
check-modem2400-g : gen_packets atest
|
||||||
|
gen_packets -B2400 -g -n 100 -o test24-g.wav
|
||||||
|
sleep 1
|
||||||
|
atest -B2400 -g -F0 -L99 -G100 test24-g.wav
|
||||||
|
sleep 1
|
||||||
|
rm test24-g.wav
|
||||||
|
|
||||||
check-modem4800 : gen_packets atest
|
check-modem4800 : gen_packets atest
|
||||||
gen_packets -B4800 -n 100 -o test48.wav
|
gen_packets -B4800 -n 100 -o test48.wav
|
||||||
sleep 1
|
sleep 1
|
||||||
|
@ -467,6 +474,9 @@ dtmftest : dtmf.c textcolor.o
|
||||||
tnctest : tnctest.c textcolor.o dtime_now.o serial_port.o misc.a
|
tnctest : tnctest.c textcolor.o dtime_now.o serial_port.o misc.a
|
||||||
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
|
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
|
||||||
|
|
||||||
|
tnctest-issue-132 : tnctest-issue-132.c textcolor.o dtime_now.o serial_port.o misc.a
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
|
||||||
|
|
||||||
|
|
||||||
# For tweaking the demodulator.
|
# For tweaking the demodulator.
|
||||||
|
|
||||||
|
@ -481,7 +491,7 @@ testagc : atest.c demod.c dsp.c demod_afsk.c demod_psk.c demod_9600.o fsk_demod_
|
||||||
dwgpsnmea.o dwgps.o serial_port.o tt_text.o dtime_now.o regex.a misc.a
|
dwgpsnmea.o dwgps.o serial_port.o tt_text.o dtime_now.o regex.a misc.a
|
||||||
rm -f atest.exe
|
rm -f atest.exe
|
||||||
$(CC) $(CFLAGS) -o atest $^
|
$(CC) $(CFLAGS) -o atest $^
|
||||||
./atest -P GGG- -F 0 ../02_Track_2.wav | grep "packets decoded in" >atest.out
|
./atest -P H+ -F 0 ../01_Track_1.wav ../02_Track_2.wav | grep "packets decoded in" >atest.out
|
||||||
echo " " > tune.h
|
echo " " > tune.h
|
||||||
|
|
||||||
|
|
||||||
|
@ -509,6 +519,8 @@ testagc96 : atest.c fsk_fast_filter.h tune.h demod.c demod_afsk.c demod_psk.c de
|
||||||
rm -f atest96.exe
|
rm -f atest96.exe
|
||||||
$(CC) $(CFLAGS) -o atest96 $^
|
$(CC) $(CFLAGS) -o atest96 $^
|
||||||
./atest96 -B 9600 ../walkabout9600c.wav | grep "packets decoded in" >atest.out
|
./atest96 -B 9600 ../walkabout9600c.wav | grep "packets decoded in" >atest.out
|
||||||
|
#./atest96 -B 9600 ../walkabout9600c.wav noisy96.wav zzz16.wav zzz16.wav zzz16.wav zzz8.wav zzz8.wav zzz8.wav | grep "packets decoded in" >atest.out
|
||||||
|
#./atest96 -B 9600 zzz16.wav zzz8.wav | grep "packets decoded in" >atest.out
|
||||||
#./atest96 -B 9600 noisy96.wav | grep "packets decoded in" >atest.out
|
#./atest96 -B 9600 noisy96.wav | grep "packets decoded in" >atest.out
|
||||||
#./atest96 -B 9600 19990303_0225_9600_8bis_22kHz.wav | grep "packets decoded in" >atest.out
|
#./atest96 -B 9600 19990303_0225_9600_8bis_22kHz.wav | grep "packets decoded in" >atest.out
|
||||||
#./atest96 -B 9600 19990303_0225_9600_16bit_22kHz.wav | grep "packets decoded in" >atest.out
|
#./atest96 -B 9600 19990303_0225_9600_16bit_22kHz.wav | grep "packets decoded in" >atest.out
|
||||||
|
@ -561,10 +573,15 @@ aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.c misc.a regex.a
|
||||||
|
|
||||||
# Note: kiss_frame.c has conditional compilation on KISSUTIL.
|
# Note: kiss_frame.c has conditional compilation on KISSUTIL.
|
||||||
|
|
||||||
kissutil : kissutil.c kiss_frame.c ax25_pad.o fcs_calc.o textcolor.o serial_port.o sock.o dtime_now.o misc.a regex.a
|
kissutil : kissutil.c kiss_frame.c ax25_pad.o fcs_calc.o textcolor.o serial_port.o dwsock.o dtime_now.o misc.a regex.a
|
||||||
$(CC) $(CFLAGS) -DKISSUTIL -o $@ $^ -lwinmm -lws2_32
|
$(CC) $(CFLAGS) -DKISSUTIL -o $@ $^ -lwinmm -lws2_32
|
||||||
|
|
||||||
|
|
||||||
|
mqtest : aprsmsg.c kiss_frame.c encode_aprs.o ax25_pad.o fcs_calc.o textcolor.o serial_port.o dwsock.o dtime_now.o latlong.o misc.a regex.a
|
||||||
|
$(CC) $(CFLAGS) -DMQTEST -DKISSUTIL -o $@ $^ -lwinmm -lws2_32
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Touch Tone to Speech sample application.
|
# Touch Tone to Speech sample application.
|
||||||
|
|
||||||
ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a regex.a
|
ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a regex.a
|
||||||
|
@ -585,6 +602,7 @@ walk96 : walk96.c dwgps.o dwgpsnmea.o kiss_frame.o \
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#--------------------------------------------------------------
|
#--------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
185
atest.c
185
atest.c
|
@ -140,7 +140,10 @@ static int e_o_f;
|
||||||
static int packets_decoded_one = 0;
|
static int packets_decoded_one = 0;
|
||||||
static int packets_decoded_total = 0;
|
static int packets_decoded_total = 0;
|
||||||
static int decimate = 0; /* Reduce that sampling rate if set. */
|
static int decimate = 0; /* Reduce that sampling rate if set. */
|
||||||
/* 1 = normal, 2 = half, etc. */
|
/* 1 = normal, 2 = half, 3 = 1/3, etc. */
|
||||||
|
|
||||||
|
static int upsample = 0; /* Upsample for G3RUH decoder. */
|
||||||
|
/* Non-zero will override the default. */
|
||||||
|
|
||||||
static struct audio_s my_audio_config;
|
static struct audio_s my_audio_config;
|
||||||
|
|
||||||
|
@ -175,6 +178,13 @@ static int sample_number = -1; /* Sample number from the file. */
|
||||||
/* Use to print timestamp, relative to beginning */
|
/* Use to print timestamp, relative to beginning */
|
||||||
/* of file, when frame was decoded. */
|
/* of file, when frame was decoded. */
|
||||||
|
|
||||||
|
// command line options.
|
||||||
|
|
||||||
|
static int B_opt = DEFAULT_BAUD; // Bits per second. Need to change all baud references to bps.
|
||||||
|
static int g_opt = 0; // G3RUH modem regardless of speed.
|
||||||
|
static int h_opt = 0; // Hexadecimal display of received packet.
|
||||||
|
|
||||||
|
|
||||||
int main (int argc, char *argv[])
|
int main (int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -276,6 +286,7 @@ int main (int argc, char *argv[])
|
||||||
//my_audio_config.achan[channel].passall = 1;
|
//my_audio_config.achan[channel].passall = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
//int this_option_optind = optind ? optind : 1;
|
//int this_option_optind = optind ? optind : 1;
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
|
@ -288,7 +299,7 @@ int main (int argc, char *argv[])
|
||||||
|
|
||||||
/* ':' following option character means arg is required. */
|
/* ':' following option character means arg is required. */
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "B:P:D:F:L:G:012",
|
c = getopt_long(argc, argv, "B:P:D:U:gF:L:G:012h",
|
||||||
long_options, &option_index);
|
long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
@ -296,63 +307,13 @@ int main (int argc, char *argv[])
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
|
||||||
case 'B': /* -B for data Bit rate */
|
case 'B': /* -B for data Bit rate */
|
||||||
/* 300 implies 1600/1800 AFSK. */
|
/* Also implies modem type based on speed. */
|
||||||
/* 1200 implies 1200/2200 AFSK. */
|
B_opt = atoi(optarg);
|
||||||
/* 2400 implies V.26 */
|
break;
|
||||||
/* 9600 implies scrambled. */
|
|
||||||
|
|
||||||
my_audio_config.achan[0].baud = atoi(optarg);
|
case 'g': /* -G Force G3RUH regardless of speed. */
|
||||||
|
|
||||||
dw_printf ("Data rate set to %d bits / second.\n", my_audio_config.achan[0].baud);
|
g_opt = 1;
|
||||||
|
|
||||||
if (my_audio_config.achan[0].baud < MIN_BAUD || my_audio_config.achan[0].baud > MAX_BAUD) {
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("Use a more reasonable bit rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD);
|
|
||||||
exit (EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We have similar logic in direwolf.c, config.c, gen_packets.c, and atest.c, */
|
|
||||||
/* that need to be kept in sync. Maybe it could be a common function someday. */
|
|
||||||
|
|
||||||
if (my_audio_config.achan[0].baud == 100) {
|
|
||||||
my_audio_config.achan[0].modem_type = MODEM_AFSK;
|
|
||||||
my_audio_config.achan[0].mark_freq = 1615;
|
|
||||||
my_audio_config.achan[0].space_freq = 1785;
|
|
||||||
strlcpy (my_audio_config.achan[0].profiles, "D", sizeof(my_audio_config.achan[0].profiles));
|
|
||||||
}
|
|
||||||
else if (my_audio_config.achan[0].baud < 600) {
|
|
||||||
my_audio_config.achan[0].modem_type = MODEM_AFSK;
|
|
||||||
my_audio_config.achan[0].mark_freq = 1600;
|
|
||||||
my_audio_config.achan[0].space_freq = 1800;
|
|
||||||
strlcpy (my_audio_config.achan[0].profiles, "D", sizeof(my_audio_config.achan[0].profiles));
|
|
||||||
}
|
|
||||||
else if (my_audio_config.achan[0].baud < 1800) {
|
|
||||||
my_audio_config.achan[0].modem_type = MODEM_AFSK;
|
|
||||||
my_audio_config.achan[0].mark_freq = DEFAULT_MARK_FREQ;
|
|
||||||
my_audio_config.achan[0].space_freq = DEFAULT_SPACE_FREQ;
|
|
||||||
// Should default to E+ or something similar later.
|
|
||||||
}
|
|
||||||
else if (my_audio_config.achan[0].baud < 3600) {
|
|
||||||
my_audio_config.achan[0].modem_type = MODEM_QPSK;
|
|
||||||
my_audio_config.achan[0].mark_freq = 0;
|
|
||||||
my_audio_config.achan[0].space_freq = 0;
|
|
||||||
strlcpy (my_audio_config.achan[0].profiles, "", sizeof(my_audio_config.achan[0].profiles));
|
|
||||||
dw_printf ("Using V.26 QPSK rather than AFSK.\n");
|
|
||||||
}
|
|
||||||
else if (my_audio_config.achan[0].baud < 7200) {
|
|
||||||
my_audio_config.achan[0].modem_type = MODEM_8PSK;
|
|
||||||
my_audio_config.achan[0].mark_freq = 0;
|
|
||||||
my_audio_config.achan[0].space_freq = 0;
|
|
||||||
strlcpy (my_audio_config.achan[0].profiles, "", sizeof(my_audio_config.achan[0].profiles));
|
|
||||||
dw_printf ("Using V.27 8PSK rather than AFSK.\n");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
my_audio_config.achan[0].modem_type = MODEM_SCRAMBLE;
|
|
||||||
my_audio_config.achan[0].mark_freq = 0;
|
|
||||||
my_audio_config.achan[0].space_freq = 0;
|
|
||||||
strlcpy (my_audio_config.achan[0].profiles, " ", sizeof(my_audio_config.achan[0].profiles)); // avoid getting default later.
|
|
||||||
dw_printf ("Using scrambled baseband signal rather than AFSK.\n");
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'P': /* -P for modem profile. */
|
case 'P': /* -P for modem profile. */
|
||||||
|
@ -375,6 +336,23 @@ int main (int argc, char *argv[])
|
||||||
my_audio_config.achan[0].decimate = decimate;
|
my_audio_config.achan[0].decimate = decimate;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'U': /* -U upsample for G3RUH to improve performance */
|
||||||
|
/* when the sample rate to baud ratio is low. */
|
||||||
|
/* Actually it is set automatically and this will */
|
||||||
|
/* override the normal calculation. */
|
||||||
|
|
||||||
|
upsample = atoi(optarg);
|
||||||
|
|
||||||
|
dw_printf ("Multiply audio sample rate by %d\n", upsample);
|
||||||
|
if (upsample < 1 || upsample > 4) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Unreasonable value for -U.\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
dw_printf ("Multiply audio sample rate by %d\n", upsample);
|
||||||
|
my_audio_config.achan[0].upsample = upsample;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'F': /* -D set "fix bits" level. */
|
case 'F': /* -D set "fix bits" level. */
|
||||||
|
|
||||||
my_audio_config.achan[0].fix_bits = atoi(optarg);
|
my_audio_config.achan[0].fix_bits = atoi(optarg);
|
||||||
|
@ -411,6 +389,11 @@ int main (int argc, char *argv[])
|
||||||
decode_only = 2;
|
decode_only = 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'h': /* Hexadecimal display. */
|
||||||
|
|
||||||
|
h_opt = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
|
|
||||||
/* Unknown option message was already printed. */
|
/* Unknown option message was already printed. */
|
||||||
|
@ -426,6 +409,75 @@ int main (int argc, char *argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set modem type based on data rate.
|
||||||
|
* (Could be overridden by -g later.)
|
||||||
|
*/
|
||||||
|
/* 300 implies 1600/1800 AFSK. */
|
||||||
|
/* 1200 implies 1200/2200 AFSK. */
|
||||||
|
/* 2400 implies V.26 QPSK. */
|
||||||
|
/* 4800 implies V.27 8PSK. */
|
||||||
|
/* 9600 implies G3RUH baseband scrambled. */
|
||||||
|
|
||||||
|
my_audio_config.achan[0].baud = B_opt;
|
||||||
|
|
||||||
|
if (my_audio_config.achan[0].baud < MIN_BAUD || my_audio_config.achan[0].baud > MAX_BAUD) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Use a more reasonable bit rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD);
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have similar logic in direwolf.c, config.c, gen_packets.c, and atest.c, */
|
||||||
|
/* that need to be kept in sync. Maybe it could be a common function someday. */
|
||||||
|
|
||||||
|
if (my_audio_config.achan[0].baud == 100) {
|
||||||
|
my_audio_config.achan[0].modem_type = MODEM_AFSK;
|
||||||
|
my_audio_config.achan[0].mark_freq = 1615;
|
||||||
|
my_audio_config.achan[0].space_freq = 1785;
|
||||||
|
strlcpy (my_audio_config.achan[0].profiles, "D", sizeof(my_audio_config.achan[0].profiles));
|
||||||
|
}
|
||||||
|
else if (my_audio_config.achan[0].baud < 600) {
|
||||||
|
my_audio_config.achan[0].modem_type = MODEM_AFSK;
|
||||||
|
my_audio_config.achan[0].mark_freq = 1600;
|
||||||
|
my_audio_config.achan[0].space_freq = 1800;
|
||||||
|
strlcpy (my_audio_config.achan[0].profiles, "D", sizeof(my_audio_config.achan[0].profiles));
|
||||||
|
}
|
||||||
|
else if (my_audio_config.achan[0].baud < 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));
|
||||||
|
}
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
my_audio_config.achan[0].modem_type = MODEM_SCRAMBLE;
|
||||||
|
my_audio_config.achan[0].mark_freq = 0;
|
||||||
|
my_audio_config.achan[0].space_freq = 0;
|
||||||
|
strlcpy (my_audio_config.achan[0].profiles, " ", sizeof(my_audio_config.achan[0].profiles)); // avoid getting default later.
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -g option means force g3RUH regardless of speed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (g_opt) {
|
||||||
|
my_audio_config.achan[0].modem_type = MODEM_SCRAMBLE;
|
||||||
|
my_audio_config.achan[0].mark_freq = 0;
|
||||||
|
my_audio_config.achan[0].space_freq = 0;
|
||||||
|
strlcpy (my_audio_config.achan[0].profiles, " ", sizeof(my_audio_config.achan[0].profiles)); // avoid getting default later.
|
||||||
|
}
|
||||||
|
|
||||||
memcpy (&my_audio_config.achan[1], &my_audio_config.achan[0], sizeof(my_audio_config.achan[0]));
|
memcpy (&my_audio_config.achan[1], &my_audio_config.achan[0], sizeof(my_audio_config.achan[0]));
|
||||||
|
|
||||||
|
|
||||||
|
@ -763,6 +815,21 @@ void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alev
|
||||||
ax25_safe_print ((char *)pinfo, info_len, 0);
|
ax25_safe_print ((char *)pinfo, info_len, 0);
|
||||||
dw_printf ("\n");
|
dw_printf ("\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -h option for hexadecimal display. (new in 1.6)
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (h_opt) {
|
||||||
|
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("------\n");
|
||||||
|
ax25_hex_dump (pp);
|
||||||
|
dw_printf ("------\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if 1 // temp experiment TODO: remove this.
|
#if 1 // temp experiment TODO: remove this.
|
||||||
|
|
||||||
#include "decode_aprs.h"
|
#include "decode_aprs.h"
|
||||||
|
@ -814,8 +881,12 @@ static void usage (void) {
|
||||||
dw_printf (" 1200 (default) baud uses 1200/2200 Hz AFSK.\n");
|
dw_printf (" 1200 (default) baud uses 1200/2200 Hz AFSK.\n");
|
||||||
dw_printf (" 9600 baud uses K9NG/G2RUH standard.\n");
|
dw_printf (" 9600 baud uses K9NG/G2RUH standard.\n");
|
||||||
dw_printf ("\n");
|
dw_printf ("\n");
|
||||||
|
dw_printf (" -g Force G3RUH modem rather rather than default for data rate.\n");
|
||||||
|
dw_printf ("\n");
|
||||||
dw_printf (" -D n Divide audio sample rate by n.\n");
|
dw_printf (" -D n Divide audio sample rate by n.\n");
|
||||||
dw_printf ("\n");
|
dw_printf ("\n");
|
||||||
|
dw_printf (" -h Print frame contents as hexadecimal bytes.\n");
|
||||||
|
dw_printf ("\n");
|
||||||
dw_printf (" -F n Amount of effort to try fixing frames with an invalid CRC. \n");
|
dw_printf (" -F n Amount of effort to try fixing frames with an invalid CRC. \n");
|
||||||
dw_printf (" 0 (default) = consider only correct frames. \n");
|
dw_printf (" 0 (default) = consider only correct frames. \n");
|
||||||
dw_printf (" 1 = Try to fix only a single bit. \n");
|
dw_printf (" 1 = Try to fix only a single bit. \n");
|
||||||
|
|
2
audio.h
2
audio.h
|
@ -138,6 +138,8 @@ struct audio_s {
|
||||||
int decimate; /* Reduce AFSK sample rate by this factor to */
|
int decimate; /* Reduce AFSK sample rate by this factor to */
|
||||||
/* decrease computational requirements. */
|
/* decrease computational requirements. */
|
||||||
|
|
||||||
|
int upsample; /* Upsample by this factor for G3RUH. */
|
||||||
|
|
||||||
int interleave; /* If > 1, interleave samples among multiple decoders. */
|
int interleave; /* If > 1, interleave samples among multiple decoders. */
|
||||||
/* Quick hack for experiment. */
|
/* Quick hack for experiment. */
|
||||||
|
|
||||||
|
|
25
ax25_pad.c
25
ax25_pad.c
|
@ -2308,16 +2308,30 @@ void ax25_hex_dump (packet_t this_p)
|
||||||
dw_printf ("%s\n", cp_text);
|
dw_printf ("%s\n", cp_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Address fields must be only upper case letters and digits.
|
||||||
|
// If less than 6 characters, trailing positions are filled with ASCII space.
|
||||||
|
// Using all zero bits in one of these 6 positions is wrong.
|
||||||
|
// Any non printable characters will be printed as "." here.
|
||||||
|
|
||||||
dw_printf (" dest %c%c%c%c%c%c %2d c/r=%d res=%d last=%d\n",
|
dw_printf (" dest %c%c%c%c%c%c %2d c/r=%d res=%d last=%d\n",
|
||||||
fptr[0]>>1, fptr[1]>>1, fptr[2]>>1, fptr[3]>>1, fptr[4]>>1, fptr[5]>>1,
|
isprint(fptr[0]>>1) ? fptr[0]>>1 : '.',
|
||||||
|
isprint(fptr[1]>>1) ? fptr[1]>>1 : '.',
|
||||||
|
isprint(fptr[2]>>1) ? fptr[2]>>1 : '.',
|
||||||
|
isprint(fptr[3]>>1) ? fptr[3]>>1 : '.',
|
||||||
|
isprint(fptr[4]>>1) ? fptr[4]>>1 : '.',
|
||||||
|
isprint(fptr[5]>>1) ? fptr[5]>>1 : '.',
|
||||||
(fptr[6]&SSID_SSID_MASK)>>SSID_SSID_SHIFT,
|
(fptr[6]&SSID_SSID_MASK)>>SSID_SSID_SHIFT,
|
||||||
(fptr[6]&SSID_H_MASK)>>SSID_H_SHIFT,
|
(fptr[6]&SSID_H_MASK)>>SSID_H_SHIFT,
|
||||||
(fptr[6]&SSID_RR_MASK)>>SSID_RR_SHIFT,
|
(fptr[6]&SSID_RR_MASK)>>SSID_RR_SHIFT,
|
||||||
fptr[6]&SSID_LAST_MASK);
|
fptr[6]&SSID_LAST_MASK);
|
||||||
|
|
||||||
dw_printf (" source %c%c%c%c%c%c %2d c/r=%d res=%d last=%d\n",
|
dw_printf (" source %c%c%c%c%c%c %2d c/r=%d res=%d last=%d\n",
|
||||||
fptr[7]>>1, fptr[8]>>1, fptr[9]>>1, fptr[10]>>1, fptr[11]>>1, fptr[12]>>1,
|
isprint(fptr[7]>>1) ? fptr[7]>>1 : '.',
|
||||||
|
isprint(fptr[8]>>1) ? fptr[8]>>1 : '.',
|
||||||
|
isprint(fptr[9]>>1) ? fptr[9]>>1 : '.',
|
||||||
|
isprint(fptr[10]>>1) ? fptr[10]>>1 : '.',
|
||||||
|
isprint(fptr[11]>>1) ? fptr[11]>>1 : '.',
|
||||||
|
isprint(fptr[12]>>1) ? fptr[12]>>1 : '.',
|
||||||
(fptr[13]&SSID_SSID_MASK)>>SSID_SSID_SHIFT,
|
(fptr[13]&SSID_SSID_MASK)>>SSID_SSID_SHIFT,
|
||||||
(fptr[13]&SSID_H_MASK)>>SSID_H_SHIFT,
|
(fptr[13]&SSID_H_MASK)>>SSID_H_SHIFT,
|
||||||
(fptr[13]&SSID_RR_MASK)>>SSID_RR_SHIFT,
|
(fptr[13]&SSID_RR_MASK)>>SSID_RR_SHIFT,
|
||||||
|
@ -2327,7 +2341,12 @@ void ax25_hex_dump (packet_t this_p)
|
||||||
|
|
||||||
dw_printf (" digi %d %c%c%c%c%c%c %2d h=%d res=%d last=%d\n",
|
dw_printf (" digi %d %c%c%c%c%c%c %2d h=%d res=%d last=%d\n",
|
||||||
n - 1,
|
n - 1,
|
||||||
fptr[n*7+0]>>1, fptr[n*7+1]>>1, fptr[n*7+2]>>1, fptr[n*7+3]>>1, fptr[n*7+4]>>1, fptr[n*7+5]>>1,
|
isprint(fptr[n*7+0]>>1) ? fptr[n*7+0]>>1 : '.',
|
||||||
|
isprint(fptr[n*7+1]>>1) ? fptr[n*7+1]>>1 : '.',
|
||||||
|
isprint(fptr[n*7+2]>>1) ? fptr[n*7+2]>>1 : '.',
|
||||||
|
isprint(fptr[n*7+3]>>1) ? fptr[n*7+3]>>1 : '.',
|
||||||
|
isprint(fptr[n*7+4]>>1) ? fptr[n*7+4]>>1 : '.',
|
||||||
|
isprint(fptr[n*7+5]>>1) ? fptr[n*7+5]>>1 : '.',
|
||||||
(fptr[n*7+6]&SSID_SSID_MASK)>>SSID_SSID_SHIFT,
|
(fptr[n*7+6]&SSID_SSID_MASK)>>SSID_SSID_SHIFT,
|
||||||
(fptr[n*7+6]&SSID_H_MASK)>>SSID_H_SHIFT,
|
(fptr[n*7+6]&SSID_H_MASK)>>SSID_H_SHIFT,
|
||||||
(fptr[n*7+6]&SSID_RR_MASK)>>SSID_RR_SHIFT,
|
(fptr[n*7+6]&SSID_RR_MASK)>>SSID_RR_SHIFT,
|
||||||
|
|
29
config.c
29
config.c
|
@ -1261,7 +1261,10 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
||||||
* Options:
|
* Options:
|
||||||
* mark:space - AFSK tones. Defaults based on speed.
|
* mark:space - AFSK tones. Defaults based on speed.
|
||||||
* num@offset - Multiple decoders on different frequencies.
|
* num@offset - Multiple decoders on different frequencies.
|
||||||
*
|
* /9 - Divide sample rate by specified number.
|
||||||
|
* *9 - Upsample ratio for G3RUH.
|
||||||
|
* [A-Z+-]+ - Letters, plus, minus for the demodulator "profile."
|
||||||
|
* g3ruh - This modem type regardless of default for speed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
else if (strcasecmp(t, "MODEM") == 0) {
|
else if (strcasecmp(t, "MODEM") == 0) {
|
||||||
|
@ -1320,7 +1323,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
||||||
p_audio_config->achan[channel].space_freq = 0;
|
p_audio_config->achan[channel].space_freq = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get mark frequency. */
|
/* Get any options. */
|
||||||
|
|
||||||
t = split(NULL,0);
|
t = split(NULL,0);
|
||||||
if (t == NULL) {
|
if (t == NULL) {
|
||||||
|
@ -1332,6 +1335,9 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
||||||
|
|
||||||
/* old style */
|
/* old style */
|
||||||
|
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Line %d: Old style (pre version 1.2) format will no longer be supported in next version.\n", line);
|
||||||
|
|
||||||
n = atoi(t);
|
n = atoi(t);
|
||||||
/* Originally the upper limit was 3000. */
|
/* Originally the upper limit was 3000. */
|
||||||
/* Version 1.0 increased to 5000 because someone */
|
/* Version 1.0 increased to 5000 because someone */
|
||||||
|
@ -1516,6 +1522,25 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (*t == '*') { /* *upsample */
|
||||||
|
int n = atoi(t+1);
|
||||||
|
|
||||||
|
if (n >= 1 && n <= 4) {
|
||||||
|
p_audio_config->achan[channel].upsample = n;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Line %d: Ignoring unreasonable upsample ratio of %d.\n", line, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (strcasecmp(t, "G3RUH") == 0) { /* Force G3RUH modem regardless of default for speed. New in 1.6. */
|
||||||
|
|
||||||
|
p_audio_config->achan[channel].modem_type = MODEM_SCRAMBLE;
|
||||||
|
p_audio_config->achan[channel].mark_freq = 0;
|
||||||
|
p_audio_config->achan[channel].space_freq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("Line %d: Unrecognized option for MODEM: %s\n", line, t);
|
dw_printf ("Line %d: Unrecognized option for MODEM: %s\n", line, t);
|
||||||
|
|
|
@ -219,8 +219,8 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet)
|
||||||
if ( ( ! A->g_quiet ) && ( (int)strlen((char*)pinfo) != info_len) ) {
|
if ( ( ! A->g_quiet ) && ( (int)strlen((char*)pinfo) != info_len) ) {
|
||||||
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf("'nul' character found in Information part. This should never happen.\n");
|
dw_printf("'nul' character found in Information part. This should never happen with APRS.\n");
|
||||||
dw_printf("It seems that %s is transmitting with defective software.\n", A->g_src);
|
dw_printf("If this is meant to be APRS, %s is transmitting with defective software.\n", A->g_src);
|
||||||
|
|
||||||
if (strcmp((char*)pinfo, "4P") == 0) {
|
if (strcmp((char*)pinfo, "4P") == 0) {
|
||||||
dw_printf("The TM-D710 will do this intermittently. A firmware upgrade is needed to fix it.\n");
|
dw_printf("The TM-D710 will do this intermittently. A firmware upgrade is needed to fix it.\n");
|
||||||
|
|
80
demod.c
80
demod.c
|
@ -63,7 +63,7 @@ static struct audio_s *save_audio_config_p;
|
||||||
|
|
||||||
// TODO: temp experiment.
|
// TODO: temp experiment.
|
||||||
|
|
||||||
static int upsample = 2; // temp experiment.
|
|
||||||
static int zerostuff = 1; // temp experiment.
|
static int zerostuff = 1; // temp experiment.
|
||||||
|
|
||||||
// Current state of all the decoders.
|
// Current state of all the decoders.
|
||||||
|
@ -648,20 +648,70 @@ int demod_init (struct audio_s *pa)
|
||||||
//#endif
|
//#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TUNE_UPSAMPLE
|
|
||||||
upsample = TUNE_UPSAMPLE;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef TUNE_ZEROSTUFF
|
#ifdef TUNE_ZEROSTUFF
|
||||||
zerostuff = TUNE_ZEROSTUFF;
|
zerostuff = TUNE_ZEROSTUFF;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need a minimum number of audio samples per bit time for good performance.
|
||||||
|
* Easier to check here because demod_9600_init might have an adjusted sample rate.
|
||||||
|
*/
|
||||||
|
|
||||||
|
float ratio = (float)(save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec)
|
||||||
|
/ (float)(save_audio_config_p->achan[chan].baud);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set reasonable upsample ratio if user did not override.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (save_audio_config_p->achan[chan].upsample == 0) {
|
||||||
|
|
||||||
|
if (ratio < 5) {
|
||||||
|
|
||||||
|
// example: 44100 / 9600 is 4.59
|
||||||
|
// Big improvement with x2.
|
||||||
|
// x4 seems to work the best.
|
||||||
|
// The other parameters are not as touchy.
|
||||||
|
// Might reduce on ARM if it takes too much CPU power.
|
||||||
|
|
||||||
|
save_audio_config_p->achan[chan].upsample = 4;
|
||||||
|
}
|
||||||
|
else if (ratio < 10) {
|
||||||
|
|
||||||
|
// 48000 / 9600 is 5.00
|
||||||
|
// Need more reasearch. Treat like above for now.
|
||||||
|
|
||||||
|
save_audio_config_p->achan[chan].upsample = 4;
|
||||||
|
}
|
||||||
|
else if (ratio < 15) {
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
save_audio_config_p->achan[chan].upsample = 2;
|
||||||
|
}
|
||||||
|
else { // >= 15
|
||||||
|
//
|
||||||
|
// An example of this might be .....
|
||||||
|
// Probably no benefit.
|
||||||
|
|
||||||
|
save_audio_config_p->achan[chan].upsample = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef TUNE_UPSAMPLE
|
||||||
|
save_audio_config_p->achan[chan].upsample = TUNE_UPSAMPLE;
|
||||||
|
#endif
|
||||||
|
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
dw_printf ("Channel %d: %d baud, K9NG/G3RUH, %s, %d sample rate x %d",
|
dw_printf ("Channel %d: %d baud, K9NG/G3RUH, %s, %d sample rate x %d",
|
||||||
chan, save_audio_config_p->achan[chan].baud,
|
chan,
|
||||||
|
save_audio_config_p->achan[chan].baud,
|
||||||
save_audio_config_p->achan[chan].profiles,
|
save_audio_config_p->achan[chan].profiles,
|
||||||
save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec, upsample);
|
save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec,
|
||||||
|
save_audio_config_p->achan[chan].upsample);
|
||||||
if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF)
|
if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF)
|
||||||
dw_printf (", DTMF decoder enabled");
|
dw_printf (", DTMF decoder enabled");
|
||||||
dw_printf (".\n");
|
dw_printf (".\n");
|
||||||
|
@ -669,6 +719,7 @@ int demod_init (struct audio_s *pa)
|
||||||
struct demodulator_state_s *D;
|
struct demodulator_state_s *D;
|
||||||
D = &demodulator_state[chan][0]; // first subchannel
|
D = &demodulator_state[chan][0]; // first subchannel
|
||||||
|
|
||||||
|
|
||||||
save_audio_config_p->achan[chan].num_subchan = 1;
|
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;
|
||||||
|
|
||||||
|
@ -681,12 +732,6 @@ int demod_init (struct audio_s *pa)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* We need a minimum number of audio samples per bit time for good performance. */
|
|
||||||
/* Easier to check here because demod_9600_init might have an adjusted sample rate. */
|
|
||||||
|
|
||||||
float ratio = (float)(save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec)
|
|
||||||
/ (float)(save_audio_config_p->achan[chan].baud);
|
|
||||||
|
|
||||||
text_color_set(DW_COLOR_INFO);
|
text_color_set(DW_COLOR_INFO);
|
||||||
dw_printf ("The ratio of audio samples per sec (%d) to data rate in baud (%d) is %.1f\n",
|
dw_printf ("The ratio of audio samples per sec (%d) to data rate in baud (%d) is %.1f\n",
|
||||||
save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec,
|
save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec,
|
||||||
|
@ -698,6 +743,9 @@ int demod_init (struct audio_s *pa)
|
||||||
}
|
}
|
||||||
else if (ratio < 5) {
|
else if (ratio < 5) {
|
||||||
dw_printf ("This is on the low side for best performance. Can you use a higher sample rate?\n");
|
dw_printf ("This is on the low side for best performance. Can you use a higher sample rate?\n");
|
||||||
|
if (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec == 44100) {
|
||||||
|
dw_printf ("For example, can you use 48000 rather than 44100?\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (ratio < 6) {
|
else if (ratio < 6) {
|
||||||
dw_printf ("Increasing the sample rate should improve decoder performance.\n");
|
dw_printf ("Increasing the sample rate should improve decoder performance.\n");
|
||||||
|
@ -709,7 +757,7 @@ int demod_init (struct audio_s *pa)
|
||||||
dw_printf ("This is a suitable ratio for good performance.\n");
|
dw_printf ("This is a suitable ratio for good performance.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
demod_9600_init (upsample * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec, save_audio_config_p->achan[chan].baud, D);
|
demod_9600_init (save_audio_config_p->achan[chan].upsample * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec, save_audio_config_p->achan[chan].baud, D);
|
||||||
|
|
||||||
if (strchr(save_audio_config_p->achan[chan].profiles, '+') != NULL) {
|
if (strchr(save_audio_config_p->achan[chan].profiles, '+') != NULL) {
|
||||||
|
|
||||||
|
@ -942,17 +990,17 @@ void demod_process_sample (int chan, int subchan, int sam)
|
||||||
/* So far, both are same in tests with different */
|
/* So far, both are same in tests with different */
|
||||||
/* optimal low pass filter parameters. */
|
/* optimal low pass filter parameters. */
|
||||||
|
|
||||||
for (k=1; k<upsample; k++) {
|
for (k=1; k<save_audio_config_p->achan[chan].upsample; k++) {
|
||||||
demod_9600_process_sample (chan, 0, D);
|
demod_9600_process_sample (chan, 0, D);
|
||||||
}
|
}
|
||||||
demod_9600_process_sample (chan, sam * upsample, D);
|
demod_9600_process_sample (chan, sam * save_audio_config_p->achan[chan].upsample, D);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
/* Linear interpolation. */
|
/* Linear interpolation. */
|
||||||
static int prev_sam;
|
static int prev_sam;
|
||||||
|
|
||||||
switch (upsample) {
|
switch (save_audio_config_p->achan[chan].upsample) {
|
||||||
case 1:
|
case 1:
|
||||||
demod_9600_process_sample (chan, sam, D);
|
demod_9600_process_sample (chan, sam, D);
|
||||||
break;
|
break;
|
||||||
|
|
67
demod_9600.c
67
demod_9600.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, 2015 John Langner, WB2OSZ
|
// Copyright (C) 2011, 2012, 2013, 2015, 2019 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
|
||||||
|
@ -141,23 +141,43 @@ void demod_9600_init (int samples_per_sec, int baud, struct demodulator_state_s
|
||||||
// case 'K': // upsample x3 with filtering.
|
// case 'K': // upsample x3 with filtering.
|
||||||
// case 'L': // upsample x4 with filtering.
|
// case 'L': // upsample x4 with filtering.
|
||||||
|
|
||||||
D->lp_filter_len_bits = 76 * 9600.0 / (44100.0 * 2.0);
|
|
||||||
|
D->lp_filter_len_bits = 1.0;
|
||||||
|
|
||||||
// Works best with odd number in some tests. Even is better in others.
|
// Works best with odd number in some tests. Even is better in others.
|
||||||
//D->lp_filter_size = ((int) (0.5f * ( D->lp_filter_len_bits * (float)samples_per_sec / (float)baud ))) * 2 + 1;
|
//D->lp_filter_size = ((int) (0.5f * ( D->lp_filter_len_bits * (float)samples_per_sec / (float)baud ))) * 2 + 1;
|
||||||
|
|
||||||
D->lp_filter_size = (int) (( D->lp_filter_len_bits * (float)samples_per_sec / baud) + 0.5f);
|
D->lp_filter_size = (int) (( D->lp_filter_len_bits * (float)samples_per_sec / baud) + 0.5f);
|
||||||
|
|
||||||
D->lp_window = BP_WINDOW_HAMMING;
|
D->lp_window = BP_WINDOW_COSINE;
|
||||||
D->lpf_baud = 0.62;
|
|
||||||
|
D->lpf_baud = 1.00;
|
||||||
|
|
||||||
D->agc_fast_attack = 0.080;
|
D->agc_fast_attack = 0.080;
|
||||||
D->agc_slow_decay = 0.00012;
|
D->agc_slow_decay = 0.00012;
|
||||||
|
|
||||||
D->pll_locked_inertia = 0.89;
|
D->pll_locked_inertia = 0.89;
|
||||||
D->pll_searching_inertia = 0.67;
|
D->pll_searching_inertia = 0.67;
|
||||||
|
|
||||||
|
D->play_it_again_sample = 0; // TODO: 1.6 experiment.
|
||||||
|
// assuming lp_filter_size > lp2_filter_size
|
||||||
|
|
||||||
|
D->lp2_filter_size = samples_per_sec / baud; // samples for 1 bit
|
||||||
|
|
||||||
|
|
||||||
// break;
|
// break;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
dw_printf ("---------- %s (%d, %d) -----------\n", __func__, samples_per_sec, baud);
|
||||||
|
dw_printf ("filter_len_bits = %.2f\n", D->lp_filter_len_bits);
|
||||||
|
dw_printf ("lp_filter_size = %d\n", D->lp_filter_size);
|
||||||
|
dw_printf ("lp_window = %d\n", D->lp_window);
|
||||||
|
dw_printf ("lpf_baud = %.2f\n", D->lpf_baud);
|
||||||
|
dw_printf ("samples per bit = %.1f\n", (double)samples_per_sec / baud);
|
||||||
|
#endif
|
||||||
|
|
||||||
D->pll_step_per_sample =
|
D->pll_step_per_sample =
|
||||||
(int) round(TICKS_PER_PLL_CYCLE * (double) baud / (double)samples_per_sec);
|
(int) round(TICKS_PER_PLL_CYCLE * (double) baud / (double)samples_per_sec);
|
||||||
|
|
||||||
|
@ -194,7 +214,13 @@ void demod_9600_init (int samples_per_sec, int baud, struct demodulator_state_s
|
||||||
|
|
||||||
//dw_printf ("demod_9600_init: call gen_lowpass(fc=%.2f, , size=%d, )\n", fc, D->lp_filter_size);
|
//dw_printf ("demod_9600_init: call gen_lowpass(fc=%.2f, , size=%d, )\n", fc, D->lp_filter_size);
|
||||||
|
|
||||||
gen_lowpass (fc, D->lp_filter, D->lp_filter_size, D->lp_window);
|
(void)gen_lowpass (fc, D->lp_filter, D->lp_filter_size, D->lp_window, 0);
|
||||||
|
|
||||||
|
// Go back and resample where bit is expected.
|
||||||
|
|
||||||
|
fc = (float)baud * 1 / (float)samples_per_sec;
|
||||||
|
|
||||||
|
(void)gen_lowpass (fc, D->lp2_filter, D->lp2_filter_size, D->lp_window, 0);
|
||||||
|
|
||||||
/* Version 1.2: Experiment with different slicing levels. */
|
/* Version 1.2: Experiment with different slicing levels. */
|
||||||
|
|
||||||
|
@ -481,15 +507,14 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D
|
||||||
*
|
*
|
||||||
* Results??? TBD
|
* Results??? TBD
|
||||||
*
|
*
|
||||||
|
* Version 1.6: New experiment where filter size to extract clock is not the same
|
||||||
|
* as filter to extract the data bit value.
|
||||||
|
*
|
||||||
*--------------------------------------------------------------------*/
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
__attribute__((hot))
|
__attribute__((hot))
|
||||||
inline static void nudge_pll (int chan, int subchan, int slice, float demod_out_f, struct demodulator_state_s *D)
|
inline static void nudge_pll (int chan, int subchan, int slice, float demod_out_f, struct demodulator_state_s *D)
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
D->slicer[slice].prev_d_c_pll = D->slicer[slice].data_clock_pll;
|
D->slicer[slice].prev_d_c_pll = D->slicer[slice].data_clock_pll;
|
||||||
|
|
||||||
// Perform the add as unsigned to avoid signed overflow error.
|
// Perform the add as unsigned to avoid signed overflow error.
|
||||||
|
@ -499,8 +524,32 @@ inline static void nudge_pll (int chan, int subchan, int slice, float demod_out_
|
||||||
|
|
||||||
/* Overflow. Was large positive, wrapped around, now large negative. */
|
/* Overflow. Was large positive, wrapped around, now large negative. */
|
||||||
|
|
||||||
|
|
||||||
|
if (D->play_it_again_sample) { // New experiment in 1.6.
|
||||||
|
|
||||||
|
// FIXME: double check position and draw picture.
|
||||||
|
|
||||||
|
int offset = ( D->lp_filter_size - D->lp2_filter_size ) / 2;
|
||||||
|
|
||||||
|
float amp = convolve (D->raw_cb + offset, D->lp2_filter, D->lp2_filter_size);
|
||||||
|
|
||||||
|
int resampled;
|
||||||
|
|
||||||
|
if (D->num_slicers > 1) {
|
||||||
|
resampled = amp - slice_point[slice] > 0;;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resampled = amp > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hdlc_rec_bit (chan, subchan, slice, resampled, 1, D->slicer[slice].lfsr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
// traditional
|
||||||
hdlc_rec_bit (chan, subchan, slice, demod_out_f > 0, 1, D->slicer[slice].lfsr);
|
hdlc_rec_bit (chan, subchan, slice, demod_out_f > 0, 1, D->slicer[slice].lfsr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Zero crossing?
|
* Zero crossing?
|
||||||
|
|
242
demod_afsk.c
242
demod_afsk.c
|
@ -382,6 +382,45 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
|
||||||
D->pll_searching_inertia = 0.64;
|
D->pll_searching_inertia = 0.64;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'H':
|
||||||
|
|
||||||
|
/* Experiment in Version 1.6 */
|
||||||
|
/* 1200 baud - Started out as a copy of E but */
|
||||||
|
/* will probably have little tweaks after the */
|
||||||
|
/* major experiment. */
|
||||||
|
/* Enhancements: */
|
||||||
|
/* + Look back and sample the bit position. */
|
||||||
|
/* + Avoid smearing by long filter and low pass. */
|
||||||
|
|
||||||
|
D->use_prefilter = 1; /* first, a bandpass filter. */
|
||||||
|
D->prefilter_baud = 0.21;
|
||||||
|
D->pre_filter_len_bits = 184 * 1200. / 44100.;
|
||||||
|
D->pre_filter_len_bits = 235 * 1200. / 44100.;
|
||||||
|
D->pre_window = BP_WINDOW_TRUNCATED;
|
||||||
|
|
||||||
|
D->ms_filter_len_bits = 65 * 1200. / 44100.; // Just over 2 bit times.
|
||||||
|
D->ms_window = BP_WINDOW_COSINE;
|
||||||
|
|
||||||
|
/* New for synchronous re-demod in 1.6. */
|
||||||
|
D->play_it_again_sample = 1;
|
||||||
|
D->m2_filter_len_bits = 44 * 1200. / 44100.; // a little more than 1 bit time.
|
||||||
|
D->s2_filter_len_bits = 48 * 1200. / 44100.; // a little more than 1 bit time.
|
||||||
|
D->ms2_window = BP_WINDOW_TRUNCATED;
|
||||||
|
D->lp_delay_fract = 0.53; // FIXME: This is backwards.
|
||||||
|
|
||||||
|
D->lpf_use_fir = 1;
|
||||||
|
D->lpf_baud = 1.10;
|
||||||
|
D->lp_filter_len_bits = 64 * 1200. / 44100.;
|
||||||
|
D->lp_window = BP_WINDOW_TRUNCATED;
|
||||||
|
|
||||||
|
D->agc_fast_attack = 0.820;
|
||||||
|
D->agc_slow_decay = 0.000214;
|
||||||
|
D->hysteresis = 0.001;
|
||||||
|
|
||||||
|
D->pll_locked_inertia = 0.765;
|
||||||
|
D->pll_searching_inertia = 0.44;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
@ -395,6 +434,9 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
|
||||||
#ifdef TUNE_MS_WINDOW
|
#ifdef TUNE_MS_WINDOW
|
||||||
D->ms_window = TUNE_MS_WINDOW;
|
D->ms_window = TUNE_MS_WINDOW;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef TUNE_MS2_WINDOW
|
||||||
|
D->ms2_window = TUNE_MS2_WINDOW;
|
||||||
|
#endif
|
||||||
#ifdef TUNE_LP_WINDOW
|
#ifdef TUNE_LP_WINDOW
|
||||||
D->lp_window = TUNE_LP_WINDOW;
|
D->lp_window = TUNE_LP_WINDOW;
|
||||||
#endif
|
#endif
|
||||||
|
@ -417,7 +459,9 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
|
||||||
#ifdef TUNE_PRE_BAUD
|
#ifdef TUNE_PRE_BAUD
|
||||||
D->prefilter_baud = TUNE_PRE_BAUD;
|
D->prefilter_baud = TUNE_PRE_BAUD;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef TUNE_LP_DELAY_FRACT
|
||||||
|
D->lp_delay_fract = TUNE_LP_DELAY_FRACT;
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate constants used for timing.
|
* Calculate constants used for timing.
|
||||||
|
@ -432,6 +476,8 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
|
||||||
|
|
||||||
D->pre_filter_size = (int) round( D->pre_filter_len_bits * (float)samples_per_sec / (float)baud );
|
D->pre_filter_size = (int) round( D->pre_filter_len_bits * (float)samples_per_sec / (float)baud );
|
||||||
D->ms_filter_size = (int) round( D->ms_filter_len_bits * (float)samples_per_sec / (float)baud );
|
D->ms_filter_size = (int) round( D->ms_filter_len_bits * (float)samples_per_sec / (float)baud );
|
||||||
|
D->m2_filter_size = (int) round( D->m2_filter_len_bits * (float)samples_per_sec / (float)baud );
|
||||||
|
D->s2_filter_size = (int) round( D->s2_filter_len_bits * (float)samples_per_sec / (float)baud );
|
||||||
D->lp_filter_size = (int) round( D->lp_filter_len_bits * (float)samples_per_sec / (float)baud );
|
D->lp_filter_size = (int) round( D->lp_filter_len_bits * (float)samples_per_sec / (float)baud );
|
||||||
|
|
||||||
/* Experiment with other sizes. */
|
/* Experiment with other sizes. */
|
||||||
|
@ -442,6 +488,12 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
|
||||||
#ifdef TUNE_MS_FILTER_SIZE
|
#ifdef TUNE_MS_FILTER_SIZE
|
||||||
D->ms_filter_size = TUNE_MS_FILTER_SIZE;
|
D->ms_filter_size = TUNE_MS_FILTER_SIZE;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef TUNE_M2_FILTER_SIZE
|
||||||
|
D->m2_filter_size = TUNE_M2_FILTER_SIZE;
|
||||||
|
#endif
|
||||||
|
#ifdef TUNE_S2_FILTER_SIZE
|
||||||
|
D->s2_filter_size = TUNE_S2_FILTER_SIZE;
|
||||||
|
#endif
|
||||||
#ifdef TUNE_LP_FILTER_SIZE
|
#ifdef TUNE_LP_FILTER_SIZE
|
||||||
D->lp_filter_size = TUNE_LP_FILTER_SIZE;
|
D->lp_filter_size = TUNE_LP_FILTER_SIZE;
|
||||||
#endif
|
#endif
|
||||||
|
@ -470,6 +522,25 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
|
||||||
exit (1);
|
exit (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (D->m2_filter_size * 2 > MAX_FILTER_SIZE)
|
||||||
|
{
|
||||||
|
text_color_set (DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Calculated filter size of %d is too large.\n", D->m2_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->s2_filter_size * 2 > MAX_FILTER_SIZE)
|
||||||
|
{
|
||||||
|
text_color_set (DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Calculated filter size of %d is too large.\n", D->s2_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)
|
if (D->lp_filter_size > MAX_FILTER_SIZE)
|
||||||
{
|
{
|
||||||
text_color_set (DW_COLOR_ERROR);
|
text_color_set (DW_COLOR_ERROR);
|
||||||
|
@ -502,10 +573,6 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
|
||||||
f1 = f1 / (float)samples_per_sec;
|
f1 = f1 / (float)samples_per_sec;
|
||||||
f2 = f2 / (float)samples_per_sec;
|
f2 = f2 / (float)samples_per_sec;
|
||||||
|
|
||||||
//gen_bandpass (f1, f2, D->pre_filter, D->pre_filter_size, BP_WINDOW_HAMMING);
|
|
||||||
//gen_bandpass (f1, f2, D->pre_filter, D->pre_filter_size, BP_WINDOW_BLACKMAN);
|
|
||||||
//gen_bandpass (f1, f2, D->pre_filter, D->pre_filter_size, BP_WINDOW_COSINE);
|
|
||||||
//gen_bandpass (f1, f2, D->pre_filter, D->pre_filter_size, D->bp_window);
|
|
||||||
gen_bandpass (f1, f2, D->pre_filter, D->pre_filter_size, D->pre_window);
|
gen_bandpass (f1, f2, D->pre_filter, D->pre_filter_size, D->pre_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,46 +592,8 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
|
||||||
dw_printf (" j shape M sin M cos \n");
|
dw_printf (" j shape M sin M cos \n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
float Gs = 0, Gc = 0;
|
|
||||||
|
|
||||||
for (j=0; j<D->ms_filter_size; j++) {
|
|
||||||
float am;
|
|
||||||
float center;
|
|
||||||
float shape = 1.0f; /* Shape is an attempt to smooth out the */
|
|
||||||
/* abrupt edges in hopes of reducing */
|
|
||||||
/* overshoot and ringing. */
|
|
||||||
/* My first thought was to use a cosine shape. */
|
|
||||||
/* Should investigate Hamming and Blackman */
|
|
||||||
/* windows mentioned in the literature. */
|
|
||||||
/* http://en.wikipedia.org/wiki/Window_function */
|
|
||||||
|
|
||||||
center = 0.5f * (D->ms_filter_size - 1);
|
|
||||||
am = ((float)(j - center) / (float)samples_per_sec) * ((float)mark_freq) * (2.0f * (float)M_PI);
|
|
||||||
|
|
||||||
shape = window (D->ms_window, D->ms_filter_size, j);
|
|
||||||
|
|
||||||
D->m_sin_table[j] = sinf(am) * shape;
|
|
||||||
D->m_cos_table[j] = cosf(am) * shape;
|
|
||||||
|
|
||||||
Gs += D->m_sin_table[j] * sinf(am);
|
|
||||||
Gc += D->m_cos_table[j] * cosf(am);
|
|
||||||
|
|
||||||
#if DEBUG1
|
|
||||||
dw_printf ("%6d %6.2f %6.2f %6.2f\n", j, shape, D->m_sin_table[j], D->m_cos_table[j]) ;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Normalize for unity gain */
|
|
||||||
|
|
||||||
#if DEBUG1
|
|
||||||
dw_printf ("Before normalizing, Gs = %.2f, Gc = %.2f\n", Gs, Gc) ;
|
|
||||||
#endif
|
|
||||||
for (j=0; j<D->ms_filter_size; j++) {
|
|
||||||
D->m_sin_table[j] = D->m_sin_table[j] / Gs;
|
|
||||||
D->m_cos_table[j] = D->m_cos_table[j] / Gc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
gen_ms (mark_freq, samples_per_sec, D->m_sin_table, D->m_cos_table, D->ms_filter_size, D->ms_window);
|
||||||
|
|
||||||
#if DEBUG1
|
#if DEBUG1
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
@ -572,39 +601,14 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
|
||||||
dw_printf ("Space\n");
|
dw_printf ("Space\n");
|
||||||
dw_printf (" j shape S sin S cos\n");
|
dw_printf (" j shape S sin S cos\n");
|
||||||
#endif
|
#endif
|
||||||
Gs = 0;
|
|
||||||
Gc = 0;
|
|
||||||
|
|
||||||
for (j=0; j<D->ms_filter_size; j++) {
|
gen_ms (space_freq, samples_per_sec, D->s_sin_table, D->s_cos_table, D->ms_filter_size, D->ms_window);
|
||||||
float as;
|
|
||||||
float center;
|
|
||||||
float shape = 1.0f;
|
|
||||||
|
|
||||||
center = 0.5 * (D->ms_filter_size - 1);
|
// Note that these are twice as long so we can try matching different phases.
|
||||||
as = ((float)(j - center) / (float)samples_per_sec) * ((float)space_freq) * (2.0f * (float)M_PI);
|
|
||||||
|
|
||||||
shape = window (D->ms_window, D->ms_filter_size, j);
|
if (D->play_it_again_sample) {
|
||||||
|
gen_ms (mark_freq, samples_per_sec, D->m2_sin_table, D->m2_cos_table, D->m2_filter_size * 2, D->ms2_window);
|
||||||
D->s_sin_table[j] = sinf(as) * shape;
|
gen_ms (space_freq, samples_per_sec, D->s2_sin_table, D->s2_cos_table, D->s2_filter_size * 2, D->ms2_window);
|
||||||
D->s_cos_table[j] = cosf(as) * shape;
|
|
||||||
|
|
||||||
Gs += D->s_sin_table[j] * sinf(as);
|
|
||||||
Gc += D->s_cos_table[j] * cosf(as);
|
|
||||||
|
|
||||||
#if DEBUG1
|
|
||||||
dw_printf ("%6d %6.2f %6.2f %6.2f\n", j, shape, D->s_sin_table[j], D->s_cos_table[j] ) ;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Normalize for unity gain */
|
|
||||||
|
|
||||||
#if DEBUG1
|
|
||||||
dw_printf ("Before normalizing, Gs = %.2f, Gc = %.2f\n", Gs, Gc) ;
|
|
||||||
#endif
|
|
||||||
for (j=0; j<D->ms_filter_size; j++) {
|
|
||||||
D->s_sin_table[j] = D->s_sin_table[j] / Gs;
|
|
||||||
D->s_cos_table[j] = D->s_cos_table[j] / Gc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -616,7 +620,11 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
|
||||||
if (D->lpf_use_fir) {
|
if (D->lpf_use_fir) {
|
||||||
float fc;
|
float fc;
|
||||||
fc = baud * D->lpf_baud / (float)samples_per_sec;
|
fc = baud * D->lpf_baud / (float)samples_per_sec;
|
||||||
gen_lowpass (fc, D->lp_filter, D->lp_filter_size, D->lp_window);
|
D->lp_filter_delay = gen_lowpass (fc, D->lp_filter, D->lp_filter_size, D->lp_window, D->lp_delay_fract);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// D->lp_filter_delay =
|
||||||
|
// Only needed for looking back and I don't expect to use IIR in that case.
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -853,15 +861,22 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat
|
||||||
* Optional bandpass filter before the mark/space discriminator.
|
* Optional bandpass filter before the mark/space discriminator.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// FIXME: calculate how much we really need.
|
||||||
|
|
||||||
|
int extra = 0;
|
||||||
|
if (D->play_it_again_sample) {
|
||||||
|
extra = D->lp_filter_size;
|
||||||
|
}
|
||||||
|
|
||||||
if (D->use_prefilter) {
|
if (D->use_prefilter) {
|
||||||
float cleaner;
|
float cleaner;
|
||||||
|
|
||||||
push_sample (fsam, D->raw_cb, D->pre_filter_size);
|
push_sample (fsam, D->raw_cb, D->pre_filter_size);
|
||||||
cleaner = convolve (D->raw_cb, D->pre_filter, D->pre_filter_size);
|
cleaner = convolve (D->raw_cb, D->pre_filter, D->pre_filter_size);
|
||||||
push_sample (cleaner, D->ms_in_cb, D->ms_filter_size);
|
push_sample (cleaner, D->ms_in_cb, D->ms_filter_size + extra);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
push_sample (fsam, D->ms_in_cb, D->ms_filter_size);
|
push_sample (fsam, D->ms_in_cb, D->ms_filter_size + extra);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -978,7 +993,7 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat
|
||||||
* Here is an excellent explanation:
|
* Here is an excellent explanation:
|
||||||
* http://www.febo.com/packet/layer-one/transmit.html
|
* http://www.febo.com/packet/layer-one/transmit.html
|
||||||
*
|
*
|
||||||
* Under real conditions, we find that the higher tone has a
|
* Under real conditions, we find that the higher tone usually has a
|
||||||
* considerably smaller amplitude due to the passband characteristics
|
* considerably smaller amplitude due to the passband characteristics
|
||||||
* of the transmitter and receiver. To make matters worse, it
|
* of the transmitter and receiver. To make matters worse, it
|
||||||
* varies considerably from one station to another.
|
* varies considerably from one station to another.
|
||||||
|
@ -1133,8 +1148,79 @@ inline static void nudge_pll (int chan, int subchan, int slice, int demod_data,
|
||||||
|
|
||||||
/* Overflow. */
|
/* Overflow. */
|
||||||
|
|
||||||
|
// In version 1.6 we will try a new experiment.
|
||||||
|
// The tone filters are about 2 bit times wide so this smears the signal.
|
||||||
|
// I originally expected this to be about 1 bit time but 2 turned out
|
||||||
|
// to give the best results after extensive experimentation.
|
||||||
|
// We are looking at half of each adjacent bit, not just the one we want.
|
||||||
|
// The low pass filter also smears the signal.
|
||||||
|
//
|
||||||
|
// We will try to look back in time and re-demodulate only the time period of the bit.
|
||||||
|
//
|
||||||
|
|
||||||
|
if (D->play_it_again_sample) { // New in 1.6. Currently 'H' demod profile.
|
||||||
|
|
||||||
|
// FIXME: double check position and draw picture.
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// This provided a slight benefit.
|
||||||
|
|
||||||
|
int offset = ( D->ms_filter_size - D->m2_filter_size ) / 2 + D->lp_filter_delay;
|
||||||
|
|
||||||
|
float m_sum1 = convolve (D->ms_in_cb + offset, D->m2_sin_table, D->m2_filter_size);
|
||||||
|
float m_sum2 = convolve (D->ms_in_cb + offset, D->m2_cos_table, D->m2_filter_size);
|
||||||
|
float m_amp = sqrtf(m_sum1 * m_sum1 + m_sum2 * m_sum2);
|
||||||
|
|
||||||
|
offset = ( D->ms_filter_size - D->s2_filter_size ) / 2 + D->lp_filter_delay;
|
||||||
|
|
||||||
|
float s_sum1 = convolve (D->ms_in_cb + offset, D->s2_sin_table, D->s2_filter_size);
|
||||||
|
float s_sum2 = convolve (D->ms_in_cb + offset, D->s2_cos_table, D->s2_filter_size);
|
||||||
|
float s_amp = sqrtf(s_sum1 * s_sum1 + s_sum2 * s_sum2);
|
||||||
|
#else
|
||||||
|
// Here we try something completely new.
|
||||||
|
// Rather than taking the vector magnitude of the I+Q components,
|
||||||
|
// correlate it with only a sine wave. We don't know the phase so
|
||||||
|
// we will try matching with a bunch of different phases and take the best match.
|
||||||
|
|
||||||
|
// Trying match with cosine as well could be beneficial for lower sample rates.
|
||||||
|
|
||||||
|
int j;
|
||||||
|
float m_amp = 0;
|
||||||
|
float s_amp = 0;
|
||||||
|
|
||||||
|
int offset = ( D->ms_filter_size - D->m2_filter_size ) / 2 + D->lp_filter_delay;
|
||||||
|
|
||||||
|
for (j = 0; j <= D->m2_filter_size; j++) {
|
||||||
|
float match = fabsf(convolve (D->ms_in_cb + offset, D->m2_sin_table + j, D->m2_filter_size));
|
||||||
|
if (match > m_amp) m_amp = match;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = ( D->ms_filter_size - D->s2_filter_size ) / 2 + D->lp_filter_delay;
|
||||||
|
|
||||||
|
for (j = 0; j <= D->s2_filter_size; j++) {
|
||||||
|
float match = fabsf(convolve (D->ms_in_cb + offset, D->s2_sin_table + j, D->s2_filter_size));
|
||||||
|
if (match > s_amp) s_amp = match;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int resampled;
|
||||||
|
if (D->num_slicers > 1) {
|
||||||
|
resampled = m_amp > s_amp * space_gain[slice];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resampled = m_amp > s_amp;
|
||||||
|
}
|
||||||
|
|
||||||
|
hdlc_rec_bit (chan, subchan, slice, resampled, 0, -1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Traditional way, after the low pass filter.
|
||||||
hdlc_rec_bit (chan, subchan, slice, demod_data, 0, -1);
|
hdlc_rec_bit (chan, subchan, slice, demod_data, 0, -1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Even if we used alternative method to extract the data bit,
|
||||||
|
// we still use the low pass output for the PLL.
|
||||||
|
|
||||||
if (demod_data != D->slicer[slice].prev_demod_data) {
|
if (demod_data != D->slicer[slice].prev_demod_data) {
|
||||||
|
|
||||||
|
|
|
@ -510,7 +510,7 @@ void demod_psk_init (enum modem_t modem_type, int samples_per_sec, int bps, char
|
||||||
*/
|
*/
|
||||||
|
|
||||||
float fc = correct_baud * D->lpf_baud / (float)samples_per_sec;
|
float fc = correct_baud * D->lpf_baud / (float)samples_per_sec;
|
||||||
gen_lowpass (fc, D->lp_filter, D->lp_filter_size, D->lp_window);
|
(void)gen_lowpass (fc, D->lp_filter, D->lp_filter_size, D->lp_window, 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* No point in having multiple numbers for signal level.
|
* No point in having multiple numbers for signal level.
|
||||||
|
|
54
direwolf.c
54
direwolf.c
|
@ -190,7 +190,7 @@ int main (int argc, char *argv[])
|
||||||
struct digi_config_s digi_config;
|
struct digi_config_s digi_config;
|
||||||
struct cdigi_config_s cdigi_config;
|
struct cdigi_config_s cdigi_config;
|
||||||
struct igate_config_s igate_config;
|
struct igate_config_s igate_config;
|
||||||
int r_opt = 0, n_opt = 0, b_opt = 0, B_opt = 0, D_opt = 0; /* Command line options. */
|
int r_opt = 0, n_opt = 0, b_opt = 0, B_opt = 0, D_opt = 0, U_opt = 0; /* Command line options. */
|
||||||
char P_opt[16];
|
char P_opt[16];
|
||||||
char l_opt_logdir[80];
|
char l_opt_logdir[80];
|
||||||
char L_opt_logfile[80];
|
char L_opt_logfile[80];
|
||||||
|
@ -199,6 +199,7 @@ int main (int argc, char *argv[])
|
||||||
|
|
||||||
int t_opt = 1; /* Text color option. */
|
int t_opt = 1; /* Text color option. */
|
||||||
int a_opt = 0; /* "-a n" interval, in seconds, for audio statistics report. 0 for none. */
|
int a_opt = 0; /* "-a n" interval, in seconds, for audio statistics report. 0 for none. */
|
||||||
|
int g_opt = 0; /* G3RUH mode, ignoring default for speed. */
|
||||||
|
|
||||||
int d_k_opt = 0; /* "-d k" option for serial port KISS. Can be repeated for more detail. */
|
int d_k_opt = 0; /* "-d k" option for serial port KISS. Can be repeated for more detail. */
|
||||||
int d_n_opt = 0; /* "-d n" option for Network KISS. Can be repeated for more detail. */
|
int d_n_opt = 0; /* "-d n" option for Network KISS. Can be repeated for more detail. */
|
||||||
|
@ -263,7 +264,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 4\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
|
//dw_printf ("Dire Wolf version %d.%d (%s) Beta Test 4\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);
|
||||||
|
|
||||||
|
|
||||||
|
@ -352,7 +353,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, "P:B:D:c:pxr:b:n:d:q:t:Ul:L:Sa:E:T:",
|
c = getopt_long(argc, argv, "P:B:gD:U:c:pxr:b:n:d:q:t:ul:L:Sa:E:T:",
|
||||||
long_options, &option_index);
|
long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
@ -404,13 +405,18 @@ int main (int argc, char *argv[])
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'g': /* -g G3RUH modem, overriding default mode for speed. */
|
||||||
|
|
||||||
|
g_opt = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'P': /* -P for modem profile. */
|
case 'P': /* -P for modem profile. */
|
||||||
|
|
||||||
//debug: dw_printf ("Demodulator profile set to \"%s\"\n", optarg);
|
//debug: dw_printf ("Demodulator profile set to \"%s\"\n", optarg);
|
||||||
strlcpy (P_opt, optarg, sizeof(P_opt));
|
strlcpy (P_opt, optarg, sizeof(P_opt));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'D': /* -D decrease AFSK demodulator sample rate */
|
case 'D': /* -D divide AFSK demodulator sample rate */
|
||||||
|
|
||||||
D_opt = atoi(optarg);
|
D_opt = atoi(optarg);
|
||||||
if (D_opt < 1 || D_opt > 8) {
|
if (D_opt < 1 || D_opt > 8) {
|
||||||
|
@ -420,6 +426,16 @@ int main (int argc, char *argv[])
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'U': /* -U multiply G3RUH demodulator sample rate (upsample) */
|
||||||
|
|
||||||
|
U_opt = atoi(optarg);
|
||||||
|
if (U_opt < 1 || U_opt > 4) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Crazy value for -U. \n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'x': /* -x for transmit calibration tones. */
|
case 'x': /* -x for transmit calibration tones. */
|
||||||
|
|
||||||
xmit_calibrate_option = 1;
|
xmit_calibrate_option = 1;
|
||||||
|
@ -518,7 +534,7 @@ int main (int argc, char *argv[])
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
case 'U': /* Print UTF-8 test and exit. */
|
case 'u': /* Print UTF-8 test and exit. */
|
||||||
|
|
||||||
dw_printf ("\n UTF-8 test string: ma%c%cana %c%c F%c%c%c%ce\n\n",
|
dw_printf ("\n UTF-8 test string: ma%c%cana %c%c F%c%c%c%ce\n\n",
|
||||||
0xc3, 0xb1,
|
0xc3, 0xb1,
|
||||||
|
@ -610,15 +626,18 @@ int main (int argc, char *argv[])
|
||||||
if (r_opt != 0) {
|
if (r_opt != 0) {
|
||||||
audio_config.adev[0].samples_per_sec = r_opt;
|
audio_config.adev[0].samples_per_sec = r_opt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n_opt != 0) {
|
if (n_opt != 0) {
|
||||||
audio_config.adev[0].num_channels = n_opt;
|
audio_config.adev[0].num_channels = n_opt;
|
||||||
if (n_opt == 2) {
|
if (n_opt == 2) {
|
||||||
audio_config.achan[1].valid = 1;
|
audio_config.achan[1].valid = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (b_opt != 0) {
|
if (b_opt != 0) {
|
||||||
audio_config.adev[0].bits_per_sample = b_opt;
|
audio_config.adev[0].bits_per_sample = b_opt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (B_opt != 0) {
|
if (B_opt != 0) {
|
||||||
audio_config.achan[0].baud = B_opt;
|
audio_config.achan[0].baud = B_opt;
|
||||||
|
|
||||||
|
@ -661,6 +680,16 @@ int main (int argc, char *argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_opt) {
|
||||||
|
|
||||||
|
// Force G3RUH mode, overriding default for speed.
|
||||||
|
// Example: -B 2400 -g
|
||||||
|
|
||||||
|
audio_config.achan[0].modem_type = MODEM_SCRAMBLE;
|
||||||
|
audio_config.achan[0].mark_freq = 0;
|
||||||
|
audio_config.achan[0].space_freq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
audio_config.statistics_interval = a_opt;
|
audio_config.statistics_interval = a_opt;
|
||||||
|
|
||||||
if (strlen(P_opt) > 0) {
|
if (strlen(P_opt) > 0) {
|
||||||
|
@ -674,6 +703,13 @@ int main (int argc, char *argv[])
|
||||||
audio_config.achan[0].decimate = D_opt;
|
audio_config.achan[0].decimate = D_opt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (U_opt != 0) {
|
||||||
|
// Increase G3RUH audio sampling rate to improve performance.
|
||||||
|
// The value is normally determined automatically based on audio
|
||||||
|
// sample rate and baud. This allows override for experimentation.
|
||||||
|
audio_config.achan[0].upsample = U_opt;
|
||||||
|
}
|
||||||
|
|
||||||
strlcpy(audio_config.timestamp_format, T_opt_timestamp, sizeof(audio_config.timestamp_format));
|
strlcpy(audio_config.timestamp_format, T_opt_timestamp, sizeof(audio_config.timestamp_format));
|
||||||
|
|
||||||
// temp - only xmit errors.
|
// temp - only xmit errors.
|
||||||
|
@ -1145,9 +1181,10 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
||||||
* If it came from DTMF decoder, send it to APRStt gateway.
|
* If it came from DTMF decoder, send it to APRStt gateway.
|
||||||
* Otherwise, it is a candidate for IGate and digipeater.
|
* Otherwise, it is a candidate for IGate and digipeater.
|
||||||
*
|
*
|
||||||
* TODO: It might be useful to have some way to simulate touch tone
|
* TODO: It would be useful to have some way to simulate touch tone
|
||||||
* sequences with BEACON sendto=R... for testing.
|
* sequences with BEACON sendto=R0 for testing.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (subchan == -1) {
|
if (subchan == -1) {
|
||||||
if (tt_config.gateway_enabled && info_len >= 2) {
|
if (tt_config.gateway_enabled && info_len >= 2) {
|
||||||
aprs_tt_sequence (chan, (char*)(pinfo+1));
|
aprs_tt_sequence (chan, (char*)(pinfo+1));
|
||||||
|
@ -1254,6 +1291,7 @@ static void usage (char **argv)
|
||||||
dw_printf (" 2400 bps uses QPSK based on V.26 standard.\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 (" 4800 bps uses 8PSK based on V.27 standard.\n");
|
||||||
dw_printf (" 9600 bps and up uses K9NG/G3RUH standard.\n");
|
dw_printf (" 9600 bps and up uses K9NG/G3RUH standard.\n");
|
||||||
|
dw_printf (" -g Force G3RUH modem regardless of speed.\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");
|
||||||
|
@ -1281,7 +1319,7 @@ static void usage (char **argv)
|
||||||
dw_printf (" -p Enable pseudo terminal for KISS protocol.\n");
|
dw_printf (" -p Enable pseudo terminal for KISS protocol.\n");
|
||||||
#endif
|
#endif
|
||||||
dw_printf (" -x Send Xmit level calibration tones.\n");
|
dw_printf (" -x Send Xmit level calibration tones.\n");
|
||||||
dw_printf (" -U Print UTF-8 test string and exit.\n");
|
dw_printf (" -u Print UTF-8 test string and exit.\n");
|
||||||
dw_printf (" -S Print symbol tables and exit.\n");
|
dw_printf (" -S Print symbol tables and exit.\n");
|
||||||
dw_printf (" -T fmt Time stamp format for sent and received frames.\n");
|
dw_printf (" -T fmt Time stamp format for sent and received frames.\n");
|
||||||
dw_printf ("\n");
|
dw_printf ("\n");
|
||||||
|
|
128
dsp.c
128
dsp.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, 2015 John Langner, WB2OSZ
|
// Copyright (C) 2011, 2012, 2013, 2015, 2019 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
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
|
|
||||||
// Don't remove this. It serves as a reminder that an experiment is underway.
|
// Don't remove this. It serves as a reminder that an experiment is underway.
|
||||||
|
|
||||||
#if defined(TUNE_MS_FILTER_SIZE) || defined(TUNE_AGC_FAST) || defined(TUNE_LPF_BAUD) || defined(TUNE_PLL_LOCKED) || defined(TUNE_PROFILE)
|
#if defined(TUNE_MS_FILTER_SIZE) || defined(TUNE_MS2_FILTER_SIZE) || defined(TUNE_AGC_FAST) || defined(TUNE_LPF_BAUD) || defined(TUNE_PLL_LOCKED) || defined(TUNE_PROFILE)
|
||||||
#define DEBUG1 1 // Don't remove this.
|
#define DEBUG1 1 // Don't remove this.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -118,13 +118,16 @@ float window (bp_window_t type, int size, int j)
|
||||||
* Inputs: fc - Cutoff frequency as fraction of sampling frequency.
|
* Inputs: fc - Cutoff frequency as fraction of sampling frequency.
|
||||||
* filter_size - Number of filter taps.
|
* filter_size - Number of filter taps.
|
||||||
* wtype - Window type, BP_WINDOW_HAMMING, etc.
|
* wtype - Window type, BP_WINDOW_HAMMING, etc.
|
||||||
|
* lp_delay_fract - Fudge factor for the delay value.
|
||||||
*
|
*
|
||||||
* Outputs: lp_filter
|
* Outputs: lp_filter
|
||||||
*
|
*
|
||||||
|
* Returns: Signal delay thru the filter in number of audio samples.
|
||||||
|
*
|
||||||
*----------------------------------------------------------------*/
|
*----------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
void gen_lowpass (float fc, float *lp_filter, int filter_size, bp_window_t wtype)
|
int gen_lowpass (float fc, float *lp_filter, int filter_size, bp_window_t wtype, float lp_delay_fract)
|
||||||
{
|
{
|
||||||
int j;
|
int j;
|
||||||
float G;
|
float G;
|
||||||
|
@ -171,14 +174,69 @@ void gen_lowpass (float fc, float *lp_filter, int filter_size, bp_window_t wtype
|
||||||
for (j=0; j<filter_size; j++) {
|
for (j=0; j<filter_size; j++) {
|
||||||
lp_filter[j] = lp_filter[j] / G;
|
lp_filter[j] = lp_filter[j] / G;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Calculate the signal delay.
|
||||||
|
// If a signal at level 0 steps to level 1, this is the time that it would
|
||||||
|
// take for the output to reach 0.5.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// Filter has one tap with value of 1.0.
|
||||||
|
// Output is immediate so I would call this delay of 0.
|
||||||
|
//
|
||||||
|
// Filter coefficients: 0.2, 0.2, 0.2, 0.2, 0.2
|
||||||
|
// "1" inputs Out
|
||||||
|
// 1 0.2
|
||||||
|
// 2 0.4
|
||||||
|
// 3 0.6
|
||||||
|
//
|
||||||
|
// In this case, the output does not change immediately.
|
||||||
|
// It takes two more samples to reach the half way point
|
||||||
|
// so it has a delay of 2.
|
||||||
|
|
||||||
|
float sum = 0;
|
||||||
|
int delay = 0;
|
||||||
|
|
||||||
|
if (lp_delay_fract == 0) lp_delay_fract = 0.5;
|
||||||
|
|
||||||
|
for (j=0; j<filter_size; j++) {
|
||||||
|
sum += lp_filter[j];
|
||||||
|
#if DEBUG1
|
||||||
|
dw_printf ("lp_filter[%d] = %.3f sum = %.3f lp_delay_fract = %.3f\n", j, lp_filter[j], sum, lp_delay_fract);
|
||||||
|
#endif
|
||||||
|
if (sum > lp_delay_fract) {
|
||||||
|
delay = j;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG1
|
||||||
|
dw_printf ("Low Pass Delay = %d samples\n", delay) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Hmmm. This might have been wasted effort. The result is always half the number of taps.
|
||||||
|
|
||||||
|
if (delay < 2 || delay > filter_size - 2) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Internal error, %s %d, delay %d for size %d\n", __func__, __LINE__, delay, filter_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (delay);
|
||||||
|
|
||||||
|
} /* end gen_lowpass */
|
||||||
|
|
||||||
|
|
||||||
|
#undef DEBUG1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------
|
/*------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* Name: gen_bandpass
|
* Name: gen_bandpass
|
||||||
*
|
*
|
||||||
* Purpose: Generate band pass filter kernel.
|
* Purpose: Generate band pass filter kernel for the prefilter.
|
||||||
|
* This is NOT for the mark/space filters.
|
||||||
*
|
*
|
||||||
* Inputs: f1 - Lower cutoff frequency as fraction of sampling frequency.
|
* Inputs: f1 - Lower cutoff frequency as fraction of sampling frequency.
|
||||||
* f2 - Upper cutoff frequency...
|
* f2 - Upper cutoff frequency...
|
||||||
|
@ -201,7 +259,6 @@ void gen_bandpass (float f1, float f2, float *bp_filter, int filter_size, bp_win
|
||||||
float G;
|
float G;
|
||||||
float center = 0.5 * (filter_size - 1);
|
float center = 0.5 * (filter_size - 1);
|
||||||
|
|
||||||
|
|
||||||
#if DEBUG1
|
#if DEBUG1
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
|
||||||
|
@ -231,6 +288,7 @@ void gen_bandpass (float f1, float f2, float *bp_filter, int filter_size, bp_win
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Normalize bandpass for unity gain in middle of passband.
|
* Normalize bandpass for unity gain in middle of passband.
|
||||||
* Can't use same technique as for lowpass.
|
* Can't use same technique as for lowpass.
|
||||||
|
@ -249,6 +307,66 @@ void gen_bandpass (float f1, float f2, float *bp_filter, int filter_size, bp_win
|
||||||
for (j=0; j<filter_size; j++) {
|
for (j=0; j<filter_size; j++) {
|
||||||
bp_filter[j] = bp_filter[j] / G;
|
bp_filter[j] = bp_filter[j] / G;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} /* end gen_bandpass */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: gen_ms
|
||||||
|
*
|
||||||
|
* Purpose: Generate mark and space filters.
|
||||||
|
*
|
||||||
|
* Inputs: fc - Tone frequency, i.e. mark or space.
|
||||||
|
* sps - Samples per second.
|
||||||
|
* filter_size - Number of filter taps.
|
||||||
|
* wtype - Window type, BP_WINDOW_HAMMING, etc.
|
||||||
|
*
|
||||||
|
* Outputs: bp_filter
|
||||||
|
*
|
||||||
|
* Reference: http://www.labbookpages.co.uk/audio/firWindowing.html
|
||||||
|
*
|
||||||
|
* Does it need to be an odd length?
|
||||||
|
*
|
||||||
|
*----------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
void gen_ms (int fc, int sps, float *sin_table, float *cos_table, int filter_size, int wtype)
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
float Gs = 0, Gc = 0;;
|
||||||
|
|
||||||
|
for (j=0; j<filter_size; j++) {
|
||||||
|
|
||||||
|
float center = 0.5f * (filter_size - 1);
|
||||||
|
float am = ((float)(j - center) / (float)sps) * ((float)fc) * (2.0f * (float)M_PI);
|
||||||
|
|
||||||
|
float shape = window (wtype, filter_size, j);
|
||||||
|
|
||||||
|
sin_table[j] = sinf(am) * shape;
|
||||||
|
cos_table[j] = cosf(am) * shape;
|
||||||
|
|
||||||
|
Gs += sin_table[j] * sinf(am);
|
||||||
|
Gc += cos_table[j] * cosf(am);
|
||||||
|
|
||||||
|
#if DEBUG1
|
||||||
|
dw_printf ("%6d %6.2f %6.2f %6.2f\n", j, shape, sin_table[j], cos_table[j]) ;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Normalize for unity gain */
|
||||||
|
|
||||||
|
#if DEBUG1
|
||||||
|
dw_printf ("Before normalizing, Gs = %.2f, Gc = %.2f\n", Gs, Gc) ;
|
||||||
|
#endif
|
||||||
|
for (j=0; j<filter_size; j++) {
|
||||||
|
sin_table[j] = sin_table[j] / Gs;
|
||||||
|
cos_table[j] = cos_table[j] / Gc;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end gen_ms */
|
||||||
|
|
||||||
|
|
||||||
/* end dsp.c */
|
/* end dsp.c */
|
5
dsp.h
5
dsp.h
|
@ -5,6 +5,9 @@
|
||||||
|
|
||||||
float window (bp_window_t type, int size, int j);
|
float window (bp_window_t type, int size, int j);
|
||||||
|
|
||||||
void gen_lowpass (float fc, float *lp_filter, int filter_size, bp_window_t wtype);
|
int gen_lowpass (float fc, float *lp_filter, int filter_size, bp_window_t wtype, float lp_delay_fract);
|
||||||
|
|
||||||
void gen_bandpass (float f1, float f2, float *bp_filter, int filter_size, bp_window_t wtype);
|
void gen_bandpass (float f1, float f2, float *bp_filter, int filter_size, bp_window_t wtype);
|
||||||
|
|
||||||
|
void gen_ms (int fc, int samples_per_sec, float *sin_table, float *cos_table, int filter_size, int wtype);
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,8 @@ struct demodulator_state_s
|
||||||
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.
|
||||||
|
|
||||||
|
int play_it_again_sample; // Enable new synchronous demod in version 1.6.
|
||||||
|
|
||||||
#define TICKS_PER_PLL_CYCLE ( 256.0 * 256.0 * 256.0 * 256.0 )
|
#define TICKS_PER_PLL_CYCLE ( 256.0 * 256.0 * 256.0 * 256.0 )
|
||||||
|
|
||||||
int pll_step_per_sample; // PLL is advanced by this much each audio sample.
|
int pll_step_per_sample; // PLL is advanced by this much each audio sample.
|
||||||
|
@ -39,9 +41,17 @@ struct demodulator_state_s
|
||||||
|
|
||||||
int ms_filter_size; /* Size of mark & space filters, in audio samples. */
|
int ms_filter_size; /* Size of mark & space filters, in audio samples. */
|
||||||
/* Started off as a guess of one bit length */
|
/* Started off as a guess of one bit length */
|
||||||
/* but somewhat longer turned out to be better. */
|
/* but about 2 bit times turned out to be better. */
|
||||||
/* Currently using same size for any prefilter. */
|
/* Currently using same size for any prefilter. */
|
||||||
|
|
||||||
|
int m2_filter_size;
|
||||||
|
int s2_filter_size; /* Size of mark & space filters, in audio samples */
|
||||||
|
/* for the synchronous demodulator. I'm expecting */
|
||||||
|
/* smaller, perhaps just over 1 bit time here. */
|
||||||
|
|
||||||
|
int lp2_filter_size; /* FSK resampling - Size of Low Pass filter, in audio samples. */
|
||||||
|
|
||||||
|
|
||||||
#define MAX_FILTER_SIZE 320 /* 304 is needed for profile C, 300 baud & 44100. */
|
#define MAX_FILTER_SIZE 320 /* 304 is needed for profile C, 300 baud & 44100. */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -49,6 +59,9 @@ struct demodulator_state_s
|
||||||
* e.g. 1 means 1/1200 second for 1200 baud.
|
* e.g. 1 means 1/1200 second for 1200 baud.
|
||||||
*/
|
*/
|
||||||
float ms_filter_len_bits;
|
float ms_filter_len_bits;
|
||||||
|
float m2_filter_len_bits;
|
||||||
|
float s2_filter_len_bits;
|
||||||
|
float lp_delay_fract;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Window type for the various filters.
|
* Window type for the various filters.
|
||||||
|
@ -57,6 +70,7 @@ struct demodulator_state_s
|
||||||
bp_window_t pre_window;
|
bp_window_t pre_window;
|
||||||
bp_window_t ms_window;
|
bp_window_t ms_window;
|
||||||
bp_window_t lp_window;
|
bp_window_t lp_window;
|
||||||
|
bp_window_t ms2_window; /* New in 1.6. */
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -80,6 +94,12 @@ struct demodulator_state_s
|
||||||
/* Previously it was always the same as the M/S */
|
/* Previously it was always the same as the M/S */
|
||||||
/* filters but in version 1.2 it's now independent. */
|
/* filters but in version 1.2 it's now independent. */
|
||||||
|
|
||||||
|
int lp_filter_delay; /* Number of samples that the low pass filter */
|
||||||
|
/* delays the signal. */
|
||||||
|
|
||||||
|
/* New in 1.6. */
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Automatic gain control. Fast attack and slow decay factors.
|
* Automatic gain control. Fast attack and slow decay factors.
|
||||||
*/
|
*/
|
||||||
|
@ -135,6 +155,18 @@ 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)));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Same for the synchronous re-demodulator.
|
||||||
|
*/
|
||||||
|
|
||||||
|
float m2_sin_table[MAX_FILTER_SIZE] __attribute__((aligned(16)));
|
||||||
|
float m2_cos_table[MAX_FILTER_SIZE] __attribute__((aligned(16)));
|
||||||
|
|
||||||
|
float s2_sin_table[MAX_FILTER_SIZE] __attribute__((aligned(16)));
|
||||||
|
float s2_cos_table[MAX_FILTER_SIZE] __attribute__((aligned(16)));
|
||||||
|
|
||||||
|
float lp2_filter[MAX_FILTER_SIZE] __attribute__((aligned(16)));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These are for PSK only.
|
* These are for PSK only.
|
||||||
* They are number of delay line taps into previous symbol.
|
* They are number of delay line taps into previous symbol.
|
||||||
|
|
|
@ -250,7 +250,9 @@ int main(int argc, char **argv)
|
||||||
/* 9600 implies scrambled. */
|
/* 9600 implies scrambled. */
|
||||||
|
|
||||||
/* If you want something else, specify -B first */
|
/* If you want something else, specify -B first */
|
||||||
/* then anything to override these defaults. */
|
/* then anything to override these defaults with -m, -s, or -g. */
|
||||||
|
|
||||||
|
// FIXME: options should not be order dependent.
|
||||||
|
|
||||||
modem.achan[0].baud = atoi(optarg);
|
modem.achan[0].baud = atoi(optarg);
|
||||||
text_color_set(DW_COLOR_INFO);
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
@ -308,6 +310,8 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
case 'g': /* -g for g3ruh scrambling */
|
case 'g': /* -g for g3ruh scrambling */
|
||||||
|
|
||||||
|
// FIXME: order dependent. -g must come after -B.
|
||||||
|
|
||||||
modem.achan[0].modem_type = MODEM_SCRAMBLE;
|
modem.achan[0].modem_type = MODEM_SCRAMBLE;
|
||||||
text_color_set(DW_COLOR_INFO);
|
text_color_set(DW_COLOR_INFO);
|
||||||
dw_printf ("Using scrambled baseband signal rather than AFSK.\n");
|
dw_printf ("Using scrambled baseband signal rather than AFSK.\n");
|
||||||
|
|
196
gen_tone.c
196
gen_tone.c
|
@ -1,10 +1,7 @@
|
||||||
//#define DEBUG 1
|
|
||||||
//#define DEBUG2 1
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// 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, 2016 John Langner, WB2OSZ
|
// Copyright (C) 2011, 2014, 2015, 2016, 2019 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
|
||||||
|
@ -106,59 +103,9 @@ static int bit_count[MAX_CHANS]; // Counter incremented for each bit transmitted
|
||||||
static int save_bit[MAX_CHANS];
|
static int save_bit[MAX_CHANS];
|
||||||
|
|
||||||
|
|
||||||
/*
|
static int prev_dat[MAX_CHANS]; // Previous data bit. Used for G3RUH style.
|
||||||
* The K9NG/G3RUH output originally took a very simple and lazy approach.
|
|
||||||
* We simply generated a square wave with + or - the desired amplitude.
|
|
||||||
* This has a couple undesirable properties.
|
|
||||||
*
|
|
||||||
* - Transmitting a square wave would splatter into adjacent
|
|
||||||
* channels of the transmitter doesn't limit the bandwidth.
|
|
||||||
*
|
|
||||||
* - The usual sample rate of 44100 is not a multiple of the
|
|
||||||
* baud rate so jitter would be added to the zero crossings.
|
|
||||||
*
|
|
||||||
* Starting in version 1.2, we try to overcome these issues by using
|
|
||||||
* a higher sample rate, low pass filtering, and down sampling.
|
|
||||||
*
|
|
||||||
* What sort of low pass filter would be appropriate? Intuitively,
|
|
||||||
* we would expect a cutoff frequency somewhere between baud/2 and baud.
|
|
||||||
* The current values were found with a small amount of trial and
|
|
||||||
* error for best results. Future improvement is certainly possible.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For low pass filtering of 9600 baud data.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Add sample to buffer and shift the rest down. */
|
|
||||||
// TODO: Can we have one copy of these in dsp.h?
|
|
||||||
|
|
||||||
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. */
|
|
||||||
|
|
||||||
static inline float convolve (const float *data, const float *filter, int filter_size)
|
|
||||||
{
|
|
||||||
float sum = 0;
|
|
||||||
int j;
|
|
||||||
|
|
||||||
for (j=0; j<filter_size; j++) {
|
|
||||||
sum += filter[j] * data[j];
|
|
||||||
}
|
|
||||||
return (sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lp_filter_size[MAX_CHANS];
|
|
||||||
static float raw[MAX_CHANS][MAX_FILTER_SIZE] __attribute__((aligned(16)));
|
|
||||||
static float lp_filter[MAX_CHANS][MAX_FILTER_SIZE] __attribute__((aligned(16)));
|
|
||||||
static int resample[MAX_CHANS];
|
|
||||||
|
|
||||||
#define UPSAMPLE 2
|
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------
|
/*------------------------------------------------------------------
|
||||||
|
@ -180,6 +127,8 @@ static int resample[MAX_CHANS];
|
||||||
*
|
*
|
||||||
* amp - Signal amplitude on scale of 0 .. 100.
|
* amp - Signal amplitude on scale of 0 .. 100.
|
||||||
*
|
*
|
||||||
|
* 100% uses the full 16 bit sample range of +-32k.
|
||||||
|
*
|
||||||
* gen_packets - True if being called from "gen_packets" utility
|
* gen_packets - True if being called from "gen_packets" utility
|
||||||
* rather than the "direwolf" application.
|
* rather than the "direwolf" application.
|
||||||
*
|
*
|
||||||
|
@ -211,9 +160,7 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets)
|
||||||
|
|
||||||
save_audio_config_p = audio_config_p;
|
save_audio_config_p = audio_config_p;
|
||||||
|
|
||||||
|
amp16bit = (int)((32767 * amp) / 100);
|
||||||
amp16bit = (32767 * amp) / 100;
|
|
||||||
|
|
||||||
|
|
||||||
for (chan = 0; chan < MAX_CHANS; chan++) {
|
for (chan = 0; chan < MAX_CHANS; chan++) {
|
||||||
|
|
||||||
|
@ -266,7 +213,15 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets)
|
||||||
f2_change_per_sample[chan] = f1_change_per_sample[chan]; // Not used.
|
f2_change_per_sample[chan] = f1_change_per_sample[chan]; // Not used.
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case MODEM_BASEBAND:
|
||||||
|
case MODEM_SCRAMBLE:
|
||||||
|
|
||||||
|
// Tone is half baud.
|
||||||
|
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].baud * 0.5 * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // AFSK
|
||||||
|
|
||||||
ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->achan[chan].baud ) + 0.5);
|
ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->achan[chan].baud ) + 0.5);
|
||||||
f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5);
|
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);
|
||||||
|
@ -298,75 +253,6 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets)
|
||||||
sine_table[j] = s;
|
sine_table[j] = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Low pass filter for 9600 baud.
|
|
||||||
*/
|
|
||||||
|
|
||||||
for (chan = 0; chan < MAX_CHANS; chan++) {
|
|
||||||
|
|
||||||
if (audio_config_p->achan[chan].valid &&
|
|
||||||
(audio_config_p->achan[chan].modem_type == MODEM_SCRAMBLE
|
|
||||||
|| audio_config_p->achan[chan].modem_type == MODEM_BASEBAND)) {
|
|
||||||
|
|
||||||
int a = ACHAN2ADEV(chan);
|
|
||||||
int samples_per_sec; /* Might be scaled up! */
|
|
||||||
int baud;
|
|
||||||
|
|
||||||
/* These numbers were by trial and error. Need more investigation here. */
|
|
||||||
|
|
||||||
float filter_len_bits = 88 * 9600.0 / (44100.0 * 2.0);
|
|
||||||
/* Filter length in number of data bits. */
|
|
||||||
/* Currently 9.58 */
|
|
||||||
|
|
||||||
float lpf_baud = 0.8; /* Lowpass cutoff freq as fraction of baud rate */
|
|
||||||
|
|
||||||
float fc; /* Cutoff frequency as fraction of sampling frequency. */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Normally, we want to generate the same thing whether sending over the air
|
|
||||||
* or putting it into a file for other testing.
|
|
||||||
* (There is an important exception. gen_packets can introduce random noise.)
|
|
||||||
* In this case, we want more aggressive low pass filtering so it looks more like
|
|
||||||
* what we see coming out of a receiver.
|
|
||||||
* Specifically, single bits of the same state have considerably reduced amplitude
|
|
||||||
* below several same values in a row.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (gen_packets) {
|
|
||||||
filter_len_bits = 4;
|
|
||||||
lpf_baud = 0.55; /* Lowpass cutoff freq as fraction of baud rate */
|
|
||||||
}
|
|
||||||
|
|
||||||
samples_per_sec = audio_config_p->adev[a].samples_per_sec * UPSAMPLE;
|
|
||||||
baud = audio_config_p->achan[chan].baud;
|
|
||||||
|
|
||||||
ticks_per_sample[chan] = (int) ((TICKS_PER_CYCLE / (double)samples_per_sec ) + 0.5);
|
|
||||||
ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)baud ) + 0.5);
|
|
||||||
|
|
||||||
lp_filter_size[chan] = (int) (( filter_len_bits * (float)samples_per_sec / baud) + 0.5);
|
|
||||||
|
|
||||||
if (lp_filter_size[chan] < 10) {
|
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
|
||||||
dw_printf ("gen_tone_init: unexpected, chan %d, lp_filter_size %d < 10\n", chan, lp_filter_size[chan]);
|
|
||||||
lp_filter_size[chan] = 10;
|
|
||||||
}
|
|
||||||
else if (lp_filter_size[chan] > MAX_FILTER_SIZE) {
|
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
|
||||||
dw_printf ("gen_tone_init: unexpected, chan %d, lp_filter_size %d > %d\n", chan, lp_filter_size[chan], MAX_FILTER_SIZE);
|
|
||||||
lp_filter_size[chan] = MAX_FILTER_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
fc = (float)baud * lpf_baud / (float)samples_per_sec;
|
|
||||||
|
|
||||||
//text_color_set(DW_COLOR_DEBUG);
|
|
||||||
//dw_printf ("gen_tone_init: chan %d, call gen_lowpass(fc=%.2f, , size=%d, )\n", chan, fc, lp_filter_size[chan]);
|
|
||||||
|
|
||||||
gen_lowpass (fc, lp_filter[chan], lp_filter_size[chan], BP_WINDOW_HAMMING);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
} /* end gen_tone_init */
|
} /* end gen_tone_init */
|
||||||
|
@ -389,6 +275,11 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets)
|
||||||
*
|
*
|
||||||
* Version 1.4: Attempt to implement 2400 and 4800 bps PSK modes.
|
* Version 1.4: Attempt to implement 2400 and 4800 bps PSK modes.
|
||||||
*
|
*
|
||||||
|
* Version 1.6: For G3RUH, rather than generating square wave and low
|
||||||
|
* pass filtering, generate the waveform directly.
|
||||||
|
* This avoids overshoot, ringing, and adding more jitter.
|
||||||
|
* Alternating bits come out has sine wave of baud/2 Hz.
|
||||||
|
*
|
||||||
*--------------------------------------------------------------------*/
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
static const int gray2phase_v26[4] = {0, 1, 3, 2};
|
static const int gray2phase_v26[4] = {0, 1, 3, 2};
|
||||||
|
@ -469,7 +360,6 @@ void tone_gen_put_bit (int chan, int dat)
|
||||||
do { /* until enough audio samples for this symbol. */
|
do { /* until enough audio samples for this symbol. */
|
||||||
|
|
||||||
int sam;
|
int sam;
|
||||||
float fsam;
|
|
||||||
|
|
||||||
switch (save_audio_config_p->achan[chan].modem_type) {
|
switch (save_audio_config_p->achan[chan].modem_type) {
|
||||||
|
|
||||||
|
@ -499,23 +389,17 @@ void tone_gen_put_bit (int chan, int dat)
|
||||||
case MODEM_BASEBAND:
|
case MODEM_BASEBAND:
|
||||||
case MODEM_SCRAMBLE:
|
case MODEM_SCRAMBLE:
|
||||||
|
|
||||||
#if DEBUG2
|
if (dat != prev_dat[chan]) {
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
tone_phase[chan] += f1_change_per_sample[chan];
|
||||||
dw_printf ("tone_gen_put_bit %d SCR\n", __LINE__);
|
|
||||||
#endif
|
|
||||||
fsam = dat ? amp16bit : (-amp16bit);
|
|
||||||
|
|
||||||
/* version 1.2 - added a low pass filter instead of square wave out. */
|
|
||||||
|
|
||||||
push_sample (fsam, raw[chan], lp_filter_size[chan]);
|
|
||||||
|
|
||||||
resample[chan]++;
|
|
||||||
if (resample[chan] >= UPSAMPLE) {
|
|
||||||
|
|
||||||
sam = (int) convolve (raw[chan], lp_filter[chan], lp_filter_size[chan]);
|
|
||||||
resample[chan] = 0;
|
|
||||||
gen_tone_put_sample (chan, a, sam);
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
if (tone_phase[chan] & 0x80000000)
|
||||||
|
tone_phase[chan] = 0xc0000000; // 270 degrees.
|
||||||
|
else
|
||||||
|
tone_phase[chan] = 0x40000000; // 90 degrees.
|
||||||
|
}
|
||||||
|
sam = sine_table[(tone_phase[chan] >> 24) & 0xff];
|
||||||
|
gen_tone_put_sample (chan, a, sam);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -532,7 +416,10 @@ void tone_gen_put_bit (int chan, int dat)
|
||||||
} while (bit_len_acc[chan] < ticks_per_bit[chan]);
|
} while (bit_len_acc[chan] < ticks_per_bit[chan]);
|
||||||
|
|
||||||
bit_len_acc[chan] -= ticks_per_bit[chan];
|
bit_len_acc[chan] -= ticks_per_bit[chan];
|
||||||
}
|
|
||||||
|
prev_dat[chan] = dat; // Only needed for G3RUH baseband/scrambled.
|
||||||
|
|
||||||
|
} /* end tone_gen_put_bit */
|
||||||
|
|
||||||
|
|
||||||
void gen_tone_put_sample (int chan, int a, int sam) {
|
void gen_tone_put_sample (int chan, int a, int sam) {
|
||||||
|
@ -547,10 +434,21 @@ void gen_tone_put_sample (int chan, int a, int sam) {
|
||||||
|
|
||||||
assert (save_audio_config_p->adev[a].bits_per_sample == 16 || save_audio_config_p->adev[a].bits_per_sample == 8);
|
assert (save_audio_config_p->adev[a].bits_per_sample == 16 || save_audio_config_p->adev[a].bits_per_sample == 8);
|
||||||
|
|
||||||
// TODO: Should print message telling user to reduce output level.
|
// Bad news if we are clipping and distorting the signal.
|
||||||
|
// We are using the full range.
|
||||||
|
// Too late to change now because everyone would need to recalibrate their
|
||||||
|
// transmit audio level.
|
||||||
|
|
||||||
if (sam < -32767) sam = -32767;
|
if (sam < -32767) {
|
||||||
else if (sam > 32767) sam = 32767;
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Warning: Audio sample %d clipped to -32767.\n", sam);
|
||||||
|
sam = -32767;
|
||||||
|
}
|
||||||
|
else if (sam > 32767) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Warning: Audio sample %d clipped to +32767.\n", sam);
|
||||||
|
sam = 32767;
|
||||||
|
}
|
||||||
|
|
||||||
if (save_audio_config_p->adev[a].num_channels == 1) {
|
if (save_audio_config_p->adev[a].num_channels == 1) {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue