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();