diff --git a/src/Makefile.am b/src/Makefile.am index d0a3c9a4..efe570fb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -368,6 +368,7 @@ fldigi_SOURCES += \ include/rsid.h \ include/rtty.h \ include/view_rtty.h \ + include/nullmodem.h \ include/rx_extract.h \ include/speak.h \ include/serial.h \ @@ -480,6 +481,7 @@ fldigi_SOURCES += \ ssb/ssb.cxx \ throb/throb.cxx \ trx/modem.cxx \ + trx/nullmodem.cxx \ trx/trx.cxx \ waterfall/colorbox.cxx \ waterfall/digiscope.cxx \ diff --git a/src/dialogs/fl_digi.cxx b/src/dialogs/fl_digi.cxx index 92bda965..7672ac33 100644 --- a/src/dialogs/fl_digi.cxx +++ b/src/dialogs/fl_digi.cxx @@ -77,6 +77,7 @@ #endif #include "rigio.h" #include "rigMEM.h" +#include "nullmodem.h" #include "psk.h" #include "cw.h" #include "mfsk.h" @@ -970,6 +971,11 @@ void init_modem(trx_mode mode, int freq) mode = NUM_MODES - 1; return init_modem(mode, freq); + case MODE_NULL: + startup_modem(*mode_info[mode].modem ? *mode_info[mode].modem : + *mode_info[mode].modem = new NULLMODEM, freq); + break; + case MODE_CW: startup_modem(*mode_info[mode].modem ? *mode_info[mode].modem : *mode_info[mode].modem = new cw, freq); @@ -2220,6 +2226,7 @@ void stopMacroTimer() progStatus.timer = 0; progStatus.repeatMacro = -1; Fl::remove_timeout(macro_timer); + Fl::remove_timeout(macro_timed_execute); btnMacroTimer->label(0); btnMacroTimer->color(FL_BACKGROUND_COLOR); @@ -2240,6 +2247,32 @@ void macro_timer(void*) Fl::repeat_timeout(1.0, macro_timer); } +void macro_timed_execute(void *) +{ + if (exec_date == zdate() && exec_time == ztime()) { + macros.timed_execute(); + btnMacroTimer->label(0); + btnMacroTimer->color(FL_BACKGROUND_COLOR); + btnMacroTimer->set_output(); + } else { + Fl::repeat_timeout(1.0, macro_timed_execute); + } +} + +void startTimedExecute(std::string &title) +{ + ENSURE_THREAD(FLMAIN_TID); + Fl::add_timeout(0.0, macro_timed_execute); + string txt = "Macro '"; + txt.append(title).append("' scheduled at "); + txt.append(exec_time).append(", on ").append(exec_date).append("\n"); + btnMacroTimer->label("SKED"); + btnMacroTimer->color(fl_rgb_color(240, 240, 0)); + btnMacroTimer->redraw_label(); + ReceiveText->clear(); + ReceiveText->add(txt.c_str(), FTextBase::CTRL); +} + void cbMacroTimerButton(Fl_Widget*, void*) { stopMacroTimer(); @@ -3017,6 +3050,7 @@ Fl_Menu_Item menu_[] = { { mode_info[MODE_PSK250].name, 0, cb_init_mode, (void *)MODE_PSK250, 0, FL_NORMAL_LABEL, 0, 14, 0}, {0,0,0,0,0,0,0,0,0}, +{ mode_info[MODE_NULL].name, 0, cb_init_mode, (void *)MODE_NULL, 0, FL_NORMAL_LABEL, 0, 14, 0}, { mode_info[MODE_SSB].name, 0, cb_init_mode, (void *)MODE_SSB, 0, FL_NORMAL_LABEL, 0, 14, 0}, { mode_info[MODE_WWV].name, 0, cb_init_mode, (void *)MODE_WWV, 0, FL_NORMAL_LABEL, 0, 14, 0}, @@ -3631,7 +3665,7 @@ void create_fl_digi_main_primary() { int Hmacrobtn; int xpos; int ypos; - int wblank; + int wBLANK; int fnt = fl_font(); int fsize = fl_size(); @@ -4273,14 +4307,14 @@ void create_fl_digi_main_primary() { Fl_Group *btngroup2 = new Fl_Group(0, Y + 1, progStatus.mainW - Hmacros, Hmacros - 1); Wmacrobtn = (btngroup2->w()) / NUMMACKEYS; Hmacrobtn = btngroup2->h() - 1; - wblank = (btngroup2->w() - NUMMACKEYS * Wmacrobtn) / 2; + wBLANK = (btngroup2->w() - NUMMACKEYS * Wmacrobtn) / 2; xpos = 0; ypos = btngroup2->y(); for (int i = 0; i < NUMMACKEYS; i++) { if (i == 4 || i == 8) { - bx = new Fl_Box(xpos, ypos, wblank, Hmacrobtn); + bx = new Fl_Box(xpos, ypos, wBLANK, Hmacrobtn); bx->box(FL_FLAT_BOX); - xpos += wblank; + xpos += wBLANK; } btnMacro[NUMMACKEYS + i] = new Fl_Button(xpos, ypos, Wmacrobtn, Hmacrobtn, macros.name[NUMMACKEYS + i].c_str()); @@ -4452,14 +4486,14 @@ void create_fl_digi_main_primary() { Fl_Group *btngroup1 = new Fl_Group(0, Y+1, progStatus.mainW - Hmacros, Hmacros-1); Wmacrobtn = (btngroup1->w()) / NUMMACKEYS; Hmacrobtn = btngroup1->h() - 1; - wblank = (btngroup1->w() - NUMMACKEYS * Wmacrobtn) / 2; + wBLANK = (btngroup1->w() - NUMMACKEYS * Wmacrobtn) / 2; xpos = 0; ypos = btngroup1->y(); for (int i = 0; i < NUMMACKEYS; i++) { if (i == 4 || i == 8) { - bx = new Fl_Box(xpos, ypos, wblank, Hmacrobtn); + bx = new Fl_Box(xpos, ypos, wBLANK, Hmacrobtn); bx->box(FL_FLAT_BOX); - xpos += wblank; + xpos += wBLANK; } btnMacro[i] = new Fl_Button(xpos, ypos, Wmacrobtn, Hmacrobtn, macros.name[i].c_str()); @@ -4817,6 +4851,7 @@ Fl_Menu_Item alt_menu_[] = { { mode_info[MODE_PSK250].name, 0, cb_init_mode, (void *)MODE_PSK250, 0, FL_NORMAL_LABEL, 0, 14, 0}, {0,0,0,0,0,0,0,0,0}, +{ mode_info[MODE_NULL].name, 0, cb_init_mode, (void *)MODE_NULL, 0, FL_NORMAL_LABEL, 0, 14, 0}, { mode_info[MODE_SSB].name, 0, cb_init_mode, (void *)MODE_SSB, 0, FL_NORMAL_LABEL, 0, 14, 0}, { mode_info[MODE_WWV].name, 0, cb_init_mode, (void *)MODE_WWV, 0, FL_NORMAL_LABEL, 0, 14, 0}, @@ -5608,10 +5643,55 @@ void get_tx_char_idle(void *) progStatus.repeatIdleTime = 0; } +int Qwait_time = 0; +int Qidle_time = 0; + +static int que_timeout = 0; +bool que_ok = true; + +void post_queue_execute(void*) +{ + if (!que_timeout) { + LOG_ERROR("%s", "timed out"); + return; + } + while (!que_ok && trx_state != STATE_RX) { + que_timeout--; + Fl::repeat_timeout(0.05, post_queue_execute); + } + trx_transmit(); +} + +void queue_execute_after_rx(void*) +{ + if (!que_timeout) { + LOG_ERROR("%s", "timed out"); + return; + } + while (trx_state == STATE_TX) { + que_timeout--; + Fl::repeat_timeout(0.05, queue_execute_after_rx); + return; + } + que_ok = false; + que_timeout = 100; // 5 seconds + Fl::add_timeout(0.05, post_queue_execute); + queue_execute(); +} + char szTestChar[] = "E|I|S|T|M|O|A|V"; int get_tx_char(void) { - if (macro_idle_on) return -1; + int c; + static int pending = -1; + enum { STATE_CHAR, STATE_CTRL }; + static int state = STATE_CHAR; + + if (!que_ok) { return -1; } + if (Qwait_time) { return -1; } + if (Qidle_time) { return -1; } + if (macro_idle_on) { return -1; } + if (idling) { return -1; } if (arq_text_available) return arq_get_char(); @@ -5619,24 +5699,19 @@ int get_tx_char(void) if (active_modem == cw_modem && progdefaults.QSKadjust) return szTestChar[2 * progdefaults.TestChar]; - int c; - static int pending = -1; + if ( progStatus.repeatMacro && progStatus.repeatIdleTime > 0 && + !idling ) { + Fl::add_timeout(progStatus.repeatIdleTime, get_tx_char_idle); + idling = true; + return -1; + } + if (pending >= 0) { c = pending; pending = -1; return c; } - enum { STATE_CHAR, STATE_CTRL }; - static int state = STATE_CHAR; - - if ( progStatus.repeatMacro && progStatus.repeatIdleTime > 0 && - !idling ) { - Fl::add_timeout(progStatus.repeatIdleTime, get_tx_char_idle); - idling = true; - } - if (idling) return -1; - if (progStatus.repeatMacro > -1 && text2repeat.length()) { c = text2repeat[repeatchar]; repeatchar++; @@ -5648,12 +5723,15 @@ int get_tx_char(void) } c = TransmitText->nextChar(); + if (c == '^' && state == STATE_CHAR) { state = STATE_CTRL; c = TransmitText->nextChar(); } switch (c) { - case -1: break; // no character available + case -1: // no character available + queue_reset(); + break; case '\n': pending = '\n'; return '\r'; @@ -5663,6 +5741,8 @@ int get_tx_char(void) REQ_SYNC(&FTextTX::clear_sent, TransmitText); state = STATE_CHAR; c = 3; // ETX + if (progStatus.timer) + REQ(startMacroTimer); break; case 'R': if (state != STATE_CTRL) @@ -5671,6 +5751,8 @@ int get_tx_char(void) if (TransmitText->eot()) { REQ_SYNC(&FTextTX::clear_sent, TransmitText); c = 3; // ETX + if (progStatus.timer) + REQ(startMacroTimer); } else c = -1; break; @@ -5688,6 +5770,19 @@ int get_tx_char(void) c = -1; REQ(clearQSO); break; + case '!': + if (state != STATE_CTRL) + break; + state = STATE_CHAR; + if (queue_must_rx()) { + c = 3; + que_timeout = 400; // 20 seconds + Fl::add_timeout(0.0, queue_execute_after_rx); + } else { + c = -1; + queue_execute(); + } + break; case '^': state = STATE_CHAR; break; @@ -5870,7 +5965,6 @@ void start_tx() if (!(active_modem->get_cap() & modem::CAP_TX)) return; trx_transmit(); - REQ(&waterfall::set_XmtRcvBtn, wf, true); } void abort_tx() @@ -5881,6 +5975,7 @@ void abort_tx() return; } if (trx_state == STATE_TX) { + queue_reset(); trx_start_modem(active_modem); } } diff --git a/src/dtmf/dtmf.cxx b/src/dtmf/dtmf.cxx index d6934011..ae52b654 100644 --- a/src/dtmf/dtmf.cxx +++ b/src/dtmf/dtmf.cxx @@ -235,8 +235,6 @@ void cDTMF::two_tones(int ch) void cDTMF::send() { - if (progdefaults.DTMFstr.empty()) return; - int c = 0, delay = 0; duration = 50; RT = (int)(active_modem->get_samplerate() * 4 / 1000.0); // 4 msec edge diff --git a/src/globals/globals.cxx b/src/globals/globals.cxx index 5ce97e0d..1287b0b4 100644 --- a/src/globals/globals.cxx +++ b/src/globals/globals.cxx @@ -43,6 +43,8 @@ using namespace std; // ... doing so will break the Fl_menu_item table 'menu_'. -Kamal const struct mode_info_t mode_info[NUM_MODES] = { + { MODE_NULL, &null_modem, "NULL", "NULL", "", "NULL", "" }, + { MODE_CW, &cw_modem, "CW", "CW", "CW", "CW", "CW" }, { MODE_CONTESTIA, &contestia_modem, "CTSTIA", "Contestia", "", "CONTESTI", "CT" }, diff --git a/src/include/fl_digi.h b/src/include/fl_digi.h index 22fff043..ff07825a 100644 --- a/src/include/fl_digi.h +++ b/src/include/fl_digi.h @@ -148,6 +148,9 @@ extern Digiscope *digiscope; extern std::string main_window_title; +extern int Qwait_time; +extern int Qidle_time; + extern void toggleRSID(); extern void set_menus(); @@ -214,6 +217,10 @@ extern void put_WARNstatus(double); extern void qsoSave_cb(Fl_Widget *b, void *); +extern bool que_ok; +extern void post_queue_execute(void*); +extern void queue_execute_after_rx(void*); + extern void put_rx_data(int *data, int len); extern int get_tx_char(); extern int get_secondary_char(); @@ -259,6 +266,10 @@ extern void set_contestia_default_integ(); extern void startMacroTimer(); extern void stopMacroTimer(); +extern void macro_timer(void *); +extern void macro_timed_execute(void *); +extern void startTimedExecute(std::string &); + extern void cb_ResetSerNbr(); extern void updateOutSerNo(); diff --git a/src/include/globals.h b/src/include/globals.h index 3eb0267c..ff1e478d 100644 --- a/src/include/globals.h +++ b/src/include/globals.h @@ -49,6 +49,8 @@ enum { MODE_PREV = -2, MODE_NEXT, + MODE_NULL, + MODE_CW, MODE_CONTESTIA, diff --git a/src/include/macros.h b/src/include/macros.h index 3f863297..9f5ff90a 100644 --- a/src/include/macros.h +++ b/src/include/macros.h @@ -36,9 +36,10 @@ struct MACROTEXT { void openMacroFile(); void saveMacroFile(); void saveMacros(const std::string& fname); - std::string expandMacro(int n); + std::string expandMacro(std::string &s); void execute(int n); void repeat(int n); + void timed_execute(); MACROTEXT(); private: std::string expanded; @@ -53,6 +54,15 @@ extern std::string info2msg; extern std::string qso_time; extern std::string qso_exchange; +extern std::string exec_date; +extern std::string exec_time; +extern std::string exec_string; + void set_macro_env(void); +void queue_reset(); +void queue_execute(); +bool queue_must_rx(); +void idleTimer(void *); + #endif diff --git a/src/include/modem.h b/src/include/modem.h index 2d3941b3..ebbba513 100644 --- a/src/include/modem.h +++ b/src/include/modem.h @@ -181,6 +181,8 @@ protected: virtual void s2nreport(void); }; +extern modem *null_modem; + extern modem *cw_modem; extern modem *mfsk8_modem; diff --git a/src/include/nullmodem.h b/src/include/nullmodem.h new file mode 100644 index 00000000..599695e4 --- /dev/null +++ b/src/include/nullmodem.h @@ -0,0 +1,48 @@ +// ---------------------------------------------------------------------------- +// NULLMODEM.h -- BASIS FOR ALL MODEMS +// +// Copyright (C) 2006 +// 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 3 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, see . +// ---------------------------------------------------------------------------- + +#ifndef _NULLMODEM_H +#define _NULLMODEM_H + +#include "trx.h" +#include "modem.h" +#include "fft.h" +#include "filters.h" +#include "complex.h" + +#define NULLMODEMSampleRate 8000 + +class NULLMODEM : public modem { +protected: +public: + NULLMODEM(); + ~NULLMODEM(); + void init(); + void rx_init(); + void restart(); + void tx_init(SoundBase *sc); + int rx_process(const double *buf, int len); + int tx_process(); + +}; + +#endif diff --git a/src/include/trx.h b/src/include/trx.h index 39435629..2bd56113 100644 --- a/src/include/trx.h +++ b/src/include/trx.h @@ -43,11 +43,8 @@ extern void trx_tune(); extern void trx_receive(); extern void trx_reset(void); -extern void trx_start_macro_timer(); -extern void trx_wait_state(void); - -extern void macro_timer(void *); +extern void trx_wait_state(void); extern state_t trx_state; extern modem *active_modem; diff --git a/src/misc/macroedit.cxx b/src/misc/macroedit.cxx index 67349a10..349a5acd 100644 --- a/src/misc/macroedit.cxx +++ b/src/misc/macroedit.cxx @@ -52,12 +52,12 @@ using namespace std; Fl_Double_Window *MacroEditDialog = (Fl_Double_Window *)0; -Fl_Button *btnMacroEditOK = (Fl_Button *)0; -Fl_Button *btnMacroEditCancel = (Fl_Button *)0; +Fl_Button *btnMacroEditApply = (Fl_Button *)0; +Fl_Button *btnMacroEditClose = (Fl_Button *)0; Fl_Button *btnInsertMacro = (Fl_Button *)0; Fl_Input2 *macrotext = (Fl_Input2 *)0; Fl_Input2 *labeltext = (Fl_Input2 *)0; -static int widths[] = {130, 0}; +static int widths[] = {150, 0}; Fl_Hold_Browser *macroDefs=(Fl_Hold_Browser *)0; @@ -151,6 +151,7 @@ void loadBrowser(Fl_Widget *widget) { w->add(_("\ttune signal for NN sec")); w->add(_("\tdelay xmt for NN sec")); w->add(_("\trepeat macro continuously")); + w->add(_("\tschedule execution")); w->add(LINE_SEP); w->add(_("\tCW identifier")); @@ -240,8 +241,10 @@ void loadBrowser(Fl_Widget *widget) { void cbMacroEditOK(Fl_Widget *w, void *) { - if (w == btnMacroEditCancel) - goto ret; + if (w == btnMacroEditClose) { + MacroEditDialog->hide(); + return; + } if (iType == MACRO_EDIT_BUTTON) { macros.text[iMacro] = macrotext->value(); @@ -264,8 +267,6 @@ void cbMacroEditOK(Fl_Widget *w, void *) } else if (iType == MACRO_EDIT_INPUT) iInput->value(macrotext->value()); -ret: - MacroEditDialog->hide(); } void cbInsertMacro(Fl_Widget *, void *) @@ -315,30 +316,30 @@ void cbInsertMacro(Fl_Widget *, void *) Fl_Double_Window* make_macroeditor(void) { - Fl_Double_Window* w = new Fl_Double_Window(768, 190, ""); + Fl_Double_Window* w = new Fl_Double_Window(800, 190, ""); - macrotext = new Fl_Input2(2, 22, 450, 140, _("Text:")); + macrotext = new Fl_Input2(2, 22, 450, 140, _("Macro Text")); macrotext->type(FL_MULTILINE_INPUT); macrotext->textfont(FL_COURIER); - macrotext->align(FL_ALIGN_TOP_LEFT); + macrotext->align(FL_ALIGN_TOP); - btnInsertMacro = new Fl_Button(454, 86, 20, 20); + btnInsertMacro = new Fl_Button(434, 2, 40, 20); btnInsertMacro->image(new Fl_Pixmap(left_arrow_icon)); btnInsertMacro->callback(cbInsertMacro); - macroDefs = new Fl_Hold_Browser(476, 22, 290, 140, _("Select Tags:")); + macroDefs = new Fl_Hold_Browser(452, 22, 346, 140, _("Select Tag")); macroDefs->column_widths(widths); - macroDefs->align(FL_ALIGN_TOP_LEFT); + macroDefs->align(FL_ALIGN_TOP); loadBrowser(macroDefs); labeltext = new Fl_Input2(2 + 450 - 115, 164, 115, 24, _("Macro Button Label:")); labeltext->textfont(FL_COURIER); - btnMacroEditOK = new Fl_Button(476 + 145 - 80 - 1, 164, 80, 24, _("OK")); - btnMacroEditOK->callback(cbMacroEditOK); + btnMacroEditApply = new Fl_Button(452 + macroDefs->w()/2 - 80 - 1, 164, 80, 24, _("Apply")); + btnMacroEditApply->callback(cbMacroEditOK); - btnMacroEditCancel = new Fl_Button(476 + 145 + 1 , 164, 80, 24, _("Cancel")); - btnMacroEditCancel->callback(cbMacroEditOK); + btnMacroEditClose = new Fl_Button(452 + macroDefs->w()/2 + 1 , 164, 80, 24, _("Close")); + btnMacroEditClose->callback(cbMacroEditOK); w->end(); w->xclass(PACKAGE_NAME); diff --git a/src/misc/macros.cxx b/src/misc/macros.cxx index 51fcf1ae..251a49f8 100644 --- a/src/misc/macros.cxx +++ b/src/misc/macros.cxx @@ -55,6 +55,7 @@ #include #include #include +#include #ifdef __WIN32__ #include "speak.h" @@ -62,6 +63,17 @@ using namespace std; +struct CMDS { string cmd; void (*fp)(string); }; +queue cmds; + +// following used for debugging and development +//void pushcmd(CMDS cmd) +//{ +// LOG_INFO("%s, # = %d", cmd.cmd.c_str(), (int)cmds.size()); +// cmds.push(cmd); +//} +#define pushcmd(a) cmds.push((a)) + MACROTEXT macros; CONTESTCNTR contest_count; static bool TransmitON = false; @@ -70,20 +82,24 @@ static int mNbr; std::string qso_time = ""; std::string qso_exchange = ""; -static bool save_xchg; -static size_t xbeg = 0, xend = 0; -string text2send = ""; -string text2repeat = ""; -string text2save = ""; +std::string exec_date = ""; +std::string exec_time = ""; +std::string exec_string = ""; + +std::string text2send = ""; +std::string text2repeat = ""; +//std::string text2save = ""; +std::string info1msg = ""; +std::string info2msg = ""; size_t repeatchar = 0; +static size_t xbeg = 0, xend = 0; +static bool save_xchg; static bool expand; static bool GET = false; - -string info1msg = ""; -string info2msg = ""; +static bool timed_exec = false; static char cutnumbers[] = "T12345678N"; static string cutstr; @@ -204,6 +220,107 @@ static void pPOST(string &s, size_t &i, size_t endbracket) s.replace(i, endbracket - i + 1, ""); } +static void setwpm(int d) +{ + sldrCWxmtWPM->value(d); + cntCW_WPM->value(d); +} + +static void doWPM(string s) +{ + int number; + string sTime = s.substr(6); + if (sTime.length() > 0) { + sscanf(sTime.c_str(), "%d", &number); + if (number < 5) number = 5; + if (number > 200) number = 200; + progdefaults.CWspeed = number; + REQ(setwpm, number); + } +} + +static void pQueWPM(string &s, size_t &i, size_t endbracket) +{ + struct CMDS cmd = { s.substr(i, endbracket - i + 1), doWPM }; + pushcmd(cmd); + s.replace(i, endbracket - i + 1, "^!"); +} + +static void setRISETIME(int d) +{ + cntCWrisetime->value(d); +} + +static void doRISETIME(string s) +{ + float number; + string sVal = s.substr(7, s.length() - 8); + if (sVal.length() > 0) { + sscanf(sVal.c_str(), "%f", &number); + if (number < 0) number = 0; + if (number > 20) number = 20; + progdefaults.CWrisetime = number; + REQ(setRISETIME, number); + } +} + +static void pQueRISETIME(string &s, size_t &i, size_t endbracket) +{ + struct CMDS cmd = { s.substr(i, endbracket - i + 1), doRISETIME }; + pushcmd(cmd); + s.replace(i, endbracket - i + 1, "^!"); +} + +static void setPRE(int d) +{ + cntPreTiming->value(d); +} + +static void doPRE(string s) +{ + float number; + string sVal = s.substr(6, s.length() - 7); + if (sVal.length() > 0) { + sscanf(sVal.c_str(), "%f", &number); + if (number < 0) number = 0; + if (number > 20) number = 20; + progdefaults.CWpre = number; + REQ(setPRE, number); + } +} + +static void pQuePRE(string &s, size_t &i, size_t endbracket) +{ + struct CMDS cmd = { s.substr(i, endbracket - i + 1), doPRE }; + pushcmd(cmd); + s.replace(i, endbracket - i + 1, "^!"); +} + +static void setPOST(int d) +{ + cntPostTiming->value(d); +} + +static void doPOST(string s) +{ + float number; + string sVal = s.substr(7, s.length() - 8); + if (sVal.length() > 0) { + sscanf(sVal.c_str(), "%f", &number); + if (number < -20) number = -20; + if (number > 20) number = 20; + progdefaults.CWpost = number; + REQ(setPOST, number); + } +} + +static void pQuePOST(string &s, size_t &i, size_t endbracket) +{ + struct CMDS cmd = { s.substr(i, endbracket - i + 1), doPOST }; + pushcmd(cmd); + s.replace(i, endbracket - i + 1, "^!"); +} + bool macro_idle_on = false; static float idleTime = 0; @@ -219,6 +336,31 @@ static void pIDLE(string &s, size_t &i, size_t endbracket) s.replace(i, endbracket - i + 1, ""); } +static void doneIDLE(void *) +{ + Qidle_time = 0; +} + +static void doIDLE(string s) +{ + float number; + string sTime = s.substr(7, s.length() - 8); + if (sTime.length() > 0) { + sscanf(sTime.c_str(), "%f", &number); + Qidle_time = 1; + Fl::add_timeout(number, doneIDLE); + } else { + Qidle_time = 0; + } +} + +static void pQueIDLE(string &s, size_t &i, size_t endbracket) +{ + struct CMDS cmd = { s.substr(i, endbracket - i + 1), doIDLE }; + pushcmd(cmd); + s.replace(i, endbracket - i + 1, "^!"); +} + static bool useTune = false; static int tuneTime = 0; @@ -249,6 +391,33 @@ static void pWAIT(string &s, size_t &i, size_t endbracket) s.replace(i, endbracket - i + 1, ""); } +static void doneWAIT(void *) +{ + Qwait_time = 0; + start_tx(); +} + +static void doWAIT(string s) +{ + int number; + string sTime = s.substr(7, s.length() - 8); + if (sTime.length() > 0) { + sscanf(sTime.c_str(), "%d", &number); + Qwait_time = number; + Fl::add_timeout (number * 1.0, doneWAIT); + } else + Qwait_time = 0; + que_ok = true; +} + +static void pQueWAIT(string &s, size_t &i, size_t endbracket) +{ + struct CMDS cmd = { s.substr(i, endbracket - i + 1), doWAIT }; + pushcmd(cmd); + s.replace(i, endbracket - i + 1, "^!"); +} + + static void pINFO1(string &s, size_t &i, size_t endbracket) { s.replace( i, 7, info1msg ); @@ -440,7 +609,6 @@ static void pID(string &s, size_t &i, size_t endbracket) static void pTEXT(string &s, size_t &i, size_t endbracket) { progdefaults.macrotextid = true; - s.replace( i, 6, ""); } @@ -450,10 +618,16 @@ static void pCWID(string &s, size_t &i, size_t endbracket) s.replace( i, 6, ""); } +static void doDTMF(string s) +{ + progdefaults.DTMFstr = s.substr(6, s.length() - 7); +} + static void pDTMF(string &s, size_t &i, size_t endbracket) { - progdefaults.DTMFstr = s.substr(i + 6, endbracket - i - 6); - s.replace(i, endbracket - i + 1, ""); + CMDS cmd = {s.substr(i, endbracket - i + 1), doDTMF}; + pushcmd(cmd); + s.replace(i, endbracket - i + 1, "^!"); } static void pRX(string &s, size_t &i, size_t endbracket) @@ -557,7 +731,7 @@ static void pCLRLOG(string &s, size_t &i, size_t endbracket) s.replace(i, 10, "^C"); } -static void pMODEM_compat(string &s, size_t &i, size_t endbracket) +static void pMODEM_compSKED(string &s, size_t &i, size_t endbracket) { size_t j, k, len = s.length(); @@ -584,6 +758,84 @@ static void pMODEM_compat(string &s, size_t &i, size_t endbracket) #include #include "re.h" +static void doMODEM(string s) +{ + static fre_t re("", REG_EXTENDED); + string tomatch = s; + + if (!re.match(tomatch.c_str())) { + que_ok = true; + return; + } + + const std::vector& o = re.suboff(); + string name = tomatch.substr(o[1].rm_so, o[1].rm_eo - o[1].rm_so); + trx_mode m; + for (m = 0; m < NUM_MODES; m++) + if (name == mode_info[m].sname) + break; + // do we have arguments and a valid modem? + if (o.size() == 2 || m == NUM_MODES) { + que_ok = true; + return; + } + + // parse arguments + vector args; + args.reserve(8); + char* end; + double d; + for (const char* p = s.c_str() + o[2].rm_so + 1; *p; p++) { + errno = 0; + d = strtod(p, &end); + if (!errno && p != end) { + args.push_back(d); + p = end; + } + else // push an invalid value + args.push_back(DBL_MIN); + } + + try { + switch (m) { + case MODE_RTTY: // carrier shift, baud rate, bits per char + if (args.at(0) != DBL_MIN) + set_rtty_shift((int)args[0]); + if (args.at(1) != DBL_MIN) + set_rtty_baud((float)args[1]); + if (args.at(2) != DBL_MIN) + set_rtty_bits((int)args[2]); + break; + case MODE_CONTESTIA: // bandwidth, tones + if (args.at(0) != DBL_MIN) + set_contestia_bw((int)args[0]); + if (args.at(1) != DBL_MIN) + set_contestia_tones((int)args[1]); + break; + case MODE_OLIVIA: // bandwidth, tones + if (args.at(0) != DBL_MIN) + set_olivia_bw((int)args[0]); + if (args.at(1) != DBL_MIN) + set_olivia_tones((int)args[1]); + break; + default: + break; + } + } + catch (const exception& e) { } + + if (active_modem->get_mode() != mode_info[m].mode) + init_modem_sync(mode_info[m].mode); + que_ok = true; +} + +static void pQueMODEM(string &s, size_t &i, size_t endbracket) +{ + struct CMDS cmd = { s.substr(i, endbracket - i + 1), doMODEM }; + pushcmd(cmd); + s.replace(i, endbracket - i + 1, "^!"); +} + static void pMODEM(string &s, size_t &i, size_t endbracket) { static fre_t re("", REG_EXTENDED); @@ -677,6 +929,8 @@ static void pAFC(string &s, size_t &i, size_t endbracket) btnAFC->do_callback(); } +//pushcmd(s.substr(i, endbracket - i + 1)); +//s.replace(i, endbracket - i + 1, "^!"); s.replace(i, endbracket - i + 1, ""); } @@ -695,6 +949,8 @@ static void pLOCK(string &s, size_t &i, size_t endbracket) wf->xmtlock->damage(); wf->xmtlock->do_callback(); } +//pushcmd(s.substr(i, endbracket - i + 1)); +//s.replace(i, endbracket - i + 1, "^!"); s.replace(i, endbracket - i + 1, ""); } @@ -712,6 +968,8 @@ static void pTX_RSID(string &s, size_t &i, size_t endbracket) btnTxRSID->do_callback(); } +//pushcmd(s.substr(i, endbracket - i + 1)); +//s.replace(i, endbracket - i + 1, "^!"); s.replace(i, endbracket - i + 1, ""); } @@ -776,6 +1034,24 @@ static void pGOHOME(string &s, size_t &i, size_t endbracket) active_modem->set_freq(progdefaults.PSKsweetspot); } +static void doGOHOME(string s) +{ + if (active_modem == cw_modem) + active_modem->set_freq(progdefaults.CWsweetspot); + else if (active_modem == rtty_modem) + active_modem->set_freq(progdefaults.RTTYsweetspot); + else + active_modem->set_freq(progdefaults.PSKsweetspot); + que_ok = true; +} + +static void pQueGOHOME(string &s, size_t &i, size_t endbracket) +{ + struct CMDS cmd = { s.substr(i, endbracket - i + 1), doGOHOME }; + pushcmd(cmd); + s.replace(i, endbracket - i + 1, "^!"); +} + static void pGOFREQ(string &s, size_t &i, size_t endbracket) { int number; @@ -791,6 +1067,28 @@ static void pGOFREQ(string &s, size_t &i, size_t endbracket) s.replace(i, endbracket - i + 1, ""); } +static void doGOFREQ(string s) +{ + int number; + string sGoFreq = s.substr(9, s.length() - 10); + if (sGoFreq.length() > 0) { + sscanf(sGoFreq.c_str(), "%d", &number); + if (number < progdefaults.LowFreqCutoff) + number = progdefaults.LowFreqCutoff; + if (number > progdefaults.HighFreqCutoff) + number = progdefaults.HighFreqCutoff; + active_modem->set_freq(number); + } + que_ok = true; +} + +static void pQueGOFREQ(string &s, size_t &i, size_t endbracket) +{ + struct CMDS cmd = { s.substr(i, endbracket - i + 1), doGOFREQ }; + pushcmd(cmd); + s.replace(i, endbracket - i + 1, "^!"); +} + static void pQSYTO(string &s, size_t &i, size_t endbracket) { s.replace( i, 7, ""); @@ -830,7 +1128,6 @@ static void pQSY(string &s, size_t &i, size_t endbracket) if (audio > progdefaults.HighFreqCutoff) audio = progdefaults.HighFreqCutoff; } - if (rf && rf != wf->rfcarrier()) qsy(rf, audio); else @@ -839,6 +1136,48 @@ static void pQSY(string &s, size_t &i, size_t endbracket) s.replace(i, endbracket - i + 1, ""); } +static void doQSY(string s) +{ + int rf = 0; + int audio = 0; + float rfd = 0; + string sGoFreq; + sGoFreq = s.substr(6, s.length() - 7); + // no frequency(s) specified + if (sGoFreq.length() == 0) { + que_ok = true; + return; + } + // rf first value + sscanf(sGoFreq.c_str(), "%f", &rfd); + if (rfd > 0) + rf = (int)(1000*rfd); + size_t pos; + if ((pos = sGoFreq.find(":")) != string::npos) { + // af second value + sGoFreq.erase(0, pos+1); + if (sGoFreq.length()) + sscanf(sGoFreq.c_str(), "%d", &audio); + if (audio < 0) audio = 0; + if (audio < progdefaults.LowFreqCutoff) + audio = progdefaults.LowFreqCutoff; + if (audio > progdefaults.HighFreqCutoff) + audio = progdefaults.HighFreqCutoff; + } + if (rf && rf != wf->rfcarrier()) + qsy(rf, audio); + else + active_modem->set_freq(audio); + que_ok = true; +} + +static void pQueQSY(string &s, size_t &i, size_t endbracket) +{ + struct CMDS cmd = { s.substr(i, endbracket - i + 1), doQSY }; + pushcmd(cmd); + s.replace(i, endbracket - i + 1, "^!"); +} + static void pRIGMODE(string& s, size_t& i, size_t endbracket) { string sMode = s.substr(i+9, endbracket - i - 9); @@ -859,7 +1198,7 @@ void set_macro_env(void) { enum { #ifndef __WOE32__ - PATH, FLDIGI_RX_IPC_KEY, FLDIGI_TX_IPC_KEY, + pSKEDH, FLDIGI_RX_IPC_KEY, FLDIGI_TX_IPC_KEY, #endif FLDIGI_XMLRPC_ADDRESS, FLDIGI_XMLRPC_PORT, FLDIGI_ARQ_ADDRESS, FLDIGI_ARQ_PORT, @@ -881,7 +1220,7 @@ void set_macro_env(void) const char* val; } env[] = { #ifndef __WOE32__ - { "PATH", "" }, + { "pSKEDH", "" }, { "FLDIGI_RX_IPC_KEY", "" }, { "FLDIGI_TX_IPC_KEY", "" }, #endif @@ -919,13 +1258,13 @@ void set_macro_env(void) }; #ifndef __WOE32__ - // PATH - static string path = ScriptsDir; - path.erase(path.length()-1,1); + // pSKEDH + static string pSKEDh = ScriptsDir; + pSKEDh.erase(pSKEDh.length()-1,1); const char* p; - if ((p = getenv("PATH"))) - path.append(":").append(p); - env[PATH].val = path.c_str(); + if ((p = getenv("pSKEDH"))) + pSKEDh.append(":").append(p); + env[pSKEDH].val = pSKEDh.c_str(); // IPC keys char key[2][8]; @@ -1132,6 +1471,65 @@ static void pCONT(string &s, size_t &i, size_t endbracket) expand = true; } +static void pSKED(string &s, size_t &i, size_t endbracket) +{ + string data = s.substr(i+6, endbracket - i - 6); + size_t p = data.find(":"); + if (p == std::string::npos) { + exec_date = zdate(); + exec_time = data; + if (exec_time.empty()) exec_time = ztime(); + } else { + exec_time = data.substr(0, p); + exec_date = data.substr(p+1); + } + timed_exec = true; + s.replace(i, endbracket - i + 1, ""); +} + +void queue_reset() +{ + if (!cmds.empty()) { + Fl::remove_timeout(post_queue_execute); + Fl::remove_timeout(queue_execute_after_rx); + Fl::remove_timeout(doneIDLE); + Fl::remove_timeout(doneWAIT); + while (!cmds.empty()) cmds.pop(); + } + Qwait_time = 0; + Qidle_time = 0; + que_ok = true; +} + +void postQueue(string s) +{ + ReceiveText->add(s.c_str(), FTextBase::CTRL); +} + +void queue_execute() +{ + if (cmds.empty()) { + Qwait_time = 0; + Qidle_time = 0; + que_ok = true; + return; + } + CMDS cmd = cmds.front(); + cmds.pop(); + cmd.fp(cmd.cmd); + LOG_INFO("%s", cmd.cmd.c_str()); + REQ(postQueue, cmd.cmd.append("\n")); + return; +} + +bool queue_must_rx() +{ +static string rxcmds = "", pMODEM_compat}, +{"", pMODEM_compSKED}, {"", pEXEC}, {"", pSTOP}, @@ -1211,9 +1609,20 @@ MTAGS mtags[] = { {"", pMAPIT}, {"", pREPEAT}, +{"", pQueGOHOME}, +{"do_callback(); } +void MACROTEXT::timed_execute() +{ + queue_reset(); + TransmitText->clear(); + text2send = expandMacro(exec_string); + TransmitText->add(text2send.c_str()); + exec_string.clear(); + active_modem->set_stopflag(false); + start_tx(); +} + void MACROTEXT::execute(int n) { - text2save = text2send = expandMacro(n); + mNbr = n; +// text2save = + text2send = expandMacro(text[n]); + + if (timed_exec) { + progStatus.repeatMacro = -1; + exec_string = text[n]; + timed_exec = false; + startTimedExecute(name[n]); + return; + } if (progStatus.repeatMacro == -1) TransmitText->add( text2send.c_str() ); @@ -1505,8 +1935,8 @@ void MACROTEXT::execute(int n) void MACROTEXT::repeat(int n) { - expandMacro(n); - LOG_INFO("%s",text2repeat.c_str()); + expandMacro(text[n]); + LOG_WARN("%s",text2repeat.c_str()); macro_idle_on = false; if (idleTime) progStatus.repeatIdleTime = idleTime; } diff --git a/src/mt63/mt63.cxx b/src/mt63/mt63.cxx index 58014beb..b944c8e9 100644 --- a/src/mt63/mt63.cxx +++ b/src/mt63/mt63.cxx @@ -79,13 +79,22 @@ int mt63::tx_process() if (c == 0x03) { stopflag = true; flush = Tx->DataInterleave; - c = 0; } if (c == -1 || stopflag == true) c = 0; - if (stopflag && flush-- == 0) { + if (stopflag) { stopflag = false; + while (--flush) { + Tx->SendChar(0); + for (int i = 0; i < Tx->Comb.Output.Len; i++) + if (fabs(Tx->Comb.Output.Data[i]) > maxval) + maxval = fabs(Tx->Comb.Output.Data[i]); + for (int i = 0; i < Tx->Comb.Output.Len; i++) { + Tx->Comb.Output.Data[i] /= maxval; + } + ModulateXmtr((Tx->Comb.Output.Data), Tx->Comb.Output.Len); + } Tx->SendJam(); for (int i = 0; i < Tx->Comb.Output.Len; i++) if (fabs(Tx->Comb.Output.Data[i]) > maxval) diff --git a/src/trx/modem.cxx b/src/trx/modem.cxx index 704adc87..a34ac0cc 100644 --- a/src/trx/modem.cxx +++ b/src/trx/modem.cxx @@ -42,6 +42,7 @@ using namespace std; +modem *null_modem = 0; modem *cw_modem = 0; modem *mfsk8_modem = 0; diff --git a/src/trx/nullmodem.cxx b/src/trx/nullmodem.cxx new file mode 100644 index 00000000..a97dab52 --- /dev/null +++ b/src/trx/nullmodem.cxx @@ -0,0 +1,91 @@ +// ---------------------------------------------------------------------------- +// NULLMODEM.cxx -- NULLMODEM modem +// +// Copyright (C) 2006 +// Dave Freese, W1HKJ +// +// This file is part of fldigi. Adapted from code contained in gMFSK source code +// distribution. +// gMFSK Copyright (C) 2001, 2002, 2003 +// 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 3 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, see . +// ---------------------------------------------------------------------------- + +#include + +#include +#include + +#include "nullmodem.h" +#include "fl_digi.h" +#include "ascii.h" + +#define null_bw 1 + +NULLMODEM:: NULLMODEM() : modem() +{ + mode = MODE_NULL; + samplerate = 8000; + restart(); +} + +NULLMODEM::~NULLMODEM() {}; + +void NULLMODEM::tx_init(SoundBase *sc) +{ + scard = sc; +} + +void NULLMODEM::rx_init() +{ + put_MODEstatus(mode); +} + +void NULLMODEM::init() +{ + modem::init(); + rx_init(); + digiscope->mode(Digiscope::SCOPE); +} + +void NULLMODEM::restart() +{ + set_bandwidth(null_bw); +} + + +//===================================================================== +// receive processing +//===================================================================== + +int NULLMODEM::rx_process(const double *buf, int len) +{ + return 0; +} + +//===================================================================== +// transmit processing +//===================================================================== + + +int NULLMODEM::tx_process() +{ + MilliSleep(10); + if ( get_tx_char() == 0x03 || stopflag) { + stopflag = false; + return -1; + } + return 0; +} diff --git a/src/trx/trx.cxx b/src/trx/trx.cxx index 2476caaa..68a69895 100644 --- a/src/trx/trx.cxx +++ b/src/trx/trx.cxx @@ -158,7 +158,6 @@ static void trx_xmit_wfall_end(int samplerate) void trx_xmit_wfall_queue(int samplerate, const double* buf, size_t len) { ENSURE_THREAD(TRX_TID); - ringbuffer::vector_type wv[2]; wv[0].buf = wv[1].buf = 0; @@ -302,15 +301,19 @@ void trx_trx_transmit_loop() return; } - push2talk->set(true); + if (active_modem != ssb_modem) { + push2talk->set(true); + REQ(&waterfall::set_XmtRcvBtn, wf, true); + } active_modem->tx_init(scard); - if (progdefaults.TransmitRSid) + if ((active_modem != null_modem && active_modem != ssb_modem) && + progdefaults.TransmitRSid) ReedSolomon->send(true); - dtmf->send(); - while (trx_state == STATE_TX) { + if (active_modem != ssb_modem && !progdefaults.DTMFstr.empty()) + dtmf->send(); try { if (active_modem->tx_process() < 0) trx_state = STATE_RX; @@ -326,9 +329,6 @@ void trx_trx_transmit_loop() trx_xmit_wfall_end(current_samplerate); -// if (progdefaults.TransmitRSid) -// ReedSolomon->send(false); - scard->flush(); if (scard->must_close(O_WRONLY)) scard->Close(O_WRONLY); @@ -407,6 +407,17 @@ void *trx_loop(void *args) trxrb.reset(); trx_signal_state(); } +/* +printf("trx state %s\n", +trx_state == STATE_ABORT ? "abort" : +trx_state == STATE_ENDED ? "ended" : +trx_state == STATE_RESTART ? "restart" : +trx_state == STATE_NEW_MODEM ? "new modem" : +trx_state == STATE_TX ? "tx" : +trx_state == STATE_TUNE ? "tune" : +trx_state == STATE_RX ? "rx" : +"unknown"); +*/ switch (trx_state) { case STATE_ABORT: delete scard; @@ -423,8 +434,6 @@ void *trx_loop(void *args) break; case STATE_TX: trx_trx_transmit_loop(); - if (progStatus.timer) - REQ(startMacroTimer); break; case STATE_TUNE: trx_tune_loop();