* Add hooks to the logger and received text handlers
* Add support for N1DQ's PSK Automatic Propagation Reporter
pull/2/head
Stelios Bounanos 2008-11-20 15:13:27 +00:00
rodzic c8a6ae11e4
commit 690daadf50
23 zmienionych plików z 1551 dodań i 42 usunięć

Wyświetl plik

@ -212,6 +212,7 @@ fldigi_SOURCES += \
include/pskeval.h \
include/ptt.h \
include/pixmaps.h \
include/pskrep.h \
include/qrunner.h \
include/lookupcall.h \
include/qrzlib.h \
@ -230,6 +231,7 @@ fldigi_SOURCES += \
include/socket.h \
include/sound.h \
include/soundconf.h \
include/spot.h \
include/stacktrace.h \
include/status.h \
include/testmodem.h \
@ -302,6 +304,8 @@ fldigi_SOURCES += \
soundcard/mixer.cxx \
soundcard/sound.cxx \
soundcard/soundconf.cxx \
spot/pskrep.cxx \
spot/spot.cxx \
throb/throb.cxx \
trx/modem.cxx \
trx/trx.cxx \

Wyświetl plik

@ -10,6 +10,7 @@
#include "waterfall.h"
#include "fl_digi.h"
#include "re.h"
#include "spot.h"
#include <FL/Enumerations.H>
#include <FL/Fl_Slider.H>
@ -350,6 +351,9 @@ void viewer_redraw()
void viewaddchr(int ch, int freq, char c) {
if (!dlgViewer) return;
if (progStatus.spot_recv)
spot_recv(c, ch, freq);
if (rfc != wf->rfcarrier() || usb != wf->USB()) viewer_redraw();
static string nuline;

Wyświetl plik

@ -1,4 +1,4 @@
// generated by Fast Light User Interface Designer (fluid) version 1.0108
// generated by Fast Light User Interface Designer (fluid) version 1.0109
#include "confdialog.h"
#include <config.h>
@ -13,6 +13,7 @@
#include "icons.h"
#include "Viewer.h"
extern void initViewer();
#include "pskrep.h"
Fl_Double_Window *dlgConfig;
void set_qrz_buttons(Fl_Button* b) {
@ -156,6 +157,65 @@ static void cb_btnStartAtSweetSpot(Fl_Check_Button* o, void*) {
progdefaults.changed = true;
}
Fl_Group *tabSpot=(Fl_Group *)0;
Fl_Check_Button *btnPSKRepAuto=(Fl_Check_Button *)0;
static void cb_btnPSKRepAuto(Fl_Check_Button* o, void*) {
progdefaults.pskrep_auto = o->value();
btnPSKRepInit->labelcolor(FL_RED);
btnPSKRepInit->redraw_label();
progdefaults.changed = true;
}
Fl_Check_Button *btnPSKRepLog=(Fl_Check_Button *)0;
static void cb_btnPSKRepLog(Fl_Check_Button* o, void*) {
progdefaults.pskrep_log = o->value();
btnPSKRepInit->labelcolor(FL_RED);
btnPSKRepInit->redraw_label();
progdefaults.changed = true;
}
Fl_Input *inpPSKRepHost=(Fl_Input *)0;
static void cb_inpPSKRepHost(Fl_Input* o, void*) {
progdefaults.pskrep_host = o->value();
btnPSKRepInit->labelcolor(FL_RED);
btnPSKRepInit->redraw_label();
progdefaults.changed = true;
}
Fl_Input *inpPSKRepPort=(Fl_Input *)0;
static void cb_inpPSKRepPort(Fl_Input* o, void*) {
progdefaults.pskrep_port = o->value();
btnPSKRepInit->labelcolor(FL_RED);
btnPSKRepInit->redraw_label();
progdefaults.changed = true;
}
Fl_Button *btnPSKRepInit=(Fl_Button *)0;
static void cb_btnPSKRepInit(Fl_Button* o, void*) {
pskrep_stop();
if (!pskrep_start())
boxPSKRepMsg->copy_label(pskrep_error());
else {
boxPSKRepMsg->label(0);
o->labelcolor(FL_FOREGROUND_COLOR);
};
}
Fl_Box *boxPSKRepMsg=(Fl_Box *)0;
Fl_Check_Button *btnPSKRepQRG=(Fl_Check_Button *)0;
static void cb_btnPSKRepQRG(Fl_Check_Button* o, void*) {
progdefaults.pskrep_qrg = o->value();
progdefaults.changed = true;
}
Fl_Group *tabModems=(Fl_Group *)0;
Fl_Tabs *tabsModems=(Fl_Tabs *)0;
@ -734,6 +794,13 @@ static void cb_inpMyQth(Fl_Input* o, void*) {
progdefaults.changed = true;
}
Fl_Input *inpMyAntenna=(Fl_Input *)0;
static void cb_inpMyAntenna(Fl_Input* o, void*) {
progdefaults.myAntenna = o->value();
progdefaults.changed = true;
}
Fl_Input *inpMyLocator=(Fl_Input *)0;
static void cb_inpMyLocator(Fl_Input* o, void*) {
@ -1802,7 +1869,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, 255, "fldigi - config");
{ Fl_Double_Window* o = new Fl_Double_Window(400, 254, "fldigi - config");
w = o;
o->tooltip("Leading/Trailing Risetimes (msec)");
o->color(FL_DARK2);
@ -1902,7 +1969,6 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600
{ tabMisc = new Fl_Group(0, 25, 400, 195, "Misc");
tabMisc->color((Fl_Color)51);
tabMisc->selection_color((Fl_Color)51);
tabMisc->hide();
{ tabsMisc = new Fl_Tabs(0, 25, 400, 195);
tabsMisc->selection_color((Fl_Color)10);
{ tabCPUspeed = new Fl_Group(0, 50, 400, 170, "CPU");
@ -1940,6 +2006,7 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600
tabMacros->end();
} // Fl_Group* tabMacros
{ tabSweetSpot = new Fl_Group(0, 50, 400, 170, "Sweet Spot");
tabSweetSpot->hide();
{ Fl_Group* o = new Fl_Group(5, 60, 390, 75);
o->box(FL_ENGRAVED_FRAME);
o->align(FL_ALIGN_TOP_LEFT|FL_ALIGN_INSIDE);
@ -1977,6 +2044,46 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600
} // Fl_Group* o
tabSweetSpot->end();
} // Fl_Group* tabSweetSpot
{ tabSpot = new Fl_Group(0, 50, 400, 170, "Spotting");
{ Fl_Group* o = new Fl_Group(2, 57, 395, 160, "PSK Reporter");
o->box(FL_ENGRAVED_FRAME);
o->align(FL_ALIGN_TOP_LEFT|FL_ALIGN_INSIDE);
{ btnPSKRepAuto = new Fl_Check_Button(15, 80, 324, 20, "Automatically spot callsigns in decoded text");
btnPSKRepAuto->down_box(FL_DOWN_BOX);
btnPSKRepAuto->callback((Fl_Callback*)cb_btnPSKRepAuto);
btnPSKRepAuto->value(progdefaults.pskrep_auto);
} // Fl_Check_Button* btnPSKRepAuto
{ btnPSKRepLog = new Fl_Check_Button(15, 104, 327, 20, "Send reception report when logging a QSO");
btnPSKRepLog->down_box(FL_DOWN_BOX);
btnPSKRepLog->callback((Fl_Callback*)cb_btnPSKRepLog);
btnPSKRepLog->value(progdefaults.pskrep_log);
} // Fl_Check_Button* btnPSKRepLog
{ inpPSKRepHost = new Fl_Input(56, 156, 220, 20, "Host:");
inpPSKRepHost->callback((Fl_Callback*)cb_inpPSKRepHost);
inpPSKRepHost->when(FL_WHEN_CHANGED);
inpPSKRepHost->value(progdefaults.pskrep_host.c_str());
} // Fl_Input* inpPSKRepHost
{ inpPSKRepPort = new Fl_Input(323, 156, 60, 20, "Port:");
inpPSKRepPort->callback((Fl_Callback*)cb_inpPSKRepPort);
inpPSKRepPort->when(FL_WHEN_CHANGED);
inpPSKRepPort->value(progdefaults.pskrep_port.c_str());
} // Fl_Input* inpPSKRepPort
{ btnPSKRepInit = new Fl_Button(303, 186, 80, 24, "Initialize");
btnPSKRepInit->callback((Fl_Callback*)cb_btnPSKRepInit);
} // Fl_Button* btnPSKRepInit
{ boxPSKRepMsg = new Fl_Box(56, 186, 220, 24, "<PSK Reporter error message>");
boxPSKRepMsg->labelfont(2);
boxPSKRepMsg->label(0);
} // Fl_Box* boxPSKRepMsg
{ btnPSKRepQRG = new Fl_Check_Button(15, 128, 357, 20, "Report QRG (enable only if you have rig control!)");
btnPSKRepQRG->down_box(FL_DOWN_BOX);
btnPSKRepQRG->callback((Fl_Callback*)cb_btnPSKRepQRG);
btnPSKRepQRG->value(progdefaults.pskrep_qrg);
} // Fl_Check_Button* btnPSKRepQRG
o->end();
} // Fl_Group* o
tabSpot->end();
} // Fl_Group* tabSpot
tabsMisc->end();
} // Fl_Tabs* tabsMisc
tabMisc->end();
@ -2108,7 +2215,6 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600
cntCWdash2dot->type(1);
cntCWdash2dot->minimum(2.5);
cntCWdash2dot->maximum(4);
cntCWdash2dot->step(0.1);
cntCWdash2dot->value(3);
cntCWdash2dot->callback((Fl_Callback*)cb_cntCWdash2dot);
cntCWdash2dot->align(FL_ALIGN_LEFT);
@ -2118,7 +2224,6 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600
cntCWrisetime->type(1);
cntCWrisetime->minimum(0);
cntCWrisetime->maximum(15);
cntCWrisetime->step(0.1);
cntCWrisetime->value(4);
cntCWrisetime->callback((Fl_Callback*)cb_cntCWrisetime);
cntCWrisetime->align(FL_ALIGN_LEFT);
@ -2182,7 +2287,6 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600
valDominoEX_BW->type(1);
valDominoEX_BW->minimum(1);
valDominoEX_BW->maximum(2);
valDominoEX_BW->step(0.1);
valDominoEX_BW->value(1.5);
valDominoEX_BW->callback((Fl_Callback*)cb_valDominoEX_BW);
o->value(progdefaults.DOMINOEX_BW);
@ -2213,7 +2317,6 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600
{ Fl_Value_Slider* o = valDomCWI = new Fl_Value_Slider(125, 179, 260, 21, "CWI threshold:");
valDomCWI->tooltip("CWI detection and suppression");
valDomCWI->type(1);
valDomCWI->step(0.01);
valDomCWI->textsize(14);
valDomCWI->callback((Fl_Callback*)cb_valDomCWI);
valDomCWI->align(FL_ALIGN_LEFT);
@ -2608,7 +2711,6 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600
valTHOR_BW->type(1);
valTHOR_BW->minimum(1);
valTHOR_BW->maximum(2);
valTHOR_BW->step(0.1);
valTHOR_BW->value(1.5);
valTHOR_BW->callback((Fl_Callback*)cb_valTHOR_BW);
o->value(progdefaults.THOR_BW);
@ -2639,7 +2741,6 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600
{ Fl_Value_Slider* o = valThorCWI = new Fl_Value_Slider(120, 174, 260, 21, "CWI threshold:");
valThorCWI->tooltip("CWI detection and suppression");
valThorCWI->type(1);
valThorCWI->step(0.01);
valThorCWI->textsize(14);
valThorCWI->callback((Fl_Callback*)cb_valThorCWI);
valThorCWI->align(FL_ALIGN_LEFT);
@ -2656,15 +2757,19 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600
tabOperator->selection_color((Fl_Color)51);
tabOperator->callback((Fl_Callback*)cb_tabOperator);
tabOperator->when(FL_WHEN_CHANGED);
{ inpMyCallsign = new Fl_Input(78, 36, 85, 24, "Callsign:");
tabOperator->hide();
{ inpMyCallsign = new Fl_Input(78, 36, 100, 24, "Callsign:");
inpMyCallsign->callback((Fl_Callback*)cb_inpMyCallsign);
} // Fl_Input* inpMyCallsign
{ inpMyName = new Fl_Input(78, 62, 120, 24, "Name:");
{ inpMyName = new Fl_Input(270, 36, 120, 24, "Name:");
inpMyName->callback((Fl_Callback*)cb_inpMyName);
} // Fl_Input* inpMyName
{ inpMyQth = new Fl_Input(78, 89, 312, 24, "Qth:");
{ inpMyQth = new Fl_Input(78, 62, 312, 24, "Qth:");
inpMyQth->callback((Fl_Callback*)cb_inpMyQth);
} // Fl_Input* inpMyQth
{ inpMyAntenna = new Fl_Input(78, 89, 312, 24, "Antenna:");
inpMyAntenna->callback((Fl_Callback*)cb_inpMyAntenna);
} // Fl_Input* inpMyAntenna
{ inpMyLocator = new Fl_Input(78, 116, 85, 24, "Locator:");
inpMyLocator->callback((Fl_Callback*)cb_inpMyLocator);
} // Fl_Input* inpMyLocator
@ -2696,7 +2801,7 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600
} // Fl_Group* o
tabOperator->end();
} // Fl_Group* tabOperator
{ tabQRZ = new Fl_Group(0, 25, 400, 195, "Qrz");
{ tabQRZ = new Fl_Group(0, 25, 400, 195, "QRZ");
tabQRZ->color((Fl_Color)51);
tabQRZ->selection_color((Fl_Color)51);
tabQRZ->hide();
@ -3209,7 +3314,6 @@ l with your sound hardware.");
valPCMvolume->type(5);
valPCMvolume->color((Fl_Color)26);
valPCMvolume->selection_color((Fl_Color)1);
valPCMvolume->step(0.01);
valPCMvolume->value(0.8);
valPCMvolume->textsize(14);
valPCMvolume->callback((Fl_Callback*)cb_valPCMvolume);

Wyświetl plik

@ -1,5 +1,5 @@
# data file for the Fltk User Interface Designer (fluid)
version 1.0108
version 1.0109
header_name {.h}
code_name {.cxx}
decl {\#include <config.h>} {}
@ -35,6 +35,9 @@ decl {\#include "Viewer.h"} {}
decl {extern void initViewer();} {}
decl {\#include "pskrep.h"} {global
}
decl {Fl_Double_Window *dlgConfig;} {public
}
@ -59,8 +62,8 @@ 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";} {}
code {} {}
Fl_Window {} {
label {fldigi - config} open
tooltip {Leading/Trailing Risetimes (msec)} xywh {606 597 400 255} type Double color 45 selection_color 51 labelsize 18 align 80 visible
label {fldigi - config} open selected
tooltip {Leading/Trailing Risetimes (msec)} xywh {951 749 400 254} type Double color 45 selection_color 51 labelsize 18 align 80 visible
} {
Fl_Tabs tabsConfigure {open
xywh {0 0 405 221} color 47 selection_color 9
@ -154,7 +157,7 @@ progdefaults.changed = true;}
}
Fl_Group tabMisc {
label Misc open
xywh {0 25 400 195} color 51 selection_color 51 hide
xywh {0 25 400 195} color 51 selection_color 51
} {
Fl_Tabs tabsMisc {open
xywh {0 25 400 195} selection_color 10
@ -201,7 +204,7 @@ progdefaults.changed = true;}
}
Fl_Group tabSweetSpot {
label {Sweet Spot} open
xywh {0 50 400 170}
xywh {0 50 400 170} hide
} {
Fl_Group {} {open
xywh {5 60 390 75} box ENGRAVED_FRAME align 21
@ -236,6 +239,75 @@ progdefaults.changed = true;}
}
}
}
Fl_Group tabSpot {
label Spotting open
xywh {0 50 400 170}
} {
Fl_Group {} {
label {PSK Reporter} open
xywh {2 57 395 160} box ENGRAVED_FRAME align 21
} {
Fl_Check_Button btnPSKRepAuto {
label {Automatically spot callsigns in decoded text}
callback {progdefaults.pskrep_auto = o->value();
btnPSKRepInit->labelcolor(FL_RED);
btnPSKRepInit->redraw_label();
progdefaults.changed = true;}
xywh {15 80 324 20} down_box DOWN_BOX
code0 {btnPSKRepAuto->value(progdefaults.pskrep_auto);}
}
Fl_Check_Button btnPSKRepLog {
label {Send reception report when logging a QSO}
callback {progdefaults.pskrep_log = o->value();
btnPSKRepInit->labelcolor(FL_RED);
btnPSKRepInit->redraw_label();
progdefaults.changed = true;}
xywh {15 104 327 20} down_box DOWN_BOX
code0 {btnPSKRepLog->value(progdefaults.pskrep_log);}
}
Fl_Input inpPSKRepHost {
label {Host:}
callback {progdefaults.pskrep_host = o->value();
btnPSKRepInit->labelcolor(FL_RED);
btnPSKRepInit->redraw_label();
progdefaults.changed = true;}
xywh {56 156 220 20} when 1
code0 {inpPSKRepHost->value(progdefaults.pskrep_host.c_str());}
}
Fl_Input inpPSKRepPort {
label {Port:}
callback {progdefaults.pskrep_port = o->value();
btnPSKRepInit->labelcolor(FL_RED);
btnPSKRepInit->redraw_label();
progdefaults.changed = true;}
xywh {323 156 60 20} when 1
code0 {inpPSKRepPort->value(progdefaults.pskrep_port.c_str());}
}
Fl_Button btnPSKRepInit {
label Initialize
callback {pskrep_stop();
if (!pskrep_start())
boxPSKRepMsg->copy_label(pskrep_error());
else {
boxPSKRepMsg->label(0);
o->labelcolor(FL_FOREGROUND_COLOR);
}}
xywh {303 186 80 24}
}
Fl_Box boxPSKRepMsg {
label {<PSK Reporter error message>}
xywh {56 186 220 24} labelfont 2
code0 {boxPSKRepMsg->label(0);}
}
Fl_Check_Button btnPSKRepQRG {
label {Report QRG (enable only if you have rig control!)}
callback {progdefaults.pskrep_qrg = o->value();
progdefaults.changed = true;}
xywh {15 128 357 20} down_box DOWN_BOX
code0 {btnPSKRepQRG->value(progdefaults.pskrep_qrg);}
}
}
}
}
}
Fl_Group tabModems {
@ -894,8 +966,8 @@ progdefaults.changed = true;}
}
Fl_Group tabOperator {
label Oper
callback {progdefaults.changed = true;} open selected
xywh {0 25 400 195} color 51 selection_color 51 when 1
callback {progdefaults.changed = true;} open
xywh {0 25 400 195} color 51 selection_color 51 when 1 hide
} {
Fl_Input inpMyCallsign {
label {Callsign:}
@ -912,17 +984,23 @@ txtSecondary->value(progdefaults.secText.c_str());
progdefaults.myCall = o->value();
update_main_title();
progdefaults.changed = true;}
xywh {78 36 85 24}
xywh {78 36 100 24}
}
Fl_Input inpMyName {
label {Name:}
callback {progdefaults.myName = o->value();
progdefaults.changed = true;}
xywh {78 62 120 24}
xywh {270 36 120 24}
}
Fl_Input inpMyQth {
label {Qth:}
callback {progdefaults.myQth = o->value();
progdefaults.changed = true;}
xywh {78 62 312 24}
}
Fl_Input inpMyAntenna {
label {Antenna:}
callback {progdefaults.myAntenna = o->value();
progdefaults.changed = true;}
xywh {78 89 312 24}
}
@ -957,7 +1035,7 @@ progdefaults.changed = true;}
}
}
Fl_Group tabQRZ {
label Qrz open
label QRZ open
xywh {0 25 400 195} color 51 selection_color 51 hide
} {
Fl_Round_Button btnQRZnotavailable {

Wyświetl plik

@ -111,6 +111,7 @@
#include "debug.h"
#include "re.h"
#include "network.h"
#include "spot.h"
Fl_Double_Window *fl_digi_main=(Fl_Double_Window *)0;
Fl_Help_Dialog *help_dialog = (Fl_Help_Dialog *)0;
@ -120,9 +121,9 @@ MixerBase* mixer = 0;
//bool useCheckButtons;
Fl_Light_Button *btnTune = (Fl_Light_Button *)0;
Fl_Light_Button *btnAutoSpot = (Fl_Light_Button *)0;
Fl_Light_Button *btnRSID = (Fl_Light_Button *)0;
Fl_Light_Button *btnTune = (Fl_Light_Button *)0;
Fl_Tile_check *TiledGroup = 0;
FTextView *ReceiveText = 0;
@ -805,6 +806,11 @@ void cb_mnuPlayback(Fl_Widget *w, void *d)
m->flags &= ~FL_MENU_VALUE;
playval = false;
}
else if (btnAutoSpot->value()) {
put_status("Spotting disabled", 3.0);
btnAutoSpot->value(0);
btnAutoSpot->do_callback();
}
}
#endif // USE_SNDFILE
@ -854,6 +860,11 @@ void cb_mnuVisitURL(Fl_Widget*, void* arg)
restoreFocus();
}
void cb_mnuVisitPSKRep(Fl_Widget*, void*)
{
cb_mnuVisitURL(0, (void*)string("http://pskreporter.info/pskmap?").append(progdefaults.myCall).c_str());
}
void html_help( const string &Html)
{
if (!help_dialog)
@ -1085,6 +1096,11 @@ void cbRSID(Fl_Widget *w, void *) {
restoreFocus();
}
void cbAutoSpot(Fl_Widget* w, void*)
{
progStatus.spot_recv = static_cast<Fl_Light_Button*>(w)->value();
}
void toggleRSID()
{
btnRSID->value(0);
@ -1185,8 +1201,21 @@ void qsoSave_cb(Fl_Widget *b, void *)
void cb_QRZ(Fl_Widget *b, void *)
{
CALLSIGNquery();
oktoclear = false;
if (!*inpCall->value())
return;
switch (Fl::event_button()) {
case FL_LEFT_MOUSE:
CALLSIGNquery();
oktoclear = false;
break;
case FL_RIGHT_MOUSE:
if (quick_choice(string("Spot \"").append(inpCall->value()).append("\"?").c_str(), false))
spot_manual(inpCall->value(), inpLoc->value());
break;
default:
break;
}
}
void status_cb(Fl_Widget *b, void *arg)
@ -1497,7 +1526,7 @@ Fl_Menu_Item menu_[] = {
{ make_icon_label("Rig Control", multimedia_player_icon), 0, (Fl_Callback*)cb_mnuRig, 0, 0, _FL_MULTI_LABEL, 0, 14, 0},
{0,0,0,0,0,0,0,0,0},
{" ", 0, 0, 0, FL_MENU_INACTIVE, FL_NORMAL_LABEL, 0, 14, 0},
{" ", 0, 0, 0, FL_MENU_INACTIVE, FL_NORMAL_LABEL, 0, 14, 0},
{"Help", 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL, 0, 14, 0},
#ifndef NDEBUG
// settle the gmfsk vs fldigi argument once and for all
@ -1505,7 +1534,8 @@ Fl_Menu_Item menu_[] = {
#endif
{ make_icon_label("Beginners' Guide", start_here_icon), 0, cb_mnuBeginnersURL, 0, 0, _FL_MULTI_LABEL, 0, 14, 0},
{ make_icon_label("Online documentation...", help_browser_icon), 0, cb_mnuVisitURL, (void *)PACKAGE_DOCS, 0, _FL_MULTI_LABEL, 0, 14, 0},
{ make_icon_label("Fldigi web site...", net_icon), 0, cb_mnuVisitURL, (void *)PACKAGE_HOME, FL_MENU_DIVIDER, _FL_MULTI_LABEL, 0, 14, 0},
{ make_icon_label("Fldigi web site...", net_icon), 0, cb_mnuVisitURL, (void *)PACKAGE_HOME, 0, _FL_MULTI_LABEL, 0, 14, 0},
{ make_icon_label("Reception reports...", pskr_icon), 0, cb_mnuVisitPSKRep, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL, 0, 14, 0},
{ make_icon_label("Command line options", utilities_terminal_icon), 0, cb_mnuCmdLineHelp, 0, 0, _FL_MULTI_LABEL, 0, 14, 0},
{ make_icon_label("Audio device info", audio_card_icon), 0, cb_mnuAudioInfo, 0, 0, _FL_MULTI_LABEL, 0, 14, 0},
{ make_icon_label("Build info", executable_icon), 0, cb_mnuBuildInfo, 0, 0, _FL_MULTI_LABEL, 0, 14, 0},
@ -1752,6 +1782,22 @@ void show_bw(const string& sWidth)
REQ_SYNC(&Fl_ComboBox::put_value, opBW, sWidth.c_str());
}
void show_spot(bool v)
{
if (v) {
mnu->size(btnAutoSpot->x(), mnu->h());
btnAutoSpot->value(progStatus.spot_recv);
btnAutoSpot->show();
}
else {
btnAutoSpot->hide();
btnAutoSpot->value(v);
btnAutoSpot->do_callback();
mnu->size(btnRSID->x(), mnu->h());
}
mnu->redraw();
}
void create_fl_digi_main() {
int pad = 1; //wSpace;
@ -1790,6 +1836,11 @@ void create_fl_digi_main() {
Fl_Tooltip::size(FL_NORMAL_SIZE);
Fl_Tooltip::enable(progdefaults.tooltips);
btnAutoSpot = new Fl_Light_Button(WNOM - 200 - pad, 0, 50, Hmenu, "Spot");
btnAutoSpot->selection_color(FL_GREEN);
btnAutoSpot->callback(cbAutoSpot, 0);
btnAutoSpot->hide();
btnRSID = new Fl_Light_Button(WNOM - 150 - pad, 0, 50, Hmenu, "RSID");
btnRSID->selection_color(FL_GREEN);
btnRSID->callback(cbRSID, 0);
@ -2438,11 +2489,11 @@ void put_rx_char(unsigned int data)
{
static unsigned int last = 0;
const char **asc = ascii;
trx_mode mode = active_modem->get_mode();
if (mailclient || mailserver || arqmode)
asc = ascii2;
if (active_modem->get_mode() == MODE_RTTY ||
active_modem->get_mode() == MODE_CW)
if (mode == MODE_RTTY || mode == MODE_CW)
asc = ascii;
int style = FTextBase::RECV;
@ -2465,7 +2516,15 @@ void put_rx_char(unsigned int data)
WriteARQ(data);
string s = iscntrl(data) ? ascii2[data & 0x7F] : string(1, data);
string s;
if (iscntrl(data))
s = ascii2[data & 0x7F];
else {
s += data;
bool viewer = (mode >= MODE_PSK_FIRST && mode <= MODE_PSK_LAST && dlgViewer && dlgViewer->visible());
if (progStatus.spot_recv && !viewer)
spot_recv(data);
}
if (Maillogfile)
Maillogfile->log_to_file(cLogfile::LOG_RX, s);
@ -2879,3 +2938,9 @@ void qsy(long long rfc, long long fmid)
else
active_modem->set_freq(fmid);
}
bool quick_choice(const char* title, bool sel)
{
Fl_Menu_Item m[] = { { "Confirm" }, { "Cancel" }, { 0 } };
return m->popup(Fl::event_x(), Fl::event_y(), title, m + !sel) == m;
}

Wyświetl plik

@ -1,4 +1,4 @@
// generated by Fast Light User Interface Designer (fluid) version 1.0108
// generated by Fast Light User Interface Designer (fluid) version 1.0109
#ifndef confdialog_h
#define confdialog_h
@ -39,6 +39,16 @@ extern Fl_Value_Input *valCWsweetspot;
extern Fl_Value_Input *valRTTYsweetspot;
extern Fl_Value_Input *valPSKsweetspot;
extern Fl_Check_Button *btnStartAtSweetSpot;
extern Fl_Group *tabSpot;
extern Fl_Check_Button *btnPSKRepAuto;
extern Fl_Check_Button *btnPSKRepLog;
extern Fl_Input *inpPSKRepHost;
extern Fl_Input *inpPSKRepPort;
#include <FL/Fl_Button.H>
extern Fl_Button *btnPSKRepInit;
#include <FL/Fl_Box.H>
extern Fl_Box *boxPSKRepMsg;
extern Fl_Check_Button *btnPSKRepQRG;
extern Fl_Group *tabModems;
extern Fl_Tabs *tabsModems;
extern Fl_Group *tabCW;
@ -126,6 +136,7 @@ extern Fl_Group *tabOperator;
extern Fl_Input *inpMyCallsign;
extern Fl_Input *inpMyName;
extern Fl_Input *inpMyQth;
extern Fl_Input *inpMyAntenna;
extern Fl_Input *inpMyLocator;
extern Fl_Check_Button *btnUseLeadingZeros;
extern Fl_Value_Input *nbrContestStart;
@ -139,7 +150,6 @@ extern Fl_Input *inpQRZusername;
extern Fl_Input *inpQRZuserpassword;
extern Fl_Round_Button *btnQRZsub;
extern Fl_Round_Button *btnHamcall;
#include <FL/Fl_Button.H>
extern Fl_Button *btnQRZpasswordShow;
extern Fl_Group *tabRig;
extern Fl_Round_Button *btnPTT[5];

Wyświetl plik

@ -211,6 +211,7 @@
ELEM_(std::string, myName, "MYNAME", "") \
ELEM_(std::string, myLocator, "MYLOC", "") \
ELEM_(std::string, secText, "SECONDARYTEXT", "") \
ELEM_(std::string, myAntenna, "MYANTENNA", "") \
/* Sound card */ \
ELEM_(int, btnAudioIOis, "AUDIOIO", SND_IDX_PORT) \
ELEM_(std::string, OSSdevice, "OSSDEVICE", "") \
@ -300,7 +301,14 @@
ELEM_(int, rx_msgid, "", 9876) \
ELEM_(int, tx_msgid, "", 6789) \
ELEM_(std::string, arq_address, "", "127.0.0.1") \
ELEM_(std::string, arq_port, "", "3122")
ELEM_(std::string, arq_port, "", "3122") \
/* PSK reporter */ \
ELEM_(bool, pskrep_auto, "PSKREPAUTO", false) \
ELEM_(bool, pskrep_log, "PSKREPLOG", false) \
ELEM_(bool, pskrep_qrg, "PSKREPQRG", false) \
ELEM_(std::string, pskrep_host, "PSKREPHOST", "report.psk.gladstonefamily.net") \
ELEM_(std::string, pskrep_port, "PSKREPPORT", "4739")
// declare the struct
#define ELEM_DECLARE_CONFIGURATION(type_, var_, tag_, ...) type_ var_;

Wyświetl plik

@ -23,6 +23,7 @@
#ifndef _DEBUG_H_
#define _DEBUG_H_
#include "util.h"
class debug
{

Wyświetl plik

@ -155,6 +155,7 @@ extern void set_AFCrange(double val);
extern void show_frequency(long long);
extern void show_mode(const string& mode);
extern void show_bw(const string& sWidth);
extern void show_spot(bool v);
extern void put_WARNstatus(double);
@ -196,4 +197,6 @@ Fl_Color adjust_color(Fl_Color fg, Fl_Color bg);
void qsy(long long rfc, long long fmid = -1LL);
bool quick_choice(const char* title = 0, bool sel = true);
#endif

Wyświetl plik

@ -53,5 +53,6 @@ extern const char *tx_icon[];
extern const char *fldigi_icon[];
extern const char *waterfall_icon[];
extern const char *dice_icon[];
extern const char *pskr_icon[];
#endif // PIXMAPS_H_

Wyświetl plik

@ -0,0 +1,8 @@
#ifndef PSKREP_H_
#define PSKREP_H_
bool pskrep_start(void);
void pskrep_stop();
const char* pskrep_error(void);
#endif // PSKREP_H_

48
src/include/spot.h 100644
Wyświetl plik

@ -0,0 +1,48 @@
// ----------------------------------------------------------------------------
// spot.h
//
// Copyright (C) 2008
// Stelios Bounanos, M0GLD
//
// 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 this program. If not, see <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#ifndef SPOT_H_
#define SPOT_H_
#include <regex.h>
#include <sys/time.h>
#include "globals.h"
typedef void (*spot_recv_cb_t)(int afreq, const char* str, const regmatch_t* sub, size_t len, void* data);
typedef void (*spot_log_cb_t)(const char* call, const char* loc, long long freq,
trx_mode mode, time_t rtime, void* data);
void spot_recv(char c, int decoder = -1, int afreq = 0);
void spot_log(const char* callsign, const char* locator = "", long long freq = 0LL,
trx_mode mode = NUM_MODES, time_t rtime = -1L);
void spot_manual(const char* callsign, const char* locator = "",
long long freq = 0LL, trx_mode mode = NUM_MODES, time_t rtime = -1L);
void spot_register_log(spot_log_cb_t lcb, void* ldata);
void spot_register_manual(spot_log_cb_t mcb, void* mdata);
void spot_register_recv(spot_recv_cb_t rcb, void* rdata, const char* re, int reflags);
void spot_unregister_log(spot_log_cb_t lcb, void* ldata);
void spot_unregister_manual(spot_log_cb_t mcb, void* mdata);
void spot_unregister_recv(spot_recv_cb_t rcb, void* rdata);
#endif // SPOT_H_

Wyświetl plik

@ -1,8 +1,6 @@
#ifndef _status_H
#define _status_H
#include <iostream>
#include <fstream>
#include <string>
#include "globals.h"
@ -40,6 +38,8 @@ struct status {
int scopeW;
int scopeH;
string LastMacroFile;
bool spot_recv;
bool spot_log;
bool bLastStateRead;

Wyświetl plik

@ -120,6 +120,11 @@ void restore_signals(void);
} // extern "C"
#endif
#ifdef __cplusplus
uint32_t simple_hash_data(const unsigned char* buf, unsigned len, uint32_t code = 0);
uint32_t simple_hash_str(const unsigned char* str, uint32_t code = 0);
#endif
#if !defined(NDEBUG) && defined(deprecated__) && defined(__GNUC__)
#include <stdio.h>
#include <string.h>

Wyświetl plik

@ -37,6 +37,9 @@
#include "main.h"
#include "modem.h"
#include "debug.h"
#include "configuration.h"
#include "status.h"
#include "spot.h"
#include <FL/fl_ask.H>
@ -146,6 +149,9 @@ char LOG_MSEPARATOR[2] = {1,0};
int submit_log(void)
{
if (progStatus.spot_log)
spot_log(inpCall->value(), inpLoc->value());
char logdate[32], logtime[32], adifdate[32];
#ifndef __CYGWIN__
int msqid, len;

Wyświetl plik

@ -64,6 +64,7 @@
#include "fileselect.h"
#include "timeops.h"
#include "debug.h"
#include "pskrep.h"
#if USE_HAMLIB
#include "rigclass.h"
@ -285,6 +286,9 @@ int main(int argc, char ** argv)
XML_RPC_Server::start(progdefaults.xmlrpc_address.c_str(), progdefaults.xmlrpc_port.c_str());
#endif
if (!pskrep_start())
LOG_ERROR("Could not start PSK reporter: %s", pskrep_error());
int ret = Fl::run();
arq_close();
@ -293,6 +297,8 @@ int main(int argc, char ** argv)
XML_RPC_Server::stop();
#endif
pskrep_stop();
for (int i = 0; i < NUM_QRUNNER_THREADS; i++) {
cbq[i]->detach();
delete cbq[i];

Wyświetl plik

@ -349,6 +349,7 @@ int configuration::setDefaults()
inpMyName->value(myName.c_str());
inpMyQth->value(myQth.c_str());
inpMyLocator->value(myLocator.c_str());
inpMyAntenna->value(myAntenna.c_str());
UseLeadingZeros = btnUseLeadingZeros->value();
ContestStart = (int)nbrContestStart->value();
ContestDigits = (int)nbrContestDigits->value();

Wyświetl plik

@ -4743,6 +4743,32 @@ const char *tx_icon[] = {
" .. ",
" "};
// pskreporter.info "favicon"
/* XPM */
const char *pskr_icon[] = {
"16 16 3 1",
" c None",
". c #FF0000",
"+ c #FFFF00",
" .. ",
" ... ",
" ..... ",
" .....+.. ",
". ...++++... ",
".. ..+++++. .. ",
"....++++++..+. ",
"..+++++++++++.. ",
"..+++++++.++++. ",
"....+++++..++.. ",
".. ..+++++.... ",
". ..+++++.. ",
" ...+... ",
" .... ",
" .. ",
" . "};
/* XPM */
const char *fldigi_icon[] = {
"48 48 215 2",

Wyświetl plik

@ -67,6 +67,8 @@ status progStatus = {
50, // int scopeW;
50, // int scopeH;
"macros.mdf", // string LastMacroFile;
false, // bool spot_recv
false, // bool spot_log
false // bool bLastStateRead;
};
@ -155,8 +157,11 @@ void status::saveLastState()
spref.set("scope_y", scopeY);
spref.set("scope_w", scopeW);
spref.set("scope_h", scopeH);
spref.set("last_macro_file", LastMacroFile.c_str());
spref.set("spot_recv", spot_recv);
spref.set("spot_log", spot_recv);
}
void status::loadLastState()
@ -216,11 +221,14 @@ void status::loadLastState()
spref.get("scope_y", scopeY, scopeY);
spref.get("scope_w", scopeW, scopeW);
spref.get("scope_h", scopeH, scopeH);
char *defbuffer;
spref.get("last_macro_file", defbuffer, "macros.mdf");
LastMacroFile = defbuffer;
if (defbuffer) free(defbuffer);
spref.get("spot_recv", i, i); spot_recv = i;
spref.get("spot_log", i, i); spot_log = i;
}
void status::initLastState()

Wyświetl plik

@ -109,3 +109,17 @@ void restore_signals(void)
nsig = 0;
pthread_mutex_unlock(&sigmutex);
}
uint32_t simple_hash_data(const unsigned char* buf, size_t len, uint32_t code)
{
for (size_t i = 0; i < len; i++)
code = ((code << 4) | (code >> (32 - 4))) ^ (uint32_t)buf[i];
return code;
}
uint32_t simple_hash_str(const unsigned char* str, uint32_t code)
{
while (*str)
code = ((code << 4) | (code >> (32 - 4))) ^ (uint32_t)*str++;
return code;
}

Wyświetl plik

@ -157,8 +157,8 @@ void viewpsk::rx_bit(int ch, int bit)
if ((shreg[ch] & 3) == 0) {
c = psk_varicode_decode(shreg[ch] >> 2);
shreg[ch] = 0;
if (c == '\n') c = ' ';
if (c >= ' ' && c <= 'z') {
if (c == '\n' || c == '\r') c = ' ';
if (isprint(c)) {
REQ(&viewaddchr, ch, (int)frequency[ch], c);
timeout[ch] = now + progdefaults.VIEWERtimeout;
}

906
src/spot/pskrep.cxx 100644
Wyświetl plik

@ -0,0 +1,906 @@
// ----------------------------------------------------------------------------
// pskrep.cxx
//
// Copyright (C) 2008
// Stelios Bounanos, M0GLD
//
// This is a client for N1DQ's PSK Automatic Propagation Reporter
// (see http://pskreporter.info/). Philip Gladstone, N1DQ, is
// thanked for his helpful explanation of the protocol.
//
//
// 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 this program. If not, see <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include <config.h>
#include <sys/utsname.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <stdint.h>
#include <cstring>
#include <cstdlib>
#include <string>
#include <map>
#include <deque>
#include <vector>
#include <algorithm>
#include <fstream>
#include <FL/Fl.H>
#include "socket.h"
#include "re.h"
#include "debug.h"
#include "util.h"
#include "trx.h"
#include "waterfall.h"
#include "configuration.h"
#include "rigsupport.h"
#include "spot.h"
#include "pskrep.h"
// -------------------------------------------------------------------------------------------------
// The regular expression that matches the spotter's buffer when it calls us.
// It must define at least two capturing groups, the second of which is the
// spotted callsign.
#define CALLSIGN_RE "[[:alnum:]]?[[:alpha:]/]+[[:digit:]]+[[:alnum:]/]+"
#define PSKREP_RE "(de|cq|qrz)[^[:alnum:]/\n]+" "(" CALLSIGN_RE ")" " +(.* +)?\\2[^[:alnum:]]+$"
// Try to flush the report queue every SEND_INTERVAL seconds.
#define SEND_INTERVAL 300
// Ignore reports that are less than DUP_INTERVAL seconds older than
// a previously sent report for the same callsign and frequency band.
// Sent reports are also garbage-collected after DUP_INTERVAL seconds.
#define DUP_INTERVAL 3600
// The first TEMPLATE_THRESHOLD packets will contain the long templates;
// the next TEMPLATE_THRESHOLD packets will include the short templates
#define TEMPLATE_THRESHOLD 3
// Resend short templates every TEMPLATE_INTERVAL seconds
#define TEMPLATE_INTERVAL 1800
// Maximum send size
#define DGRAM_MAX (1500-14-24-8)
#define PSKREP_QUEUE_FILE "pskrqueue.txt"
#define PSKREP_ID_FILE "pskrkey.txt"
// -------------------------------------------------------------------------------------------------
using namespace std;
enum status_t { STATUS_NEW, STATUS_PENDING, STATUS_SENT };
enum rtype_t { PSKREP_AUTO = 1, PSKREP_LOG = 2, PSKREP_MANUAL = 3 };
struct rcpt_report_t
{
rcpt_report_t(trx_mode m = 0, long long f = 0, time_t t = 0,
rtype_t p = PSKREP_AUTO, string loc = "")
: mode(m), freq(f), rtime(t), rtype(p),
status(STATUS_NEW), locator(loc) { }
trx_mode mode;
long long freq;
time_t rtime;
rtype_t rtype;
status_t status;
string locator;
};
enum band_t {
BAND_LMW, BAND_160M, BAND_80M, BAND_75M, BAND_60M, BAND_40M, BAND_30M, BAND_20M,
BAND_17M, BAND_15M, BAND_12M, BAND_10M, BAND_6M, BAND_4M, BAND_2M, BAND_125CM,
BAND_70CM, BAND_33CM, BAND_23CM, BAND_13CM, BAND_9CM, BAND_OTHER
};
// A band_map_t holds a list of reception reports (for a particular callsign and band)
typedef deque<rcpt_report_t> band_map_t;
// A call_map_t holds reception reports for a particular callsign
typedef map<band_t, band_map_t> call_map_t;
// A container of this type holds all reception reports, sorted by callsign and band
typedef map<string, call_map_t> queue_t;
class pskrep_sender
{
public:
pskrep_sender(const string& call, const string& loc, const string& ant,
const string& host_, const string& port_,
const string& long_id_, const string& short_id_);
~pskrep_sender();
bool append(const string& callsign, const band_map_t::value_type& r);
bool send(void);
private:
void write_station_info(void);
void write_preamble(void);
string recv_callsign, recv_locator, recv_antenna;
string host, port;
string long_id, short_id;
static const unsigned char long_station_info_template[];
static const unsigned char short_station_info_template[];
static const unsigned char rcpt_record_template[];
vector<unsigned char> long_station_info;
vector<unsigned char> short_station_info;
uint32_t identifier;
uint32_t sequence_number;
unsigned template_count;
time_t last_template;
Socket* send_socket;
unsigned char* dgram;
size_t dgram_size;
size_t report_offset;
void create_socket(void);
pthread_t resolver_thread;
static void* resolver(void* obj);
static const char hexsym[];
static const string& printhex(const unsigned char* s, size_t len);
static size_t pad(size_t len, size_t mult);
};
class pskrep
{
public:
pskrep(const string& call, const string& loc, const string& ant,
const string& host, const string& port,
const string& long_id, const string& short_id,
bool reg_auto, bool reg_log, bool reg_manual);
~pskrep();
static void recv(int afreq, const char* str, const regmatch_t* calls, size_t len, void* obj);
static void log(const char* call, const char* loc, long long freq, trx_mode mode, time_t rtime, void* obj);
static void manual(const char* call, const char* loc, long long freq, trx_mode mode, time_t rtime, void* obj);
bool progress(void);
static band_t band(long long freq_hz);
static fre_t locator_re;
private:
void append(string call, const char* loc, long long freq, trx_mode mode, time_t rtime, rtype_t rtype);
void gc(void);
void load_queue(void);
void save_queue(void);
static bool not_sent(const band_map_t::value_type& r)
{
return r.status != STATUS_SENT;
}
queue_t queue;
pskrep_sender sender;
};
fre_t pskrep::locator_re("[a-r]{2}[0-9]{2}[a-x]{2}", REG_EXTENDED | REG_NOSUB | REG_ICASE);
#define SHORT_ID_SIZE 4
#define LONG_ID_SIZE 8
// -------------------------------------------------------------------------------------------------
static string error_string;
static bool pskrep_check(void)
{
struct {
const string* var;
const char* msg;
} check[] = {
{ &progdefaults.myCall, "callsign" },
{ &progdefaults.myLocator, "locator" },
{ &progdefaults.myAntenna, "antenna info" },
};
for (size_t i = 0; i < sizeof(check)/sizeof(*check); i++) {
if (check[i].var->empty()) {
error_string.assign("Error: missing ").append(check[i].msg);
return false;
}
}
if (!pskrep::locator_re.match(progdefaults.myLocator.c_str())) {
error_string = "Error: bad IARU locator";
return false;
}
return true;
}
const char* pskrep_error(void)
{
return error_string.c_str();
}
static void pskrep_progress(void* obj)
{
if (reinterpret_cast<pskrep*>(obj)->progress())
Fl::add_timeout(SEND_INTERVAL, pskrep_progress, obj);
else
pskrep_stop();
}
static void pskrep_make_id(string& id, size_t len)
{
id.resize(len);
ifstream f("/dev/urandom");
if (f) {
for (size_t i = 0; i < len; i++)
while ((id[i] = f.get()) != EOF && !isgraph(id[i]));
f.close();
}
else {
unsigned seed = time(NULL);
if (!progdefaults.myCall.empty())
seed ^= simple_hash_str((const unsigned char*)progdefaults.myCall.c_str());
srandom(seed);
for (size_t i = 0; i < len; i++)
while (!isgraph(id[i] = random() % 0x7F));
}
}
static pskrep* pskr = 0;
bool pskrep_start(void)
{
if (pskr)
return true;
else if (!pskrep_check())
return false;
// get identifier
string fname = HomeDir;
fname.append(PSKREP_ID_FILE);
ifstream in(fname.c_str());
string long_id, short_id;
if (in)
in >> long_id >> short_id;
if (!in || in.eof()) {
in.close();
pskrep_make_id(long_id, LONG_ID_SIZE);
pskrep_make_id(short_id, SHORT_ID_SIZE);
ofstream out(fname.c_str());
if (out)
out << long_id << ' ' << short_id << '\n';
else
LOG_ERROR("Could not write identifiers (\"%s\", \"%s\") to %s",
long_id.c_str(), short_id.c_str(), fname.c_str());
}
pskr = new pskrep(progdefaults.myCall, progdefaults.myLocator, progdefaults.myAntenna,
progdefaults.pskrep_host, progdefaults.pskrep_port, long_id, short_id,
progdefaults.pskrep_auto, progdefaults.pskrep_log, true);
Fl::add_timeout(SEND_INTERVAL, pskrep_progress, pskr);
return true;
}
void pskrep_stop(void)
{
Fl::remove_timeout(pskrep_progress, pskr);
delete pskr;
pskr = 0;
}
// -------------------------------------------------------------------------------------------------
pskrep::pskrep(const string& call, const string& loc, const string& ant,
const string& host, const string& port,
const string& long_id, const string& short_id,
bool reg_auto, bool reg_log, bool reg_manual)
: sender(call, loc, ant, host, port, long_id, short_id)
{
if (reg_auto)
spot_register_recv(pskrep::recv, this, PSKREP_RE, REG_EXTENDED | REG_ICASE);
if (reg_log)
spot_register_log(pskrep::log, this);
if (reg_manual)
spot_register_manual(pskrep::manual, this);
load_queue();
}
pskrep::~pskrep()
{
spot_unregister_recv(pskrep::recv, this);
spot_unregister_log(pskrep::log, this);
spot_unregister_manual(pskrep::manual, this);
save_queue();
}
// This function is called by spot_recv() when its buffer matches our PSKREP_RE
void pskrep::recv(int afreq, const char* str, const regmatch_t* calls, size_t len, void* obj)
{
if (unlikely(calls[2].rm_so == -1 || calls[2].rm_eo == -1))
return;
string call(str, calls[2].rm_so, calls[2].rm_eo - calls[2].rm_so);
long long freq = afreq;
if (!wf->USB())
freq = -freq;
freq += wf->rfcarrier();
LOG_DEBUG("Spotted \"%s\" in buffer \"%s\"", call.c_str(), str);
reinterpret_cast<pskrep*>(obj)->append(call.c_str(), "", freq,
active_modem->get_mode(), time(NULL), PSKREP_AUTO);
}
// This function is called by spot_log()
void pskrep::log(const char* call, const char* loc, long long freq, trx_mode mode, time_t rtime, void* obj)
{
reinterpret_cast<pskrep*>(obj)->append(call, loc, freq, mode, rtime, PSKREP_LOG);
}
// This function is called by spot_manual()
void pskrep::manual(const char* call, const char* loc, long long freq, trx_mode mode, time_t rtime, void* obj)
{
reinterpret_cast<pskrep*>(obj)->append(call, loc, freq, mode, rtime, PSKREP_MANUAL);
}
void pskrep::append(string call, const char* loc, long long freq, trx_mode mode, time_t rtime, rtype_t rtype)
{
if (unlikely(call.empty()))
return;
transform(call.begin(), call.end(), call.begin(), static_cast<int (*)(int)>(toupper));
if (!progdefaults.pskrep_qrg)
freq = 0LL;
if (*loc && !locator_re.match(loc))
loc = "";
band_map_t& bandq = queue[call][band(freq)];
if (bandq.empty() || rtime - bandq.back().rtime >= DUP_INTERVAL) { // add new
bandq.push_back(rcpt_report_t(mode, freq, rtime, rtype, loc));
LOG_INFO("Added (call=\"%s\", loc=\"%s\", mode=\"%s\", freq=%lld, time=%ld, type=%u)",
call.c_str(), loc, mode_info[mode].adif_name, freq, rtime, rtype);
save_queue();
}
else if (!bandq.empty()) {
band_map_t::value_type& r = bandq.back();
if (r.status != STATUS_SENT && *loc && r.locator != loc) { // update last
r.locator = loc;
r.rtype = rtype;
LOG_INFO("Updated (call=\"%s\", loc=\"%s\", mode=\"%s\", freq=%lld, time=%ld, type=%u)",
call.c_str(), loc, mode_info[r.mode].adif_name, r.freq, r.rtime, rtype);
save_queue();
}
}
}
// Handle queued reports
bool pskrep::progress(void)
{
if (queue.empty())
return true;
unsigned nrep = 0;
bool sender_full = false;
for (queue_t::iterator i = queue.begin(); i != queue.end(); ++i) {
for (call_map_t::iterator j = i->second.begin(); j != i->second.end(); ++j) {
for (band_map_t::iterator k = j->second.begin(); k != j->second.end(); ++k) {
switch (k->status) {
case STATUS_NEW:
if ((sender_full = !sender.append(i->first, *k)))
goto send_reports;
k->status = STATUS_PENDING;
nrep++;
break;
case STATUS_PENDING: // sent in last cycle
k->status = STATUS_SENT;
default:
break;
}
}
}
}
send_reports:
LOG_INFO("Found %u new report(s)", nrep);
if (nrep) {
if (!sender.send()) {
LOG_ERROR("Sender failed, disabling pskreporter");
return false;
}
return progress();
}
gc();
save_queue();
return true;
}
// Delete sent reports that are older than DUP_INTERVAL seconds
void pskrep::gc(void)
{
time_t threshold = time(NULL) - DUP_INTERVAL;
unsigned rm = 0;
for (queue_t::iterator i = queue.begin(); i != queue.end() ; ) {
for (call_map_t::iterator j = i->second.begin(); j != i->second.end() ; ) {
band_map_t& b = j->second;
band_map_t::iterator k = find_if(b.begin(), b.end(), not_sent);
if (k != b.begin() && k == b.end())
--k;
rm += k - b.begin();
k = b.erase(b.begin(), k);
if (k != b.end() && k->status == STATUS_SENT && k->rtime <= threshold) {
b.erase(k);
rm++;
}
if (b.empty())
i->second.erase(j++);
else
++j;
}
if (i->second.empty())
queue.erase(i++);
else
++i;
}
LOG_DEBUG("Removed %zu sent report(s)", rm);
}
static ostream& operator<<(ostream& out, const rcpt_report_t& r);
static istream& operator>>(istream& in, rcpt_report_t& r);
static ostream& operator<<(ostream& out, const queue_t& q);
static istream& operator>>(istream& in, queue_t& q);
void pskrep::save_queue(void)
{
string fname = HomeDir;
fname.append(PSKREP_QUEUE_FILE);
ofstream out(fname.c_str());
if (out)
out << queue;
else
LOG_ERROR("Could not write %s", fname.c_str());
}
void pskrep::load_queue(void)
{
string fname = HomeDir;
fname.append(PSKREP_QUEUE_FILE);
ifstream in(fname.c_str());
if (!in)
return;
in >> queue;
// restore pending reports as new
for (queue_t::iterator i = queue.begin(); i != queue.end(); ++i)
for (call_map_t::iterator j = i->second.begin(); j != i->second.end(); ++j)
for (band_map_t::iterator k = j->second.begin(); k != j->second.end(); ++k)
if (k->status == STATUS_PENDING)
k->status = STATUS_NEW;
}
band_t pskrep::band(long long freq_hz)
{
switch (freq_hz / 1000000LL) {
case 0: return BAND_LMW;
case 1: return BAND_160M;
case 3: return BAND_80M;
case 4: return BAND_75M;
case 5: return BAND_60M;
case 7: return BAND_40M;
case 10: return BAND_30M;
case 14: return BAND_20M;
case 18: return BAND_17M;
case 21: return BAND_15M;
case 24: return BAND_12M;
case 28 ... 29: return BAND_10M;
case 50 ... 54: return BAND_6M;
case 70 ... 71: return BAND_4M;
case 144 ... 148: return BAND_2M;
case 222 ... 225: return BAND_125CM;
case 420 ... 450: return BAND_70CM;
case 902 ... 928: return BAND_33CM;
case 1240 ... 1325: return BAND_23CM;
case 2300 ... 2450: return BAND_13CM;
case 3300 ... 3500: return BAND_9CM;
}
return BAND_OTHER;
}
// -------------------------------------------------------------------------------------------------
// Text fields must be <= 254 bytes
#define MAX_TEXT_SIZE 254
// Records must be padded to a multiple of 4
#define PAD 4
pskrep_sender::pskrep_sender(const string& call, const string& loc, const string& ant,
const string& host_, const string& port_,
const string& long_id_, const string& short_id_)
: recv_callsign(call, 0, MAX_TEXT_SIZE), recv_locator(loc, 0, MAX_TEXT_SIZE),
recv_antenna(ant, 0, MAX_TEXT_SIZE),
host(host_, 0, MAX_TEXT_SIZE), port(port_, 0, MAX_TEXT_SIZE),
long_id(long_id_, 0, LONG_ID_SIZE), short_id(short_id_, 0, SHORT_ID_SIZE),
sequence_number(0), template_count(2 * TEMPLATE_THRESHOLD), last_template(0),
send_socket(0), dgram_size(0), report_offset(0)
{
create_socket();
dgram = new unsigned char[DGRAM_MAX];
write_station_info();
}
pskrep_sender::~pskrep_sender()
{
delete send_socket;
delete [] dgram;
}
// fldigi uses 0x0219 as the long station info template id (bytes 4,5)
const unsigned char pskrep_sender::long_station_info_template[] = {
0x00, 0x03, 0x00, 0x34, 0x02, 0x19, 0x00, 0x05, 0x00, 0x00,
0x80, 0x02, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // receiverCallsign
0x80, 0x04, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // receiverLocator
0x80, 0x0C, 0x00, 0x08, 0x00, 0x00, 0x76, 0x8F, // persistentIdentifier
0x80, 0x08, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // decoderSoftware
0x80, 0x09, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // anntennaInformation
0x00, 0x00
};
// fldigi uses 0x0218 as the short station info template id (bytes 4,5)
const unsigned char pskrep_sender::short_station_info_template[] = {
0x00, 0x03, 0x00, 0x24, 0x02, 0x18, 0x00, 0x03, 0x00, 0x00,
0x80, 0x02, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // receiverCallsign
0x80, 0x04, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // receiverLocator
0x80, 0x0C, 0x00, 0x08, 0x00, 0x00, 0x76, 0x8F, // persistentIdentifier
0x00, 0x00
};
void pskrep_sender::write_station_info(void)
{
char prog_info[MAX_TEXT_SIZE];
size_t prog_len;
prog_len = snprintf(prog_info, sizeof(prog_info), "%s", PACKAGE_TARNAME "-" PACKAGE_VERSION);
prog_len = MIN(prog_len, sizeof(prog_info));
struct utsname u;
if (uname(&u) != -1) {
prog_len += snprintf(prog_info+prog_len, sizeof(prog_info)-prog_len, "/%s-%s", u.sysname, u.machine);
prog_len = MIN(prog_len, sizeof(prog_info));
}
size_t call_len = recv_callsign.length(),
loc_len = recv_locator.length(),
ant_len = recv_antenna.length();
size_t long_len, short_len;
// Long station info length
long_len = 4 + // 4-byte header
1 + call_len + // 1-byte call length + call string length
1 + loc_len + // 1-byte loc length + loc string length
8 + // 8-byte identifier
1 + prog_len + // 1-byte prog length + prog string length
1 + ant_len; // 1-byte ant length + ant string length
long_len = pad(long_len, PAD);
// Short station info length
short_len = 4 + // 4-byte header
1 + call_len + // 1-byte call length + call string length
1 + loc_len + // 1-byte loc length + loc string length
8; // 8-byte identifier
short_len = pad(short_len, PAD);
long_station_info.resize(long_len);
short_station_info.resize(short_len);
unsigned char* p;
size_t npad;
// Write the long station info
p = &long_station_info[0];
// header
memcpy(p, long_station_info_template + 4, 2); p += 2;
*reinterpret_cast<uint16_t*>(p) = htons(long_len); p += sizeof(uint16_t);
// call
*p++ = call_len; memcpy(p, recv_callsign.data(), call_len); p += call_len;
// locator
*p++ = loc_len; memcpy(p, recv_locator.data(), loc_len); p += loc_len;
// identifier
memcpy(p, long_id.data(), LONG_ID_SIZE); p += LONG_ID_SIZE;
// program
*p++ = prog_len; memcpy(p, prog_info, prog_len); p += prog_len;
// antenna
*p++ = ant_len; memcpy(p, recv_antenna.data(), ant_len); p += ant_len;
// pad
npad = &long_station_info[0] + long_len - p;
if (npad)
memset(p, 0, npad);
LOG_DEBUG("long_station_info=\"%s\"", printhex(&long_station_info[0], long_len).c_str());
// Write the short station info
p = &short_station_info[0];
// header
memcpy(p, short_station_info_template + 4, 2); p += 2;
*reinterpret_cast<uint16_t*>(p) = htons(short_len); p += sizeof(uint16_t);
// call
*p++ = call_len; memcpy(p, recv_callsign.data(), call_len); p += call_len;
// locator
*p++ = loc_len; memcpy(p, recv_locator.data(), loc_len); p += loc_len;
// identifier
memcpy(p, long_id.data(), LONG_ID_SIZE); p += LONG_ID_SIZE;
// pad
npad = &short_station_info[0] + short_len - p;
if (npad)
memset(p, 0, npad);
LOG_DEBUG("short_station_info=\"%s\"", printhex(&short_station_info[0], short_len).c_str());
}
// fldigi uses 0x022C as the reception record template id (bytes 4,5)
const unsigned char pskrep_sender::rcpt_record_template[] = {
0x00, 0x02, 0x00, 0x34, 0x02, 0x2C, 0x00, 0x06,
0x80, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // senderCallsign
0x00, 0x96, 0x00, 0x04, // flowStartSeconds
0x80, 0x05, 0x00, 0x04, 0x00, 0x00, 0x76, 0x8F, // frequency
0x80, 0x0A, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // mode (adif string)
0x80, 0x03, 0xff, 0xff, 0x00, 0x00, 0x76, 0x8F, // senderLocator (if known)
0x80, 0x0B, 0x00, 0x01, 0x00, 0x00, 0x76, 0x8F // flags (informationSource)
};
void pskrep_sender::write_preamble(void)
{
time_t now = time(NULL);
unsigned char* p = dgram;
// header
*p++ = 0x00; *p++ = 0x0A; /* length written later */ p += 2;
/* time written later */ p += sizeof(uint32_t);
*reinterpret_cast<uint32_t*>(p) = htonl(sequence_number); p += sizeof(uint32_t);
memcpy(p, short_id.data(), SHORT_ID_SIZE); p += SHORT_ID_SIZE;
const unsigned char* station_info_template;
size_t tlen;
vector<unsigned char>* station_info;
if (template_count == 0 && now - last_template >= TEMPLATE_INTERVAL)
template_count = TEMPLATE_THRESHOLD;
if (template_count > TEMPLATE_THRESHOLD) {
station_info_template = long_station_info_template;
tlen = sizeof(long_station_info_template);
station_info = &long_station_info;
}
else if (template_count >= 0) {
station_info_template = short_station_info_template;
tlen = sizeof(short_station_info_template);
station_info = &short_station_info;
}
if (template_count > 0) {
memcpy(p, rcpt_record_template, sizeof(rcpt_record_template)); p += sizeof(rcpt_record_template);
memcpy(p, station_info_template, tlen); p += tlen;
template_count--;
last_template = now;
}
// station info record
memcpy(p, &(*station_info)[0], station_info->size()); p += station_info->size();
report_offset = p - dgram;
// write report record header
memcpy(p, rcpt_record_template + 4, 2); p += 2; /* length written later */ p += sizeof(uint16_t);
dgram_size = p - dgram;
}
bool pskrep_sender::append(const string& callsign, const band_map_t::value_type& r)
{
if (dgram_size == 0)
write_preamble();
size_t call_len = callsign.length();
call_len = MIN(MAX_TEXT_SIZE, call_len);
const char* mode = mode_info[r.mode].adif_name;
size_t mode_len = strlen(mode);
mode_len = MIN(MAX_TEXT_SIZE, mode_len);
size_t loc_len = MIN(MAX_TEXT_SIZE, r.locator.length());
// call_len + call + time + freq + mode_len + mode + loc_len + loc + info
size_t rlen = 1 + call_len + 4 + 4 + 1 + mode_len + 1 + loc_len + 1;
if (pad(rlen, PAD) + dgram_size > DGRAM_MAX) // datagram full
return false;
LOG_INFO("Appending report (call=%s mode=%s freq=%lld time=%ld type=%u)",
callsign.c_str(), mode_info[r.mode].adif_name, r.freq, r.rtime, r.rtype);
unsigned char* start = dgram + dgram_size;
unsigned char* p = start;
// call
*p++ = call_len; memcpy(p, callsign.data(), call_len); p += call_len;
// 4-byte reception time
*reinterpret_cast<uint32_t*>(p) = htonl(r.rtime); p += sizeof(uint32_t);
// 4-byte freq
*reinterpret_cast<uint32_t*>(p) = htonl(r.freq); p += sizeof(uint32_t);
// mode
*p++ = mode_len; memcpy(p, mode, mode_len); p += mode_len;
// locator
*p++ = loc_len; memcpy(p, r.locator.data(), loc_len); p += loc_len;
// info source
*p++ = r.rtype;
LOG_DEBUG(" \"%s\"", printhex(start, p - start).c_str());
dgram_size += rlen;
return true;
}
void* pskrep_sender::resolver(void* obj)
{
pskrep_sender* s = reinterpret_cast<pskrep_sender*>(obj);
try {
s->send_socket = new Socket(Address(s->host.c_str(), s->port.c_str(), "udp"));
s->send_socket->connect();
}
catch (const SocketException& e) {
LOG_ERROR("Could not resolve %s: %s", s->host.c_str(), e.what());
}
return NULL;
}
void pskrep_sender::create_socket(void)
{
if (pthread_create(&resolver_thread, NULL, resolver, this) != 0) {
LOG_PERROR("pthread_create");
return;
}
}
bool pskrep_sender::send(void)
{
if (!send_socket)
return false;
// empty dgram or no reports (shouldn't happen)
if (dgram_size == 0 || dgram_size == report_offset + 4) {
LOG_DEBUG("Not sending empty dgram: %zu %zu", dgram_size, report_offset);
return false;
}
// Finish writing the report record:
// do we need padding?
size_t npad = (dgram_size - report_offset) % PAD;
if (npad) {
npad = PAD - npad;
memset(dgram + dgram_size, 0x0, npad);
dgram_size += npad;
}
// write length
*reinterpret_cast<uint16_t*>(dgram + report_offset + 2) = htons(dgram_size - report_offset);
// finish writing the datagram
*reinterpret_cast<uint16_t*>(dgram + 2) = htons(dgram_size);
*reinterpret_cast<uint32_t*>(dgram + 4) = htonl(time(NULL));
bool ret;
LOG_DEBUG("Sending datagram (%zu): \"%s\"", dgram_size, printhex(dgram, dgram_size).c_str());
try {
if ((size_t)send_socket->send(dgram, dgram_size) != dgram_size)
throw SocketException("short write");
ret = true;
}
catch (const SocketException& e) {
LOG_ERROR("Could not send datagram to %s port %s: %s", host.c_str(), port.c_str(), e.what());
ret = false;
}
// increment this regardless of any errors
sequence_number++;
dgram_size = 0;
return ret;
}
// Pad len to a multiple of mult
size_t pskrep_sender::pad(size_t len, size_t mult)
{
size_t r = len % mult;
return r ? len + mult - r : len;
}
const char pskrep_sender::hexsym[] = "0123456789ABCDEF";
const string& pskrep_sender::printhex(const unsigned char* s, size_t len)
{
static string hex;
if (unlikely(len == 0))
return hex.erase();
hex.resize(len * 3 - 1);
string::iterator i = hex.begin();
size_t j;
for (j = 0; j < len-1; j++) {
*i++ = hexsym[s[j] >> 4];
*i++ = hexsym[s[j] & 0xF];
*i++ = ' ';
}
*i++ = hexsym[s[j] >> 4];
*i = hexsym[s[j] & 0xF];
return hex;
}
// -------------------------------------------------------------------------------------------------
static istream& operator>>(istream& in, rtype_t& t)
{
int i;
in >> i;
t = static_cast<rtype_t>(i);
return in;
}
static istream& operator>>(istream& in, status_t& t)
{
int i;
in >> i;
t = static_cast<status_t>(i);
return in;
}
static istream& operator>>(istream& in, rcpt_report_t& r)
{
in >> r.mode >> r.freq >> r.rtime >> r.rtype >> r.status >> r.locator;
if (*r.locator.c_str() == '?') r.locator.clear();
return in;
}
static ostream& operator<<(ostream& out, const rcpt_report_t& r)
{
return out << r.mode << ' ' << r.freq << ' ' << r.rtime << ' '
<< r.rtype << ' ' << r.status << ' ' << (r.locator.empty() ? "?" : r.locator);
}
static ostream& operator<<(ostream& out, const queue_t& q)
{
for (queue_t::const_iterator i = q.begin(); i != q.end(); ++i)
for (call_map_t::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
for (band_map_t::const_iterator k = j->second.begin(); k != j->second.end(); ++k)
out << *k << " " << j->first << " " << i->first << '\n';
return out;
}
static istream& operator>>(istream& in, queue_t& q)
{
rcpt_report_t rep;
int band;
string call;
while (in >> rep >> band >> call)
q[call][static_cast<band_t>(band)].push_back(rep);
return in;
}

203
src/spot/spot.cxx 100644
Wyświetl plik

@ -0,0 +1,203 @@
// ----------------------------------------------------------------------------
// spot.cxx
//
// Copyright (C) 2008
// Stelios Bounanos, M0GLD
//
// 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 this program. If not, see <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include <config.h>
#include <vector>
#include <map>
#include "trx.h"
#include "globals.h"
#include "re.h"
#include "fl_digi.h"
#include "debug.h"
#include "spot.h"
#define SEARCHLEN 32
#define DECBUFSIZE 8 * SEARCHLEN
using namespace std;
struct callback_t {
void* data;
spot_log_cb_t lcb;
spot_log_cb_t mcb;
spot_recv_cb_t rcb;
fre_t* re;
};
static map<int, string> buffers;
typedef vector<callback_t> cblist_t;
static cblist_t cblist;
void spot_recv(char c, int decoder, int afreq)
{
static trx_mode last_mode = NUM_MODES + 1;
switch (decoder) {
case -1: // mode without multiple decoders
decoder = active_modem->get_mode();
if (last_mode > NUM_MODES)
last_mode = decoder;
else if (last_mode != decoder) {
buffers.clear();
last_mode = decoder;
}
break;
default:
if (last_mode > NUM_MODES)
last_mode = active_modem->get_mode();
else if (last_mode != active_modem->get_mode()) {
buffers.clear();
last_mode = active_modem->get_mode();
}
break;
}
if (afreq == 0)
afreq = active_modem->get_freq();
string& buf = buffers[decoder];
buf.reserve(DECBUFSIZE);
buf += c;
string::size_type n = buf.length();
if (n == DECBUFSIZE)
buf.erase(0, DECBUFSIZE - SEARCHLEN);
const char* search = buf.c_str() + (n > SEARCHLEN ? n - SEARCHLEN : 0);
for (cblist_t::iterator i = cblist.begin(); i != cblist.end(); ++i) {
if (i->rcb && unlikely(i->re->match(search))) {
const vector<regmatch_t>& m = i->re->suboff();
if (m.empty())
i->rcb(afreq, search, NULL, 0, i->data);
else
i->rcb(afreq, search, &m[0], m.size(), i->data);
}
}
}
static void get_log_details(long long& freq, trx_mode& mode, time_t& rtime)
{
if (mode == NUM_MODES)
mode = active_modem->get_mode();
if (mode >= MODE_WWV)
return;
if (freq == 0LL)
freq = active_modem->get_freq();
if (!wf->USB())
freq = -freq;
freq += wf->rfcarrier();
if (rtime == -1L)
rtime = time(NULL);
}
void spot_log(const char* callsign, const char* locator, long long freq, trx_mode mode, time_t rtime)
{
get_log_details(freq, mode, rtime);
for (cblist_t::const_iterator i = cblist.begin(); i != cblist.end(); ++i)
if (i->lcb)
i->lcb(callsign, locator, freq, mode, rtime, i->data);
}
void spot_manual(const char* callsign, const char* locator, long long freq, trx_mode mode, time_t rtime)
{
get_log_details(freq, mode, rtime);
for (cblist_t::const_iterator i = cblist.begin(); i != cblist.end(); ++i)
if (i->mcb)
i->mcb(callsign, locator, freq, mode, rtime, i->data);
}
//
// A callback of type spot_log_cb_t is registered with a data argument.
// The callback is invoked every time a QSO is logged.
//
void spot_register_log(spot_log_cb_t lcb, void* ldata)
{
callback_t c = { ldata, lcb, 0, 0, 0 };
cblist.push_back(c);
}
//
// A callback of type spot_log_cb_t is registered with a data argument.
// The callback is invoked every time the user manually spots a callsign.
//
void spot_register_manual(spot_log_cb_t mcb, void* mdata)
{
callback_t c = { mdata, 0, mcb, 0, 0 };
cblist.push_back(c);
}
//
// A callback of type spot_recv_cb_t is registered with a regular
// expression (RE, RE_flags). If the RE matches the spotter's search
// buffer, the callback is invoked with offsets into the search buffer
// indicating substring matches, if the RE defines any, and with its
// data argument. The offset format is described in regexec(3). The
// buffer and offsets are only valid during that particular invocation.
// Clients should use anchoring to avoid repeated calls.
//
void spot_register_recv(spot_recv_cb_t rcb, void* rdata, const char* re, int reflags)
{
callback_t c = { rdata, 0, 0, rcb, new fre_t(re, reflags) };
cblist.push_back(c);
show_spot(true);
}
void spot_unregister_log(spot_log_cb_t lcb, void* ldata)
{
for (cblist_t::reverse_iterator ri = cblist.rbegin(); ri != cblist.rend(); ++ri) {
if (lcb == ri->lcb && ldata == ri->data) {
cblist.erase((++ri).base());
break;
}
}
}
void spot_unregister_manual(spot_log_cb_t mcb, void* mdata)
{
for (cblist_t::reverse_iterator ri = cblist.rbegin(); ri != cblist.rend(); ++ri) {
if (mcb == ri->mcb && mdata == ri->data) {
cblist.erase((++ri).base());
break;
}
}
}
void spot_unregister_recv(spot_recv_cb_t rcb, void* rdata)
{
cblist_t::reverse_iterator ri;
for (ri = cblist.rbegin(); ri != cblist.rend(); ++ri) {
if (rcb == ri->rcb && rdata == ri->data) {
cblist.erase((++ri).base());
break;
}
}
for (ri = cblist.rbegin(); ri != cblist.rend(); ++ri)
if (ri->rcb) break;
show_spot(ri != cblist.rend());
}