From b295c0f8770cb28929df9ae7100d534e0bd6e8be Mon Sep 17 00:00:00 2001 From: Stelios Bounanos Date: Thu, 28 Feb 2008 08:13:53 +0000 Subject: [PATCH] Upstream version 2.10L --- ChangeLog | 5 +- configure.ac | 2 +- src/Makefile.am | 12 + src/dialogs/confdialog.cxx | 92 +- src/dialogs/confdialog.fl | 86 +- src/dialogs/fl_digi.cxx | 23 +- src/globals/globals.cxx | 6 +- src/include/confdialog.h | 5 +- src/include/configuration.h | 4 +- src/include/dsp.h | 1121 +++++++++++++++++++++++ src/include/globals.h | 6 +- src/include/modem.h | 3 + src/include/mt63.h | 63 ++ src/include/mt63base.h | 417 +++++++++ src/misc/configuration.cxx | 3 + src/mt63/alias_1k.dat | 166 ++++ src/mt63/alias_2k.dat | 166 ++++ src/mt63/alias_k5.dat | 294 ++++++ src/mt63/dsp.cxx | 1674 +++++++++++++++++++++++++++++++++++ src/mt63/morse.dat | 140 +++ src/mt63/mt63.cxx | 241 +++++ src/mt63/mt63base.cxx | 1224 +++++++++++++++++++++++++ src/mt63/mt63intl.dat | 51 ++ src/mt63/symbol.dat | 545 ++++++++++++ src/trx/modem.cxx | 3 + 25 files changed, 6280 insertions(+), 72 deletions(-) create mode 100644 src/include/dsp.h create mode 100644 src/include/mt63.h create mode 100644 src/include/mt63base.h create mode 100644 src/mt63/alias_1k.dat create mode 100644 src/mt63/alias_2k.dat create mode 100644 src/mt63/alias_k5.dat create mode 100644 src/mt63/dsp.cxx create mode 100644 src/mt63/morse.dat create mode 100644 src/mt63/mt63.cxx create mode 100644 src/mt63/mt63base.cxx create mode 100644 src/mt63/mt63intl.dat create mode 100644 src/mt63/symbol.dat diff --git a/ChangeLog b/ChangeLog index 744204d4..58a0b816 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,8 +16,9 @@ Change Log: 12) Fixes to mfsk picture mode decoding 13) Changed frequency2 data file format, downward compatible 14) Added FELD_SLOWHELL, FELD_HELLX5 and FELD_HELLX9 modems - 15 Added support to the PortAudio backend for separate capture and playback - devices and sample rates + 15) Added support to the PortAudio backend for separate capture and playback + devices and sample rates + 16) Added mt63 500/1000/2000 modes 2.09 1) Modified src/Makefile.am for FreeBSD name space clash 2) Added psk multi-channel viewer with regex search capability diff --git a/configure.ac b/configure.ac index a368d3ab..167123a5 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ AC_COPYRIGHT([Copyright (C) 2007, 2008 Stelios Bounanos, M0GLD (m0gld AT enotty DOT net)]) AC_PREREQ(2.61) -AC_INIT([fldigi], [2.10K], [w1hkj AT w1hkj DOT com]) +AC_INIT([fldigi], [2.10L], [w1hkj AT w1hkj DOT com]) AC_CONFIG_AUX_DIR([build-aux]) # define build, build_cpu, build_vendor, build_os diff --git a/src/Makefile.am b/src/Makefile.am index 1fa37f3d..2d063cc7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -161,6 +161,9 @@ fldigi_SOURCES += \ include/mixer.h \ include/modem.h \ include/morse.h \ + include/mt63base.h \ + include/mt63.h \ + include/dsp.h \ include/newinstall.h \ include/olivia.h \ include/picture.h \ @@ -227,6 +230,9 @@ fldigi_SOURCES += \ misc/threads.cxx \ misc/timeops.cxx \ misc/util.cxx \ + mt63/dsp.cxx \ + mt63/mt63.cxx \ + mt63/mt63base.cxx \ olivia/olivia.cxx \ psk/psk.cxx \ psk/pskcoeff.cxx \ @@ -279,6 +285,12 @@ EXTRA_fldigi_SOURCES += \ feld/FeldStyl-14.cxx \ feld/FeldVert-14.cxx \ feld/FeldWide-14.cxx \ + mt63/alias_1k.dat \ + mt63/alias_2k.dat \ + mt63/morse.dat \ + mt63/symbol.dat \ + mt63/alias_k5.dat \ + mt63/mt63intl.dat \ trx/tune.cxx # Additional non-source files that we distribute diff --git a/src/dialogs/confdialog.cxx b/src/dialogs/confdialog.cxx index 7219bf42..5b773d49 100644 --- a/src/dialogs/confdialog.cxx +++ b/src/dialogs/confdialog.cxx @@ -1028,6 +1028,13 @@ progdefaults.changed = true; initViewer(); } +Fl_Spinner *cntChannels=(Fl_Spinner *)0; + +static void cb_cntChannels(Fl_Spinner* o, void*) { + progdefaults.VIEWERchannels = (int)(o->value()); +initViewer(); +} + Fl_Spinner *cntStartFrequency=(Fl_Spinner *)0; static void cb_cntStartFrequency(Fl_Spinner* o, void*) { @@ -1036,13 +1043,6 @@ progdefaults.changed = true; initViewer(); } -Fl_Spinner *cntChannels=(Fl_Spinner *)0; - -static void cb_cntChannels(Fl_Spinner* o, void*) { - progdefaults.VIEWERchannels = (int)(o->value()); -initViewer(); -} - Fl_Spinner *cntTimeout=(Fl_Spinner *)0; static void cb_cntTimeout(Fl_Spinner* o, void*) { @@ -1050,6 +1050,25 @@ static void cb_cntTimeout(Fl_Spinner* o, void*) { progdefaults.changed = true; } +Fl_Group *tabMT63=(Fl_Group *)0; + +Fl_Check_Button *btnMT63_8bit=(Fl_Check_Button *)0; + +static void cb_btnMT63_8bit(Fl_Check_Button* o, void*) { + progdefaults.mt63_8bit = o->value(); +progdefaults.changed = true; +} + +Fl_Check_Button *btnmt63_interleave=(Fl_Check_Button *)0; + +static void cb_btnmt63_interleave(Fl_Check_Button* o, void*) { + if (o->value() == 1) +progdefaults.mt63_interleave = 64; +else +progdefaults.mt63_interleave = 32; +progdefaults.changed = true; +} + Fl_Group *tabRTTY=(Fl_Group *)0; Fl_Choice *selShift=(Fl_Choice *)0; @@ -1167,7 +1186,7 @@ static const char szStopBits[] = "1|1.5|2"; static const char szOliviaTones[] = "2|4|8|16|32|64|128|256"; static const char szOliviaBandwidth[] = "125|250|500|1000|2000"; static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600|115200|230400|460800"; - { Fl_Double_Window* o = new Fl_Double_Window(400, 254, "fldigi - config"); + { Fl_Double_Window* o = new Fl_Double_Window(400, 250, "fldigi - config"); w = o; o->color(FL_DARK2); o->selection_color((Fl_Color)51); @@ -1967,6 +1986,7 @@ fect after a restart."); { Fl_Group* o = tabFeld = new Fl_Group(0, 50, 400, 170, "Feld"); o->color((Fl_Color)51); o->selection_color((Fl_Color)51); + o->hide(); { Fl_Choice* o = selHellFont = new Fl_Choice(175, 62, 122, 20, "Feld Hell Font:"); o->down_box(FL_BORDER_BOX); o->labelfont(4); @@ -2063,7 +2083,7 @@ fect after a restart."); } { Fl_Group* o = tabPSK = new Fl_Group(0, 50, 400, 170, "Psk"); o->hide(); - { Fl_Counter* o = cntSearchRange = new Fl_Counter(25, 60, 80, 21, "Search Range"); + { Fl_Counter* o = cntSearchRange = new Fl_Counter(120, 60, 80, 21, "Search Range"); o->type(1); o->minimum(10); o->maximum(500); @@ -2073,16 +2093,16 @@ fect after a restart."); o->align(FL_ALIGN_RIGHT); o->value(progdefaults.SearchRange); } - { Fl_Group* o = new Fl_Group(15, 87, 370, 53, "PskMail Server"); + { Fl_Group* o = new Fl_Group(3, 175, 395, 43, "PskMail Server"); o->box(FL_ENGRAVED_FRAME); o->align(FL_ALIGN_TOP_LEFT|FL_ALIGN_INSIDE); - { Fl_Check_Button* o = btnPSKmailSweetSpot = new Fl_Check_Button(20, 106, 130, 20, "use sweetspot"); + { Fl_Check_Button* o = btnPSKmailSweetSpot = new Fl_Check_Button(25, 190, 130, 20, "use sweetspot"); o->down_box(FL_DOWN_BOX); o->value(1); o->callback((Fl_Callback*)cb_btnPSKmailSweetSpot); o->value(progdefaults.PSKmailSweetSpot); } - { Fl_Counter* o = cntServerOffset = new Fl_Counter(150, 104, 80, 21, "Server Search Range"); + { Fl_Counter* o = cntServerOffset = new Fl_Counter(165, 189, 80, 21, "Server Search Range"); o->type(1); o->minimum(10); o->maximum(500); @@ -2094,32 +2114,20 @@ fect after a restart."); } o->end(); } - o->end(); - } - { Fl_Group* o = new Fl_Group(0, 50, 400, 170, "PskViewer"); - o->hide(); - { Fl_Group* o = new Fl_Group(5, 60, 390, 155); + { Fl_Group* o = new Fl_Group(3, 85, 395, 90, "Psk Viewer"); o->box(FL_ENGRAVED_FRAME); o->align(FL_ALIGN_TOP_LEFT|FL_ALIGN_INSIDE); - { Fl_Check_Button* o = btnMarquee = new Fl_Check_Button(25, 87, 120, 15, "Marquee style"); + { Fl_Check_Button* o = btnMarquee = new Fl_Check_Button(11, 110, 120, 15, "Marquee style"); o->down_box(FL_DOWN_BOX); o->callback((Fl_Callback*)cb_btnMarquee); o->value(progdefaults.VIEWERmarquee); } - { Fl_Check_Button* o = btnShowFrequencies = new Fl_Check_Button(25, 125, 150, 15, "Show Frequencies"); + { Fl_Check_Button* o = btnShowFrequencies = new Fl_Check_Button(11, 144, 109, 15, "Show Freq\'s"); o->down_box(FL_DOWN_BOX); o->callback((Fl_Callback*)cb_btnShowFrequencies); o->value(progdefaults.VIEWERshowfreq); } - { Fl_Spinner* o = cntStartFrequency = new Fl_Spinner(190, 120, 60, 25, "Start Frequency:"); - o->callback((Fl_Callback*)cb_cntStartFrequency); - o->align(FL_ALIGN_RIGHT); - o->minimum(200); - o->maximum(1000); - o->step(100); - o->value(progdefaults.VIEWERstart); - } - { Fl_Spinner* o = cntChannels = new Fl_Spinner(190, 82, 50, 25, "# Channels:"); + { Fl_Spinner* o = cntChannels = new Fl_Spinner(135, 140, 39, 25, "# Channels"); o->callback((Fl_Callback*)cb_cntChannels); o->align(FL_ALIGN_RIGHT); o->minimum(5); @@ -2127,7 +2135,15 @@ fect after a restart."); o->step(1); o->value(progdefaults.VIEWERchannels); } - { Fl_Spinner* o = cntTimeout = new Fl_Spinner(190, 155, 50, 25, "Aging (sec)"); + { Fl_Spinner* o = cntStartFrequency = new Fl_Spinner(135, 105, 50, 25, "Start Freq"); + o->callback((Fl_Callback*)cb_cntStartFrequency); + o->align(FL_ALIGN_RIGHT); + o->minimum(200); + o->maximum(1000); + o->step(100); + o->value(progdefaults.VIEWERstart); + } + { Fl_Spinner* o = cntTimeout = new Fl_Spinner(261, 105, 50, 25, "Aging (sec)"); o->callback((Fl_Callback*)cb_cntTimeout); o->align(FL_ALIGN_RIGHT); o->minimum(10); @@ -2139,6 +2155,24 @@ fect after a restart."); } o->end(); } + { Fl_Group* o = tabMT63 = new Fl_Group(0, 50, 400, 170, "MT-63"); + { Fl_Group* o = new Fl_Group(5, 60, 390, 155); + o->box(FL_ENGRAVED_FRAME); + o->align(FL_ALIGN_TOP_LEFT|FL_ALIGN_INSIDE); + { Fl_Check_Button* o = btnMT63_8bit = new Fl_Check_Button(55, 90, 163, 15, "8 bit extended chars"); + o->down_box(FL_DOWN_BOX); + o->callback((Fl_Callback*)cb_btnMT63_8bit); + o->value(progdefaults.mt63_8bit); + } + { Fl_Check_Button* o = btnmt63_interleave = new Fl_Check_Button(55, 120, 165, 15, "64 bit interleave"); + o->down_box(FL_DOWN_BOX); + o->callback((Fl_Callback*)cb_btnmt63_interleave); + o->value(0);if (progdefaults.mt63_interleave == 64) o->value(1); + } + o->end(); + } + o->end(); + } { Fl_Group* o = tabRTTY = new Fl_Group(0, 50, 400, 170, "RTTY"); o->color((Fl_Color)51); o->selection_color((Fl_Color)51); diff --git a/src/dialogs/confdialog.fl b/src/dialogs/confdialog.fl index d6f9615f..e272b415 100644 --- a/src/dialogs/confdialog.fl +++ b/src/dialogs/confdialog.fl @@ -35,7 +35,7 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600 code {} {} Fl_Window {} { label {fldigi - config} open - xywh {868 365 400 254} type Double color 45 selection_color 51 align 80 visible + xywh {520 101 400 250} type Double color 45 selection_color 51 align 80 visible } { Fl_Tabs tabsConfigure {open xywh {0 0 405 220} color 47 selection_color 9 @@ -668,7 +668,7 @@ o->label((inpQRZuserpassword->type() & FL_SECRET_INPUT) ? "Show" : "Hide");} label OSS callback {update_sound_config(SND_IDX_OSS); progdefaults.changed = true; -resetSoundCard();} selected +resetSoundCard();} xywh {5 63 100 25} down_box DIAMOND_DOWN_BOX selection_color 1 } Fl_Input_Choice menuOSSDev { @@ -1052,7 +1052,7 @@ progdefaults.changed = true;} } Fl_Group tabFeld { label Feld open - xywh {0 50 400 170} color 51 selection_color 51 + xywh {0 50 400 170} color 51 selection_color 51 hide } { Fl_Choice selHellFont { label {Feld Hell Font:} @@ -1066,7 +1066,7 @@ progdefaults.changed = true;} open Fl_Value_Slider sldrHellBW { label {Filter BW} callback {active_modem->set_bandwidth(sldrHellBW->value()); -progdefaults.changed = true;} selected +progdefaults.changed = true;} xywh {30 190 345 20} type Horizontal color 215 align 5 minimum 10 maximum 2400 step 5 value 400 textsize 14 } Fl_Check_Button btnHellXmtWidth { @@ -1162,18 +1162,18 @@ progdefaults.changed = true;} callback {progdefaults.SearchRange = (int)o->value(); wf->redraw_marker(); progdefaults.changed = true;} - xywh {25 60 80 21} type Simple align 8 minimum 10 maximum 500 step 10 value 200 + xywh {120 60 80 21} type Simple align 8 minimum 10 maximum 500 step 10 value 200 code0 {o->value(progdefaults.SearchRange);} } Fl_Group {} { label {PskMail Server} open - xywh {15 87 370 53} box ENGRAVED_FRAME align 21 + xywh {3 175 395 43} box ENGRAVED_FRAME align 21 } { Fl_Check_Button btnPSKmailSweetSpot { label {use sweetspot} callback {progdefaults.PSKmailSweetSpot = o->value(); progdefaults.changed = true;} - xywh {20 106 130 20} down_box DOWN_BOX value 1 + xywh {25 190 130 20} down_box DOWN_BOX value 1 code0 {o->value(progdefaults.PSKmailSweetSpot);} } Fl_Counter cntServerOffset { @@ -1181,60 +1181,56 @@ progdefaults.changed = true;} callback {progdefaults.ServerOffset = (int)o->value(); wf->redraw_marker(); progdefaults.changed = true;} - xywh {150 104 80 21} type Simple align 8 minimum 10 maximum 500 step 10 value 200 + xywh {165 189 80 21} type Simple align 8 minimum 10 maximum 500 step 10 value 200 code0 {o->value(progdefaults.SearchRange);} } } - } - Fl_Group {} { - label PskViewer open - xywh {0 50 400 170} hide - } { - Fl_Group {} {open - xywh {5 60 390 155} box ENGRAVED_FRAME align 21 + Fl_Group {} { + label {Psk Viewer} open + xywh {3 85 395 90} box ENGRAVED_FRAME align 21 } { Fl_Check_Button btnMarquee { label {Marquee style} callback {progdefaults.VIEWERmarquee = o->value(); progdefaults.changed = true; initViewer();} - xywh {25 87 120 15} down_box DOWN_BOX + xywh {11 110 120 15} down_box DOWN_BOX code0 {o->value(progdefaults.VIEWERmarquee);} } Fl_Check_Button btnShowFrequencies { - label {Show Frequencies} + label {Show Freq's} callback {progdefaults.VIEWERshowfreq = o->value(); progdefaults.changed = true; initViewer();} - xywh {25 125 150 15} down_box DOWN_BOX + xywh {11 144 109 15} down_box DOWN_BOX code0 {o->value(progdefaults.VIEWERshowfreq);} } - Fl_Spinner cntStartFrequency { - label {Start Frequency:} - callback {progdefaults.VIEWERstart = (int)(o->value()); -progdefaults.changed = true; -initViewer();} - xywh {190 120 60 25} align 8 - code0 {o->minimum(200);} - code1 {o->maximum(1000);} - code2 {o->step(100);} - code3 {o->value(progdefaults.VIEWERstart);} - } Fl_Spinner cntChannels { - label {\# Channels:} + label {\# Channels} callback {progdefaults.VIEWERchannels = (int)(o->value()); initViewer();} - xywh {190 82 50 25} align 8 + xywh {135 140 39 25} align 8 code0 {o->minimum(5);} code1 {o->maximum(30);} code2 {o->step(1);} code3 {o->value(progdefaults.VIEWERchannels);} } + Fl_Spinner cntStartFrequency { + label {Start Freq} + callback {progdefaults.VIEWERstart = (int)(o->value()); +progdefaults.changed = true; +initViewer();} + xywh {135 105 50 25} align 8 + code0 {o->minimum(200);} + code1 {o->maximum(1000);} + code2 {o->step(100);} + code3 {o->value(progdefaults.VIEWERstart);} + } Fl_Spinner cntTimeout { label {Aging (sec)} callback {progdefaults.VIEWERtimeout = (int)(o->value()); progdefaults.changed = true;} - xywh {190 155 50 25} align 8 + xywh {261 105 50 25} align 8 code0 {o->minimum(10);} code1 {o->maximum(180);} code2 {o->step(1);} @@ -1242,6 +1238,32 @@ progdefaults.changed = true;} } } } + Fl_Group tabMT63 { + label {MT-63} open selected + xywh {0 50 400 170} + } { + Fl_Group {} {open + xywh {5 60 390 155} box ENGRAVED_FRAME align 21 + } { + Fl_Check_Button btnMT63_8bit { + label {8 bit extended chars} + callback {progdefaults.mt63_8bit = o->value(); +progdefaults.changed = true;} + xywh {55 90 163 15} down_box DOWN_BOX + code0 {o->value(progdefaults.mt63_8bit);} + } + Fl_Check_Button btnmt63_interleave { + label {64 bit interleave} + callback {if (o->value() == 1) +progdefaults.mt63_interleave = 64; +else +progdefaults.mt63_interleave = 32; +progdefaults.changed = true;} + xywh {55 120 165 15} down_box DOWN_BOX + code0 {o->value(0);if (progdefaults.mt63_interleave == 64) o->value(1);} + } + } + } Fl_Group tabRTTY { label RTTY open xywh {0 50 400 170} color 51 selection_color 51 hide diff --git a/src/dialogs/fl_digi.cxx b/src/dialogs/fl_digi.cxx index 7805965b..985c98d1 100644 --- a/src/dialogs/fl_digi.cxx +++ b/src/dialogs/fl_digi.cxx @@ -53,6 +53,7 @@ #include "psk.h" #include "cw.h" #include "mfsk.h" +#include "mt63.h" #include "rtty.h" #include "olivia.h" #include "dominoex.h" @@ -178,6 +179,13 @@ Fl_Menu_Item quick_change_mfsk[] = { { 0 } }; +Fl_Menu_Item quick_change_mt63[] = { + { mode_info[MODE_MT63_500].name, 0, cb_init_mode, (void *)MODE_MT63_500 }, + { mode_info[MODE_MT63_1000].name, 0, cb_init_mode, (void *)MODE_MT63_1000 }, + { mode_info[MODE_MT63_2000].name, 0, cb_init_mode, (void *)MODE_MT63_2000 }, + { 0 } +}; + Fl_Menu_Item quick_change_domino[] = { { mode_info[MODE_DOMINOEX4].name, 0, cb_init_mode, (void *)MODE_DOMINOEX4 }, { mode_info[MODE_DOMINOEX5].name, 0, cb_init_mode, (void *)MODE_DOMINOEX5 }, @@ -361,6 +369,13 @@ void init_modem(trx_mode mode) quick_change = quick_change_mfsk; break; + case MODE_MT63_500: case MODE_MT63_1000: case MODE_MT63_2000 : + startup_modem(*mode_info[mode].modem ? *mode_info[mode].modem : + *mode_info[mode].modem = new mt63(mode)); + quick_change = quick_change_mt63; + modem_config_tab = tabMT63; + break; + case MODE_BPSK31: case MODE_PSK63: case MODE_PSK125: case MODE_PSK250: startup_modem(*mode_info[mode].modem ? *mode_info[mode].modem : *mode_info[mode].modem = new psk(mode)); @@ -939,6 +954,12 @@ Fl_Menu_Item menu_[] = { { mode_info[MODE_MFSK16].name, 0, cb_init_mode, (void *)MODE_MFSK16, 0, FL_NORMAL_LABEL, 0, 14, 0}, {0,0,0,0,0,0,0,0,0}, +{"MT63", 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL, 0, 14, 0}, +{ mode_info[MODE_MT63_500].name, 0, cb_init_mode, (void *)MODE_MT63_500, 0, FL_NORMAL_LABEL, 0, 14, 0}, +{ mode_info[MODE_MT63_1000].name, 0, cb_init_mode, (void *)MODE_MT63_1000, 0, FL_NORMAL_LABEL, 0, 14, 0}, +{ mode_info[MODE_MT63_2000].name, 0, cb_init_mode, (void *)MODE_MT63_2000, 0, FL_NORMAL_LABEL, 0, 14, 0}, +{0,0,0,0,0,0,0,0,0}, + {"PSK", 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL, 0, 14, 0}, { mode_info[MODE_BPSK31].name, 0, cb_init_mode, (void *)MODE_BPSK31, 0, FL_NORMAL_LABEL, 0, 14, 0}, { mode_info[MODE_QPSK31].name, 0, cb_init_mode, (void *)MODE_QPSK31, 0, FL_NORMAL_LABEL, 0, 14, 0}, @@ -946,10 +967,8 @@ Fl_Menu_Item menu_[] = { { mode_info[MODE_QPSK63].name, 0, cb_init_mode, (void *)MODE_QPSK63, 0, FL_NORMAL_LABEL, 0, 14, 0}, { mode_info[MODE_PSK125].name, 0, cb_init_mode, (void *)MODE_PSK125, 0, FL_NORMAL_LABEL, 0, 14, 0}, { mode_info[MODE_QPSK125].name, 0, cb_init_mode, (void *)MODE_QPSK125, 0, FL_NORMAL_LABEL, 0, 14, 0}, -#ifdef USE250 { mode_info[MODE_PSK250].name, 0, cb_init_mode, (void *)MODE_PSK250, 0, FL_NORMAL_LABEL, 0, 14, 0}, { mode_info[MODE_QPSK250].name, 0, cb_init_mode, (void *)MODE_QPSK250, 0, FL_NORMAL_LABEL, 0, 14, 0}, -#endif {0,0,0,0,0,0,0,0,0}, { mode_info[MODE_OLIVIA].name, 0, cb_init_mode, (void *)MODE_OLIVIA, 0, FL_NORMAL_LABEL, 0, 14, 0}, diff --git a/src/globals/globals.cxx b/src/globals/globals.cxx index 822d5708..61591c56 100644 --- a/src/globals/globals.cxx +++ b/src/globals/globals.cxx @@ -63,6 +63,10 @@ const struct mode_info_t mode_info[NUM_MODES] = { { MODE_MFSK8, &mfsk8_modem, "MFSK-8", "MFSK-8", "MFSK16" }, { MODE_MFSK16, &mfsk16_modem, "MFSK16", "MFSK-16", "MFSK8" }, + { MODE_MT63_500, &mt63_500_modem, "MT63-500", "MT63-500", "" }, + { MODE_MT63_1000, &mt63_1000_modem, "MT63-1XX", "MT63-1000", "" }, + { MODE_MT63_2000, &mt63_2000_modem, "MT63-2XX", "MT63-2000", "" }, + { MODE_BPSK31, &psk31_modem, "BPSK31", "BPSK-31", "PSK31" }, { MODE_QPSK31, &qpsk31_modem, "QPSK31", "QPSK-31", "QPSK31" }, { MODE_PSK63, &psk63_modem, "PSK-63", "BPSK-63", "PSK63" }, @@ -83,8 +87,6 @@ const struct mode_info_t mode_info[NUM_MODES] = { { MODE_THROBX2, &throbx2_modem, "THRBX2", "ThrobX 2", "" }, { MODE_THROBX4, &throbx4_modem, "THRBX4", "ThrobX 4", "" }, -// { MODE_MT63, 0, "MT63", "MT-63", "" }, - { MODE_WWV, &wwv_modem, "WWV", "WWV", "" }, { MODE_ANALYSIS, &anal_modem, "ANALYSIS", "Freq Analysis", "" } diff --git a/src/include/confdialog.h b/src/include/confdialog.h index dca9a1de..c5bdc9f0 100644 --- a/src/include/confdialog.h +++ b/src/include/confdialog.h @@ -162,9 +162,12 @@ extern Fl_Check_Button *btnPSKmailSweetSpot; extern Fl_Counter *cntServerOffset; extern Fl_Check_Button *btnMarquee; extern Fl_Check_Button *btnShowFrequencies; -extern Fl_Spinner *cntStartFrequency; extern Fl_Spinner *cntChannels; +extern Fl_Spinner *cntStartFrequency; extern Fl_Spinner *cntTimeout; +extern Fl_Group *tabMT63; +extern Fl_Check_Button *btnMT63_8bit; +extern Fl_Check_Button *btnmt63_interleave; extern Fl_Group *tabRTTY; extern Fl_Choice *selShift; extern Fl_Choice *selBaud; diff --git a/src/include/configuration.h b/src/include/configuration.h index a4163864..0639734d 100644 --- a/src/include/configuration.h +++ b/src/include/configuration.h @@ -66,7 +66,6 @@ struct configuration { double CWpost; bool CWid; int CWIDwpm; - // FELD-HELL bool FELD_IDLE; // OLIVIA @@ -76,6 +75,9 @@ struct configuration { int oliviasinteg; // DOMINOEX double DOMINOEX_BW; +// MT63 + bool mt63_8bit; + int mt63_interleave; // User interface data int Fontnbr; int FontSize; diff --git a/src/include/dsp.h b/src/include/dsp.h new file mode 100644 index 00000000..d64051cc --- /dev/null +++ b/src/include/dsp.h @@ -0,0 +1,1121 @@ +/* + * dsp.h -- various DSP algorithms + * + * based on mt63 code by Pawel Jalocha + * Copyright (C) 1999-2004 Pawel Jalocha, SP9VRC + * Copyright (c) 2007-2008 Dave Freese, W1HKJ + * + * This file is part of fldigi. + * + * fldigi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * fldigi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty oF + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MT63; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + */ + +#include +#include +#include + +// ---------------------------------------------------------------------------- +// double/other-complex type + +template struct Cdspcmpx { type re,im; } ; + +typedef Cdspcmpx dspCmpx; + +// Some complex operators +template + inline void operator +=(Cdspcmpx &Dst, Cdspcmpx &Src) +{ Dst.re+=Src.re; Dst.im+=Src.im; } + +template + inline void operator -=(Cdspcmpx &Dst, Cdspcmpx &Src) +{ Dst.re-=Src.re; Dst.im-=Src.im; } + +template + inline void operator *=(Cdspcmpx &Dst, num Src) +{ Dst.re*=Src; Dst.im*=Src; } + +template + inline void operator /=(Cdspcmpx &Dst, num Src) +{ Dst.re/=Src; Dst.im/=Src; } + +// scalar product of two vectors +template + inline double dspScalProd(Cdspcmpx &A, Cdspcmpx &B) +{ return A.re*B.re+A.im*B.im; } + +template + inline double dspScalProd(typeA Ia, typeA Qa, Cdspcmpx &B) +{ return Ia*B.re+Qa*B.im; } + +// complex multiply +template + inline void CdspcmpxMultAxB(Cdspcmpx &Dst, Cdspcmpx &A, Cdspcmpx &B) +{ Dst.re=A.re*B.re-A.im*B.im; + Dst.im=A.re*B.im+A.im*B.re; } + +template + inline void CdspcmpxMultAxB(typeDst &DstI, typeDst &DstQ, Cdspcmpx &A, Cdspcmpx &B) +{ DstI=A.re*B.re-A.im*B.im; + DstQ=A.re*B.im+A.im*B.re; } + +// complex multiply, second argument with a "star" (B.im is negated) +template + inline void CdspcmpxMultAxBs(Cdspcmpx &Dst, Cdspcmpx &A, Cdspcmpx &B) +{ Dst.re=A.re*B.re+A.im*B.im; + Dst.im=A.im*B.re-A.re*B.im; } + +// ---------------------------------------------------------------------------- +// signed 16-bit format (standard 16-bit audio) + +typedef short dspS16; + +// ---------------------------------------------------------------------------- + +template inline int dspRedspAllocArray(type **Array, int Size) +{ (*Array)=(type *)realloc(*Array,Size*sizeof(type)); + return (*Array)==NULL; } + +template inline int dspAllocArray(type **Array, int Size) +{ (*Array)=(type *)malloc(Size*sizeof(type)); + return (*Array)==NULL; } + +template inline void dspClearArray(type *Array, int Size) +{ memset(Array,0,Size*sizeof(type)); } + +template inline void dspCopyArray(type *Dst, type *Src, int Size) +{ memcpy(Dst,Src,Size*sizeof(type)); } + +template inline void dspMoveArray(type *Dst, type *Src, int Size) +{ memmove(Dst,Src,Size*sizeof(type)); } + +template int dspAllocArray2D(type ***Array, int Size1, int Size2) +{ int i; + (*Array)=(type **)malloc(Size1*(sizeof(type *))); + if((*Array)==NULL) return 1; + for(i=0; i void dspFreeArray2D(type **Array, int Size1) +{ int i; for(i=0; i void dspClearArray2D(type **Array, int Size1, int Size2) +{ int i; for(i=0; i class dspSeq +{ public: + dspSeq(); + ~dspSeq(); + int EnsureSpace(int ReqSpace); // make sure that there is enough space + void Free(void); // free space to save RAM when buffer is not in use + int Space; // that much is allocated in *Data + int Len; // that much is filled up + type *Data; // contains Len elements +} ; + +template dspSeq::dspSeq() { + Data = NULL; + Len = Space = 0; +} + +template dspSeq::~dspSeq() { + free(Data); +} + +template int dspSeq::EnsureSpace(int ReqSpace) +{ + if (ReqSpace <= Space) + return 0; + Data = (type *)realloc(Data, ReqSpace * sizeof(type)); + if (Data == NULL) { + Space = Len = 0; + return -1; + } + Space = ReqSpace; + return 0; +} + +template void dspSeq::Free(void) +{ + free(Data); + Data = NULL; + Space = Len = 0; +} + +typedef dspSeq float_buff; +typedef dspSeq double_buff; +typedef dspSeq dspCmpx_buff; +typedef dspSeq dspCmpx_buff; +// typedef dspSeq int16_buff; <- this doesn't work - why ?! +typedef dspSeq dspS16_buff; +typedef dspSeq char_buff; + +// ---------------------------------------------------------------------------- +// First-In First-Out pipes + +template class dspFIFO +{ public: + dspFIFO(); ~dspFIFO(); + int Preset(int Max); + void Free(void); + void Clear(void); + int Inp(type Elem); + int Out(type &Elem); + int InpReady(void); + int OutReady(void); + private: + type *Buff; + int Size; + int Rd,Wr; +} ; + +template dspFIFO::dspFIFO() { Buff=NULL; } +template dspFIFO::~dspFIFO() { free(Buff); } + +template void dspFIFO::Free(void) { free(Buff); Buff=NULL; } + +template int dspFIFO::Preset(int Max) +{ Size=Max+1; + if(dspRedspAllocArray(&Buff,Size)) return -1; + Rd=0; Wr=0; return 0; } + +template void dspFIFO::Clear(void) { Rd=Wr; } + +template int dspFIFO::Inp(type Elem) +{ int w=Wr; + Buff[w]=Elem; w+=1; if(w>=Size) w=0; + if(w==Rd) return -1; + Wr=w; return 0; } + +template int dspFIFO::Out(type &Elem) +{ if(Rd==Wr) return -1; + Elem=Buff[Rd]; Rd+=1; if(Rd>=Size) Rd=0; return 0; } + +template int dspFIFO::OutReady(void) +{ return (Wr>=Rd) ? Wr-Rd : Wr-Rd+Size; } + +template int dspFIFO::InpReady(void) +{ return (Rd>Wr) ? Rd-Wr-1 : Rd-Wr+Size-1; } + +typedef dspFIFO char_dspFIFO; + +// ---------------------------------------------------------------------------- +// dspPower of single and complex values and dspSequences of these + +inline double dspPower(double X) { return X*X; } +inline double dspPower(double I, double Q) { return I*I + Q*Q; } +inline double dspPower(dspCmpx X) { return X.re*X.re+X.im*X.im; } + +double dspPower(double *X, int Len); +double dspPower(double *I, double *Q, int Len); +double dspPower(dspCmpx *X, int Len); + +inline double dspPower(double_buff *buff) { return dspPower(buff->Data,buff->Len); } +inline double dspPower(dspCmpx_buff *buff) { return dspPower(buff->Data,buff->Len); } + +// dspAmplitude calculations + +inline double dspAmpl(double I, double Q) { return sqrt(I*I+Q*Q); } +inline double dspAmpl(dspCmpx X) { return sqrt(X.re*X.re+X.im*X.im); } + +// dspPhase calculation (output = <-PI..PI) ) + +inline double dspPhase(double I, double Q) { return atan2(Q,I); } +inline double dspPhase(dspCmpx X) { return atan2(X.im,X.re); } + +// dspPhase normalization + +inline double dspPhaseNorm(double dspPhase) +{ if(dspPhase>=M_PI) return dspPhase-2*M_PI; + if(dspPhase<(-M_PI)) return dspPhase+2*M_PI; + return dspPhase; } + +// ---------------------------------------------------------------------------- +// min./max. of integers + +inline int dspIntmin(int i1, int i2) +{ return i1i2 ? i1 : i2; } + +inline int dspIntmin(int i1, int i2, int i3) +{ return i1i2 ? (i1>i3 ? i1 : i3) : (i2>i3 ? i2 : i3); } + +// ---------------------------------------------------------------------------- +// Extreme search, dspAverage, fitting + +double dspAverage(double *Data, int Len); + +int dspCountInRange(double *Data, int Len, double Low, double Upp); + +inline int dspCountInRange(double_buff *Input, double Low, double Upp) +{ return dspCountInRange(Input->Data,Input->Len,Low,Upp); } + +inline double dspRMS(double *Data, int Len) { return sqrt(dspPower(Data,Len)/Len); } +inline double dspRMS(dspCmpx *Data, int Len) { return sqrt(dspPower(Data,Len)/Len); } +inline double dspRMS(double_buff *Input) { return dspRMS(Input->Data,Input->Len); } +inline double dspRMS(dspCmpx_buff *Input) { return dspRMS(Input->Data,Input->Len); } + +template type dspFindMin(type *Data, int Len) +{ type Min; int i; + Min=Data[0]; + for(i=1; i type dspFindMin(type *Data, int Len, int &MinPos) +{ type Min; int i,pos; + Min=Data[0]; pos=0; + for(i=1; i type dspFindMax(type *Data, int Len) +{ type Max; int i; + Max=Data[0]; + for(i=1; iMax) Max=Data[i]; + return Max; } + +template type dspFindMax(type *Data, int Len, int &MaxPos) +{ type Max; int i,pos; + Max=Data[0]; pos=0; + for(i=1; iMax) { Max=Data[i]; pos=i; } + MaxPos=pos; return Max; } + +double dspFindMaxdspPower(dspCmpx *Data, int Len); +double dspFindMaxdspPower(dspCmpx *Data, int Len, int &MaxPos); + +double dspFitPoly1(double *Data, int Len, double &A, double &B); +double dspFitPoly2(double *Data, int Len, double &A, double &B, double &C); + +void dspFitPoly2(double Data[3], double &A, double &B, double &C); + +// ---------------------------------------------------------------------------- +// "selective" dspAverage fit + +template + int dspSelFitAver(type *Data, int Len, double SelThres, int Loops, + double &Aver, double &dspRMS, int &Sel) +{ int i,loop,Incl,prev; double Sum,ErrSum,Lev,dLev,Diff,Thres; + for(ErrSum=Sum=0.0,i=0; i + int dspSelFitAver(Cdspcmpx *Data, int Len, double SelThres, int Loops, + Cdspcmpx &Aver, double &dspRMS, int &Sel) +{ int i,loop,Incl,prev; dspCmpx Sum,Lev,dLev; double ErrSum,Diff,Thres; + for(ErrSum=0.0,Sum.re=Sum.im=0.0,i=0; i + void dspWhiteNoise(type &X) +{ double Rand,dspPower,dspPhase; + Rand=((double)rand()+1.0)/((double)RAND_MAX+1.0); dspPower=sqrt(-2*log(Rand)); + Rand=(double)rand()/(double)RAND_MAX; dspPhase=2*M_PI*Rand; + X=dspPower*cos(dspPhase); } + +template + void CdspcmpxdspWhiteNoise(Cdspcmpx &X) +{ double Rand,dspPower,dspPhase; + Rand=((double)rand()+1.0)/((double)RAND_MAX+1.0); dspPower=sqrt(-log(Rand)); + Rand=(double)rand()/(double)RAND_MAX; dspPhase=2*M_PI*Rand; + X.re=dspPower*cos(dspPhase); X.im=dspPower*sin(dspPhase); } + +// ---------------------------------------------------------------------------- +// various window shapes (for the FFT and FIR filters) +// these functions are supposed to be called with the argument "dspPhase" +// between -PI and +PI. Most (or even all) will return zero for input +// euqal -PI or +PI. + +double dspWindowHanning(double dspPhase); +double dspWindowBlackman3(double dspPhase); + +// ---------------------------------------------------------------------------- +// FIR shape calculation for a flat response from FreqLow to FreqUpp + +void dspWinFirI(double LowOmega, double UppOmega, + double *Shape, int Len, double (*Window)(double), double shift=0.0); +void WinFirQ(double LowOmega, double UppOmega, + double *Shape, int Len, double (*Window)(double), double shift=0.0); + +// ---------------------------------------------------------------------------- +// convert 16-bit signed or 8-bit unsigned into doubles + +void dspConvS16todouble(dspS16 *dspS16, double *dbl, int Len, double Gain=1.0/32768.0); +int dspConvS16todouble(dspS16 *dspS16, double_buff *dbl, int Len, double Gain=1.0/32768.0); + +void dspConvdoubleTodspS16(double *dbl, dspS16 *dspS16, int Len, double Gain=32768.0); +inline int dspConvdoubleTodspS16(double_buff *dbl, dspS16_buff *dspS16, double Gain=32768.0) +{ int err=dspS16->EnsureSpace(dbl->Len); if(err) return -1; + dspConvdoubleTodspS16(dbl->Data,dspS16->Data, dbl->Len,Gain); + dspS16->Len=dbl->Len; return 0; } + +void dspConvU8todouble(unsigned char *U8, double *dbl, int Len, double Gain=1.0/128.0); +int dspConvU8todouble(unsigned char *U8, double_buff *dbl, int Len, double Gain=1.0/128.0); + +// ---------------------------------------------------------------------------- +// other converts + +void dspConvCmpxTodspPower(dspCmpx *Inp, int InpLen, double *Out); +int dspConvCmpxTodspPower(dspCmpx_buff *Input, double_buff *Output); + +void dspConvCmpxTodspAmpl(dspCmpx *Inp, int InpLen, double *Out); +int dspConvCmpxTodspAmpl(dspCmpx_buff *Input, double_buff *Output); + +void dspConvCmpxTodspPhase(dspCmpx *Inp, int InpLen, double *Out); +int dspConvCmpxTodspPhase(dspCmpx_buff *Input, double_buff *Output); + +// ---------------------------------------------------------------------------- +// Pulse noise limiter + +class dspPulseLimiter +{ public: + dspPulseLimiter(); ~dspPulseLimiter(); + void Free(void); + int Preset(int TapLen, double Limit=4.0); + int Process(double *Inp, int InpLen, double *Out); + int Process(double_buff *Input); + double_buff Output; + double dspRMS; + private: + int Len; + double Thres; + double *Tap; + int Ptr; + double PwrSum; +} ; + +// ---------------------------------------------------------------------------- +// Signal level monitor + +class dspLevelMonitor +{ public: + dspLevelMonitor(); ~dspLevelMonitor(); + int Preset(double Integ, double Range=0.75); + int Process(double *Inp, int Len); + int Process(double_buff *Input); + double dspRMS; + double OutOfRange; + private: + double PwrMid,PwrOut; + double OutOfRangeMid; + double MaxSqr; + double W1,W2,W5; +} ; + +// ---------------------------------------------------------------------------- +// Automatic Gain/Level Control for the Mixer + +class dspMixerAutoLevel +{ public: + dspMixerAutoLevel(); // ~dspMixerAutoLevel(); + int Process(double *Inp, int InpLen); + int Process(double_buff *Inp) { return Process(Inp->Data, Inp->Len); } + public: + int IntegLen; // mean dspPower integration time [sdspAmples] + double MinMS; // minimum acceptable dspAverage dspPower + double MaxMS; // maximum acceptable dspAverage dspPower + int PeakHold; // level holding time after a peak [sdspAmples] + int MinHold; // minimal time between changing the mixer level [sdspAmples] + int AdjStep; // mixer level adjusting step + int MinLevel; // mimimum allowed mixer level + int MaxLevel; // maximum allowed mixer level + double AvedspRMS; // dspAverage dspPower of the input signal + int Hold; // time counter for holding levels + int Level; // actual mixer level +} ; + +// ---------------------------------------------------------------------------- +// Two-element IIR low pass filter + +struct dspLowPass2elem { double Mid,Out; } ; + +struct dspLowPass2weight { double W1,W2,W5; } ; + +// first calculate the coefficiants W1,W2 and W5 for given integration time +template + inline void dspLowPass2Coeff(typeLen IntegLen, typeW &W1, typeW &W2, typeW &W5) +{ W1=1.0/IntegLen; W2=2.0/IntegLen; W5=5.0/IntegLen; } + +template + inline void dspLowPass2Coeff(typeLen IntegLen, dspLowPass2weight &Weight) +{ Weight.W1=1.0/IntegLen; Weight.W2=2.0/IntegLen; Weight.W5=5.0/IntegLen; } + +// then you can process sdspAmples +template + inline void dspLowPass2(typeInp Inp, typeOut &Mid, typeOut &Out, + typeW W1, typeW W2, typeW W5) +{ double Sum, Diff; + Sum=Mid+Out; Diff=Mid-Out; Mid+=W2*Inp-W1*Sum; Out+=W5*Diff; } + +template + inline void dspLowPass2(typeInp Inp, dspLowPass2elem &Elem, + typeW W1, typeW W2, typeW W5) +{ double Sum, Diff; + Sum=Elem.Mid+Elem.Out; Diff=Elem.Mid-Elem.Out; Elem.Mid+=W2*Inp-W1*Sum; Elem.Out+=W5*Diff; } + +template + inline void dspLowPass2(typeInp Inp, dspLowPass2elem &Elem, dspLowPass2weight &Weight) +{ double Sum, Diff; + Sum=Elem.Mid+Elem.Out; + Diff=Elem.Mid-Elem.Out; + Elem.Mid+=Weight.W2*Inp-Weight.W1*Sum; + Elem.Out+=Weight.W5*Diff; } + +void dspLowPass2(dspCmpx *Inp, dspCmpx *Mid, dspCmpx *Out, + double W1, double W2, double W5); + +// ---------------------------------------------------------------------------- +// periodic low pass + +class dspPeriodLowPass2 +{ public: + dspPeriodLowPass2(); + ~dspPeriodLowPass2(); + void Free(void); + int Preset(int Period, double IntegLen); + int Process(double Inp, double &Out); + int Process(double *Inp, int InpLen, double *Out); + int Process(double_buff *Input); + double_buff Output; + private: + int Len; double *TapMid,*TapOut; int TapPtr; + double W1,W2,W5; +} ; + +// ---------------------------------------------------------------------------- +// a simple dspDelay + +template + class dspDelay +{ public: + dspDelay(); ~dspDelay(); + void Free(void); int Preset(int len); + void Process(type *Inp, int InpLen, type *Out); + int Process(dspSeq *Input); + dspSeq Output; + private: + int Len; type *Tap; int TapPtr; +} ; + +template + dspDelay::dspDelay() { Tap=NULL; } + +template + dspDelay::~dspDelay() { free(Tap); } + +template + void dspDelay::Free(void) { free(Tap); Tap=NULL; } + +template + int dspDelay::Preset(int dspDelayLen) +{ Len=dspDelayLen; if(dspRedspAllocArray(&Tap,Len)) return -1; + dspClearArray(Tap,Len); TapPtr=0; return 0; } + +template + void dspDelay::Process(type *Inp, int InpLen, type *Out) +{ int i,batch; + for(i=0; i=Len) TapPtr=0; } +} + +template + int dspDelay::Process(dspSeq *Input) +{ int err=Output.EnsureSpace(Input->Len); if(err) return -1; + Process(Input->Data,Input->Len,Output.Data); + Output.Len=Input->Len; return 0; } + +// ---------------------------------------------------------------------------- +// dspDelayLine, like dspDelay but more flexible +// The idea is that we hold addressable history of at least MaxdspDelay +// sdspAmples. +// After each input batch is processed, the InpPtr points to the first sdspAmple +// of this batch and we can address sdspAmples backwards upto MaxdspDelay. +// For more optimal performace we allocate more RAM than just for MaxdspDelay. +// Infact the allocated size (MaxSize) should be at least +// MaxdspDelay plus the largest expected input length. + +template + class dspDelayLine +{ public: + dspDelayLine(); ~dspDelayLine(); + void Free(void); + int Preset(int MaxdspDelay, int MaxSize=0); + int Process(type *Inp, int Len); + int Process(dspSeq *Input); + type *Line; // line storage + int dspDelay; // how many (at least) backward sdspAmples are stored + int LineSize; // allocated size + int DataLen; // length of the valid data + type *InpPtr; // first sdspAmple for the most recent processed batch + int InpLen; // number of sdspAmples for the most recent input +} ; + +template + dspDelayLine::dspDelayLine() { Line=NULL; } + +template + dspDelayLine::~dspDelayLine() { free(Line); } + +template + void dspDelayLine::Free(void) { free(Line); Line=NULL; } + +template + int dspDelayLine::Preset(int MaxdspDelay, int MaxSize) +{ LineSize=MaxSize; if(LineSize<(2*MaxdspDelay)) LineSize=2*MaxdspDelay; + DataLen=MaxdspDelay; dspDelay=MaxdspDelay; + if(dspRedspAllocArray(&Line,LineSize)) return -1; + dspClearArray(Line,LineSize); + InpPtr=Line+DataLen; InpLen=0; return 0; } + +template + int dspDelayLine::Process(type *Inp, int Len) +{ if((DataLen+Len)>LineSize) + { dspMoveArray(Line,Line+DataLen-dspDelay,dspDelay); DataLen=dspDelay; } + if((DataLen+Len)>LineSize) return -1; + dspCopyArray(Line+DataLen,Inp,Len); + InpPtr=Line+DataLen; InpLen=Len; DataLen+=Len; + return 0; } + +template + int dspDelayLine::Process(dspSeq *Input) +{ return Process(Input->Data,Input->Len); } + +// ---------------------------------------------------------------------------- +// Low pass "moving box" FIR filter +// very unpure spectral response but CPU complexity very low +// and independent on the integration time + +class dspBoxFilter +{ public: + dspBoxFilter(); ~dspBoxFilter(); + void Free(void); + int Preset(int BoxLen); + int Process(double Inp, double &Out); + int Process(double *Inp, int InpLen, double *Out); + int Process(double_buff *Input); + void Recalibrate(); + double_buff Output; + private: + int Len; double *Tap; int TapPtr; double Sum; +} ; + +class dspCmpxBoxFilter +{ public: + dspCmpxBoxFilter(); ~dspCmpxBoxFilter(); + void Free(void); + int Preset(int BoxLen); + int Process(dspCmpx *Inp, int InpLen, dspCmpx *Out); + void Recalibrate(); + int Process(dspCmpx_buff *Input); + dspCmpx_buff Output; + private: + int Len; dspCmpx *Tap; int TapPtr; dspCmpx Sum; +} ; + +// ---------------------------------------------------------------------------- +// FIR filter with a given shape + +class dspFirFilter +{ public: + dspFirFilter(); ~dspFirFilter(); + void Free(void); + int Preset(int FilterLen, double *FilterShape=(double*)NULL); + int Process(double *Inp, int InpLen, double *Out); + int Process(double_buff *Input); + // Response(double Freq, double *Resp); + int ComputeShape(double LowOmega, double UppOmega, double (*Window)(double)); + // UseExternShape(double *shape); + double_buff Output; + private: + int Len; // Tap/Shape length + double *Shape; // Response shape + int ExternShape; // that we are using an externally provided shape + double *Tap; int TapPtr; +} ; + +// ---------------------------------------------------------------------------- +// a pair of FIR filters. quadrature split, decimate +// the decimation rate must be integer + +class dspQuadrSplit +{ public: + dspQuadrSplit(); ~dspQuadrSplit(); + void Free(void); + int Preset(int FilterLen, + double *FilterShape_I, double *FilterShape_Q, + int DecimateRate); + int ComputeShape(double LowOmega, double UppOmega, double (*Window)(double)); +// int Process(double *Inp, int InpLen, +// double *OutI, double *OutQ, int MaxOutLen, int *OutLen); +// int Process(double *Inp, int InpLen, +// dspCmpx *Out, int MaxOutLen, int *OutLen); + int Process(double_buff *Input); + dspCmpx_buff Output; + private: + int Len; + double_buff Tap; + double *ShapeI, *ShapeQ; int ExternShape; + int Rate; +} ; + +// ---------------------------------------------------------------------------- +// reverse of dspQuadrSplit: interpolates and combines the I/Q +// back into 'real' signal. + +class dspQuadrComb +{ public: + dspQuadrComb(); ~dspQuadrComb(); + void Free(void); + int Preset(int FilterLen, + double *FilterShape_I, double *FilterShape_Q, + int DecimateRate); + int ComputeShape(double LowOmega, double UppOmega, double (*Window)(double)); + int Process(dspCmpx_buff *Input); + double_buff Output; + private: + int Len; double *Tap; int TapPtr; + double *ShapeI, *ShapeQ; int ExternShape; + int Rate; +} ; + +// ---------------------------------------------------------------------------- +// complex mix with an oscilator (carrier) +// here we could avoid computing sine/cos at every sdspAmple + +class dspCmpxMixer +{ public: + dspCmpxMixer(); // ~dspCmpxMixer(); + void Free(void); + int Preset(double CarrierOmega); + int ProcessFast(double *InpI, double *InpQ, int InpLen, + double *OutI, double *OutQ); + int Process(dspCmpx *Inp, int InpLen, dspCmpx *Out); + int ProcessFast(dspCmpx *Inp, int InpLen, dspCmpx *Out); + int Process(dspCmpx_buff *Input); + int ProcessFast(dspCmpx_buff *Input); + dspCmpx_buff Output; + public: + double dspPhase,Omega; +} ; + +// ---------------------------------------------------------------------------- +// FM demodulator (dspPhase rotation speed-meter) + +class dspFMdemod +{ public: + dspFMdemod(); // ~dspFMdemod(); + int Preset(double CenterOmega); + int Process(double *InpI, double *InpQ, int InpLen, double *Out); + int Process(dspCmpx *Inp, int InpLen, double *Out); + int Process(dspCmpx_buff *Input); + double_buff Output; + private: + double PrevdspPhase; + public: + double RefOmega; +} ; + +// ---------------------------------------------------------------------------- +// Rate converter - real input/output, linear interpolation +// expect large error when high frequency components are present +// thus the best place to convert rates is after a low pass filter +// of a demodulator. + +class dspRateConvLin +{ public: + dspRateConvLin(); // ~dspRateConvLin(); + void SetOutVsInp(double OutVsInp); + void SetInpVsOut(double InpVsOut); + int Process(double_buff *InpBuff); + double_buff Output; + private: + double OutStep, OutdspPhase; + double PrevSdspAmple; +} ; + +// ---------------------------------------------------------------------------- +// Rate converter - real input/output, quadratic interpolation +// similar limits like for RateConv1 + +class dspRateConvQuadr +{ public: + dspRateConvQuadr(); // ~dspRateConvQuadr(); + void SetOutVsInp(double OutVsInp); + void SetInpVsOut(double InpVsOut); + int Process(double *Inp, int InpLen, + double *Out, int MaxOutLen, int *OutLen); + int Process(double_buff *InpBuff); + double_buff Output; + private: + double OutStep, OutdspPhase; + double Tap[4]; int TapPtr; +} ; + +// ---------------------------------------------------------------------------- +// Rate converter, real input/output, +// bandwidth-limited interpolation, several shifted FIR filters + +class dspRateConvBL +{ public: + dspRateConvBL(); ~dspRateConvBL(); + void Free(void); + int Preset(int FilterLen, double *FilterShape[], int FilterShapeNum); + int ComputeShape(double LowOmega, double UppOmega, double (*Window)(double)); + void SetOutVsInp(double OutVsInp); + void SetInpVsOut(double InpVsOut); + int Process(double_buff *Input); + int ProcessLinI(double_buff *Input); + double_buff Output; + private: + double OutStep, OutdspPhase; + int Len; + double *Tap; int TapSize; + double **Shape; int ShapeNum; int ExternShape; +} ; + +// ---------------------------------------------------------------------------- +// Sliding window (for FFT input) + +class dspCmpxSlideWindow +{ public: + dspCmpxSlideWindow(); ~dspCmpxSlideWindow(); + void Free(void); + int Preset(int WindowLen, int SlideDist, double *WindowShape=(double*)NULL); + int SetWindow(double (*NewWindow)(double dspPhase), double Scale=1.0); + int Process(dspCmpx_buff *Input); + dspCmpx_buff Output; + private: + int Len; // Window length + dspCmpx *Buff; // storage + int Dist; // distance between slides + int Ptr; // data pointer in Buff + double *Window; // window shape + int ExternWindow; +} ; + +// ---------------------------------------------------------------------------- +// Overlapping window (for IFFT output) + +class dspCmpxOverlapWindow +{ public: + dspCmpxOverlapWindow(); ~dspCmpxOverlapWindow(); + void Free(void); + int Preset(int WindowLen, int SlideDist, double *WindowShape=(double*)NULL); + int SetWindow(double (*NewWindow)(double dspPhase), double Scale=1.0); + void Process(dspCmpx *Inp, dspCmpx *Out); + int ProcessSilence(int Slides=1); + int Process(dspCmpx_buff *Input); + int Process(dspCmpx *Input); +// int Process(dspCmpx_buff *Input); + dspCmpx_buff Output; + private: + int Len; // Window length + dspCmpx *Buff; // storage + int Dist; // distance between slides + double *Window; // window shape + int ExternWindow; +} ; + +// ---------------------------------------------------------------------------- +// FFT dspPhase corrector + +class dspFFT_TimeShift +{ public: + dspFFT_TimeShift(); + ~dspFFT_TimeShift(); + void Free(void); + int Preset(int FFTlen, int Backwards=0); + int Process(dspCmpx *Data, int Time); + private: + int Len; // FFT length + int LenMask; // length-1 for pointer wrapping + dspCmpx *FreqTable; // sin/cos table + int dspPhase; +} ; + +// ---------------------------------------------------------------------------- +// bit synchronizer, the bit rate is the input rate divided by four + +class dspDiffBitSync4 +{ public: + dspDiffBitSync4(int IntegBits); ~dspDiffBitSync4(); + void Free(void); + int Process(double *Inp, int InpLen, + double *BitOut, double *IbitOut, + int MaxOutLen, int *OutLen); + double GetSyncDriftRate(); // get aver. sync. drift + double GetSyncConfid(); + private: // eg. 0.01 means 1 bit drift per 100 bits + double *InpTap; int InpTapLen, InpTapPtr; // buffer tap, length and pointer + int IntegLen; // integrate tdspIntming over that many bits + double W1,W2,W5; // weights for the two-stage IIR lopass filter + double DiffInteg0[4], DiffInteg[4]; // signal diff. integrators + int DiffTapPtr; // integrator and bit-sdspAmpling pointer + int BitPtr; double SyncdspPhase; // sync. pointer/dspPhase + double SyncDrift0,SyncDrift; // low pass filter for the sync. drift rate + double SyncConfid; +} ; + +// ---------------------------------------------------------------------------- +// bit slicer, SNR/Tune meter + +class dspBitSlicer +{ public: + dspBitSlicer(int IntegBits); + ~dspBitSlicer(); + int Process(double *Bits, double *IBits, int InpLen, double *OutBits); + double GetSigToNoise(); double GetdspAmplAsym(); double GetTimeAsym(); + private: + int IntegLen,TapLen; double W1,W2,W5; + double Sum0[2], Sum[2]; + double SumSq0[2], SumSq[2]; + double TimeAsym0, TimeAsym; + double dspAmplAsym0, dspAmplAsym; + double Noise[2]; double OptimThres; + double *Tap; int TapPtr; + double PrevBit, PrevIBit; +} ; + +// ---------------------------------------------------------------------------- +// The decoder for the HDLC frames, +// makes no AX.25 CRC check, only the length in bytes against MinLen and MaxLen +// however it does not pass frames with non-complete bytes. + +class dspHDLCdecoder +{ public: + dspHDLCdecoder(int minlen, int maxlen, int diff, int invert, + int chan, int (*handler)(int, char *, int)); + ~dspHDLCdecoder(); + int Process(double *Inp, int InpLen); + public: + int AllFrameCount; + int BadFrameCount; + private: + int MinLen,MaxLen; + int RxDiff,RxInvert; + int ChanId; + int (*FrameHandler)(int ChanId, char *Frame, int Len); + char *Buff; int Len,PrevLev; + unsigned int ShiftReg; int BitCount,Count1s; +} ; + +// ---------------------------------------------------------------------------- +// AX.25 CRC + +short unsigned int dspAX25CRC(char *Data, int Len); + +// ---------------------------------------------------------------------------- +// check if the given number (an integer) is a dspPower of 2 +template int dspPowerOf2(type I) +{ int c; if(I<=0) return 0; + for(c=0; I!=0; I>>=1) c+=I&1; + return c==1; } + +// ---------------------------------------------------------------------------- + +class dsp_r2FFT // radix-2 FFT +{ public: // size must a dspPower of 2: 2,4,8,16,32,64,128,256,... + dsp_r2FFT(); + ~dsp_r2FFT(); + void Free(void); + // preset tables for given processing size + int Preset(int size); + // scramble/unscramble input + void Scramble(dspCmpx x[]); + // apply input window + // separate the result of a two real channels FFT + void SeparTwoReals(dspCmpx Buff[], dspCmpx Out0[], dspCmpx Out1[]); + // join spectra of two real channels + void JoinTwoReals(dspCmpx Inp0[], dspCmpx Inp1[], dspCmpx Buff[]); + // core process: the classic tripple loop of butterflies + void CoreProc(dspCmpx x[]); + // complex FFT process in place, includes unscrambling + inline void ProcInPlace(dspCmpx x[]) { Scramble(x); CoreProc(x); } + // define the FFT window and input/output scales (NULL => rectangular window) + public: + int Size; // FFT size + int *BitRevIdx; // Bit-reverse indexing table for data (un)scrambling + dspCmpx *Twiddle; // Twiddle factors (sine/cos values) + private: +// double *Window; // window shape (NULL => rectangular window +// double WinInpScale, WinOutScale; // window scales on input/output + private: + // classic radix-2 butterflies + inline void FFTbf(dspCmpx &x0, dspCmpx &x1, dspCmpx &W); + // special 2-elem. FFT for the first pass + inline void FFT2(dspCmpx &x0, dspCmpx &x1); + // special 2-elem. FFT for the second pass + inline void FFT4(dspCmpx &x0, dspCmpx &x1, dspCmpx &x2, dspCmpx &x3); +} ; + +// --------------------------------------------------------------------------- +// Sliding window FFT for spectral analysis (e.g. SETI) +// input: real-valued time-domain signal, +// output: complex-valued Fourier Transform +// +// We use a little trick here to process two real-valued FFT +// in one go using the complex FFT engine. +// This cuts the CPU but makes the input->output dspDelay longer. + +class dspSlideWinFFT +{ public: + dspSlideWinFFT(); ~dspSlideWinFFT(); + void Free(); + int Preset(int size, int step, double *window); + int Preset(int size, int step, + double (*NewWindow)(double dspPhase), double Scale=1.0); + int SetWindow(double *window); + int SetWindow(double (*NewWindow)(double dspPhase), double Scale=1.0); + int Process(double_buff *Input); + dsp_r2FFT FFT; // FFT engine + dspCmpx_buff Output; // output buffer + int Size; int SizeMask; // FFT size, size mask for pointer wrapping + int Dist; int Left; // distance between slides, sdspAmples left before the next slide + int Slide; // even/odd slide + private: + double *SlideBuff; int SlidePtr; // sliding window buffer, pointer + double *Window; int ExternWindow; // window shape + dspCmpx *FFTbuff; // FFT buffer +} ; + +// --------------------------------------------------------------------------- +// Overlapping window Inverse FFT to convert spectra into time-domain signal + +class dspOvlapWinIFFT +{ public: + dspOvlapWinIFFT(); ~dspOvlapWinIFFT(); + void Free(void); + int Preset(int size, int step, double *window); + int Preset(int size, int step, + double (*NewWindow)(double dspPhase), double Scale=1.0); + int SetWindow(double *window); + int SetWindow(double (*NewWindow)(double dspPhase), double Scale=1.0); + int Process(dspCmpx *Input); + dsp_r2FFT FFT; // FFT engine + double_buff Output; // output buffer + int Size; int SizeMask; // FFT size, size mask for pointer wrapping + int Dist; // distance between slides + int Slide; + private: + dspCmpx *Spectr[2]; + dspCmpx *FFTbuff; // FFT buffer + double *Window; int ExternWindow; // window shape + double *OvlapBuff; int OvlapPtr; +} ; + +// --------------------------------------------------------------------------- +// Sliding window FFT for spectral processing (e.g. de-noising) +// input: real-valued signal +// in the middle you are given a chance to process +// the complex-valued Fourier Transform (SpectraProc() routine). +// output: real-valued signal +// If you don't touch the spectra in SpectralProc() +// the output will be an exact copy (only dspDelayed) of the input. + +class dspSlideWinFFTproc +{ public: + dspSlideWinFFTproc(); ~dspSlideWinFFTproc(); + void Free(void); + int Preset(int size, int step, void (*proc)(dspCmpx *Spectra, int Len), + double *window); + int Preset(int size, int step, void (*proc)(dspCmpx *Spectra, int Len), + double (*NewWindow)(double dspPhase), double Scale=0.0); + int SetWindow(double *window); + int SetWindow(double (*NewWindow)(double dspPhase), double Scale=0.0); + int Process(double_buff *Input); + dsp_r2FFT FFT; + double_buff Output; + int Size; int SizeMask; + int Dist; int Left; + int Slide; + private: + double *SlideBuff; int SlidePtr; + double *Window; int ExternWindow; + dspCmpx *FFTbuff; + dspCmpx *Spectr[2]; + void (*SpectraProc)(dspCmpx *Spectra, int Len); + double *OvlapBuff; int OvlapPtr; +} ; + +// --------------------------------------------------------------------------- +// Walsh (Hadamard ?) transform. + +void dspWalshTrans(double *Data, int Len); +void dspWalshInvTrans(double *Data, int Len); + +// --------------------------------------------------------------------------- + + + diff --git a/src/include/globals.h b/src/include/globals.h index c650af7b..750f8aef 100644 --- a/src/include/globals.h +++ b/src/include/globals.h @@ -69,6 +69,10 @@ enum { MODE_MFSK8, MODE_MFSK16, + MODE_MT63_500, + MODE_MT63_1000, + MODE_MT63_2000, + MODE_BPSK31, MODE_QPSK31, MODE_PSK63, @@ -89,8 +93,6 @@ enum { MODE_THROBX2, MODE_THROBX4, -// MODE_MT63, - MODE_WWV, MODE_ANALYSIS, diff --git a/src/include/modem.h b/src/include/modem.h index 378d118c..d23a110c 100644 --- a/src/include/modem.h +++ b/src/include/modem.h @@ -181,6 +181,9 @@ public: extern modem *cw_modem; extern modem *mfsk8_modem; extern modem *mfsk16_modem; +extern modem *mt63_500_modem; +extern modem *mt63_1000_modem; +extern modem *mt63_2000_modem; extern modem *feld_modem; extern modem *feld_slowmodem; extern modem *feld_x5modem; diff --git a/src/include/mt63.h b/src/include/mt63.h new file mode 100644 index 00000000..cf712dc8 --- /dev/null +++ b/src/include/mt63.h @@ -0,0 +1,63 @@ +// +// mt63.h +// +// Copyright (C) 2006 +// Dave Freese, W1HKJ +// +// This file is part of fldigi. Adapted from code contained in gmfsk source code +// distribution. +// Copyright (C) 2005 +// Tomi Manninen (oh2bns@sral.fi) +// +// fldigi is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// fldigi is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with fldigi; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// + +#ifndef MT63_MODEM_H +#define MT63_MODEM_H + +#include "sound.h" +#include "confdialog.h" +#include "dsp.h" +#include "mt63base.h" +#include "trx.h" + +using namespace std; + +class mt63 : public modem { +private: + int Interleave; + int flush; + int escape; + + MT63tx *Tx; + MT63rx *Rx; + + dspLevelMonitor *InpLevel; + double_buff *InpBuff; + +public: + mt63(trx_mode mode); + ~mt63(); + void init(); + void rx_init(); + void tx_init(SoundBase*); + void restart(); + int rx_process(const double *buf, int len); + int tx_process(); +}; + + +#endif diff --git a/src/include/mt63base.h b/src/include/mt63base.h new file mode 100644 index 00000000..a945197f --- /dev/null +++ b/src/include/mt63base.h @@ -0,0 +1,417 @@ +/* + * mt63base.h -- MT63 transmitter and receiver in C++ for LINUX + * + * Copyright (c) 2007, 2008 Dave Freese, W1HKJ + * + * base class for use by fldigi + * modified from original + * excluded CW_ID which is a part of the base modem class for fldigi + * + * based on mt63 code by Pawel Jalocha + * Copyright (C) 1999-2004 Pawel Jalocha, SP9VRC + * Copyright (c) 2007-2008 Dave Freese, W1HKJ + * + * This file is part of fldigi. + * + * fldigi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * fldigi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty oF + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MT63; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef MT63BASE_H +#define MT63BASE_H + +// ========================================================================== +// Morse Encoder +/* +class MorseEncoder +{ public: + MorseEncoder(); + ~MorseEncoder(); + void Free(void); + int SetTxMsg(char *Msg); // set the message to be transmitted + int NextKey(void); // get the next key state (ON of OFF) + private: + char *TxMsg; + int TxPtr; + long Code; +} ; +*/ + +// ========================================================================== +// Character encoder and block interleaver for the MT63 modem +/* +How to use this class: +1. Create or declare an object like: + MT63encoder Encoder; +2. Preset the object for the given number of carriers and interleave: + err=Encoder.Preset(,,); + MT63 uses 64 carriers and interleave of 32 or 64 + - the corresponding interleave patterns can be found in mt63.dat. + If Encode.Preset() returns non-zero you are in big troubles ! +3. For each character to be encode you make the call: + Encoder.Process(); + and you should then take the bits from the Encode.Output + - these are the bits to be sent out on the carriers. + (for MT63 logical 0 means carrier flip, logical 1 is no flip). +4. At any time you can call Encoder.Preset() again to change the parameters + (this will clean away the interleaver pipe). +*/ + +// MT63 modem specific routines, made to be compatible with the MT63ASC.ASM +// (c) 1999 Pawel Jalocha, SP9VRC, jalocha@hpdel1.ifj.edu.pl +// Date: 05-NOV-1999 + +class MT63encoder +{ public: + MT63encoder(); + ~MT63encoder(); + void Free(); + int Preset(int Carriers, int Intlv, int *Pattern, int RandFill=0); + int Process(char code); + char *Output; + private: + int DataCarriers; + char CodeMask; + int IntlvLen; + int IntlvSize; + int *IntlvPatt; + char *IntlvPipe; + int IntlvPtr; + double *WalshBuff; +} ; + +// ========================================================================== +// MT63 envelope (non-synchronous) time and frequency synchronizer +// experimental status: looks like it's not good enough. +/* +class MT63sync +{ public: + MT63sync(); + ~MT63sync(); + void Free(void); + int Preset(int FFTlen, int FirstCarr, int CarrSepar, int Carriers, int Steps, + int Margin, int Integ); + int Process(dspCmpx *SpectraSlice); + int SdspAmpleNow; + int Locked; + double FreqOfs; + double TimeOfs; + private: + int FFTmask; + int FirstDataCarr; + int DataCarrSepar; + int DataCarriers; + int ScanMargin; + int ScanFirst; + int ScanLen; + int StepsPerSymb; + int ScanSize; + double *PwrIntegMid,*PwrIntegOut; + int IntegPtr; + int NodspRMSize; + double *NormPwr; + // int SymbPtr; + double W1,W2,W5; +} ; +*/ +// ========================================================================== +// MT63 deinterleaver and decoder +/* +How to use this class: +1. Create or declare an object: + MT63decoder Decoder; +2. Preset given parameters with Decoder.Preset(); + Decoder.Preset(); + Number of carriers and interleave are same as for MT63encoder. + "Margin" is the number of extra carriers demodulated on the side + because with the MT63 you cannot say with full confidence which + is really the first carrier: the FEC decoder have to tell you this. + "Integ" is the integration period to find the best FEC match. + "Integ" is measured in MT63 symbols (at 1000 kHz we do 10 symbols/s). +3. For each symbol period feed the demodulated data into the object: + Decoder.Process(); + and then get the demodulated character code from Decoder.Output + You can get as well the measured signal-to-noise ratio from Decoder.SNR + and the index of the first carrier (according to the FEC match) + from Decoder.CarrOfs +4. You can change the parameters at any time with Decoder.Preset() + (this will clean the data pipes). +*/ + +class MT63decoder +{ public: + MT63decoder(); + ~MT63decoder(); + void Free(); + int Preset(int Carriers, int Intlv, int *Pattern, int Margin, int Integ); + int Process(double *Data); + char Output; + double SignalToNoise; + int CarrOfs; + + private: + int DataCarriers; + double *IntlvPipe; + int IntlvLen; + int IntlvSize; + int IntlvPtr; + int *IntlvPatt; + + double *WalshBuff; + + int ScanLen; + int ScanSize; + double *DecodeSnrMid,*DecodeSnrOut; + double W1,W2,W5; + char *DecodePipe; + int DecodeLen; + int DecodeSize; + int DecodePtr; + +} ; + +// ========================================================================== +// MT63 transmitter +/* +How to use this class: +1. Create or declare an object: + MT63tx Tx; +2. Preset parameters: + Tx.Preset(,); + Allowed values are: bandwidth=500,1000,2000; interleave=0,1; + Non-zero value returned means there was a problem... +3. For each character to be sent: + Tx.SendChar(); + After each call to SendChar() you must read the samples + from the Tx.Comb.Output.Data, the number of samples to read + is in Tx.Comb.Output.Len. They are in double floating point, so you should + convert them to 16-bit integers and output them to your soundcard. +4. If you have nothing to transmit, you must not stop, because + you have to keep the sound going. MT63 transmits NL characters (code=0) + in this case. +5. When you are done with all the characters and you want to stop, + you should still put some NL characters in to flush the interleave + thus please call the Tx.SendChar() Tx.DataInterleave times + (still add few more characters to flush the windowed IFFT buffers). + After that the MT63 transmits a jamming dspSequence for some time + to speed up carrier drop at the receiver: you do this by calling + Tx.SendJam(); +6. You can transmit few symbols of silence by: + Tx.SendSilence() + to make a gracefull switch-off. + Remember: each time you call SendChar(), SendJam() or SendSilence() + you must send the contains of Tx.Comb.Output out to your soundcard. + Each Tx.SendXxx() produces the amount of sound corresponding to one + symbol time that is 0.1 second for the 1000 Hz mode. + The soundcard output rate must be 8000 Hz, rather precisely, + that is the error should be below 1 Hz. If it is not you should + use the rate converter: look into mt63tx for an example. +7. Inbetween transmissions you may change the settings by calling + the Tx.Preset() again. +*/ + +class MT63tx +{ public: + MT63tx(); ~MT63tx(); + void Free(void); + int Preset(int BandWidth=1000, int LongInterleave=0);//, char *ID=NULL); + int SendTune(void); + int SendChar(char ch); + int SendJam(void); + int SendSilence(void); + + private: + int DataCarriers; // the number of data carriers + int FirstDataCarr; // the FFT index of the first data carrier + // int DataCarrSepar; // separation [FFT bins] between data carriers + int WindowLen; // FFT window and symbol shape length + double *TxWindow; // The shape of the FFT window (=symbol shape) + // int SymbolSepar; // separation between symbols on a carrier + int AliasFilterLen; // anti-alias filter length + double *AliasShapeI,*AliasShapeQ; // and shapes + int DecimateRatio; // decimation/interpolation after/before filter + int *InterleavePattern; // how the bits of one block are placed on data carriers + double TxdspAmpl; // dspAmplitude applied to generate a carrier (before IFFT) + long CarrMarkCode; + int CarrMarkdspAmpl; + +// MorseEncoder CW_Coder; // CW encoder +// char *CW_ID; // Morse Code identifier to be transmitted along the MT63 signal +// int CW_Carr; // the carrier index to transmit the CW +// double CW_dspAmpl; // CW dspAmplitude +// int CW_dspPhase; // CW dspPhase +// int CW_dspPhaseCorr; // CW dspPhase correction + + MT63encoder Encoder; // data encode and interleaver + int *TxVect; // modulator vector (dspPhases) + int *dspPhaseCorr; // dspPhase corrections for each carrier + dspCmpx_buff WindowBuff; // FFT/window buffer + dsp_r2FFT FFT; // FFT engine + dspCmpxOverlapWindow Window; // overlapping window + int ProcessTxVect(); + + public: + int DataInterleave; + dspQuadrComb Comb; // the output of this module is in Comb.Output +} ; + +// ========================================================================== +// MT63 receiver +/* +How to use this class: +1. Declare the object: + MT63rx Rx; +2. Preset paramateres + Rx.Preset(,,); + For weak signals I recommend integration of 32 or more, + otherwise 16 is enough. By the way, 16 means 1.6 second for 1000 Hz mode + because then we transmit 10 symbols per second. +3. Feed doubleing point sdspAmples into the Rx.Process, if you have signed 16-bit + sdspAmples, you should convert them first to doubleing point - look at how + I do it in mt63rx.cc +4. After EACH new batch of sdspAmples + you should look into Rx.Output for the decoded characters. + You can egzamin the receiver status at any time by calling: + Rx.SYNC_LockStatus() => logical value 0 or 1 + Rx.SYNC_Confidence() => lock confidence: a double between 0.0 and 1.0 + Rx.FEC_SNR() => signal-to-noise seen by FEC + Rx.TotalFreqOffset() => measured frequency offset in [Hz] + assuming 8000 Hz sdspAmpling +*/ + +class MT63rx +{ public: + MT63rx(); ~MT63rx(); + void Free(void); + int Preset(int BandWidth=1000, int LongInterleave=0, int Integ=16, + void (*Display)(double *Spectra, int Len)=NULL); + int Process(double_buff *Input); + char_buff Output; // decoded characters + + int SYNC_LockStatus(void); // 1 => locked, 0 => not locked + double SYNC_Confidence(void); // lock confidence <0..1> + double SYNC_FreqOffset(void); + double SYNC_FreqDevdspRMS(void); + double SYNC_TimeOffset(void); + double TotalFreqOffset(); // Total frequency offset in [Hz] + double FEC_SNR(void); // signal-to-noise ratio at the FEC + int FEC_CarrOffset(void); + + private: + dspQuadrSplit InpSplit; // input filter, I/Q splitter, decimator + dspCmpxMixer TestOfs; // frequency offset for tests + + dspDelayLine ProcLine; // processing pipe + int ProcdspDelay; // processing dspDelay for optimal symbol probing + int SyncProcPtr; // sdspAmpling pointer for the synchronizer + int DataProcPtr; // sdspAmpling pointer for the data demodulator + + dsp_r2FFT FFT; // FFT engine + int WindowLen; // FFT window length = symbol shape length + int WindowLenMask; // WindowLen-1 for pointer wrapping + double *RxWindow; // FFT window shape = symbol shape + + void (*SpectraDisplay)(double *Spectra, int Len); + double *SpectradspPower; + + int AliasFilterLen; // anti-alias filter length + double *AliasShapeI,*AliasShapeQ; // and shapes + int DecimateRatio; // decimation/interpolation after/before filter + + int *InterleavePattern; // how the bits of one block are placed on data carriers + int DataInterleave; // data interleave depth + + int DataCarriers; // number of carriers + int FirstDataCarr; // the FFT index of the first data carrier + // int DataCarrSepar; // freq. separation between carriers [FFT bins] + long CarrMarkCode; // code to mark carriers (not in use here) + // int SymbolSepar; // time separation between symbols [sdspAmples] + int ScanMargin; // How many carriers up and down to search + int IntegLen; // Over how many symbols we integrate to synchronize + + int SymbolDiv; // =4 we probe the input 4 times per symbol time + int SyncStep; // SymbolSepar/SymbolDiv + int ScanFirst; // first carrier to scan + int ScanLen; // number of carriers to scan + + dspCmpx *FFTbuff; + dspCmpx *FFTbuff2; + + // here starts the time/frequency synchronizer + void SyncProcess(dspCmpx *Slice); + + dspCmpx *SyncPipe[4]; // FFT result buffer for sync. + int SyncPtr; // wrapping pointer for SyncPipe and integrators + int SymbPtr; // points about where the symbol is + + dspCmpx *SyncPhCorr; // dspPhase corrections for the sync. processor + + dspCmpx *CorrelMid[4], *CorrelOut[4]; // correlation integrator + double *dspPowerMid, *dspPowerOut; // carrier dspPower integrator + dspCmpx *CorrelNorm[4]; // normalized correlation + double W1,W2,W5; // correlation integrator weights + double W1p,W2p,W5p; // dspPower integrator weights + + dspCmpx *CorrelAver[4]; // sliding sum to fit the carrier pattern + int FitLen; + + void DoCorrelSum(dspCmpx *Correl1, dspCmpx *Correl2, dspCmpx *Aver); + + dspCmpx *SymbFit; // vectors to match symbol shift and confidence + int SymbFitPos; // "smoothed" peak position + + double *FreqPipe; // smoothing pipe for frequency offset + dspCmpx *SymbPipe; // smoothing pipe for symbol shift + int TrackPipeLen; // tracking pipe length + int TrackPipePtr; // pipe pointer + double AverFreq; // dspAveraged frequency + dspCmpx AverSymb; // dspAveraged symbol dspPhase + + double SyncLockThres; // lock confidence threshold + double SyncHoldThres; // minimal confidence to hold the lock + + int SyncLocked; // locked or not locked + double SyncSymbConf; // current smoothed confidence + double SyncFreqOfs; // current smoothed frequency offset + double SyncFreqDev; // frequency deviation (dspRMS) + double SyncSymbShift; // current smoothed symbol time shift + + // here starts the data decoder + void DataProcess(dspCmpx *EvenSlice, dspCmpx *OddSlice, double FreqOfs, int TimeDist); + + int DataScanMargin; // +/- data carriers to scan for best FEC match + int DataScanLen; // total number of data carriers being processed + int DataScanFirst; + + dspCmpx *RefDataSlice; // reference data slice for differential dspPhase decode + dspCmpx *DataVect; // differentially decoded data vactor + + int DataPipeLen; // pipe length + int DataPipePtr; // wrapping pointer + dspCmpx **DataPipe; // decoded vectors pipe + double *DataPwrMid,*DataPwrOut; // carrier dspPower integrator + dspCmpx *DataSqrMid,*DataSqrOut; // carrier complex square integrator + double dW1,dW2,dW5; // integrator constants + + double *DatadspPhase; // differential decoded dspPhases + double *DatadspPhase2; // rather for debugging, not use otherwise + + MT63decoder Decoder; + +// MT63sync EnvSync; // envelope synchronizer (experimental) + +} ; + +#endif // MT63_BASE_H diff --git a/src/misc/configuration.cxx b/src/misc/configuration.cxx index 30714e3f..2e14d20b 100644 --- a/src/misc/configuration.cxx +++ b/src/misc/configuration.cxx @@ -80,6 +80,9 @@ configuration progdefaults = { 2, // int oliviasinteg // DOMINOEX 2.0, // double DOMINOEX_BW; +// MT63 + false, // bool mt63_8bit; + 32, // int mt63_interleave; // 0, // int Fontnbr 16, // int Fontsize diff --git a/src/mt63/alias_1k.dat b/src/mt63/alias_1k.dat new file mode 100644 index 00000000..8e44012c --- /dev/null +++ b/src/mt63/alias_1k.dat @@ -0,0 +1,166 @@ +/* + * alias_1k.dat -- Anti-alias filter 1000 Hz bandwidth + * + * Copyright (C) 1999-2004 Pawel Jalocha, SP9VRC + * + * This file is part of MT63. + * + * MT63 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MT63 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MT63; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +// Filter coefficiants made by ALIAS.C for the following parameters: +// FilterLen=64 SampleRate=8000.0 FreqLow=500.0 FreqUpp=1500.0 +// PassBandLow=375.0 PassBandUpp=1625.0 StopBandLow=0.0 StopBandUpp=2000.0 +// => PeakInStopBand=-90.99 dB +// Programmers's scale factor: 2.000000 +// Programmers's comments: Anti-alias filter 1000 Hz bandwidth, decimation by 4 + +const int Alias_1k_Len=64; + +double Alias_1k_I[Alias_1k_Len] = { + +0.00003605 , // 0 + +0.00001835 , // 1 + -0.00022227 , // 2 + -0.00079785 , // 3 + -0.00099442 , // 4 + +0.00032296 , // 5 + +0.00276603 , // 6 + +0.00365685 , // 7 + +0.00128973 , // 8 + -0.00107943 , // 9 + +0.00195568 , // 10 + +0.00914871 , // 11 + +0.01100689 , // 12 + +0.00254789 , // 13 + -0.00580382 , // 14 + -0.00014844 , // 15 + +0.01341757 , // 16 + +0.01123057 , // 17 + -0.01328109 , // 18 + -0.03176715 , // 19 + -0.01791993 , // 20 + +0.00579429 , // 21 + -0.00986091 , // 22 + -0.06425601 , // 23 + -0.08967807 , // 24 + -0.04429128 , // 25 + +0.00513920 , // 26 + -0.04459511 , // 27 + -0.16886923 , // 28 + -0.19065374 , // 29 + +0.01930718 , // 30 + +0.34486939 , // 31 + +0.50345887 , // 32 + +0.34486939 , // 33 + +0.01930718 , // 34 + -0.19065374 , // 35 + -0.16886923 , // 36 + -0.04459511 , // 37 + +0.00513920 , // 38 + -0.04429128 , // 39 + -0.08967807 , // 40 + -0.06425601 , // 41 + -0.00986091 , // 42 + +0.00579429 , // 43 + -0.01791993 , // 44 + -0.03176715 , // 45 + -0.01328109 , // 46 + +0.01123057 , // 47 + +0.01341757 , // 48 + -0.00014844 , // 49 + -0.00580382 , // 50 + +0.00254789 , // 51 + +0.01100689 , // 52 + +0.00914871 , // 53 + +0.00195568 , // 54 + -0.00107943 , // 55 + +0.00128973 , // 56 + +0.00365685 , // 57 + +0.00276603 , // 58 + +0.00032296 , // 59 + -0.00099442 , // 60 + -0.00079785 , // 61 + -0.00022227 , // 62 + +0.00001835 // 63 +} ; + +double Alias_1k_Q[Alias_1k_Len] = { + -0.00000000 , // 0 + -0.00009527 , // 1 + -0.00023082 , // 2 + +0.00005162 , // 3 + +0.00123007 , // 4 + +0.00255193 , // 5 + +0.00207549 , // 6 + -0.00064302 , // 7 + -0.00244045 , // 8 + +0.00005205 , // 9 + +0.00410793 , // 10 + +0.00211830 , // 11 + -0.00729235 , // 12 + -0.01359800 , // 13 + -0.00757272 , // 14 + +0.00172023 , // 15 + -0.00460378 , // 16 + -0.02559228 , // 17 + -0.03408530 , // 18 + -0.01416468 , // 19 + +0.00731461 , // 20 + -0.00712536 , // 21 + -0.04328548 , // 22 + -0.04099291 , // 23 + +0.01821691 , // 24 + +0.06428190 , // 25 + +0.02790538 , // 26 + -0.03602086 , // 27 + +0.01583703 , // 28 + +0.22015579 , // 29 + +0.40003327 , // 30 + +0.32856209 , // 31 + -0.00000000 , // 32 + -0.32856209 , // 33 + -0.40003327 , // 34 + -0.22015579 , // 35 + -0.01583703 , // 36 + +0.03602086 , // 37 + -0.02790538 , // 38 + -0.06428190 , // 39 + -0.01821691 , // 40 + +0.04099291 , // 41 + +0.04328548 , // 42 + +0.00712536 , // 43 + -0.00731461 , // 44 + +0.01416468 , // 45 + +0.03408530 , // 46 + +0.02559228 , // 47 + +0.00460378 , // 48 + -0.00172023 , // 49 + +0.00757272 , // 50 + +0.01359800 , // 51 + +0.00729235 , // 52 + -0.00211830 , // 53 + -0.00410793 , // 54 + -0.00005205 , // 55 + +0.00244045 , // 56 + +0.00064302 , // 57 + -0.00207549 , // 58 + -0.00255193 , // 59 + -0.00123007 , // 60 + -0.00005162 , // 61 + +0.00023082 , // 62 + +0.00009527 // 63 +} ; + diff --git a/src/mt63/alias_2k.dat b/src/mt63/alias_2k.dat new file mode 100644 index 00000000..40629fe3 --- /dev/null +++ b/src/mt63/alias_2k.dat @@ -0,0 +1,166 @@ +/* + * alias_2k.dat -- Anti-alias filter 2000 Hz bandwidth + * + * Copyright (C) 1999-2004 Pawel Jalocha, SP9VRC + * + * This file is part of MT63. + * + * MT63 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MT63 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MT63; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +// Filter coefficiants made by ALIAS.C for the following parameters: +// FilterLen=64 SampleRate=8000.0 FreqLow=500.0 FreqUpp=2500.0 +// PassBandLow=375.0 PassBandUpp=2625.0 StopBandLow=0.0 StopBandUpp=3000.0 +// => PeakInStopBand=-89.35 dB +// Programmers's scale factor: 1.000000 +// Programmers's comments: Anti-alias filter 2000 Hz bandwidth, decimation by 2 + +const int Alias_2k_Len=64; + +double Alias_2k_I[Alias_2k_Len] = { + +0.00007244 , // 0 + +0.00002831 , // 1 + -0.00031179 , // 2 + -0.00027704 , // 3 + +0.00026218 , // 4 + -0.00057780 , // 5 + -0.00083336 , // 6 + +0.00119188 , // 7 + +0.00014945 , // 8 + -0.00077327 , // 9 + +0.00360739 , // 10 + +0.00224833 , // 11 + -0.00032116 , // 12 + +0.00696821 , // 13 + +0.00439713 , // 14 + -0.00157675 , // 15 + +0.00906983 , // 16 + +0.00372045 , // 17 + -0.00786599 , // 18 + +0.00735339 , // 19 + -0.00253048 , // 20 + -0.02189728 , // 21 + +0.00134354 , // 22 + -0.01477966 , // 23 + -0.04434362 , // 24 + -0.00479913 , // 25 + -0.03000591 , // 26 + -0.07609801 , // 27 + +0.00258946 , // 28 + -0.04271980 , // 29 + -0.14710769 , // 30 + +0.14198817 , // 31 + +0.42372962 , // 32 + +0.14198817 , // 33 + -0.14710769 , // 34 + -0.04271980 , // 35 + +0.00258946 , // 36 + -0.07609801 , // 37 + -0.03000591 , // 38 + -0.00479913 , // 39 + -0.04434362 , // 40 + -0.01477966 , // 41 + +0.00134354 , // 42 + -0.02189728 , // 43 + -0.00253048 , // 44 + +0.00735339 , // 45 + -0.00786599 , // 46 + +0.00372045 , // 47 + +0.00906983 , // 48 + -0.00157675 , // 49 + +0.00439713 , // 50 + +0.00696821 , // 51 + -0.00032116 , // 52 + +0.00224833 , // 53 + +0.00360739 , // 54 + -0.00077327 , // 55 + +0.00014945 , // 56 + +0.00119188 , // 57 + -0.00083336 , // 58 + -0.00057780 , // 59 + +0.00026218 , // 60 + -0.00027704 , // 61 + -0.00031179 , // 62 + +0.00002831 // 63 +} ; + +double Alias_2k_Q[Alias_2k_Len] = { + -0.00000000 , // 0 + -0.00015159 , // 1 + -0.00014608 , // 2 + +0.00036770 , // 3 + +0.00010339 , // 4 + -0.00031581 , // 5 + +0.00128580 , // 6 + +0.00120582 , // 7 + -0.00051862 , // 8 + +0.00225671 , // 9 + +0.00226918 , // 10 + -0.00216340 , // 11 + +0.00181728 , // 12 + +0.00176390 , // 13 + -0.00692361 , // 14 + -0.00105199 , // 15 + -0.00081355 , // 16 + -0.01498960 , // 17 + -0.00512332 , // 18 + -0.00331671 , // 19 + -0.02395357 , // 20 + -0.00602102 , // 21 + -0.00049919 , // 22 + -0.02943663 , // 23 + +0.00351421 , // 24 + +0.01576926 , // 25 + -0.02737092 , // 26 + +0.03572558 , // 27 + +0.06272088 , // 28 + -0.01659059 , // 29 + +0.15254255 , // 30 + +0.33394426 , // 31 + -0.00000000 , // 32 + -0.33394426 , // 33 + -0.15254255 , // 34 + +0.01659059 , // 35 + -0.06272088 , // 36 + -0.03572558 , // 37 + +0.02737092 , // 38 + -0.01576926 , // 39 + -0.00351421 , // 40 + +0.02943663 , // 41 + +0.00049919 , // 42 + +0.00602102 , // 43 + +0.02395357 , // 44 + +0.00331671 , // 45 + +0.00512332 , // 46 + +0.01498960 , // 47 + +0.00081355 , // 48 + +0.00105199 , // 49 + +0.00692361 , // 50 + -0.00176390 , // 51 + -0.00181728 , // 52 + +0.00216340 , // 53 + -0.00226918 , // 54 + -0.00225671 , // 55 + +0.00051862 , // 56 + -0.00120582 , // 57 + -0.00128580 , // 58 + +0.00031581 , // 59 + -0.00010339 , // 60 + -0.00036770 , // 61 + +0.00014608 , // 62 + +0.00015159 // 63 +} ; + diff --git a/src/mt63/alias_k5.dat b/src/mt63/alias_k5.dat new file mode 100644 index 00000000..20d46ee7 --- /dev/null +++ b/src/mt63/alias_k5.dat @@ -0,0 +1,294 @@ +/* + * alias_k5.dat -- Anti-alias filter 500 Hz bandwidth + * + * Copyright (C) 1999-2004 Pawel Jalocha, SP9VRC + * + * This file is part of MT63. + * + * MT63 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MT63 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MT63; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +// Filter coefficiants made by ALIAS.C for the following parameters: +// FilterLen=128 SampleRate=8000.0 FreqLow=500.0 FreqUpp=1000.0 +// PassBandLow=437.5 PassBandUpp=1062.5 StopBandLow=250.0 StopBandUpp=1250.0 +// => PeakInStopBand=-86.20 dB +// Programmers's scale factor: 4.000000 +// Programmers's comments: Anti-alias filter 500 Hz bandwidth, decimation by 8 + +const int Alias_k5_Len=128; + +double Alias_k5_I[Alias_k5_Len] = { + +0.00006549 , // 0 + +0.00007616 , // 1 + +0.00006321 , // 2 + -0.00007051 , // 3 + -0.00037947 , // 4 + -0.00077842 , // 5 + -0.00101934 , // 6 + -0.00079747 , // 7 + +0.00003634 , // 8 + +0.00128166 , // 9 + +0.00238256 , // 10 + +0.00269672 , // 11 + +0.00193090 , // 12 + +0.00045168 , // 13 + -0.00082397 , // 14 + -0.00100872 , // 15 + +0.00002675 , // 16 + +0.00132515 , // 17 + +0.00130571 , // 18 + -0.00111559 , // 19 + -0.00545495 , // 20 + -0.00950601 , // 21 + -0.01047729 , // 22 + -0.00687776 , // 23 + +0.00011406 , // 24 + +0.00696202 , // 25 + +0.00989474 , // 26 + +0.00760013 , // 27 + +0.00255231 , // 28 + -0.00031461 , // 29 + +0.00291281 , // 30 + +0.01174794 , // 31 + +0.02064676 , // 32 + +0.02211535 , // 33 + +0.01194783 , // 34 + -0.00696440 , // 35 + -0.02537662 , // 36 + -0.03322892 , // 37 + -0.02665276 , // 38 + -0.01145836 , // 39 + +0.00006418 , // 40 + -0.00230763 , // 41 + -0.01840573 , // 42 + -0.03588720 , // 43 + -0.03743444 , // 44 + -0.01306806 , // 45 + +0.03074821 , // 46 + +0.07251687 , // 47 + +0.08836159 , // 48 + +0.06850261 , // 49 + +0.02612053 , // 50 + -0.00913110 , // 51 + -0.01081820 , // 52 + +0.02342696 , // 53 + +0.06482083 , // 54 + +0.06779523 , // 55 + -0.00021458 , // 56 + -0.13123843 , // 57 + -0.27054300 , // 58 + -0.33962227 , // 59 + -0.27857291 , // 60 + -0.08448838 , // 61 + +0.17772565 , // 62 + +0.40136534 , // 63 + +0.48897105 , // 64 + +0.40136534 , // 65 + +0.17772565 , // 66 + -0.08448838 , // 67 + -0.27857291 , // 68 + -0.33962227 , // 69 + -0.27054300 , // 70 + -0.13123843 , // 71 + -0.00021458 , // 72 + +0.06779523 , // 73 + +0.06482083 , // 74 + +0.02342696 , // 75 + -0.01081820 , // 76 + -0.00913110 , // 77 + +0.02612053 , // 78 + +0.06850261 , // 79 + +0.08836159 , // 80 + +0.07251687 , // 81 + +0.03074821 , // 82 + -0.01306806 , // 83 + -0.03743444 , // 84 + -0.03588720 , // 85 + -0.01840573 , // 86 + -0.00230763 , // 87 + +0.00006418 , // 88 + -0.01145836 , // 89 + -0.02665276 , // 90 + -0.03322892 , // 91 + -0.02537662 , // 92 + -0.00696440 , // 93 + +0.01194783 , // 94 + +0.02211535 , // 95 + +0.02064676 , // 96 + +0.01174794 , // 97 + +0.00291281 , // 98 + -0.00031461 , // 99 + +0.00255231 , // 100 + +0.00760013 , // 101 + +0.00989474 , // 102 + +0.00696202 , // 103 + +0.00011406 , // 104 + -0.00687776 , // 105 + -0.01047729 , // 106 + -0.00950601 , // 107 + -0.00545495 , // 108 + -0.00111559 , // 109 + +0.00130571 , // 110 + +0.00132515 , // 111 + +0.00002675 , // 112 + -0.00100872 , // 113 + -0.00082397 , // 114 + +0.00045168 , // 115 + +0.00193090 , // 116 + +0.00269672 , // 117 + +0.00238256 , // 118 + +0.00128166 , // 119 + +0.00003634 , // 120 + -0.00079747 , // 121 + -0.00101934 , // 122 + -0.00077842 , // 123 + -0.00037947 , // 124 + -0.00007051 , // 125 + +0.00006321 , // 126 + +0.00007616 // 127 +} ; + +double Alias_k5_Q[Alias_k5_Len] = { + -0.00000000 , // 0 + -0.00005367 , // 1 + -0.00016555 , // 2 + -0.00031225 , // 3 + -0.00036198 , // 4 + -0.00013710 , // 5 + +0.00044740 , // 6 + +0.00124698 , // 7 + +0.00186810 , // 8 + +0.00184150 , // 9 + +0.00093521 , // 10 + -0.00058853 , // 11 + -0.00200435 , // 12 + -0.00252488 , // 13 + -0.00187551 , // 14 + -0.00063367 , // 15 + +0.00001967 , // 16 + -0.00088264 , // 17 + -0.00319853 , // 18 + -0.00540709 , // 19 + -0.00536877 , // 20 + -0.00180828 , // 21 + +0.00444555 , // 22 + +0.01048985 , // 23 + +0.01293826 , // 24 + +0.01021951 , // 25 + +0.00399079 , // 26 + -0.00159281 , // 27 + -0.00262530 , // 28 + +0.00149295 , // 29 + +0.00697645 , // 30 + +0.00777564 , // 31 + -0.00010048 , // 32 + -0.01493909 , // 33 + -0.02926626 , // 34 + -0.03410235 , // 35 + -0.02512027 , // 36 + -0.00643493 , // 37 + +0.01119978 , // 38 + +0.01734888 , // 39 + +0.00962767 , // 40 + -0.00347093 , // 41 + -0.00757142 , // 42 + +0.00724618 , // 43 + +0.03765704 , // 44 + +0.06671856 , // 45 + +0.07363882 , // 46 + +0.04816692 , // 47 + -0.00023240 , // 48 + -0.04602175 , // 49 + -0.06349448 , // 50 + -0.04533960 , // 51 + -0.01075524 , // 52 + +0.00463299 , // 53 + -0.02695458 , // 54 + -0.10175325 , // 55 + -0.17821917 , // 56 + -0.19595859 , // 57 + -0.11176981 , // 58 + +0.06782857 , // 59 + +0.27891973 , // 60 + +0.42579279 , // 61 + +0.42868878 , // 62 + +0.26809297 , // 63 + -0.00000000 , // 64 + -0.26809297 , // 65 + -0.42868878 , // 66 + -0.42579279 , // 67 + -0.27891973 , // 68 + -0.06782857 , // 69 + +0.11176981 , // 70 + +0.19595859 , // 71 + +0.17821917 , // 72 + +0.10175325 , // 73 + +0.02695458 , // 74 + -0.00463299 , // 75 + +0.01075524 , // 76 + +0.04533960 , // 77 + +0.06349448 , // 78 + +0.04602175 , // 79 + +0.00023240 , // 80 + -0.04816692 , // 81 + -0.07363882 , // 82 + -0.06671856 , // 83 + -0.03765704 , // 84 + -0.00724618 , // 85 + +0.00757142 , // 86 + +0.00347093 , // 87 + -0.00962767 , // 88 + -0.01734888 , // 89 + -0.01119978 , // 90 + +0.00643493 , // 91 + +0.02512027 , // 92 + +0.03410235 , // 93 + +0.02926626 , // 94 + +0.01493909 , // 95 + +0.00010048 , // 96 + -0.00777564 , // 97 + -0.00697645 , // 98 + -0.00149295 , // 99 + +0.00262530 , // 100 + +0.00159281 , // 101 + -0.00399079 , // 102 + -0.01021951 , // 103 + -0.01293826 , // 104 + -0.01048985 , // 105 + -0.00444555 , // 106 + +0.00180828 , // 107 + +0.00536877 , // 108 + +0.00540709 , // 109 + +0.00319853 , // 110 + +0.00088264 , // 111 + -0.00001967 , // 112 + +0.00063367 , // 113 + +0.00187551 , // 114 + +0.00252488 , // 115 + +0.00200435 , // 116 + +0.00058853 , // 117 + -0.00093521 , // 118 + -0.00184150 , // 119 + -0.00186810 , // 120 + -0.00124698 , // 121 + -0.00044740 , // 122 + +0.00013710 , // 123 + +0.00036198 , // 124 + +0.00031225 , // 125 + +0.00016555 , // 126 + +0.00005367 // 127 +} ; + diff --git a/src/mt63/dsp.cxx b/src/mt63/dsp.cxx new file mode 100644 index 00000000..1db05a3f --- /dev/null +++ b/src/mt63/dsp.cxx @@ -0,0 +1,1674 @@ +/* + * dsp.cc -- various DSP algorithms + * + * based on mt63 code by Pawel Jalocha + * Copyright (C) 1999-2004 Pawel Jalocha, SP9VRC + * Copyright (c) 2007-2008 Dave Freese, W1HKJ + * + * This file is part of fldigi. + * + * fldigi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * fldigi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty oF + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MT63; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +// Please note, that you should not rely on the correctness +// of these routines. They generally work well, but you may find +// differences in respect to the mathematical formulas: signs flipped, +// orders swapped, etc. + +#include // only when we do some control printf's +#include +#include +#include "dsp.h" + +// ---------------------------------------------------------------------------- + +double dspPower(double *X, int Len) +{ double Sum; + for(Sum=0.0; Len; Len--,X++) + Sum+=(*X)*(*X); + return Sum; } + +double dspPower(double *I, double *Q, int Len) +{ double Sum; + for(Sum=0.0; Len; Len--,I++,Q++) + Sum+=(*I)*(*I)+(*Q)*(*Q); + return Sum; } + +double dspPower(dspCmpx *X, int Len) +{ double Sum; + for(Sum=0.0; Len; Len--,X++) + Sum+=(X->re)*(X->re)+(X->im)*(X->im); + return Sum; } + +// ---------------------------------------------------------------------------- +// dspAverage, extremes, fitting + +double dspAverage(double *Data, int Len) +{ double Sum; int i; + for(Sum=0.0,i=0; iMax) Max=Pwr; + } return Max; } + +double dspFindMaxdspPower(dspCmpx *Data, int Len, int &MaxPos) +{ double Max,Pwr; int i,pos; + Max=dspPower(Data[0]); pos=0; + for(i=1; iMax) { Max=Pwr; pos=i; } + } MaxPos=pos; return Max; } + +double dspFitPoly1(double *Data, int Len, double &A, double &B) +{ double Sum; int i; + A=(Data[Len-1]-Data[0])/(Len-1); + for(Sum=0.0,i=0; iEnsureSpace(Len); if(err) return -1; + dspConvS16todouble(dspS16,dble->Data,Len,Gain); dble->Len=Len; return 0; } + +void dspConvdoubleTodspS16(double *dble, dspS16 *dspS16, int Len, double Gain) +{ double out; for(; Len; Len--) + { out=(*dble++)*Gain; + if(out>32767.0) out=32767.0; + else if(out<(-32767.0)) out=(-32767.0); + (*dspS16++)=(short int)floor(out+0.5); } +} // we could count the over/underflows ? + +void dspConvU8todouble(unsigned char *U8, double *dble, int Len, double Gain) +{ for(; Len; Len--) (*dble++)=((int)(*U8++)-128)*Gain; } + +int dspConvU8todouble(unsigned char *U8, double_buff *dble, int Len, double Gain) +{ int err=dble->EnsureSpace(Len); if(err) return -1; + dspConvU8todouble(U8,dble->Data,Len,Gain); dble->Len=Len; return 0; } + +// ---------------------------------------------------------------------------- +// other converts + +void dspConvCmpxTodspPower(dspCmpx *Inp, int Len, double *Out) +{ for(; Len; Len--) (*Out++)=dspPower(*Inp++); } + +int dspConvCmpxTodspPower(dspCmpx_buff *Input, double_buff *Output) +{ int err=Output->EnsureSpace(Input->Len); if(err) return err; + dspConvCmpxTodspPower(Input->Data,Input->Len,Output->Data); + Output->Len=Input->Len; return 0; } + +void dspConvCmpxTodspAmpl(dspCmpx *Inp, int Len, double *Out) +{ for(; Len; Len--) (*Out++)=sqrt(dspPower(*Inp++)); } + +int dspConvCmpxTodspAmpl(dspCmpx_buff *Input, double_buff *Output) +{ int err=Output->EnsureSpace(Input->Len); if(err) return err; + dspConvCmpxTodspAmpl(Input->Data,Input->Len,Output->Data); + Output->Len=Input->Len; return 0; } + +void dspConvCmpxTodspPhase(dspCmpx *Inp, int Len, double *Out) +{ for(; Len; Len--) (*Out++)=dspPhase(*Inp++); } + +int dspConvCmpxTodspPhase(dspCmpx_buff *Input, double_buff *Output) +{ int err=Output->EnsureSpace(Input->Len); if(err) return err; + dspConvCmpxTodspPhase(Input->Data,Input->Len,Output->Data); + Output->Len=Input->Len; return 0; } + +// ---------------------------------------------------------------------------- +// Pulse noise limiter + +dspPulseLimiter::dspPulseLimiter() { Tap=NULL; } + +dspPulseLimiter::~dspPulseLimiter() { free(Tap); } + +void dspPulseLimiter::Free(void) { free(Tap); Tap=NULL; } + +int dspPulseLimiter::Preset(int TapLen, double LimitThres) +{ Len=TapLen; Thres=LimitThres*LimitThres; + if(dspRedspAllocArray(&Tap,Len)) return -1; + dspClearArray(Tap,Len); Ptr=0; PwrSum=0.0; return 0; } + +int dspPulseLimiter::Process(double *Inp, int InpLen, double *Out) +{ int i,o; double Lim; + for(i=0; i=Len) Ptr-=Len; + o=Ptr+(Len/2); if(o>=Len) o-=Len; + Lim=Thres*PwrSum/Len; + if(Tap[o]*Tap[o]<=Lim) Out[i]=Tap[o]; + else { if(Tap[o]>0.0) Out[i]=sqrt(Lim); else Out[i]=(-sqrt(Lim)); } + } + for(PwrSum=0.0, i=0; iLen); if(err) return -1; + Process(Input->Data,Input->Len,Output.Data); + Output.Len=Input->Len; return 0; } + +// ---------------------------------------------------------------------------- +// Signal level monitor + +dspLevelMonitor::dspLevelMonitor() +{ } + +dspLevelMonitor::~dspLevelMonitor() +{ } + +int dspLevelMonitor::Preset(double Integ, double Range) +{ dspLowPass2Coeff(Integ,W1,W2,W5); + MaxSqr=Range*Range; + PwrMid=0.0; PwrOut=0.0; dspRMS=0.0; + OutOfRangeMid=0.0; OutOfRange=0.0; + return 0; } + +int dspLevelMonitor::Process(double *Inp, int Len) +{ int i,Out; double Sqr,Sum; + if(Len<=0) return 0; + for(Sum=0.0,Out=0,i=0; iMaxSqr); } + dspLowPass2(Sum/Len,PwrMid,PwrOut,W1,W2,W5); + dspLowPass2((double)Out/Len,OutOfRangeMid,OutOfRange,W1,W2,W5); + if(OutOfRange<0.0) OutOfRange=0.0; + if(PwrOut<=0.0) dspRMS=0.0; else dspRMS=sqrt(PwrOut); + return 0; } + +int dspLevelMonitor::Process(double_buff *Input) +{ return Process(Input->Data,Input->Len); } + +// ---------------------------------------------------------------------------- +// Automatic Gain/Level Control for the Mixer + +dspMixerAutoLevel::dspMixerAutoLevel() +{ MinMS=0.01; MaxMS=0.05; + IntegLen=8000; PeakHold=4000; MinHold=800; + MinLevel=0; MaxLevel=100; AdjStep=1; + Level=75; Hold=(-IntegLen); AvedspRMS=0.0; +} + +int dspMixerAutoLevel::Process(double *Inp, int InpLen) +{ double MS=dspPower(Inp,InpLen)/IntegLen; + double W=1.0-((double)InpLen)/IntegLen; + + AvedspRMS = AvedspRMS*W + MS; + Hold+=InpLen; if(HoldMaxMS) + { Level-=AdjStep; if(LevelMaxLevel) Level=MaxLevel; + Hold=0; return 1; } + + return 0; +} + +// ---------------------------------------------------------------------------- + +void dspLowPass2(dspCmpx *Inp, dspCmpx *Mid, dspCmpx *Out, double W1, double W2, double W5) +{ double Sum, Diff; +// printf("\n[dspLowPass2] %6.3f %6.3f %6.3f",Inp->re,Mid->re,Out->re); + Sum=Mid->re+Out->re; Diff=Mid->re-Out->re; + Mid->re+=W2*Inp->re-W1*Sum; Out->re+=W5*Diff; +// printf(" => %6.3f %6.3f\n",Mid->re,Out->re); + Sum=Mid->im+Out->im; Diff=Mid->im-Out->im; + Mid->im+=W2*Inp->im-W1*Sum; Out->im+=W5*Diff; } + +// ---------------------------------------------------------------------------- +// periodic low pass + +dspPeriodLowPass2::dspPeriodLowPass2() { TapMid=NULL; TapOut=NULL; } + +dspPeriodLowPass2::~dspPeriodLowPass2() { free(TapMid); free(TapOut); Output.Free(); } + +void dspPeriodLowPass2::Free(void) +{ free(TapMid); TapMid=NULL; + free(TapOut); TapOut=NULL; } + +int dspPeriodLowPass2::Preset(int Period, double IntegLen) +{ int i; + Len=Period; + if(dspRedspAllocArray(&TapMid,Len)) goto Error; + if(dspRedspAllocArray(&TapOut,Len)) goto Error; + for(i=0; i=Len) TapPtr=0; + return 0; } + +int dspPeriodLowPass2::Process(double *Inp, int InpLen, double *Out) +{ int i,batch; + for(i=0; i=Len) TapPtr=0; } + return 0; +} + +int dspPeriodLowPass2::Process(double_buff *Input) +{ int err=Output.EnsureSpace(Input->Len); if(err) return -1; + Process(Input->Data,Input->Len,Output.Data); + Output.Len=Input->Len; return 0; } + +// ---------------------------------------------------------------------------- +// Low pass "moving box" FIR filter +// very unpure spectral response but complexity very low +// and independent on the integration time + +dspBoxFilter::dspBoxFilter() { Tap=NULL; } + +dspBoxFilter::~dspBoxFilter() { free(Tap); } + +void dspBoxFilter::Free(void) { free(Tap); Tap=NULL; Output.Free(); } + +int dspBoxFilter::Preset(int BoxLen) +{ int i; + if(dspRedspAllocArray(&Tap,BoxLen)) return -1; + for(i=0; i=Len) TapPtr=0; } + for(Sum=0, i=0; iLen); if(err) return err; + Process(Input->Data,Input->Len,Output.Data); + Output.Len=Input->Len; return 0; } + + +dspCmpxBoxFilter::dspCmpxBoxFilter() { Tap=NULL; } + +dspCmpxBoxFilter::~dspCmpxBoxFilter() { free(Tap); } + +void dspCmpxBoxFilter::Free(void) { free(Tap); Tap=NULL; Output.Free(); } + +int dspCmpxBoxFilter::Preset(int BoxLen) +{ int i; + if(dspRedspAllocArray(&Tap,BoxLen)) return -1; + for(i=0; i=Len) TapPtr=0; } + for(Sum.re=Sum.im=0.0, i=0; iLen); if(err) return err; + Process(Input->Data,Input->Len,Output.Data); + Output.Len=Input->Len; return 0; } + +// ---------------------------------------------------------------------------- +// FIR filter with a given shape + +dspFirFilter::dspFirFilter() { Tap=NULL; ExternShape=1; } +dspFirFilter::~dspFirFilter() { free(Tap); if(!ExternShape) free(Shape); } + +void dspFirFilter::Free(void) +{ free(Tap); Tap=NULL; + if(!ExternShape) free(Shape); + Shape=NULL; Output.Free(); } + +int dspFirFilter::Preset(int FilterLen, double *FilterShape) +{ int i; + if(dspRedspAllocArray(&Tap,FilterLen)) return -1; + for(i=0; i=Len) TapPtr=0; + for(Sum=0,s=0,t=TapPtr; tLen); if(err) return err; + Process(Input->Data,Input->Len,Output.Data); + Output.Len=Input->Len; return 0; } + +// ---------------------------------------------------------------------------- +// a pair of FIR filters. for quadrature split & decimate +// the decimation rate must be an integer + +dspQuadrSplit::dspQuadrSplit() { ExternShape = 1; } + +dspQuadrSplit::~dspQuadrSplit() +{ if(!ExternShape) { free(ShapeI); free(ShapeQ); } } + +void dspQuadrSplit::Free(void) +{ Tap.Free(); + if(!ExternShape) { free(ShapeI); free(ShapeQ); } + ShapeI=NULL; ShapeQ=NULL; + Output.Free(); } + +int dspQuadrSplit::Preset(int FilterLen, + double *FilterShape_I, double *FilterShape_Q, + int DecimateRate) +{ Len=FilterLen; + if(!ExternShape) { free(ShapeI); free(ShapeQ); } + ShapeI=FilterShape_I; ShapeQ=FilterShape_Q; ExternShape=1; + Tap.EnsureSpace(Len); Tap.Len=Len; + dspClearArray(Tap.Data,Tap.Len); + Rate=DecimateRate; + return 0; } + +int dspQuadrSplit::ComputeShape(double LowOmega,double UppOmega, + double (*Window)(double)) +{ if(ExternShape) + { ShapeI=NULL; ShapeQ=NULL; ExternShape=0; } + if(dspRedspAllocArray(&ShapeI,Len)) return -1; + if(dspRedspAllocArray(&ShapeQ,Len)) return -1; + dspWinFirI(LowOmega,UppOmega,ShapeI,Len,Window); + WinFirQ(LowOmega,UppOmega,ShapeQ,Len,Window); + return 0; } + +int dspQuadrSplit::Process(double_buff *Input) +{ + int err,i,s,t,o,l; + double SumI,SumQ; + double *Inp; + dspCmpx *Out; + int InpLen; + + InpLen = Input->Len; + err = Tap.EnsureSpace(Tap.Len + InpLen); + if(err) return err; + dspCopyArray(Tap.Data+Tap.Len,Input->Data,InpLen); +// printf("dspQuadrSplit: InpLen=%d, Tap.Len=%d",InpLen,Tap.Len); + Tap.Len += InpLen; + Inp = Tap.Data; +// printf(" -> %d",Tap.Len); + err = Output.EnsureSpace( InpLen / Rate + 2); + if(err) return err; + Out = Output.Data; + for(l = Tap.Len-Len,o = 0, i = 0; i < l; i += Rate) { + for(SumI = SumQ = 0.0, s = i,t = 0; t < Len; t++,s++) { + SumI += Inp[s] * ShapeI[t]; + SumQ += Inp[s] * ShapeQ[t]; + } + Out[o].re=SumI; + Out[o++].im=SumQ; + } + Tap.Len -= i; + dspMoveArray(Tap.Data,Tap.Data+i,Tap.Len); + Output.Len = o; +// printf(" => Tap.Len=%d\n",Tap.Len); + return 0; +} + +// ---------------------------------------------------------------------------- +// reverse of dspQuadrSplit: interpolates and combines the I/Q +// back into 'real' signal. + +dspQuadrComb::dspQuadrComb() { Tap=NULL; ExternShape=1; } + +dspQuadrComb::~dspQuadrComb() +{ free(Tap); if(!ExternShape) { free(ShapeI); free(ShapeQ); } } + +void dspQuadrComb::Free(void) +{ free(Tap); Tap=NULL; + if(!ExternShape) { free(ShapeI); free(ShapeQ); } + ShapeI=NULL; ShapeQ=NULL; + Output.Free(); } + +int dspQuadrComb::Preset(int FilterLen, + double *FilterShape_I, double *FilterShape_Q, + int DecimateRate) +{ int i; + Len=FilterLen; + if(dspRedspAllocArray(&Tap,Len)) return -1; + if(!ExternShape) { free(ShapeI); free(ShapeQ); } + ShapeI=FilterShape_I; ShapeQ=FilterShape_Q; ExternShape=1; + for(i=0; iLen; + err=Output.EnsureSpace(InpLen*Rate); if(err) return err; + Inp=Input->Data; Out=Output.Data; Output.Len=InpLen*Rate; + for(o=0,i=0; i=2*M_PI) dspPhase-=2*M_PI; } + return InpLen; +} + +int dspCmpxMixer::ProcessFast(double *InpI, double *InpQ, int InpLen, + double *OutI, double *OutQ) +{ int i; double dI,dQ,I,Q,nI,nQ, N; + dI=cos(Omega); dQ=sin(Omega); + I=cos(dspPhase); Q=sin(dspPhase); + for(i=0; i=2*M_PI) dspPhase-=2*M_PI; } + return InpLen; +} + +int dspCmpxMixer::ProcessFast(dspCmpx *Inp, int InpLen, dspCmpx *Out) +{ int i; double dI,dQ,I,Q,nI,nQ, N; + dI=cos(Omega); dQ=sin(Omega); + I=cos(dspPhase); Q=sin(dspPhase); + for(i=0; iLen); if(err) return err; + Process(Input->Data,Input->Len,Output.Data); + Output.Len=Input->Len; return 0; } + +int dspCmpxMixer::ProcessFast(dspCmpx_buff *Input) +{ int err=Output.EnsureSpace(Input->Len); if(err) return err; + ProcessFast(Input->Data,Input->Len,Output.Data); + Output.Len=Input->Len; return 0; } + +// ---------------------------------------------------------------------------- +// FM demodulator (dspPhase rotation speed meter) + +dspFMdemod::dspFMdemod() { PrevdspPhase=0.0; RefOmega=0.0; } + +int dspFMdemod::Preset(double CenterOmega) { RefOmega=CenterOmega; return 0; } + +int dspFMdemod::Process(double *InpI, double *InpQ, int InpLen, double *Out) +{ int i; double dspPhase,dspPhaseDiff; + for(i=0; i=M_PI) dspPhaseDiff-=2*M_PI; + else if(dspPhaseDiff<(-M_PI)) dspPhaseDiff+=2*M_PI; + Out[i]=dspPhaseDiff; PrevdspPhase=dspPhase; + } return InpLen; +} + +int dspFMdemod::Process(dspCmpx *Inp, int InpLen, double *Out) +{ int i; double dspPhase,dspPhaseDiff; + for(i=0; i=M_PI) dspPhaseDiff-=2*M_PI; + else if(dspPhaseDiff<(-M_PI)) dspPhaseDiff+=2*M_PI; + Out[i]=dspPhaseDiff; PrevdspPhase=dspPhase; + } return InpLen; +} + +int dspFMdemod::Process(dspCmpx_buff *Input) +{ int err=Output.EnsureSpace(Input->Len); if(err) return err; + Process(Input->Data,Input->Len,Output.Data); + Output.Len=Input->Len; return 0; } + +// ---------------------------------------------------------------------------- +// Rate converter - real input/output, linear interpolation +// expect large error when high frequency components are present +// thus the best place to convert rates is after a low pass filter +// of a demodulator. + +dspRateConvLin::dspRateConvLin() { PrevSdspAmple=0; OutdspPhase=0; OutStep=1.0; } + +void dspRateConvLin::SetOutVsInp(double OutVsInp) { OutStep=1.0/OutVsInp; } + +void dspRateConvLin::SetInpVsOut(double InpVsOut) { OutStep=InpVsOut; } + +int dspRateConvLin::Process(double_buff *Input) +{ int err,i,o; double *Inp,*Out; int InpLen; + Inp=Input->Data; InpLen=Input->Len; + err=Output.EnsureSpace((int)ceil(InpLen/OutStep)+2); if(err) return err; + Out=Output.Data; + for(o=0; OutdspPhase<0; ) + { Out[o++]=Inp[0]*(1.0+OutdspPhase)-OutdspPhase*PrevSdspAmple; OutdspPhase+=OutStep; } + for(i=0; i<(InpLen-1); ) + { if(OutdspPhase>=1.0) { OutdspPhase-=1.0; i++; } + else { Out[o++]=Inp[i]*(1.0-OutdspPhase)+Inp[i+1]*OutdspPhase; + OutdspPhase+=OutStep; } + } Output.Len=o; PrevSdspAmple=Inp[i]; OutdspPhase-=1.0; + return 0; +} + +// ---------------------------------------------------------------------------- +// Rate converter - real input/output, quadratic interpolation +// similar limits like for RateConv1 + +dspRateConvQuadr::dspRateConvQuadr() +{ int i; for(i=0; i<4; i++) Tap[i]=0; + OutStep=1.0; OutdspPhase=0; TapPtr=0; } + +void dspRateConvQuadr::SetOutVsInp(double OutVsInp) { OutStep=1.0/OutVsInp; } + +void dspRateConvQuadr::SetInpVsOut(double InpVsOut) { OutStep=InpVsOut; } + +int dspRateConvQuadr::Process(double *Inp, int InpLen, + double *Out, int MaxOutLen, int *OutLen) +{ int i,o,t; double Ref0,Ref1,Diff0,Diff1; + for(o=i=0; (i=1.0) + { Tap[TapPtr]=(*Inp++); i++; TapPtr=(TapPtr+1)&3; OutdspPhase-=1.0; } + else + { t=TapPtr; Diff0=(Tap[t^2]-Tap[t])/2; Ref1=Tap[t^2]; + t=(t+1)&3; Diff1=(Tap[t^2]-Tap[t])/2; Ref0=Tap[t]; + (*Out++)=Ref0*(1.0-OutdspPhase)+Ref1*OutdspPhase // linear piece + -(Diff1-Diff0)*OutdspPhase*(1.0-OutdspPhase)/2; // quadr. piece + o++; OutdspPhase+=OutStep; } + } (*OutLen)=o; return i; +} + +int dspRateConvQuadr::Process(double_buff *Input) +{ int err,i,o,t; double Ref0,Ref1,Diff0,Diff1; double *Inp,*Out; int InpLen; + Inp=Input->Data; InpLen=Input->Len; + err=Output.EnsureSpace((int)ceil(InpLen/OutStep)+2); if(err) return err; + Out=Output.Data; + for(o=i=0; i=1.0) + { Tap[TapPtr]=(*Inp++); i++; TapPtr=(TapPtr+1)&3; OutdspPhase-=1.0; } + else + { t=TapPtr; Diff0=(Tap[t^2]-Tap[t])/2; Ref1=Tap[t^2]; + t=(t+1)&3; Diff1=(Tap[t^2]-Tap[t])/2; Ref0=Tap[t]; + (*Out++)=Ref0*(1.0-OutdspPhase)+Ref1*OutdspPhase // linear piece + -(Diff1-Diff0)*OutdspPhase*(1.0-OutdspPhase)/2; // quadr. piece + o++; OutdspPhase+=OutStep; } + } Output.Len=o; + return 0; +} + +// ---------------------------------------------------------------------------- +// Rate converter, real input/output, +// bandwidth-limited interpolation, several shifted FIR filters + +dspRateConvBL::dspRateConvBL() +{ Tap=NULL; Shape=NULL; ExternShape=1; } + +dspRateConvBL::~dspRateConvBL() { Free(); } + +void dspRateConvBL::Free(void) +{ int s; + free(Tap); Tap=NULL; if(ExternShape) return; + if(Shape) + { for(s=0; sData; InpLen=Input->Len; + err=Output.EnsureSpace((int)ceil(InpLen/OutStep)+2); if(err) return err; + Out=Output.Data; + if((Len+InpLen)>TapSize) + { Tap=(double*)realloc(Tap,(Len+InpLen)*sizeof(double)); if(Tap==NULL) return -1; + TapSize=Len+InpLen; } + memcpy(Tap+Len,Inp,InpLen*sizeof(double)); + for(o=i=0; i=1.0) { OutdspPhase-=1.0; i++; } + else { idx=(int)floor(OutdspPhase*ShapeNum); shape=Shape[idx]; + for(Sum=0.0,t=0; tData; InpLen=Input->Len; + err=Output.EnsureSpace((int)ceil(InpLen/OutStep)+2); if(err) return err; + Out=Output.Data; + if((Len+InpLen)>TapSize) + { Tap=(double*)realloc(Tap,(Len+InpLen)*sizeof(double)); if(Tap==NULL) return -1; + TapSize=Len+InpLen; } + memcpy(Tap+Len,Inp,InpLen*sizeof(double)); + for(o=i=0; i=1.0) { OutdspPhase-=1.0; i++; } + else { idx=(int)floor(OutdspPhase*ShapeNum); d=OutdspPhase*ShapeNum-idx; + shape=Shape[idx]; + for(Sum0=0.0,t=0; t=ShapeNum) { idx=0; i++; } + shape=Shape[idx]; + for(Sum1=0.0,t=0; tWindowLen) return -1; + Len=WindowLen; Dist=SlideDist; + if(dspRedspAllocArray(&Buff,Len)) return -1; + for(i=0; iData; int InpLen=Input->Len; + int i,len,err; + Output.Len=0; + while(InpLen>0) + { len=dspIntmin(Len-Ptr,InpLen); + memcpy(Buff+Ptr,Inp,len*sizeof(dspCmpx)); Ptr+=len; Inp+=len; InpLen-=len; + if(Ptr>=Len) + { len=Output.Len; err=Output.EnsureSpace(len+Len); if(err) return err; + if(Window==NULL) memcpy(Output.Data,Buff,Len*sizeof(dspCmpx)); + else for(i=0; iWindowLen) return -1; + Len=WindowLen; Dist=SlideDist; + if(dspRedspAllocArray(&Buff,Len)) return -1; + for(i=0; iLen; i+=Len) + { err=Output.EnsureSpace(Output.Len+Dist); if(err) return err; + Process(Input->Data+i,Output.Data+Output.Len); + Output.Len+=Dist; } + return 0; +} + +int dspCmpxOverlapWindow::Process(dspCmpx *Input) +{ int err; + err=Output.EnsureSpace(Dist); if(err) return err; + Process(Input,Output.Data); + Output.Len=Dist; + return 0; +} + +void dspCmpxOverlapWindow::Process(dspCmpx *Inp, dspCmpx *Out) +{ int i; + if(Window==NULL) + { for(i=0; i=InpTapLen) InpTapPtr=0; + diff+=(InpTap[InpTapPtr]=(*Inp++)); DiffTapPtr=(DiffTapPtr+1)&3; + dspLowPass2(diff*diff,DiffInteg0[DiffTapPtr],DiffInteg[DiffTapPtr],W1,W2,W5); + if(DiffTapPtr==BitPtr) + { for(Sum=0,t=0; t<4; t++) Sum+=DiffInteg[t]; + t=DiffTapPtr; SumI = DiffInteg[t]-DiffInteg[t^2]; + t=(t+1)&3; SumQ = DiffInteg[t]-DiffInteg[t^2]; + if((Sum==0.0)||((SyncConfid=(SumI*SumI+SumQ*SumQ)/(Sum*Sum))==0.0)) + { (*BitOut++)=0; (*IbitOut++)=0; o++; continue; } + dspPhase=atan2(-SumQ,-SumI)*(4/(2*M_PI)); + dspLowPass2(dspPhase-SyncdspPhase,SyncDrift0,SyncDrift,W1,W2,W5); SyncdspPhase=dspPhase; + if(dspPhase>0.52) { step=1; SyncdspPhase-=1.0; } + else if(dspPhase<(-0.52)) { step=(-1); SyncdspPhase+=1.0; } + else step=0; +double Samp[5],bit,ibit,dx; int p; + p=InpTapPtr-4*IntegLen-2; if(p<0) p+=InpTapLen; + for(t=0; t<5; t++) { Samp[t]=InpTap[p++]; if(p>=InpTapLen) p=0; } + dx=dspPhase-0.5; + // bit=Samp[2]+dx*(Samp[2]-Samp[1]); // linear interpolation + bit=Samp[2]*(1.0+dx)-Samp[1]*dx // or quadratic + +((Samp[3]-Samp[1])-(Samp[2]-Samp[0]))/2*dx*(1.0+dx)/2; + ibit=Samp[4]+dx*(Samp[4]-Samp[3]); //linear interpolation is enough + (*BitOut++)=bit; (*IbitOut++)=ibit; o++; + } else if(DiffTapPtr==(BitPtr^2)) + { BitPtr=(BitPtr+step)&3; step=0; } + } (*OutLen)=o; return i; +} + +double dspDiffBitSync4::GetSyncConfid() { return 4*SyncConfid; } + +double dspDiffBitSync4::GetSyncDriftRate() { return SyncDrift/4; } + +// ---------------------------------------------------------------------------- +// bit slicer, SNR/Tune meter + +dspBitSlicer::dspBitSlicer(int IntegBits) +{ int i; + TapLen=IntegLen=IntegBits; + Tap=(double *)malloc(TapLen*sizeof(double)); + for(i=0; i0; + dspLowPass2(Bit,Sum0[l],Sum[l],W1,W2,W5); + dspLowPass2(Bit*Bit,SumSq0[l],SumSq[l],W1,W2,W5); + Noise[l]=sqrt(SumSq[l]-Sum[l]*Sum[l]); + if(Noise[0]+Noise[1]<=0) OptimThres=0; + else OptimThres=(Sum[0]*Noise[1]+Sum[1]*Noise[0])/(Noise[0]+Noise[1]); + soft=Tap[TapPtr]-OptimThres; // we could do a better soft-decision + if(Bit*PrevBit<0) + { dspLowPass2(PrevIBit,dspAmplAsym0,dspAmplAsym,W1,W2,W5); + if(Bit>0) PrevIBit=(-PrevIBit); + dspLowPass2(PrevIBit,TimeAsym0,TimeAsym,W1,W2,W5); } + (*OutBits++)=soft; PrevBit=Bit; PrevIBit=IBits[i]; + Tap[TapPtr]=Bit; TapPtr++; if(TapPtr>=TapLen) TapPtr=0; + } return InpLen; +} + +double dspBitSlicer::GetSigToNoise() +{ return Noise[1]>0 ? (Sum[1]-OptimThres)/Noise[1] : 0.0; } + +double dspBitSlicer::GetdspAmplAsym() +{ double Sweep=Sum[1]-Sum[0]; return Sweep>0 ? 2*dspAmplAsym/Sweep : 0.0; } + +double dspBitSlicer::GetTimeAsym() +{ double Sweep=Sum[1]-Sum[0]; return Sweep>0 ? 2*TimeAsym/Sweep : 0.0; } + +// ---------------------------------------------------------------------------- +// The decoder for the HDLC frames, +// makes no AX.25 CRC check, only the length in bytes against MinLen and MaxLen +// however it does not pass frames with non-complete bytes. + +dspHDLCdecoder::dspHDLCdecoder(int minlen, int maxlen, int diff, int invert, + int chan, int (*handler)(int, char *, int)) +{ MinLen=minlen; MaxLen=maxlen; RxDiff=diff; RxInvert=invert; + ChanId=chan; FrameHandler=handler; + Buff=(char *)malloc(MaxLen); Len=(-1); + PrevLev=0; ShiftReg=0; BitCount=0; Count1s=0; + AllFrameCount=0; BadFrameCount=0; +} + +dspHDLCdecoder::~dspHDLCdecoder() +{ free(Buff); } + +int dspHDLCdecoder::Process(double *Inp, int InpLen) +{ int i,lev,bit,Flag; + + for(i=0; i0; + bit=(lev^(PrevLev&RxDiff))^RxInvert; PrevLev=lev; + ShiftReg=(ShiftReg>>1)|(bit<<7); BitCount+=1; Flag=0; + if(bit) Count1s+=1; else + { if(Count1s>=7) Len=(-1); + else if(Count1s==6) Flag=1; + else if(Count1s==5) { ShiftReg<<=1; BitCount-=1; } + Count1s=0; } + if(Flag) + { if((Len>=MinLen)&&(BitCount==8)) (*FrameHandler)(ChanId,Buff,Len); + Len=0; BitCount=0; } + else if(Len>=0) + { if(BitCount==8) + { if(Len>8)^dspAX25CRCtable[idx]; + } CRC^=0xFFFF; + return CRC; +} + +// ---------------------------------------------------------------------------- +// radix-2 FFT + +// constructor +dsp_r2FFT::dsp_r2FFT() { BitRevIdx=NULL; Twiddle=NULL; /* Window=NULL; */ } + +// destructor: free twiddles, bit-reverse lookup and window tables +dsp_r2FFT::~dsp_r2FFT() { free(BitRevIdx); free(Twiddle); /* free(Window); */ } + +void dsp_r2FFT::Free(void) +{ free(BitRevIdx); BitRevIdx=NULL; + free(Twiddle); Twiddle=NULL; } + +// .......................................................................... + +// a radix-2 FFT bufferfly +inline void dsp_r2FFT::FFTbf(dspCmpx &x0, dspCmpx &x1, dspCmpx &W) +{ dspCmpx x1W; + x1W.re=x1.re*W.re+x1.im*W.im; // x1W.re=x1.re*W.re-x1.im*W.im; + x1W.im=(-x1.re*W.im)+x1.im*W.re; // x1W.im=x1.re*W.im+x1.im*W.re; + x1.re=x0.re-x1W.re; + x1.im=x0.im-x1W.im; + x0.re=x0.re+x1W.re; + x0.im=x0.im+x1W.im; +} + +// 2-point FFT +inline void dsp_r2FFT::FFT2(dspCmpx &x0, dspCmpx &x1) +{ dspCmpx x1W; + x1W.re=x1.re; + x1W.im=x1.im; + x1.re=x0.re-x1.re; + x1.im=x0.im-x1.im; + x0.re+=x1W.re; + x0.im+=x1W.im; +} + +// 4-point FFT +// beware: these depend on the convention for the twiddle factors ! +inline void dsp_r2FFT::FFT4(dspCmpx &x0, dspCmpx &x1, dspCmpx &x2, dspCmpx &x3) +{ dspCmpx x1W; + x1W.re=x2.re; + x1W.im=x2.im; + x2.re=x0.re-x1W.re; + x2.im=x0.im-x1W.im; + x0.re=x0.re+x1W.re; + x0.im=x0.im+x1W.im; + x1W.re=x3.im; + x1W.im=(-x3.re); + x3.re=x1.re-x1W.re; + x3.im=x1.im-x1W.im; + x1.re=x1.re+x1W.re; + x1.im=x1.im+x1W.im; +} + +// .......................................................................... + +// bit reverse (in place) the dspSequence (before the actuall FTT) +void dsp_r2FFT::Scramble(dspCmpx x[]) +{ int idx,ridx; dspCmpx tmp; + for(idx=0; idxidx) + { tmp=x[idx]; x[idx]=x[ridx]; x[ridx]=tmp; + /* printf("%d <=> %d\n",idx,ridx); */ } +} + +// Preset for given processing size +int dsp_r2FFT::Preset(int size) +{ int err,idx,ridx,mask,rmask; double dspPhase; + if(!dspPowerOf2(size)) goto Error; + Size=size; + err=dspRedspAllocArray(&BitRevIdx,Size); if(err) goto Error; + err=dspRedspAllocArray(&Twiddle,Size); if(err) goto Error; + for(idx=0; idx %6.4f => %6.4f %6.4f\n", + idx,dspPhase,Twiddle[idx].re,Twiddle[idx].im); */ } + for(ridx=0,idx=0; idx>=1,rmask<<=1) + { if(idx&mask) ridx|=rmask; } + BitRevIdx[idx]=ridx; /* printf("%04x %04x\n",idx,ridx); */ } +// free(Window); Window=NULL; WinInpScale=1.0/Size; WinOutScale=0.5; + return 0; + + Error: Free(); return -1; +} + +// .......................................................................... + +// radix-2 FFT: the first and the second pass are by hand +// looks like there is no gain by separating the second pass +// and even the first pass is in question ? +void dsp_r2FFT::CoreProc(dspCmpx x[]) +{ int Groups,GroupHalfSize,Group,Bf,TwidIdx; + int HalfSize=Size/2; + for(Bf=0; Bf>=1, GroupHalfSize<<=1) + for(Group=0,Bf=0; Groupoutput dspDelay longer. + +dspSlideWinFFT::dspSlideWinFFT() +{ SlideBuff=NULL; FFTbuff=NULL; Window=NULL; ExternWindow=1; } + +dspSlideWinFFT::~dspSlideWinFFT() +{ free(SlideBuff); free(FFTbuff); if(!ExternWindow) free(Window); } + +void dspSlideWinFFT::Free(void) +{ free(SlideBuff); SlideBuff=NULL; + free(FFTbuff); FFTbuff=NULL; + if(!ExternWindow) free(Window); + Window=NULL; ExternWindow=1; + Output.Free(); } + +int dspSlideWinFFT::Preset(int size, int step, double *window) +{ int err,i; + + Size=size; SizeMask=Size-1; + err=FFT.Preset(Size); if(err) goto Error; + + if(!ExternWindow) { free(Window); ExternWindow=1; } + Window=window; + + err=dspRedspAllocArray(&FFTbuff,Size); if(err) goto Error; + + err=dspRedspAllocArray(&SlideBuff,Size); if(err) goto Error; + for(i=0; iData; InpLen=Input->Len; Output.Len=0; + while(InpLen) + { for(i=len=dspIntmin(InpLen,Left); i; i--) + { SlideBuff[SlidePtr++]=(*Inp++); SlidePtr&=SizeMask; } + InpLen-=len; Left-=len; + if(Left==0) + { Slide^=1; Left=Dist; + if(Slide) + { for(t=0,i=SlidePtr; iData; InpLen=Input->Len; Output.Len=0; + while(InpLen) + { for(i=len=dspIntmin(InpLen,Left); i; i--) + { SlideBuff[SlidePtr++]=(*Inp++); SlidePtr&=SizeMask; } + InpLen-=len; Left-=len; + if(Left==0) + { Slide^=1; Left=Dist; + if(Slide) + { for(t=0,i=SlidePtr; i + +#include +#include +#include + +#include "mt63.h" + +using namespace std; + +static int IntegLen = 32; // integration period for sync./data tracking + +void mt63::tx_init(SoundBase *sb) +{ + scard = sb; +// int err; +// err = Tx->Preset((int)bandwidth, Interleave); +// if (err) +// fprintf(stderr, "mt63_txinit: init failed\n"); + set_freq(500.0 + bandwidth / 2.0); + flush = Tx->DataInterleave; +} + +void mt63::rx_init() +{ +// int err; +// err = Rx->Preset((int)bandwidth, Interleave, IntegLen); +// if (err) +// fprintf(stderr, "mt63_rxinit: init failed\n"); + set_freq(500.0 + bandwidth / 2.0); + InpLevel->Preset(64.0, 0.75); + escape = 0; +} + +int mt63::tx_process() +{ + int c; + + c = get_tx_char(); + if (c == 0x03) { + stopflag = true; + flush = Tx->DataInterleave; + c = 0; + } + + if (stopflag && flush-- == 0) + return -1; + + if ((progdefaults.mt63_8bit && c > 255) || (!progdefaults.mt63_8bit && c > 127)) + c = '.'; + + put_echo_char(c); + + if (c > 127) { + c &= 127; + Tx->SendChar(127); + ModulateXmtr((Tx->Comb.Output.Data), Tx->Comb.Output.Len); + } + + Tx->SendChar(c); + ModulateXmtr((Tx->Comb.Output.Data), Tx->Comb.Output.Len); + + return 0; +} + +int mt63::rx_process(const double *buf, int len) +{ + double snr; + unsigned int c; + int i; + + if (Interleave != progdefaults.mt63_interleave) { + Interleave = progdefaults.mt63_interleave; + restart(); + } + + if (InpBuff->EnsureSpace(len) == -1) { + fprintf(stderr, "mt63_rxprocess: buffer error\n"); + return -1; + } + for (i = 0; i < len; i++) + InpBuff->Data[i] = buf[i]; + InpBuff->Len = len; + + InpLevel->Process(InpBuff); + Rx->Process(InpBuff); + + snr = Rx->FEC_SNR(); + if (snr > 99.9) + snr = 99.9; + display_metric(snr); + + if (squelchon && snr < squelch) + return 0; + + for (i = 0; i < Rx->Output.Len; i++) { + c = Rx->Output.Data[i]; + + if (!progdefaults.mt63_8bit) { + put_rx_char(c); + continue; + } + + if ((c < 8) && (escape == 0)) + continue; + + if (c == 127) { + escape = 1; + continue; + } + + if (escape) { + c += 128; + escape = 0; + } + + put_rx_char(c); + } + + return 0; +} + +void mt63::restart() +{ + int err; + + put_MODEstatus(mode); + digiscope->mode(Digiscope::BLANK); + set_freq(500.0 + bandwidth / 2.0); + + err = Tx->Preset((int)bandwidth, Interleave); + if (err) + fprintf(stderr, "mt63_txinit: init failed\n"); +// flush = Tx->DataInterleave; + + err = Rx->Preset((int)bandwidth, Interleave, IntegLen); + if (err) + fprintf(stderr, "mt63_rxinit: init failed\n"); + InpLevel->Preset(64.0, 0.75); +} + +void mt63::init() +{ + modem::init(); + restart(); +} + +mt63::mt63 (trx_mode mt63_mode) : modem() +{ + mode = mt63_mode; + switch (mode) { + case MODE_MT63_500: + bandwidth = 500; + break; + case MODE_MT63_1000: + bandwidth = 1000; + break; + case MODE_MT63_2000: + bandwidth = 2000; + break; + } + Interleave = progdefaults.mt63_interleave; + + Tx = new MT63tx; + Rx = new MT63rx; + + InpLevel = new dspLevelMonitor; + InpBuff = new double_buff; + + samplerate = 8000; + fragmentsize = 1024; + + init(); +} + +mt63::~mt63() +{ + if (Tx) delete Tx; + if (Rx) delete Rx; + + if (InpLevel) delete InpLevel; + if (InpBuff) delete InpBuff; +} + +/* +static int mt63_txprocess(struct trx *trx) +{ + struct mt63 *s = (struct mt63 *) trx->modem; + int c; + + c = trx_get_tx_char(); + + if (c == -1) { + if (trx->stopflag && s->flush-- == 0) + return -1; + c = 0; + } else + s->flush = s->Tx->DataInterleave; + + if ((trx->mt63_esc && c > 255) || (!trx->mt63_esc && c > 127)) + c = '.'; + + trx_put_echo_char(c); + + if (c > 127) { + c &= 127; + s->Tx->SendChar(127); + sound_write(s->Tx->Comb.Output.Data, s->Tx->Comb.Output.Len); + } + + s->Tx->SendChar(c); + sound_write(s->Tx->Comb.Output.Data, s->Tx->Comb.Output.Len); + + return 0; +} +*/ diff --git a/src/mt63/mt63base.cxx b/src/mt63/mt63base.cxx new file mode 100644 index 00000000..d258e412 --- /dev/null +++ b/src/mt63/mt63base.cxx @@ -0,0 +1,1224 @@ +/* + * mt63base.cc -- MT63 transmitter and receiver in C++ for LINUX + * + * Copyright (c) 2007, 2008 Dave Freese, W1HKJ + * + * base class for use by fldigi + * modified from original + * excluded CW_ID which is a part of the base modem class for fldigi + * changed all floats to double and removed all float functions/methods + * + * based on mt63 code by Pawel Jalocha + * Copyright (C) 1999-2004 Pawel Jalocha, SP9VRC + * Copyright (c) 2007-2008 Dave Freese, W1HKJ + * + * This file is part of fldigi. + * + * fldigi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * fldigi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty oF + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MT63; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include // only for control printf's +// #include + +#include "dsp.h" + +#include "mt63base.h" + +//#include "morse.dat" // Morse Code table + +#include "symbol.dat" // symbol shape +#include "mt63intl.dat" // interleave patterns +#include "alias_k5.dat" // anti-alias filter shapes +#include "alias_1k.dat" // for 500, 1000 and 2000 Hz modes +#include "alias_2k.dat" + +// ========================================================================== +// Morse Encoder +/* +MorseEncoder::MorseEncoder() +{ TxMsg=NULL; } + +MorseEncoder::~MorseEncoder() +{ free(TxMsg); } + +void MorseEncoder::Free(void) +{ free(TxMsg); TxMsg=NULL; } + +int MorseEncoder::SetTxMsg(char *Msg) +{ int len=strlen(Msg)+1; + if(dspRedspAllocArray(&TxMsg,len)) return -1; + dspCopyArray(TxMsg,Msg,len); TxPtr=0; Code=0L; + return 0; } + +int MorseEncoder::NextKey(void) +{ int key,ch; + if(TxMsg==NULL) return -1; + if(Code<=1) + { ch=TxMsg[TxPtr]; if(ch==0) return -1; + TxPtr++; + if(ch>=1; return key; +} +*/ +// ========================================================================== +// MT63 transmitter code + +MT63tx::MT63tx() +{ + TxVect=NULL; + dspPhaseCorr=NULL; +} + +MT63tx::~MT63tx() +{ + free(TxVect); + free(dspPhaseCorr); +} + +void MT63tx::Free(void) +{ free(TxVect); TxVect=NULL; + free(dspPhaseCorr); dspPhaseCorr=NULL; + Encoder.Free(); FFT.Free(); Window.Free(); Comb.Free(); + WindowBuff.Free(); +} + +int MT63tx::Preset(int BandWidth, int LongInterleave) +{ + int err; + int i,p,step,incr,mask; + + switch(BandWidth) { + case 500: + FirstDataCarr=256; + AliasShapeI=Alias_k5_I; + AliasShapeQ=Alias_k5_Q; + AliasFilterLen=Alias_k5_Len; + DecimateRatio=8; + break; + case 1000: + FirstDataCarr=128; + AliasShapeI=Alias_1k_I; + AliasShapeQ=Alias_1k_Q; + AliasFilterLen=Alias_1k_Len; + DecimateRatio=4; + break; + case 2000: + FirstDataCarr=64; + AliasShapeI=Alias_2k_I; + AliasShapeQ=Alias_2k_Q; + AliasFilterLen=Alias_2k_Len; + DecimateRatio=2; + break; + default: + return -1; + } + + DataCarriers=64; +// DataCarrSepar=4; +// SymbolSepar=200; + WindowLen=SymbolLen; + TxWindow=SymbolShape; + TxdspAmpl=4.0/DataCarriers; // for maximum output level we can set TxdspAmpl=4.0/DataCarriers + CarrMarkCode=0x16918BBEL; + CarrMarkdspAmpl=0; // WindowLen/32; + + if(LongInterleave) { DataInterleave=64; InterleavePattern=LongIntlvPatt; } + else { DataInterleave=32; InterleavePattern=ShortIntlvPatt; } + + if(dspRedspAllocArray(&TxVect, DataCarriers)) goto Error; + if(dspRedspAllocArray(&dspPhaseCorr, DataCarriers)) goto Error; + err=WindowBuff.EnsureSpace(2*WindowLen); if(err) goto Error; + WindowBuff.Len=2*WindowLen; + + err=Encoder.Preset(DataCarriers,DataInterleave,InterleavePattern,1); + if(err) goto Error; + err=FFT.Preset(WindowLen); + if(err) goto Error; + err=Window.Preset(WindowLen,SymbolSepar/2,TxWindow); + if(err) goto Error; + err=Comb.Preset(AliasFilterLen,AliasShapeI,AliasShapeQ,DecimateRatio); + if(err) goto Error; + + mask=FFT.Size-1; + +// Preset the initial dspPhase for each data carrier. +// Here we only compute indexes to the FFT twiddle factors +// so the actuall vector is FFT.Twiddle[TxVect[i]] + for(step=0,incr=1,p=0,i=0; i ", ch, ch>=' ' ? ch : '.'); + for(i=0; i only dspPhase correction + { TxVect[i]=(TxVect[i]+dspPhaseCorr[i])&mask; } + else // data bit = 0 => dspPhase flip + dspPhase correction + { TxVect[i]=(TxVect[i]+dspPhaseCorr[i]+flip)&mask; } + } + + ProcessTxVect(); + return 0; +} + +int MT63tx::SendJam(void) +{ int i,mask,left,right; + + mask=FFT.Size-1; left=FFT.Size/4; right=3*(FFT.Size/4); + for(i=0; i=IntlvLen) p-=IntlvLen; } + return 0; + +Error: Free(); return -1; +} + +int MT63encoder::Process(char code) // encode an ASCII character "code" +{ int i,k; + code&=CodeMask; + for(i=0; i=IntlvSize) k-=IntlvSize; + Output[i]=IntlvPipe[k+i]; + } IntlvPtr+=DataCarriers; if(IntlvPtr>=IntlvSize) IntlvPtr-=IntlvSize; + } + else + { for(i=0; i0.0) + { Sum/=StepsPerSymb; Sum*=ScanLen; // printf("%3d: %8.5f\n",c,Sum); + for(p=c,i=0; i=2*DataCarrSepar) n=0; + } + } + + IntegPtr+=ScanLen; if(IntegPtr>=ScanSize) IntegPtr=0; + + if(IntegPtr==0) + { printf("NormPwr:\n"); + for(i=0; i=IntlvLen) p-=IntlvLen; } + // printf("\n"); + + IntlvSize=(IntlvLen+1)*ScanSize; + if(dspRedspAllocArray(&IntlvPipe,IntlvSize)) goto Error; + dspClearArray(IntlvPipe,IntlvSize); IntlvPtr=0; + + if(dspRedspAllocArray(&WalshBuff,DataCarriers)) goto Error; + + if(dspRedspAllocArray(&DecodeSnrMid,ScanLen)) goto Error; + if(dspRedspAllocArray(&DecodeSnrOut,ScanLen)) goto Error; + dspClearArray(DecodeSnrMid,ScanLen); + dspClearArray(DecodeSnrOut,ScanLen); + + SignalToNoise=0.0; CarrOfs=0; + + return 0; +Error: + Free(); return -1; +} + +int MT63decoder::Process(double *data) +{ int s,i,k; double Min,Max,Sig,Noise,SNR; int MinPos,MaxPos,code; + + dspCopyArray(IntlvPipe+IntlvPtr,data,ScanSize); + + // printf("Decoder [%d/%d/%d]: \n",IntlvPtr,IntlvSize,ScanSize); + for(s=0; s=IntlvSize) k-=IntlvSize; } + WalshBuff[i]=IntlvPipe[k+s+i]; // printf(" %4d",k/ScanSize); + } // printf("\n"); + dspWalshTrans(WalshBuff,DataCarriers); + Min=dspFindMin(WalshBuff,DataCarriers,MinPos); + Max=dspFindMax(WalshBuff,DataCarriers,MaxPos); + if(fabs(Max)>fabs(Min)) + { code=MaxPos+DataCarriers; + Sig=fabs(Max); WalshBuff[MaxPos]=0.0; } + else + { code=MinPos; + Sig=fabs(Min); WalshBuff[MinPos]=0.0; } + Noise=dspRMS(WalshBuff,DataCarriers); + if(Noise>0.0) SNR=Sig/Noise; else SNR=0.0; + dspLowPass2(SNR,DecodeSnrMid[s],DecodeSnrOut[s],W1,W2,W5); + // printf("%2d: %02x => %c, %5.2f/%5.2f=>%5.2f <%5.2f>\n", + // s,code,code<' ' ? '.' : (char)code, + // Sig,Noise,SNR,DecodeSnrOut[s]); + DecodePipe[DecodePtr+s]=code; + } + IntlvPtr+=ScanSize; if(IntlvPtr>=IntlvSize) IntlvPtr=0; + DecodePtr+=ScanLen; if(DecodePtr>=DecodeSize) DecodePtr=0; + Max=dspFindMax(DecodeSnrOut,ScanLen,MaxPos); + Output=DecodePipe[DecodePtr+MaxPos]; + SignalToNoise=Max; CarrOfs=MaxPos-(ScanLen-1)/2; +/* + code=Output; + if((code>=' ')||(code=='\n')||(code=='\r')) printf("%c",code); + else if(code!='\0') printf("<%02X>",code); +*/ + return 0; +} + +// ========================================================================== +// MT63 receiver code + +MT63rx::MT63rx() +{ int s; + + FFTbuff=NULL; FFTbuff2=NULL; + + for(s=0; s<4; s++) SyncPipe[s]=NULL; + SyncPhCorr=NULL; + for(s=0; s<4; s++) { CorrelMid[s]=NULL; CorrelOut[s]=NULL; } + dspPowerMid=NULL; dspPowerOut=NULL; + for(s=0; s<4; s++) CorrelNorm[s]=NULL; + for(s=0; s<4; s++) CorrelAver[s]=NULL; + SymbFit=NULL; SymbPipe=NULL; FreqPipe=NULL; + + RefDataSlice=NULL; + + DataPipeLen=0; DataPipe=NULL; + DataPwrMid=NULL; DataPwrOut=NULL; + DataSqrMid=NULL; DataSqrOut=NULL; + + DataVect=NULL; + + DatadspPhase=NULL; + DatadspPhase2=NULL; + + SpectradspPower=NULL; +} + +MT63rx::~MT63rx() +{ int s; + + free(FFTbuff); free(FFTbuff2); + + for(s=0; s<4; s++) free(SyncPipe[s]); + free(SyncPhCorr); + for(s=0; s<4; s++) { free(CorrelMid[s]); free(CorrelOut[s]); } + free(dspPowerMid); free(dspPowerOut); + for(s=0; s<4; s++) free(CorrelNorm[s]); + for(s=0; s<4; s++) free(CorrelAver[s]); + free(SymbFit); free(SymbPipe); free(FreqPipe); + + free(RefDataSlice); + + dspFreeArray2D(DataPipe,DataPipeLen); + // for(s=0; sLen,ProcLine.InpLen); + while((SyncProcPtr+WindowLen)", + // SyncSymbConf,SyncLocked,SyncProcPtr,SyncPtr,SymbPtr,SyncSymbShift,SyncFreqOfs); + if(SyncPtr==SymbPtr) + { s1=SyncProcPtr-ProcdspDelay+((int)SyncSymbShift-SymbPtr*SyncStep); + s2=s1+SymbolSepar/2; +// printf(" SdspAmple at %d,%d (SyncProcPtr-%d), time diff.=%d\n",s1,s2,SyncProcPtr-s1,s1-DataProcPtr); + DataProcess(ProcLine.InpPtr+s1,ProcLine.InpPtr+s2,SyncFreqOfs,s1-DataProcPtr); + DataProcPtr=s1; + } + // printf("\n"); + SyncProcPtr+=SyncStep; + } + SyncProcPtr-=ProcLine.InpLen; DataProcPtr-=ProcLine.InpLen; + return 0; +} + +void MT63rx::DoCorrelSum(dspCmpx *Correl1, dspCmpx *Correl2, dspCmpx *Aver) +{ dspCmpx sx; int i,s,d; + s=2*DataCarrSepar; d=DataCarriers*DataCarrSepar; + sx.re=sx.im=0.0; + for(i=0; i0.0) { dI=(I*I-Q*Q)/A; dQ=(2*I*Q)/A; } + else { dI=dQ=0.0; } + dspLowPass2(P,dspPowerMid[i],dspPowerOut[i],W1p,W2p,W5p); + pI=PrevSlice[i].re*SyncPhCorr[i].re-PrevSlice[i].im*SyncPhCorr[i].im; + pQ=PrevSlice[i].re*SyncPhCorr[i].im+PrevSlice[i].im*SyncPhCorr[i].re; + Correl.re=dQ*pQ+dI*pI; + Correl.im=dQ*pI-dI*pQ; + dspLowPass2(&Correl,CorrelMid[SyncPtr]+i,CorrelOut[SyncPtr]+i,W1,W2,W5); + PrevSlice[i].re=dI; PrevSlice[i].im=dQ; + } + + if(SyncPtr==(SymbPtr^2)) + { + for(s=0; s0.0) + { CorrelNorm[s][i].re=CorrelOut[s][i].re/dspPowerOut[i]; + CorrelNorm[s][i].im=CorrelOut[s][i].im/dspPowerOut[i]; } + else CorrelNorm[s][i].im=CorrelNorm[s][i].re=0.0; + } + } + +/* + // another way to normalize - a better one ? + for(i=0; i0.0) + { for(s=0; s1) j-=(k-1)*DataCarrSepar; else if(k<(-1)) j-=(k+1)*DataCarrSepar; + SymbFitPos=j; +// printf(" => %2d",j); + if(P>0.0) + { SymbConf=dspAmpl(SymbFit[j]) + 0.5*(dspAmpl(SymbFit[j+1])+dspAmpl(SymbFit[j-1])); + SymbConf*=0.5; + I=SymbFit[j].re + 0.5*(SymbFit[j-1].re+SymbFit[j+1].re); + Q=SymbFit[j].im + 0.5*(SymbFit[j-1].im+SymbFit[j+1].im); + SymbTime.re=I; SymbTime.im=Q; + SymbShift=(dspPhase(SymbTime)/(2*M_PI))*SymbolDiv; + if(SymbShift<0) SymbShift+=SymbolDiv; + // for(i=j-1; i<=j+1; i++) printf(" [%+5.2f,%+5.2f]",SymbFit[i].re,SymbFit[i].im); + // make first estimation of FreqOfs + // printf(" -> [%+5.2f,%+5.2f] =>",I,Q); + // for(i=j-2; i<=j+2; i++) printf(" %+6.3f",I*SymbFit[i].re+Q*SymbFit[i].im); + pI = dspScalProd(I,Q,SymbFit[j]) + + 0.7*dspScalProd(I,Q,SymbFit[j-1]) + + 0.7*dspScalProd(I,Q,SymbFit[j+1]); + pQ = 0.7*dspScalProd(I,Q,SymbFit[j+1]) + - 0.7*dspScalProd(I,Q,SymbFit[j-1]) + + 0.5*dspScalProd(I,Q,SymbFit[j+2]) + - 0.5*dspScalProd(I,Q,SymbFit[j-2]); + FreqOfs=j+dspPhase(pI,pQ)/(2.0*M_PI/8); +/* SYNC TEST */ + // refine the FreqOfs + i=(int)floor(FreqOfs+0.5); + s=(int)floor(SymbShift); s2=(s+1)&(SymbolDiv-1); +// printf(" [%5.2f,%2d,%d,%d] ",FreqOfs,i,s,s2); + w0=(s+1-SymbShift); w1=(SymbShift-s); +// printf(" [%4.2f,%4.2f] ",w0,w1); + A=(0.5*WindowLen)/SymbolSepar; + I=w0*CorrelAver[s][i].re+w1*CorrelAver[s2][i].re; + Q=w0*CorrelAver[s][i].im+w1*CorrelAver[s2][i].im; +// printf(" [%5.2f,%2d] -> [%+5.2f,%+5.2f]",FreqOfs,i,I,Q); +// FreqOfs=i+dspPhase(I,Q)/(2.0*M_PI)*0.5*A; +// printf(" => %5.2f",FreqOfs); + F0=i+dspPhase(I,Q)/(2.0*M_PI)*A-FreqOfs; + Fl=F0-A; Fu=F0+A; + if(fabs(Fl) (%5.2f,%5.2f,%5.2f) => %5.2f",Fl,F0,Fu,FreqOfs); + + } else { SymbTime.re=SymbTime.im=0.0; SymbConf=0.0; SymbShift=0.0; FreqOfs=0.0; } + + // here we have FreqOfs and SymbTime.re/im + + // printf("FreqOfs=%5.2f",FreqOfs); + + if(SyncLocked) + { // flip the SymbTime if it doesn't agree with the dspAverage + if(dspScalProd(SymbTime,AverSymb)<0.0) + { SymbTime.re=(-SymbTime.re); SymbTime.im=(-SymbTime.im); + FreqOfs-=DataCarrSepar; } + // reduce the freq. offset towards the dspAverage offset + A=2*DataCarrSepar; + k=(int)floor((FreqOfs-AverFreq)/A+0.5); FreqOfs-=k*A; +/* SYNC TEST */ + A=(0.5*WindowLen)/SymbolSepar; + F0=FreqOfs-AverFreq; // correct freq. auto-correlator wrap + Fl=F0-A; Fu=F0+A; + if(fabs(Fl) (%5.2f,%5.2f,%5.2f) => %5.2f",Fl,F0,Fu,FreqOfs); + + } else // of if(SyncLocked) + { // flip SymbTime if it doesn't agree with the previous + if(dspScalProd(SymbTime,SymbPipe[TrackPipePtr])<0.0) + { SymbTime.re=(-SymbTime.re); SymbTime.im=(-SymbTime.im); + FreqOfs-=DataCarrSepar; } + // reduce the FreqOfs towards zero + A=2*DataCarrSepar; + k=(int)floor(FreqOfs/A+0.5); FreqOfs-=k*A; +/* SYNC TEST */ + F0=FreqOfs-FreqPipe[TrackPipePtr]; + Fl=F0-A; Fu=F0+A; + if(fabs(Fl) [%+5.2f,%+5.2f], %5.2f",SymbTime.re,SymbTime.im,FreqOfs); + + TrackPipePtr+=1; if(TrackPipePtr>=TrackPipeLen) TrackPipePtr-=TrackPipeLen; + SymbPipe[TrackPipePtr]=SymbTime; // put SymbTime and FreqOfs into pipes + FreqPipe[TrackPipePtr]=FreqOfs; // for averaging + + // find dspAverage symbol time + Loops=dspSelFitAver(SymbPipe,TrackPipeLen,(double)3.0,4,AverSymb,dspRMS,Incl); + // printf(" AverSymb=[%+5.2f,%+5.2f], dspRMS=%5.3f/%2d", + // AverSymb.re,AverSymb.im,dspRMS,Incl); + // find dspAverage freq. offset + Loops=dspSelFitAver(FreqPipe,TrackPipeLen,(double)2.5,4,AverFreq,dspRMS,Incl); + SyncFreqDev=dspRMS; + // printf(" AverFreq=%+5.2f, dspRMS=%5.3f/%2d",AverFreq,dspRMS,Incl); + + SymbConf=dspAmpl(AverSymb); + SyncSymbConf=SymbConf; + SyncFreqOfs=AverFreq; + if(SymbConf>0.0) + { SymbShift=dspPhase(AverSymb)/(2*M_PI)*SymbolSepar; + if(SymbShift<0.0) SymbShift+=SymbolSepar; + SymbPtr=(int)floor((dspPhase(AverSymb)/(2*M_PI))*SymbolDiv); + if(SymbPtr<0) SymbPtr+=SymbolDiv; + SyncSymbShift=SymbShift; } + + if(SyncLocked) + { if((SyncSymbConf0.250)) SyncLocked=0; } + else + { if((SyncSymbConf>SyncLockThres)&&(SyncFreqDev<0.125)) SyncLocked=1; } + + SyncSymbConf*=0.5; + + // printf(" => SyncLocked=%d, SyncSymbShift=%5.1f, SymbPtr=%d", + // SyncLocked,SyncSymbShift,SymbPtr); + + // printf("\n"); + + } // enf of if(SyncPtr==(SymbPtr^2)) + +} + +void MT63rx::DataProcess(dspCmpx *EvenSlice, dspCmpx *OddSlice, double FreqOfs, int TimeDist) +{ int i,c,r; + dspCmpx Freq,Phas; + int incr,p; + double I,Q,P; + dspCmpx Dtmp; dspCmpx Ftmp; +// double Aver,dspRMS; int Loops,Incl; + +// Here we pickup a symbol in the data history. The time/freq. synchronizer +// told us where it is in time and at which frequency offset (FreqOfs) +// TimeDist is the distance in sdspAmples from the symbol we analyzed +// in the previous call to this routine + +// FreqOfs=0.0; // for DEBUG only ! + +// printf("DataProcess: FreqOfs=%5.3f, TimeDist=%d, Locked=%d\n", +// FreqOfs,TimeDist,SyncLocked); + + P=(-2*M_PI*FreqOfs)/WindowLen; // make ready for frequency correction + Freq.re=cos(P); Freq.im=sin(P); + Phas.re=1.0; Phas.im=0.0; + for(i=0; i=DataPipeLen) DataPipePtr=0; + + for(i=0; i0.0) + { P=DataVect[i].re/DataPwrOut[i]; + if(P>1.0) P=1.0; else if(P<(-1.0)) P=(-1.0); + DatadspPhase[i]=P; + } else DatadspPhase[i]=0.0; + } + Decoder.Process(DatadspPhase); + Output.EnsureSpace(Output.Len+1); + Output.Data[Output.Len]=Decoder.Output; + Output.Len+=1; +/* + printf("Demodulator output vectors:\n"); + for(i=0; i %8.5f\n", + i,DataVect[i].re,DataVect[i].im,DataPwrOut[i], DatadspPhase[i]); + } +*/ +/* + for(i=0; i0.0) P=dspPhase(DataVect[i]); else P=0.0; + DatadspPhase[i]=P; + P*=2; if(P>M_PI) P-=2*M_PI; else if(P<(-M_PI)) P+=2*M_PI; + DatadspPhase2[i]=P; + printf("%2d: %6.3f [%6.3f,%6.3f] [%8.5f,%8.5f], %5.2f, %5.2f", + i, DataPwrOut[i], DataSqrOut[i].re,DataSqrOut[i].im, + DataVect[i].re,DataVect[i].im, DatadspPhase[i],DatadspPhase2[i]); + if(DataPwrOut[i]>0.0) + printf(" %6.3f",dspAmpl(DataSqrOut[i])/DataPwrOut[i]); + printf("\n"); + } + Loops=dspSelFitAver(DatadspPhase2,DataScanLen,(double)2.5,4,Aver,dspRMS,Incl); + printf("Aver=%5.2f, dspRMS=%5.2f, Incl=%d\n",Aver,dspRMS,Incl); +*/ +} + +int MT63rx::SYNC_LockStatus(void) { return SyncLocked; } + +double MT63rx::SYNC_Confidence(void) +{ return SyncSymbConf<=1.0 ? SyncSymbConf : 1.0; } + +double MT63rx::SYNC_FreqOffset(void) { return SyncFreqOfs/DataCarrSepar; } + +double MT63rx::SYNC_FreqDevdspRMS(void) { return SyncFreqDev/DataCarrSepar; } + +double MT63rx::SYNC_TimeOffset(void) { return SyncSymbShift/SymbolSepar; } + +double MT63rx::FEC_SNR(void) { return Decoder.SignalToNoise; } + +int MT63rx::FEC_CarrOffset(void) { return Decoder.CarrOfs; } + +double MT63rx::TotalFreqOffset(void) +{ return (SyncFreqOfs+DataCarrSepar*Decoder.CarrOfs)*(8000.0/DecimateRatio)/WindowLen; } diff --git a/src/mt63/mt63intl.dat b/src/mt63/mt63intl.dat new file mode 100644 index 00000000..a77a623e --- /dev/null +++ b/src/mt63/mt63intl.dat @@ -0,0 +1,51 @@ +/* + * mt63intl.dat -- interleave patterns + * + * Copyright (C) 1999-2004 Pawel Jalocha, SP9VRC + * + * This file is part of MT63. + * + * MT63 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MT63 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MT63; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +// interleave pattern for the original MT63ASC (short interleave) +int ShortIntlvPatt[64] = { + 4,5,6,7, + 4,5,6,7, + 4,5,6,7, + 4,5,6,7, + 4,5,6,7, + 4,5,6,7, + 4,5,6,7, + 4,5,6,7, + 4,5,6,7, + 4,5,6,7, + 4,5,6,7, + 4,5,6,7, + 4,5,6,7, + 4,5,6,7, + 4,5,6,7, + 4,5,6,7 } ; + +// interleave pattern for doubled interleave +int LongIntlvPatt[64] = { + 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, + 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, + 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48, + 49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,0 +} ; + + diff --git a/src/mt63/symbol.dat b/src/mt63/symbol.dat new file mode 100644 index 00000000..6a75fcf9 --- /dev/null +++ b/src/mt63/symbol.dat @@ -0,0 +1,545 @@ +/* + * symbol.dat -- Symbol shape for the MT63 modem, taken directly + * from the MT63ASC code for the EVM56K. + * + * Copyright (C) 1999-2004 Pawel Jalocha, SP9VRC + * + * This file is part of MT63. + * + * MT63 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MT63 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MT63; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +const int SymbolLen=512; // the shape's length +const int SymbolSepar=200; // the distance in samples between symbols on a carrier +const int DataCarrSepar=4; // carriers must be spaced by 4 FFT bins + // (complex FFT length equals to SymbolLen) + +double SymbolShape[SymbolLen] = { + -0.00000000 , // 0 + +0.00000665 , // 1 + +0.00002657 , // 2 + +0.00005975 , // 3 + +0.00010613 , // 4 + +0.00016562 , // 5 + +0.00023810 , // 6 + +0.00032341 , // 7 + +0.00042134 , // 8 + +0.00053162 , // 9 + +0.00065389 , // 10 + +0.00078773 , // 11 + +0.00093261 , // 12 + +0.00108789 , // 13 + +0.00125283 , // 14 + +0.00142653 , // 15 + +0.00160798 , // 16 + +0.00179599 , // 17 + +0.00198926 , // 18 + +0.00218628 , // 19 + +0.00238542 , // 20 + +0.00258487 , // 21 + +0.00278264 , // 22 + +0.00297662 , // 23 + +0.00316452 , // 24 + +0.00334394 , // 25 + +0.00351232 , // 26 + +0.00366701 , // 27 + +0.00380526 , // 28 + +0.00392424 , // 29 + +0.00402109 , // 30 + +0.00409288 , // 31 + +0.00413671 , // 32 + +0.00414969 , // 33 + +0.00412898 , // 34 + +0.00407182 , // 35 + +0.00397555 , // 36 + +0.00383764 , // 37 + +0.00365574 , // 38 + +0.00342767 , // 39 + +0.00315145 , // 40 + +0.00282534 , // 41 + +0.00244787 , // 42 + +0.00201781 , // 43 + +0.00153424 , // 44 + +0.00099653 , // 45 + +0.00040435 , // 46 + -0.00024231 , // 47 + -0.00094314 , // 48 + -0.00169753 , // 49 + -0.00250453 , // 50 + -0.00336293 , // 51 + -0.00427118 , // 52 + -0.00522749 , // 53 + -0.00622977 , // 54 + -0.00727569 , // 55 + -0.00836272 , // 56 + -0.00948809 , // 57 + -0.01064886 , // 58 + -0.01184193 , // 59 + -0.01306405 , // 60 + -0.01431189 , // 61 + -0.01558198 , // 62 + -0.01687083 , // 63 + -0.01817486 , // 64 + -0.01949051 , // 65 + -0.02081416 , // 66 + -0.02214223 , // 67 + -0.02347113 , // 68 + -0.02479733 , // 69 + -0.02611728 , // 70 + -0.02742752 , // 71 + -0.02872457 , // 72 + -0.03000504 , // 73 + -0.03126551 , // 74 + -0.03250262 , // 75 + -0.03371298 , // 76 + -0.03489320 , // 77 + -0.03603988 , // 78 + -0.03714954 , // 79 + -0.03821868 , // 80 + -0.03924367 , // 81 + -0.04022079 , // 82 + -0.04114620 , // 83 + -0.04201589 , // 84 + -0.04282570 , // 85 + -0.04357126 , // 86 + -0.04424801 , // 87 + -0.04485118 , // 88 + -0.04537575 , // 89 + -0.04581648 , // 90 + -0.04616787 , // 91 + -0.04642421 , // 92 + -0.04657955 , // 93 + -0.04662769 , // 94 + -0.04656225 , // 95 + -0.04637665 , // 96 + -0.04606414 , // 97 + -0.04561786 , // 98 + -0.04503082 , // 99 + -0.04429599 , // 100 + -0.04340631 , // 101 + -0.04235475 , // 102 + -0.04113436 , // 103 + -0.03973834 , // 104 + -0.03816006 , // 105 + -0.03639316 , // 106 + -0.03443155 , // 107 + -0.03226956 , // 108 + -0.02990192 , // 109 + -0.02732385 , // 110 + -0.02453112 , // 111 + -0.02152012 , // 112 + -0.01828789 , // 113 + -0.01483216 , // 114 + -0.01115146 , // 115 + -0.00724508 , // 116 + -0.00311317 , // 117 + +0.00124328 , // 118 + +0.00582236 , // 119 + +0.01062127 , // 120 + +0.01563627 , // 121 + +0.02086273 , // 122 + +0.02629504 , // 123 + +0.03192674 , // 124 + +0.03775043 , // 125 + +0.04375787 , // 126 + +0.04993995 , // 127 + +0.05628681 , // 128 + +0.06278780 , // 129 + +0.06943159 , // 130 + +0.07620621 , // 131 + +0.08309914 , // 132 + +0.09009732 , // 133 + +0.09718730 , // 134 + +0.10435526 , // 135 + +0.11158715 , // 136 + +0.11886870 , // 137 + +0.12618560 , // 138 + +0.13352351 , // 139 + +0.14086819 , // 140 + +0.14820561 , // 141 + +0.15552198 , // 142 + +0.16280389 , // 143 + +0.17003841 , // 144 + +0.17721311 , // 145 + +0.18431620 , // 146 + +0.19133661 , // 147 + +0.19826401 , // 148 + +0.20508896 , // 149 + +0.21180289 , // 150 + +0.21839823 , // 151 + +0.22486845 , // 152 + +0.23120806 , // 153 + +0.23741270 , // 154 + +0.24347919 , // 155 + +0.24940549 , // 156 + +0.25519079 , // 157 + +0.26083547 , // 158 + +0.26634116 , // 159 + +0.27171067 , // 160 + +0.27694807 , // 161 + +0.28205857 , // 162 + +0.28704860 , // 163 + +0.29192571 , // 164 + +0.29669855 , // 165 + +0.30137684 , // 166 + +0.30597130 , // 167 + +0.31049362 , // 168 + +0.31495636 , // 169 + +0.31937292 , // 170 + +0.32375741 , // 171 + +0.32812465 , // 172 + +0.33249001 , // 173 + +0.33686936 , // 174 + +0.34127898 , // 175 + +0.34573545 , // 176 + +0.35025554 , // 177 + +0.35485613 , // 178 + +0.35955412 , // 179 + +0.36436627 , // 180 + +0.36930915 , // 181 + +0.37439902 , // 182 + +0.37965170 , // 183 + +0.38508250 , // 184 + +0.39070609 , // 185 + +0.39653642 , // 186 + +0.40258662 , // 187 + +0.40886890 , // 188 + +0.41539446 , // 189 + +0.42217341 , // 190 + +0.42921470 , // 191 + +0.43652603 , // 192 + +0.44411383 , // 193 + +0.45198311 , // 194 + +0.46013753 , // 195 + +0.46857925 , // 196 + +0.47730896 , // 197 + +0.48632585 , // 198 + +0.49562756 , // 199 + +0.50521021 , // 200 + +0.51506840 , // 201 + +0.52519520 , // 202 + +0.53558220 , // 203 + +0.54621950 , // 204 + +0.55709582 , // 205 + +0.56819849 , // 206 + +0.57951351 , // 207 + +0.59102568 , // 208 + +0.60271860 , // 209 + +0.61457478 , // 210 + +0.62657574 , // 211 + +0.63870210 , // 212 + +0.65093366 , // 213 + +0.66324951 , // 214 + +0.67562817 , // 215 + +0.68804763 , // 216 + +0.70048553 , // 217 + +0.71291922 , // 218 + +0.72532590 , // 219 + +0.73768272 , // 220 + +0.74996688 , // 221 + +0.76215572 , // 222 + +0.77422687 , // 223 + +0.78615828 , // 224 + +0.79792836 , // 225 + +0.80951602 , // 226 + +0.82090079 , // 227 + +0.83206287 , // 228 + +0.84298315 , // 229 + +0.85364335 , // 230 + +0.86402598 , // 231 + +0.87411443 , // 232 + +0.88389296 , // 233 + +0.89334677 , // 234 + +0.90246195 , // 235 + +0.91122553 , // 236 + +0.91962547 , // 237 + +0.92765062 , // 238 + +0.93529073 , // 239 + +0.94253642 , // 240 + +0.94937916 , // 241 + +0.95581122 , // 242 + +0.96182562 , // 243 + +0.96741616 , // 244 + +0.97257728 , // 245 + +0.97730410 , // 246 + +0.98159233 , // 247 + +0.98543825 , // 248 + +0.98883864 , // 249 + +0.99179079 , // 250 + +0.99429241 , // 251 + +0.99634163 , // 252 + +0.99793696 , // 253 + +0.99907728 , // 254 + +0.99976178 , // 255 + +0.99999000 , // 256 + +0.99976178 , // 257 + +0.99907728 , // 258 + +0.99793696 , // 259 + +0.99634163 , // 260 + +0.99429241 , // 261 + +0.99179079 , // 262 + +0.98883864 , // 263 + +0.98543825 , // 264 + +0.98159233 , // 265 + +0.97730410 , // 266 + +0.97257728 , // 267 + +0.96741616 , // 268 + +0.96182562 , // 269 + +0.95581122 , // 270 + +0.94937916 , // 271 + +0.94253642 , // 272 + +0.93529073 , // 273 + +0.92765062 , // 274 + +0.91962547 , // 275 + +0.91122553 , // 276 + +0.90246195 , // 277 + +0.89334677 , // 278 + +0.88389296 , // 279 + +0.87411443 , // 280 + +0.86402598 , // 281 + +0.85364335 , // 282 + +0.84298315 , // 283 + +0.83206287 , // 284 + +0.82090079 , // 285 + +0.80951602 , // 286 + +0.79792836 , // 287 + +0.78615828 , // 288 + +0.77422687 , // 289 + +0.76215572 , // 290 + +0.74996688 , // 291 + +0.73768272 , // 292 + +0.72532590 , // 293 + +0.71291922 , // 294 + +0.70048553 , // 295 + +0.68804763 , // 296 + +0.67562817 , // 297 + +0.66324951 , // 298 + +0.65093366 , // 299 + +0.63870210 , // 300 + +0.62657574 , // 301 + +0.61457478 , // 302 + +0.60271860 , // 303 + +0.59102568 , // 304 + +0.57951351 , // 305 + +0.56819849 , // 306 + +0.55709582 , // 307 + +0.54621950 , // 308 + +0.53558220 , // 309 + +0.52519520 , // 310 + +0.51506840 , // 311 + +0.50521021 , // 312 + +0.49562756 , // 313 + +0.48632585 , // 314 + +0.47730896 , // 315 + +0.46857925 , // 316 + +0.46013753 , // 317 + +0.45198311 , // 318 + +0.44411383 , // 319 + +0.43652603 , // 320 + +0.42921470 , // 321 + +0.42217341 , // 322 + +0.41539446 , // 323 + +0.40886890 , // 324 + +0.40258662 , // 325 + +0.39653642 , // 326 + +0.39070609 , // 327 + +0.38508250 , // 328 + +0.37965170 , // 329 + +0.37439902 , // 330 + +0.36930915 , // 331 + +0.36436627 , // 332 + +0.35955412 , // 333 + +0.35485613 , // 334 + +0.35025554 , // 335 + +0.34573545 , // 336 + +0.34127898 , // 337 + +0.33686936 , // 338 + +0.33249001 , // 339 + +0.32812465 , // 340 + +0.32375741 , // 341 + +0.31937292 , // 342 + +0.31495636 , // 343 + +0.31049362 , // 344 + +0.30597130 , // 345 + +0.30137684 , // 346 + +0.29669855 , // 347 + +0.29192571 , // 348 + +0.28704860 , // 349 + +0.28205857 , // 350 + +0.27694807 , // 351 + +0.27171067 , // 352 + +0.26634116 , // 353 + +0.26083547 , // 354 + +0.25519079 , // 355 + +0.24940549 , // 356 + +0.24347919 , // 357 + +0.23741270 , // 358 + +0.23120806 , // 359 + +0.22486845 , // 360 + +0.21839823 , // 361 + +0.21180289 , // 362 + +0.20508896 , // 363 + +0.19826401 , // 364 + +0.19133661 , // 365 + +0.18431620 , // 366 + +0.17721311 , // 367 + +0.17003841 , // 368 + +0.16280389 , // 369 + +0.15552198 , // 370 + +0.14820561 , // 371 + +0.14086819 , // 372 + +0.13352351 , // 373 + +0.12618560 , // 374 + +0.11886870 , // 375 + +0.11158715 , // 376 + +0.10435526 , // 377 + +0.09718730 , // 378 + +0.09009732 , // 379 + +0.08309914 , // 380 + +0.07620621 , // 381 + +0.06943159 , // 382 + +0.06278780 , // 383 + +0.05628681 , // 384 + +0.04993995 , // 385 + +0.04375787 , // 386 + +0.03775043 , // 387 + +0.03192674 , // 388 + +0.02629504 , // 389 + +0.02086273 , // 390 + +0.01563627 , // 391 + +0.01062127 , // 392 + +0.00582236 , // 393 + +0.00124328 , // 394 + -0.00311317 , // 395 + -0.00724508 , // 396 + -0.01115146 , // 397 + -0.01483216 , // 398 + -0.01828789 , // 399 + -0.02152012 , // 400 + -0.02453112 , // 401 + -0.02732385 , // 402 + -0.02990192 , // 403 + -0.03226956 , // 404 + -0.03443155 , // 405 + -0.03639316 , // 406 + -0.03816006 , // 407 + -0.03973834 , // 408 + -0.04113436 , // 409 + -0.04235475 , // 410 + -0.04340631 , // 411 + -0.04429599 , // 412 + -0.04503082 , // 413 + -0.04561786 , // 414 + -0.04606414 , // 415 + -0.04637665 , // 416 + -0.04656225 , // 417 + -0.04662769 , // 418 + -0.04657955 , // 419 + -0.04642421 , // 420 + -0.04616787 , // 421 + -0.04581648 , // 422 + -0.04537575 , // 423 + -0.04485118 , // 424 + -0.04424801 , // 425 + -0.04357126 , // 426 + -0.04282570 , // 427 + -0.04201589 , // 428 + -0.04114620 , // 429 + -0.04022079 , // 430 + -0.03924367 , // 431 + -0.03821868 , // 432 + -0.03714954 , // 433 + -0.03603988 , // 434 + -0.03489320 , // 435 + -0.03371298 , // 436 + -0.03250262 , // 437 + -0.03126551 , // 438 + -0.03000504 , // 439 + -0.02872457 , // 440 + -0.02742752 , // 441 + -0.02611728 , // 442 + -0.02479733 , // 443 + -0.02347113 , // 444 + -0.02214223 , // 445 + -0.02081416 , // 446 + -0.01949051 , // 447 + -0.01817486 , // 448 + -0.01687083 , // 449 + -0.01558198 , // 450 + -0.01431189 , // 451 + -0.01306405 , // 452 + -0.01184193 , // 453 + -0.01064886 , // 454 + -0.00948809 , // 455 + -0.00836272 , // 456 + -0.00727569 , // 457 + -0.00622977 , // 458 + -0.00522749 , // 459 + -0.00427118 , // 460 + -0.00336293 , // 461 + -0.00250453 , // 462 + -0.00169753 , // 463 + -0.00094314 , // 464 + -0.00024231 , // 465 + +0.00040435 , // 466 + +0.00099653 , // 467 + +0.00153424 , // 468 + +0.00201781 , // 469 + +0.00244787 , // 470 + +0.00282534 , // 471 + +0.00315145 , // 472 + +0.00342767 , // 473 + +0.00365574 , // 474 + +0.00383764 , // 475 + +0.00397555 , // 476 + +0.00407182 , // 477 + +0.00412898 , // 478 + +0.00414969 , // 479 + +0.00413671 , // 480 + +0.00409288 , // 481 + +0.00402109 , // 482 + +0.00392424 , // 483 + +0.00380526 , // 484 + +0.00366701 , // 485 + +0.00351232 , // 486 + +0.00334394 , // 487 + +0.00316452 , // 488 + +0.00297662 , // 489 + +0.00278264 , // 490 + +0.00258487 , // 491 + +0.00238542 , // 492 + +0.00218628 , // 493 + +0.00198926 , // 494 + +0.00179599 , // 495 + +0.00160798 , // 496 + +0.00142653 , // 497 + +0.00125283 , // 498 + +0.00108789 , // 499 + +0.00093261 , // 500 + +0.00078773 , // 501 + +0.00065389 , // 502 + +0.00053162 , // 503 + +0.00042134 , // 504 + +0.00032341 , // 505 + +0.00023810 , // 506 + +0.00016562 , // 507 + +0.00010613 , // 508 + +0.00005975 , // 509 + +0.00002657 , // 510 + +0.00000665 // 511 +} ; + + diff --git a/src/trx/modem.cxx b/src/trx/modem.cxx index bb8feaaf..ae6848ce 100644 --- a/src/trx/modem.cxx +++ b/src/trx/modem.cxx @@ -14,6 +14,9 @@ modem *cw_modem = 0; modem *mfsk8_modem = 0; modem *mfsk16_modem = 0; +modem *mt63_500_modem = 0; +modem *mt63_1000_modem = 0; +modem *mt63_2000_modem = 0; modem *feld_modem = 0; modem *feld_slowmodem = 0; modem *feld_x5modem = 0;