kopia lustrzana https://github.com/jamescoxon/dl-fldigi
Add spotting
* Add hooks to the logger and received text handlers * Add support for N1DQ's PSK Automatic Propagation Reporterpull/2/head
rodzic
c8a6ae11e4
commit
690daadf50
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#ifndef _DEBUG_H_
|
||||
#define _DEBUG_H_
|
||||
|
||||
#include "util.h"
|
||||
|
||||
class debug
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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_
|
||||
|
|
|
|||
|
|
@ -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_
|
||||
|
|
@ -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_
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
Ładowanie…
Reference in New Issue