diff --git a/CHANGES.md b/CHANGES.md index b22e3b3..d1ff7b3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,8 @@ - "-g" option to force G3RUH mode for lower speeds where a different modem type may be the default. +- 2400 bps compatibility with MFJ-2400. See ***2400-4800-PSK-for-APRS-Packet-Radio.pdf*** for details + - "atest -h" will display the frame in hexadecimal for closer inspection. - Add support for Multi-GNSS NMEA sentences. diff --git a/Makefile.linux b/Makefile.linux index d695f92..591526d 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -735,7 +735,7 @@ install-rpi : # 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-modem2400-g check-modem4800 +check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest pad2test xidtest dtmftest check-modem1200 check-modem300 check-modem9600 check-modem19200 check-modem2400-a check-modem2400-b check-modem2400-g check-modem4800 # Can we encode and decode at popular data rates? @@ -763,11 +763,17 @@ check-modem19200 : gen_packets atest ./atest -B19200 -F1 -L64 -G68 /tmp/test19.wav rm /tmp/test19.wav -check-modem2400 : gen_packets atest - ./gen_packets -B2400 -n 100 -o /tmp/test24.wav - ./atest -B2400 -F0 -L70 -G78 /tmp/test24.wav - ./atest -B2400 -F1 -L80 -G87 /tmp/test24.wav - rm /tmp/test24.wav +check-modem2400-a : gen_packets atest + ./gen_packets -B2400 -j -n 100 -o /tmp/test24-a.wav + ./atest -B2400 -j -F0 -L76 -G80 test24-a.wav + ./atest -B2400 -j -F1 -L84 -G88 test24-a.wav + rm /tmp/test24-a.wav + +check-modem2400-b : gen_packets atest + ./gen_packets -B2400 -J -n 100 -o /tmp/test24-b.wav + ./atest -B2400 -J -F0 -L79 -G83 test24-b.wav + ./atest -B2400 -J -F1 -L87 -G91 test24-b.wav + rm /tmp/test24-b.wav check-modem2400-g : gen_packets atest ./gen_packets -B2400 -g -n 100 -o /tmp/test24-g.wav diff --git a/Makefile.win b/Makefile.win index 51f3519..44f9855 100644 --- a/Makefile.win +++ b/Makefile.win @@ -277,7 +277,7 @@ strlcat.o : misc/strlcat.c # 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-modem2400-g check-modem4800 +check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest pad2test xidtest dtmftest check-modem1200 check-modem300 check-modem9600 check-modem19200 check-modem2400-a check-modem2400-b check-modem2400-g check-modem4800 # Can we encode and decode at popular data rates? # Verify that single bit fixup increases the count. @@ -324,13 +324,21 @@ check-modem19200 : gen_packets atest sleep 1 rm test19.wav -check-modem2400 : gen_packets atest - gen_packets -B2400 -n 100 -o test24.wav +check-modem2400-a : gen_packets atest + gen_packets -B2400 -j -n 100 -o test24-a.wav sleep 1 - atest -B2400 -F0 -L70 -G78 test24.wav - atest -B2400 -F1 -L80 -G87 test24.wav + atest -B2400 -j -F0 -L76 -G80 test24-a.wav + atest -B2400 -j -F1 -L84 -G88 test24-a.wav sleep 1 - rm test24.wav + rm test24-a.wav + +check-modem2400-b : gen_packets atest + gen_packets -B2400 -J -n 100 -o test24-b.wav + sleep 1 + atest -B2400 -J -F0 -L79 -G83 test24-b.wav + atest -B2400 -J -F1 -L87 -G91 test24-b.wav + sleep 1 + rm test24-b.wav check-modem2400-g : gen_packets atest gen_packets -B2400 -g -n 100 -o test24-g.wav @@ -518,8 +526,8 @@ testagc96 : atest.c fsk_fast_filter.h tune.h demod.c demod_afsk.c demod_psk.c de misc.a regex.a rm -f atest96.exe $(CC) $(CFLAGS) -o atest96 $^ - ./atest96 -B 9600 ../walkabout9600c.wav | grep "packets decoded in" >atest.out - #./atest96 -B 9600 ../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 ../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 ../walkabout9600c.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 19990303_0225_9600_8bis_22kHz.wav | grep "packets decoded in" >atest.out @@ -536,10 +544,22 @@ testagc24 : atest.c fsk_fast_filter.h tune.h demod.c demod_afsk.c demod_psk.c de misc.a regex.a rm -f atest24.exe sleep 1 - $(CC) $(CFLAGS) -o atest24 $^ + $(CC) $(CFLAGS) -o atest24mfj $^ ./atest24 -B 2400 test2400.wav | grep "packets decoded in" >atest.out echo " " > tune.h +testagc24mfj : atest.c fsk_fast_filter.h tune.h demod.c demod_afsk.c demod_psk.c demod_9600.c \ + dsp.o hdlc_rec.o hdlc_rec2.o multi_modem.o \ + rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o \ + dwgpsnmea.o dwgps.o serial_port.o latlong.o \ + symbols.o tt_text.o textcolor.o telemetry.o dtime_now.o \ + misc.a regex.a + rm -f atest24mfj.exe + sleep 1 + $(CC) $(CFLAGS) -o atest24mfj $^ + ./atest24mfj -F 1 -B 2400 ../ref-doc/MFJ-2400-PSK/2k4_short.wav + echo " " > tune.h + testagc48 : atest.c fsk_fast_filter.h tune.h demod.c demod_afsk.c demod_psk.c demod_9600.c \ dsp.o hdlc_rec.o hdlc_rec2.o multi_modem.o \ rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o \ diff --git a/atest.c b/atest.c index 91b5114..d68764b 100644 --- a/atest.c +++ b/atest.c @@ -2,7 +2,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016 John Langner, WB2OSZ +// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2019 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -182,7 +182,10 @@ static int sample_number = -1; /* Sample number from the file. */ 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 j_opt = 0; /* 2400 bps PSK compatible with direwolf <= 1.5 */ +static int J_opt = 0; /* 2400 bps PSK compatible MFJ-2400 and maybe others. */ static int h_opt = 0; // Hexadecimal display of received packet. +static char P_opt[16] = ""; // Demodulator profiles. int main (int argc, char *argv[]) @@ -219,49 +222,6 @@ int main (int argc, char *argv[]) my_audio_config.adev[0].samples_per_sec = DEFAULT_SAMPLES_PER_SEC; my_audio_config.adev[0].bits_per_sample = DEFAULT_BITS_PER_SAMPLE; - // Results v0.9: - // - // fix_bits = 0 971 packets, 69 sec - // fix_bits = SINGLE 990 64 - // fix_bits = DOUBLE 992 65 - // fix_bits = TRIPLE 992 67 - // fix_bits = TWO_SEP 1004 476 - - // Essentially no difference in time for those with order N time. - // Time increases greatly for the one with order N^2 time. - - - // Results: version 1.1, decoder C, my_audio_config.fix_bits = RETRY_MAX - 1 - // - // 971 NONE - // +19 SINGLE - // +2 DOUBLE - // +12 TWO_SEP - // +3 REMOVE_MANY - // ---- - // 1007 Total in 1008 sec. More than twice as long as earlier version. - - // Results: version 1.1, decoders ABC, my_audio_config.fix_bits = RETRY_MAX - 1 - // - // 976 NONE - // +21 SINGLE - // +1 DOUBLE - // +22 TWO_SEP - // +1 MANY - // +3 REMOVE_MANY - // ---- - // 1024 Total in 2042 sec. - // About 34 minutes of CPU time for a roughly 40 minute CD. - // Many computers wouldn't be able to keep up. - - // The SINGLE and TWO_SEP techniques are the most effective. - // Should we reorder the enum values so that TWO_SEP - // comes after SINGLE? That way "FIX_BITS 2" would - // use the two most productive techniques and not waste - // time on the others. People with plenty of CPU power - // to spare can still specify larger numbers for the other - // techniques with less return on investment. - for (channel=0; channel 0) { + dw_printf ("Demodulator profile set to \"%s\"\n", P_opt); + strlcpy (my_audio_config.achan[0].profiles, P_opt, sizeof(my_audio_config.achan[0].profiles)); + } + memcpy (&my_audio_config.achan[1], &my_audio_config.achan[0], sizeof(my_audio_config.achan[0])); @@ -879,11 +883,15 @@ static void usage (void) { dw_printf (" atest [ options ] wav-file-in\n"); dw_printf ("\n"); dw_printf (" -B n Bits/second for data. Proper modem automatically selected for speed.\n"); - dw_printf (" 300 baud uses 1600/1800 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 (" 300 bps defaults to AFSK tones of 1600 & 1800.\n"); + dw_printf (" 1200 bps uses AFSK tones of 1200 & 2200.\n"); + dw_printf (" 2400 bps uses QPSK based on V.26 standard.\n"); + dw_printf (" 4800 bps uses 8PSK based on V.27 standard.\n"); + dw_printf (" 9600 bps and up uses K9NG/G3RUH standard.\n"); dw_printf ("\n"); - dw_printf (" -g Force G3RUH modem rather rather than default for data rate.\n"); + dw_printf (" -g Use G3RUH modem rather rather than default for data rate.\n"); + dw_printf (" -j 2400 bps QPSK compatible with direwolf <= 1.5.\n"); + dw_printf (" -J 2400 bps QPSK compatible with MFJ-2400.\n"); dw_printf ("\n"); dw_printf (" -D n Divide audio sample rate by n.\n"); dw_printf ("\n"); @@ -894,8 +902,12 @@ static void usage (void) { dw_printf (" 1 = Try to fix only a single bit. \n"); dw_printf (" more = Try modifying more bits to get a good CRC.\n"); dw_printf ("\n"); - dw_printf (" -P m Select the demodulator type such as A, B, C, D (default for 300 baud),\n"); - dw_printf (" E (default for 1200 baud), F, A+, B+, C+, D+, E+, F+.\n"); + dw_printf (" -L Error if less than this number decoded.\n"); + dw_printf ("\n"); + dw_printf (" -G Error if greater than this number decoded.\n"); + dw_printf ("\n"); + dw_printf (" -P m Select the demodulator type such as D (default for 300 bps),\n"); + dw_printf (" E+ (default for 1200 bps), PQRS for 2400 bps, etc.\n"); dw_printf ("\n"); dw_printf (" -0 Use channel 0 (left) of stereo audio (default).\n"); dw_printf (" -1 use channel 1 (right) of stereo audio.\n"); @@ -918,11 +930,11 @@ static void usage (void) { dw_printf (" bits per second.\n"); dw_printf ("\n"); dw_printf (" atest 02_Track_2.wav\n"); - dw_printf (" atest -P C+ 02_Track_2.wav\n"); + dw_printf (" atest -P E- 02_Track_2.wav\n"); dw_printf (" atest -F 1 02_Track_2.wav\n"); - dw_printf (" atest -P C+ -F 1 02_Track_2.wav\n"); + dw_printf (" atest -P E- -F 1 02_Track_2.wav\n"); dw_printf ("\n"); - dw_printf (" Try different combinations of options to find the best decoding\n"); + dw_printf (" Try different combinations of options to compare decoding\n"); dw_printf (" performance.\n"); exit (1); diff --git a/audio.h b/audio.h index b427cfe..0cda022 100644 --- a/audio.h +++ b/audio.h @@ -18,7 +18,7 @@ #include "direwolf.h" /* for MAX_CHANS used throughout the application. */ #include "ax25_pad.h" /* for AX25_MAX_ADDR_LEN */ - +#include "version.h" /* @@ -141,6 +141,20 @@ struct audio_s { /* Might try MFJ-2400 / CCITT v.26 / Bell 201 someday. */ /* No modem. Might want this for DTMF only channel. */ + enum v26_e { V26_UNSPECIFIED=0, V26_A, V26_B } v26_alternative; + + // Original implementaion used alternative A for 2400 bbps PSK. + // Years later, we discover that MFJ-2400 used alternative B. + // It's likely the others did too. + // For release 1.6, default to original style but print warning. + // Later default to MFJ compatible and still print warning if + // if user did not pick one explicitly. + +#if (MAJOR_VERSION > 1) || (MINOR_VERSION > 6) +#define V26_DEFAULT V26_B +#else +#define V26_DEFAULT V26_A +#endif enum dtmf_decode_t { DTMF_DECODE_OFF, DTMF_DECODE_ON } dtmf_decode; diff --git a/config.c b/config.c index 348618e..9d6c083 100644 --- a/config.c +++ b/config.c @@ -761,6 +761,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, /* set to radio when corresponding */ /* audio device is defined. */ p_audio_config->achan[channel].modem_type = MODEM_AFSK; + p_audio_config->achan[channel].v26_alternative = V26_UNSPECIFIED; p_audio_config->achan[channel].mark_freq = DEFAULT_MARK_FREQ; /* -m option */ p_audio_config->achan[channel].space_freq = DEFAULT_SPACE_FREQ; /* -s option */ p_audio_config->achan[channel].baud = DEFAULT_BAUD; /* -b option */ @@ -1264,6 +1265,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, * *9 - Upsample ratio for G3RUH. * [A-Z+-]+ - Letters, plus, minus for the demodulator "profile." * g3ruh - This modem type regardless of default for speed. + * v26a or v26b - V.26 alternative. a=original, b=MFJ compatible */ else if (strcasecmp(t, "MODEM") == 0) { @@ -1540,6 +1542,19 @@ void config_init (char *fname, struct audio_s *p_audio_config, p_audio_config->achan[channel].space_freq = 0; } + else if (strcasecmp(t, "V26A") == 0 || /* Compatible with direwolf versions <= 1.5. New in 1.6. */ + strcasecmp(t, "V26B") == 0) { /* Compatible with MFJ-2400. New in 1.6. */ + + if (p_audio_config->achan[channel].modem_type != MODEM_QPSK || + p_audio_config->achan[channel].baud != 2400) { + + text_color_set(DW_COLOR_ERROR); + dw_printf ("Line %d: %s option can only be used with 2400 bps PSK.\n", line, t); + continue; + } + p_audio_config->achan[channel].v26_alternative = (strcasecmp(t, "V26A") == 0) ? V26_A : V26_B; + } + else { text_color_set(DW_COLOR_ERROR); dw_printf ("Line %d: Unrecognized option for MODEM: %s\n", line, t); diff --git a/demod.c b/demod.c index baa28db..312123b 100644 --- a/demod.c +++ b/demod.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016 John Langner, WB2OSZ +// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2019 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -94,7 +94,6 @@ static int sample_count[MAX_CHANS][MAX_SUBCHANS]; int demod_init (struct audio_s *pa) { - //int j; int chan; /* Loop index over number of radio channels. */ char profile; @@ -520,6 +519,31 @@ int demod_init (struct audio_s *pa) case MODEM_QPSK: // New for 1.4 + // In versions 1.4 and 1.5, V.26 "Alternative A" was used. + // years later, I discover that the MFJ-2400 used "Alternative B." + // It looks like the other two manufacturers use the same but we + // can't be sure until we find one for compatbility testing. + + // In version 1.6 we add a choice for the user. + // If neither one was explicitly specified, print a message and take + // a default. My current thinking is that we default to direwolf <= 1.5 + // compatible for version 1.6 and MFJ compatible after that. + + if (save_audio_config_p->achan[chan].v26_alternative == V26_UNSPECIFIED) { + + text_color_set(DW_COLOR_ERROR); + dw_printf ("Two incompatible versions of 2400 bps QPSK are now available.\n"); + dw_printf ("For compatbility with direwolf <= 1.5, use 'V26A' modem option in config file.\n"); + dw_printf ("For compatbility MFJ-2400 use 'V26B' modem option in config file.\n"); + dw_printf ("Command line options -j and -J can be used for channel 0.\n"); + dw_printf ("For more information, read the Dire Wolf User Guide and\n"); + dw_printf ("2400-4800-PSK-for-APRS-Packet-Radio.pdf.\n"); + dw_printf ("The default in this release could be different in a later release.\n"); + + save_audio_config_p->achan[chan].v26_alternative = V26_DEFAULT; + } + + // TODO: See how much CPU this takes on ARM and decide if we should have different defaults. if (strlen(save_audio_config_p->achan[chan].profiles) == 0) { @@ -539,6 +563,12 @@ int demod_init (struct audio_s *pa) save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec); if (save_audio_config_p->achan[chan].decimate != 1) dw_printf (" / %d", save_audio_config_p->achan[chan].decimate); + + if (save_audio_config_p->achan[chan].v26_alternative == V26_B) + dw_printf (", compatible with MFJ-2400"); + else + dw_printf (", compatible with earlier direwolf"); + if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF) dw_printf (", DTMF decoder enabled"); dw_printf (".\n"); @@ -556,6 +586,7 @@ int demod_init (struct audio_s *pa) // save_audio_config_p->achan[chan].modem_type, profile); demod_psk_init (save_audio_config_p->achan[chan].modem_type, + save_audio_config_p->achan[chan].v26_alternative, save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate, save_audio_config_p->achan[chan].baud, profile, @@ -610,6 +641,7 @@ int demod_init (struct audio_s *pa) // save_audio_config_p->achan[chan].modem_type, profile); demod_psk_init (save_audio_config_p->achan[chan].modem_type, + save_audio_config_p->achan[chan].v26_alternative, save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate, save_audio_config_p->achan[chan].baud, profile, diff --git a/demod_psk.c b/demod_psk.c index 3b2a487..1870b7e 100644 --- a/demod_psk.c +++ b/demod_psk.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2016 John Langner, WB2OSZ +// Copyright (C) 2016, 2019 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@ //#define DEBUG4 1 /* capture PSK demodulator output to log files */ +//#define DEBUG5 1 /* Print bit stream */ /*------------------------------------------------------------------ @@ -63,8 +64,7 @@ * http://www.brazoriacountyares.org/winlink-collection/TNC%20manuals/Kantronics/2400_modem_operators_guide@rgf.pdf * * - * The MFJ and AEA both use the EXAR XR-2123 PSK modem chip. - * The Kantronics has a P423 ??? + * From what I'm able to gather, they all used the EXAR XR-2123 PSK modem chip. * * Can't find the chip specs on the EXAR website so Google it. * @@ -79,8 +79,8 @@ * "bis" and "ter" are from Latin for second and third. * I used the "ter" version which has phase shifts of 0, 90, 180, and 270 degrees. * - * There are other references to an alternative B which uses other multiples of 45. - * The XR-2123 data sheet mentions only multiples of 90. That's what I went with. + * There are ealier references to an alternative B which uses other phase shifts offset + * by another 45 degrees. * * The XR-2123 does not perform the scrambling as specified in V.26 so I wonder if * the vendors implemented it in software or just left it out. @@ -161,6 +161,8 @@ static inline float my_atan2f (float y, float x) * * Inputs: modem_type - MODEM_QPSK or MODEM_8PSK. * + * v26_alt - V26_A (classic) or V25_B (MFJ compatible) + * * samples_per_sec - Audio sample rate. * * bps - Bits per second. @@ -189,7 +191,7 @@ static inline float my_atan2f (float y, float x) * *----------------------------------------------------------------*/ -void demod_psk_init (enum modem_t modem_type, int samples_per_sec, int bps, char profile, struct demodulator_state_s *D) +void demod_psk_init (enum modem_t modem_type, enum v26_e v26_alt, int samples_per_sec, int bps, char profile, struct demodulator_state_s *D) { int correct_baud; // baud is not same as bits/sec here! int carrier_freq; @@ -199,6 +201,8 @@ void demod_psk_init (enum modem_t modem_type, int samples_per_sec, int bps, char memset (D, 0, sizeof(struct demodulator_state_s)); D->modem_type = modem_type; + D->v26_alt = v26_alt; + D->num_slicers = 1; // Haven't thought about this yet. Is it even applicable? @@ -210,6 +214,8 @@ void demod_psk_init (enum modem_t modem_type, int samples_per_sec, int bps, char if (modem_type == MODEM_QPSK) { + assert (D->v26_alt != V26_UNSPECIFIED); + correct_baud = bps / 2; // Originally I thought of scaling it to the data rate, // e.g. 2400 bps -> 1800 Hz, but decided to make it a @@ -645,7 +651,16 @@ void demod_psk_process_sample (int chan, int subchan, int sam, struct demodulato id = ((int)((delta / (2.f * (float)M_PI) + 1.f) * 256.f)) & 0xff; if (D->modem_type == MODEM_QPSK) { - demod_phase_shift = ((id + 32) >> 6) & 0x3; +#ifdef TUNE_PSKOFFSET + demod_phase_shift = ((id + TUNE_PSKOFFSET) >> 6) & 0x3; +#else + if (D->v26_alt == V26_B) { + demod_phase_shift = ((id + 2) >> 6) & 0x3; // MFJ compatible + } + else { + demod_phase_shift = ((id + 32) >> 6) & 0x3; // Classic + } +#endif } else { demod_phase_shift = ((id + 16) >> 5) & 0x7; @@ -671,25 +686,20 @@ void demod_psk_process_sample (int chan, int subchan, int sam, struct demodulato if (D->modem_type == MODEM_QPSK) { -#if 1 // Speed up special case. - if (I > 0) { - if (Q > 0) - demod_phase_shift = 0; /* 0 to 90 degrees, etc. */ - else - demod_phase_shift = 1; - } - else { - if (Q > 0) - demod_phase_shift = 3; - else - demod_phase_shift = 2; - } -#else - a = my_atan2f(I,Q); + float a = my_atan2f(I,Q); int id = ((int)((a / (2.f * (float)M_PI) + 1.f) * 256.f)) & 0xff; // 128 compensates for 180 degree phase shift due // to 1 1/2 carrier cycles per symbol period. - demod_phase_shift = ((id + 128) >> 6) & 0x3; + +#ifdef TUNE_PSKOFFSET + demod_phase_shift = ((id + TUNE_PSKOFFSET) >> 6) & 0x3; +#else + if (D->v26_alt == V26_B) { + demod_phase_shift = ((id + 98) >> 6) & 0x3; // MFJ compatible + } + else { + demod_phase_shift = ((id + 128) >> 6) & 0x3; // Classic + } #endif } else { @@ -752,7 +762,13 @@ void demod_psk_process_sample (int chan, int subchan, int sam, struct demodulato } /* end demod_psk_process_sample */ + +#ifdef TUNE_GRAY +TUNE_GRAY +#else static const int phase_to_gray_v26[4] = {0, 1, 3, 2}; +#endif + static const int phase_to_gray_v27[8] = {1, 0, 2, 3, 7, 6, 4, 5}; @@ -813,6 +829,9 @@ inline static void nudge_pll (int chan, int subchan, int slice, int demod_bits, a * 360 / (2*M_PI), delta * 360 / (2*M_PI), demod_bits, (gray >> 1) & 1, gray & 1); //dw_printf ("phaseshift=%d, bits= %d %d \n", demod_bits, (gray >> 1) & 1, gray & 1); +#endif +#if DEBUG5 + dw_printf ("%d\n%d\n", (gray >> 1) & 1, gray & 1); #endif hdlc_rec_bit (chan, subchan, slice, (gray >> 1) & 1, 0, -1); hdlc_rec_bit (chan, subchan, slice, gray & 1, 0, -1); diff --git a/demod_psk.h b/demod_psk.h index 0f5830d..134b199 100644 --- a/demod_psk.h +++ b/demod_psk.h @@ -2,6 +2,6 @@ /* demod_psk.h */ -void demod_psk_init (enum modem_t modem_type, int samples_per_sec, int bps, char profile, struct demodulator_state_s *D); +void demod_psk_init (enum modem_t modem_type, enum v26_e v26_alt, int samples_per_sec, int bps, char profile, struct demodulator_state_s *D); void demod_psk_process_sample (int chan, int subchan, int sam, struct demodulator_state_s *D); diff --git a/direwolf.c b/direwolf.c index 73fc831..604fe88 100644 --- a/direwolf.c +++ b/direwolf.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017 John Langner, WB2OSZ +// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -201,6 +201,8 @@ int main (int argc, char *argv[]) int t_opt = 1; /* Text color option. */ 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 j_opt = 0; /* 2400 bps PSK compatible with direwolf <= 1.5 */ + int J_opt = 0; /* 2400 bps PSK compatible MFJ-2400 and maybe others. */ 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. */ @@ -354,7 +356,7 @@ int main (int argc, char *argv[]) /* ':' following option character means arg is required. */ - c = getopt_long(argc, argv, "P:B:gD:U:c:pxr:b:n:d:q:t:ul:L:Sa:E:T:", + c = getopt_long(argc, argv, "P:B:gjJD:U:c:pxr:b:n:d:q:t:ul:L:Sa:E:T:", long_options, &option_index); if (c == -1) break; @@ -411,6 +413,16 @@ int main (int argc, char *argv[]) g_opt = 1; break; + case 'j': /* -j V.26 compatible with earlier direwolf. */ + + j_opt = 1; + break; + + case 'J': /* -J V.26 compatible with MFJ-2400. */ + + J_opt = 1; + break; + case 'P': /* -P for modem profile. */ //debug: dw_printf ("Demodulator profile set to \"%s\"\n", optarg); @@ -691,11 +703,34 @@ int main (int argc, char *argv[]) audio_config.achan[0].space_freq = 0; } + if (j_opt) { + + // V.26 compatible with earlier versions of direwolf. + // Example: -B 2400 -j or simply -j + + audio_config.achan[0].v26_alternative = V26_A; + audio_config.achan[0].modem_type = MODEM_QPSK; + audio_config.achan[0].mark_freq = 0; + audio_config.achan[0].space_freq = 0; + audio_config.achan[0].baud = 2400; + } + if (J_opt) { + + // V.26 compatible with MFJ and maybe others. + // Example: -B 2400 -J or simply -J + + audio_config.achan[0].v26_alternative = V26_B; + audio_config.achan[0].modem_type = MODEM_QPSK; + audio_config.achan[0].mark_freq = 0; + audio_config.achan[0].space_freq = 0; + audio_config.achan[0].baud = 2400; + } + + audio_config.statistics_interval = a_opt; if (strlen(P_opt) > 0) { /* -P for modem profile. */ - /* TODO: Not yet documented. Should probably since it is consistent with atest. */ strlcpy (audio_config.achan[0].profiles, P_opt, sizeof(audio_config.achan[0].profiles)); } @@ -1293,6 +1328,9 @@ static void usage (char **argv) 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 (" -g Force G3RUH modem regardless of speed.\n"); + dw_printf (" -j 2400 bps QPSK compatible with direwolf <= 1.5.\n"); + dw_printf (" -J 2400 bps QPSK compatible with MFJ-2400.\n"); + dw_printf (" -P xxx Modem Profiles.\n"); dw_printf (" -D n Divide audio sample rate by n for channel 0.\n"); dw_printf (" -d Debug options:\n"); dw_printf (" a a = AGWPE network protocol client.\n"); diff --git a/doc/2400-4800-PSK-for-APRS-Packet-Radio.pdf b/doc/2400-4800-PSK-for-APRS-Packet-Radio.pdf index 077c5ff..4efd364 100644 Binary files a/doc/2400-4800-PSK-for-APRS-Packet-Radio.pdf and b/doc/2400-4800-PSK-for-APRS-Packet-Radio.pdf differ diff --git a/doc/User-Guide.pdf b/doc/User-Guide.pdf index f05dbbc..5632798 100644 Binary files a/doc/User-Guide.pdf and b/doc/User-Guide.pdf differ diff --git a/fsk_demod_state.h b/fsk_demod_state.h index cb791dd..8e89db7 100644 --- a/fsk_demod_state.h +++ b/fsk_demod_state.h @@ -28,6 +28,8 @@ struct demodulator_state_s */ enum modem_t modem_type; // MODEM_AFSK, MODEM_8PSK, etc. + enum v26_e v26_alt; // Which alternative when V.26. + char profile; // 'A', 'B', etc. Upper case. // Only needed to see if we are using 'F' to take fast path. @@ -145,6 +147,7 @@ struct demodulator_state_s float pre_filter[MAX_FILTER_SIZE] __attribute__((aligned(16))); + /* * Kernel for the mark and space detection filters. */ @@ -227,6 +230,7 @@ struct demodulator_state_s float m_valley, s_valley; float m_amp_prev, s_amp_prev; + /* * For the PLL and data bit timing. * starting in version 1.2 we can have multiple slicers for one demodulator. diff --git a/gen_packets.c b/gen_packets.c index 1b34908..63c3439 100644 --- a/gen_packets.c +++ b/gen_packets.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2013, 2014, 2015, 2016 John Langner, WB2OSZ +// Copyright (C) 2011, 2013, 2014, 2015, 2016, 2019 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -164,6 +164,9 @@ int main(int argc, char **argv) int chan; int experiment = 0; + int g_opt = 0; + int j_opt = 0; + int J_opt = 0; /* * Set up default values for the modem. @@ -215,7 +218,7 @@ int main(int argc, char **argv) /* ':' following option character means arg is required. */ - c = getopt_long(argc, argv, "gm:s:a:b:B:r:n:o:z:82M:X", + c = getopt_long(argc, argv, "gjJm:s:a:b:B:r:n:o:z:82M:X", long_options, &option_index); if (c == -1) break; @@ -310,11 +313,17 @@ int main(int argc, char **argv) case 'g': /* -g for g3ruh scrambling */ - // FIXME: order dependent. -g must come after -B. + g_opt = 1; + break; - modem.achan[0].modem_type = MODEM_SCRAMBLE; - text_color_set(DW_COLOR_INFO); - dw_printf ("Using scrambled baseband signal rather than AFSK.\n"); + case 'j': /* -j V.26 compatible with earlier direwolf. */ + + j_opt = 1; + break; + + case 'J': /* -J V.26 compatible with MFJ-2400. */ + + J_opt = 1; break; case 'm': /* -m for Mark freq */ @@ -449,6 +458,41 @@ int main(int argc, char **argv) } } +// These must be processed after -B option. + + if (g_opt) { /* -g for g3ruh scrambling */ + + modem.achan[0].modem_type = MODEM_SCRAMBLE; + text_color_set(DW_COLOR_INFO); + dw_printf ("Using G3RUH mode regardless of bit rate.\n"); + } + + if (j_opt) { /* -j V.26 compatible with earlier direwolf. */ + + modem.achan[0].v26_alternative = V26_A; + modem.achan[0].modem_type = MODEM_QPSK; + modem.achan[0].mark_freq = 0; + modem.achan[0].space_freq = 0; + modem.achan[0].baud = 2400; + } + + if (J_opt) { /* -J V.26 compatible with MFJ-2400. */ + + modem.achan[0].v26_alternative = V26_B; + modem.achan[0].modem_type = MODEM_QPSK; + modem.achan[0].mark_freq = 0; + modem.achan[0].space_freq = 0; + modem.achan[0].baud = 2400; + } + + if (modem.achan[0].modem_type == MODEM_QPSK && + modem.achan[0].v26_alternative == V26_UNSPECIFIED) { + + text_color_set(DW_COLOR_ERROR); + dw_printf ("ERROR: Either -j or -J must be specified when using 2400 bps QPSK.\n"); + usage (argv); + exit (1); + } /* * Open the output file. @@ -689,6 +733,8 @@ static void usage (char **argv) dw_printf (" -b Bits / second for data. Default is %d.\n", DEFAULT_BAUD); dw_printf (" -B Bits / second for data. Proper modem selected for 300, 1200, 2400, 4800, 9600.\n"); dw_printf (" -g Scrambled baseband rather than AFSK.\n"); + dw_printf (" -j 2400 bps QPSK compatible with direwolf <= 1.5.\n"); + dw_printf (" -J 2400 bps QPSK compatible with MFJ-2400.\n"); dw_printf (" -m Mark frequency. Default is %d.\n", DEFAULT_MARK_FREQ); dw_printf (" -s Space frequency. Default is %d.\n", DEFAULT_SPACE_FREQ); dw_printf (" -r Audio sample Rate. Default is %d.\n", DEFAULT_SAMPLES_PER_SEC); diff --git a/gen_tone.c b/gen_tone.c index 69d5b05..18afa4a 100644 --- a/gen_tone.c +++ b/gen_tone.c @@ -280,6 +280,8 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets) * This avoids overshoot, ringing, and adding more jitter. * Alternating bits come out has sine wave of baud/2 Hz. * + * Version 1.6: MFJ-2400 compatibility for V.26. + * *--------------------------------------------------------------------*/ static const int gray2phase_v26[4] = {0, 1, 3, 2}; @@ -304,6 +306,8 @@ void tone_gen_put_bit (int chan, int dat) dat = 0; } +// TODO: change to switch instead of if if if + if (save_audio_config_p->achan[chan].modem_type == MODEM_QPSK) { int dibit; @@ -324,7 +328,9 @@ void tone_gen_put_bit (int chan, int dat) symbol = gray2phase_v26[dibit]; tone_phase[chan] += symbol * PHASE_SHIFT_90; - + if (save_audio_config_p->achan[chan].v26_alternative == V26_B) { + tone_phase[chan] += PHASE_SHIFT_45; + } bit_count[chan]++; } diff --git a/man1/atest.1 b/man1/atest.1 index 9470496..a1b554c 100644 --- a/man1/atest.1 +++ b/man1/atest.1 @@ -21,17 +21,46 @@ atest \- Decode AX.25 frames from an audio file. .SH OPTIONS + .TP -.BI "-B " "n" -Bits / second for data. Proper modem selected for 300, 1200, 9600. -300 baud uses 1600/1800 Hz AFSK. -1200 (default) baud uses 1200/2200 Hz AFSK. -9600 baud uses K9NG/G2RUH standard. +.BI "-B " "n" +Data rate in bits/sec. Standard values are 300, 1200, 2400, 4800, 9600. +.PD 0 +.RS +.RS +300 bps defaults to AFSK tones of 1600 & 1800. +.P +1200 bps uses AFSK tones of 1200 & 2200. +.P +2400 bps uses QPSK based on V.26 standard. +.P +4800 bps uses 8PSK based on V.27 standard. +.P +9600 bps and up uses K9NG/G3RUH standard. +.RE +.RE +.PD + +.TP +.BI "-g " +Force G3RUH modem regardless of data rate. + +.TP +.BI "-j " +2400 bps QPSK compatible with Dire Wolf <= 1.5. + +.TP +.BI "-J " +2400 bps QPSK compatible with MFJ-2400. .TP .BI "-D " "n" Divide audio sample rate by n. +.TP +.BI "-h " +Print frame contents as hexadecimal bytes. + .TP .BI "-F " "n" Amount of effort to try fixing frames with an invalid CRC. @@ -39,9 +68,17 @@ Amount of effort to try fixing frames with an invalid CRC. 1 = Try to fix only a single bit. more = Try modifying more bits to get a good CRC. +.TP +.BI "-L " +Error if Less than this number decoded. + +.TP +.BI "-G " +Error if Greater than this number decoded. + .TP .BI "-P " "m" -Select the demodulator type such as A, B, C, D (default for 300 baud), E (default for 1200 baud), F, A+, B+, C+, D+, E+, F+. +Select the demodulator type such as D (default for 300 bps), E+ (default for 1200 bps), PQRS for 2400 bps, etc. @@ -72,20 +109,20 @@ This generates and decodes 3 test files with 1200, 300, and 9600 bits per second .PD 0 .B atest 02_Track_2.wav .P -.B atest -P C+ 02_Track_2.wav +.B atest -P E- 02_Track_2.wav .P .B atest -F 1 02_Track_2.wav .P -.B atest -P C+ -F 1 02_Track_2.wav +.B atest -P E- -F 1 02_Track_2.wav .PD .P .RS -Try different combinations of options to find the best decoding performance. +Try different combinations of options to compare decoding performance. .RE .P .SH SEE ALSO More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location. -Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll +Applications in this package: aclients, atest, cm108, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll diff --git a/man1/direwolf.1 b/man1/direwolf.1 index 700790d..b9dfeff 100644 --- a/man1/direwolf.1 +++ b/man1/direwolf.1 @@ -48,19 +48,35 @@ Audio sample size for first channel. 8 or 16. Default 16. .TP .BI "-B " "n" -Data rate in bits/sec for first channel. Standard values are 300, 1200, 9600. +Data rate in bits/sec for first channel. Standard values are 300, 1200, 2400, 4800, 9600. .PD 0 -.RS .RS -If < 600, tones are set to 1600 & 1800. +.RS +300 bps defaults to AFSK tones of 1600 & 1800. .P -If > 2400, K9NG/G3RUH scrambling is used. +1200 bps uses AFSK tones of 1200 & 2200. .P -Otherwise, AFSK tones are set to 1200 & 2200. +2400 bps uses QPSK based on V.26 standard. +.P +4800 bps uses 8PSK based on V.27 standard. +.P +9600 bps and up uses K9NG/G3RUH standard. .RE .RE .PD +.TP +.BI "-g " +Force G3RUH modem regardless of data rate. + +.TP +.BI "-j " +2400 bps QPSK compatible with Dire Wolf <= 1.5. + +.TP +.BI "-J " +2400 bps QPSK compatible with MFJ-2400. + .TP .BI "-D " "n" Divide audio sample by n for first channel. @@ -92,6 +108,10 @@ o = Output controls such as PTT and DCD. i = IGate .P h = Hamlib verbose level. Repeat for more. +.P +m = Monitor heard station list. +.P +f = Packet filtering. .RE .RE .PD @@ -152,5 +172,5 @@ rtl_fm \-f 144.39M \-o 4 \- | direwolf \-n 1 \-r 24000 \-b 16 \- .SH SEE ALSO More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location. -Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll +Applications in this package: aclients, atest, cm108, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll diff --git a/man1/gen_packets.1 b/man1/gen_packets.1 index 39f8925..773a88e 100644 --- a/man1/gen_packets.1 +++ b/man1/gen_packets.1 @@ -32,12 +32,36 @@ Signal amplitude in range of 0-200%. Default 50. Note that 100% is corresponds Bits / second for data. Default is 1200. .TP -.BI "-B " "n" -Bits / second for data. Proper modem selected for 300, 1200, 9600. +.BI "-B " "n" +Data rate in bits/sec for first channel. Standard values are 300, 1200, 2400, 4800, 9600. +.PD 0 +.RS +.RS +300 bps defaults to AFSK tones of 1600 & 1800. +.P +1200 bps uses AFSK tones of 1200 & 2200. +.P +2400 bps uses QPSK based on V.26 standard. +.P +4800 bps uses 8PSK based on V.27 standard. +.P +9600 bps and up uses K9NG/G3RUH standard. +.RE +.RE +.PD .TP -.BI "-g" -Scrambled baseband rather than AFSK. +.BI "-g " +Force G3RUH modem regardless of data rate. + +.TP +.BI "-j " +2400 bps QPSK compatible with Dire Wolf <= 1.5. + +.TP +.BI "-J " +2400 bps QPSK compatible with MFJ-2400. + .TP .BI "-m " "n" @@ -112,5 +136,5 @@ Read message from stdin and put quarter volume sound into the file x.wav. Decod .SH SEE ALSO More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location. -Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, ll2utm, log2gpx, text2tt, tt2text, utm2ll +Applications in this package: aclients, atest, cm108, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll