dl-fldigi/src/spot/spot.cxx

250 wiersze
6.7 KiB
C++

// ----------------------------------------------------------------------------
// spot.cxx
//
// Copyright (C) 2008-2009
// 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 <list>
#if HAVE_STD_HASH
# include <unordered_map>
#elif HAVE_STD_TR1_HASH
# include <tr1/unordered_map>
#else
# error "No std::hash or std::tr1::hash support"
#endif
#include "trx.h"
#include "globals.h"
#include "re.h"
#include "fl_digi.h"
#include "debug.h"
#include "spot.h"
// the number of characters that we match our REs against
#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;
};
typedef list<callback_t> cblist_t;
struct fre_hash : std::unary_function<const fre_t*, size_t>
{
size_t operator()(const fre_t* r) const { return r->hash(); }
};
struct fre_comp : std::unary_function<const fre_t*, bool>
{
size_t operator()(const fre_t* l, const fre_t* r) const { return *l == *r; }
};
typedef list<callback_t*> callback_p_list_t;
#if HAVE_STD_HASH
typedef std::unordered_map<fre_t*, callback_p_list_t, fre_hash, fre_comp> rcblist_t;
static std::unordered_map<int, string> buffers;
#elif HAVE_STD_TR1_HASH
typedef tr1::unordered_map<fre_t*, callback_p_list_t, fre_hash, fre_comp> rcblist_t;
static tr1::unordered_map<int, string> buffers;
#endif
static cblist_t cblist;
static rcblist_t rcblist;
void spot_recv(char c, int decoder, int afreq, int md)
{
static trx_mode last_mode = NUM_MODES + 1;
if (decoder == -1) { // mode without multiple decoders
decoder = active_modem->get_mode();
if (last_mode != active_modem->get_mode()) {
buffers.clear();
last_mode = active_modem->get_mode();
}
} else if (last_mode != md) {
buffers.clear();
last_mode = md;
}
if (afreq == 0)
afreq = active_modem->get_freq();
string& buf = buffers[decoder];
if (unlikely(buf.capacity() < DECBUFSIZE))
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 (rcblist_t::iterator i = rcblist.begin(); i != rcblist.end(); ++i) {
if (unlikely(i->first->match(search))) {
const vector<regmatch_t>& m = i->first->suboff();
for (list<callback_t*>::iterator j = i->second.begin();
j != i->second.end() && (*j)->rcb; ++j) {
if (m.empty())
(*j)->rcb(last_mode, afreq, search, NULL, 0, (*j)->data);
else
(*j)->rcb(last_mode, afreq, search, &m[0], m.size(), (*j)->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 };
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 };
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 };
cblist.push_back(c);
fre_t* fre = new fre_t(re, reflags);
rcblist_t::iterator i = rcblist.find(fre);
if (i != rcblist.end()) {
i->second.push_back(&cblist.back());
delete fre;
}
else
rcblist[fre].push_back(&cblist.back());
show_spot(true);
}
void spot_unregister_log(spot_log_cb_t lcb, const void* ldata)
{
for (cblist_t::iterator i = cblist.begin(); i != cblist.end(); ++i) {
if (lcb == i->lcb && ldata == i->data) {
cblist.erase(i);
break;
}
}
}
void spot_unregister_manual(spot_log_cb_t mcb, const void* mdata)
{
for (cblist_t::iterator i = cblist.begin(); i != cblist.end(); ++i) {
if (mcb == i->mcb && mdata == i->data) {
cblist.erase(i);
break;
}
}
}
void spot_unregister_recv(spot_recv_cb_t rcb, const void* rdata)
{
cblist_t::iterator i;
callback_t* p = 0;
for (i = cblist.begin(); i != cblist.end(); ++i) {
if (rcb == i->rcb && rdata == i->data) {
p = &*i;
cblist.erase(i);
break;
}
}
if (!p)
return;
// remove pointer from rcblist
for (rcblist_t::iterator j = rcblist.begin(); j != rcblist.end(); ++j) {
for (list<callback_t*>::iterator k = j->second.begin(); k != j->second.end(); ++k) {
if (*k == p) {
j->second.erase(k);
if (j->second.empty()) {
delete j->first;
rcblist.erase(j);
}
goto out;
}
}
}
out:
for (i = cblist.begin(); i != cblist.end(); ++i)
if (i->rcb) break;
show_spot(i != cblist.end());
}