Subscribe to config changes now possible

The receiver configuration variables SQL_HANGTIME, SQL_EXTENDED_HANGTIME
and SQL_EXTENDED_HANGTIME_THRESH are now settable from within a C++
module for example.

This is made possible by the addition of the Config::valueUpdated signal
that is emitted when the Config::setValue function is called.
This commit is contained in:
Tobias Blomberg 2020-01-19 21:46:54 +01:00
parent 2c20d274ec
commit 92ef3e45f2
11 changed files with 133 additions and 65 deletions

View File

@ -18,6 +18,9 @@
* AudioSelector::autoSelectEnabled() can now be called with a constant source.
* Add a signal to the Config class so that one can subscribe to changes in the
configuration.
1.6.0 -- 01 Sep 2019

View File

@ -125,7 +125,6 @@ using namespace Async;
Config::~Config(void)
{
//fclose(file);
} /* Config::~Config */
@ -133,19 +132,19 @@ bool Config::open(const string& name)
{
errno = 0;
file = fopen(name.c_str(), "r");
FILE *file = fopen(name.c_str(), "r");
if (file == NULL)
{
return false;
}
bool success = parseCfgFile();
bool success = parseCfgFile(file);
fclose(file);
file = NULL;
return success;
} /* Config::open */
@ -186,7 +185,6 @@ const string &Config::getValue(const string& section, const string& tag) const
}
return val_it->second;
} /* Config::getValue */
@ -227,10 +225,10 @@ void Config::setValue(const std::string& section, const std::string& tag,
{
Values &values = sections[section];
values[tag] = value;
valueUpdated(section, tag);
} /* Config::setValue */
/****************************************************************************
*
* Protected member functions
@ -275,7 +273,7 @@ void Config::setValue(const std::string& section, const std::string& tag,
* Bugs:
*----------------------------------------------------------------------------
*/
bool Config::parseCfgFile(void)
bool Config::parseCfgFile(FILE *file)
{
char line[16384];
int line_no = 0;

View File

@ -46,6 +46,7 @@ An example of how to use the Config class
****************************************************************************/
#include <stdio.h>
#include <sigc++/sigc++.h>
#include <string>
#include <map>
@ -139,7 +140,7 @@ class Config
/**
* @brief Default constuctor
*/
Config(void) : file(NULL) {}
Config(void) {}
/**
* @brief Destructor
@ -413,13 +414,26 @@ class Config
* is created.
* Note that this function will not write anything back to the
* associated configuration file. It will only set the value in memory.
*
* The valueUpdated signal will be emitted so that subscribers can get
* notified when the value of a configuration variable is changed.
*/
void setValue(const std::string& section, const std::string& tag,
const std::string& value);
/**
* @brief A signal that is emitted when a config value is updated
* @param section The config section of the update
* @param tag The tag (variable name) of the update
*
* This signal is emitted whenever a configuration variable is changed
* by calling the setValue function.
*/
sigc::signal<void, const std::string&, const std::string&> valueUpdated;
private:
typedef std::map<std::string, std::string> Values;
typedef std::map<std::string, Values> Sections;
typedef std::map<std::string, std::string> Values;
typedef std::map<std::string, Values> Sections;
struct csv_whitespace : std::ctype<char>
{
static const mask* make_table(void)
@ -434,10 +448,11 @@ class Config
: std::ctype<char>(make_table(), false, refs) {}
};
FILE *file;
Sections sections;
bool parseCfgFile(void);
//Config(const Config&);
//Config& operator=(const Config&);
bool parseCfgFile(FILE *file);
char *trimSpaces(char *line);
char *parseSection(char *line);
char *parseDelimitedString(char *str, char begin_tok, char end_tok);

View File

@ -37,6 +37,10 @@
* ModuleEchoLink: Now possible to silently ignore all incoming connections
using the DROP_ALL_INCOMING configuration variable.
* The receiver configuration variables SQL_HANGTIME, SQL_EXTENDED_HANGTIME and
SQL_EXTENDED_HANGTIME_THRESH are now settable from within a C++ module for
example.
1.7.0 -- 01 Sep 2019

View File

@ -166,12 +166,12 @@ class NetTrxAdapter
private:
static std::map<std::string, NetTrxAdapter*> net_trx_adapters;
Uplink *ul;
Async::Config cfg;
TxAdapter *txa1;
TxAdapter *txa2;
RxAdapter *rxa1;
std::string net_uplink_name;
Uplink* ul;
Async::Config& cfg;
TxAdapter* txa1;
TxAdapter* txa2;
RxAdapter* rxa1;
std::string net_uplink_name;
NetTrxAdapter(const NetTrxAdapter&);
NetTrxAdapter& operator=(const NetTrxAdapter&);

View File

@ -191,7 +191,7 @@ class DtmfDecoder : public sigc::trackable, public Async::AudioSink
private:
static const unsigned DEFAULT_HANGTIME = 0;
Async::Config m_cfg;
Async::Config& m_cfg;
std::string m_name;
unsigned m_hangtime;

View File

@ -228,6 +228,12 @@ class AudioUdpSink : public UdpSocket, public AudioSink
*
****************************************************************************/
namespace {
typedef const char *CfgTag;
CfgTag CFG_SQL_HANGTIME = "SQL_HANGTIME";
CfgTag CFG_SQL_EXTENDED_HANGTIME = "SQL_EXTENDED_HANGTIME";
CfgTag CFG_SQL_EXTENDED_HANGTIME_THRESH = "SQL_EXTENDED_HANGTIME_THRESH";
};
/****************************************************************************
@ -237,7 +243,7 @@ class AudioUdpSink : public UdpSocket, public AudioSink
****************************************************************************/
LocalRxBase::LocalRxBase(Config &cfg, const std::string& name)
: Rx(cfg, name), cfg(cfg), mute_state(MUTE_ALL),
: Rx(cfg, name), mute_state(MUTE_ALL),
squelch_det(0), siglevdet(0), /* siglev_offset(0.0), siglev_slope(1.0), */
tone_dets(0), sql_valve(0), delay(0), sql_tail_elim(0),
preamp_gain(0), mute_valve(0), sql_hangtime(0), sql_extended_hangtime(0),
@ -267,25 +273,25 @@ bool LocalRxBase::initialize(void)
}
bool deemphasis = false;
cfg.getValue(name(), "DEEMPHASIS", deemphasis);
cfg().getValue(name(), "DEEMPHASIS", deemphasis);
int delay_line_len = 0;
bool mute_1750 = false;
if (cfg.getValue(name(), "1750_MUTING", mute_1750))
if (cfg().getValue(name(), "1750_MUTING", mute_1750))
{
delay_line_len = max(delay_line_len, TONE_1750_MUTING_PRE);
}
cfg.getValue(name(), "SQL_TAIL_ELIM", sql_tail_elim);
cfg().getValue(name(), "SQL_TAIL_ELIM", sql_tail_elim);
if (sql_tail_elim > 0)
{
delay_line_len = max(delay_line_len, sql_tail_elim);
}
cfg.getValue(name(), "PREAMP", preamp_gain);
cfg().getValue(name(), "PREAMP", preamp_gain);
bool peak_meter = false;
cfg.getValue(name(), "PEAK_METER", peak_meter);
cfg().getValue(name(), "PEAK_METER", peak_meter);
// Get the audio source object
AudioSource *prev_src = audioSource();
@ -298,7 +304,7 @@ bool LocalRxBase::initialize(void)
prev_src = input_fifo;
SvxLink::SepPair<string, uint16_t> raw_audio_fwd_dest;
if (cfg.getValue(name(), "RAW_AUDIO_UDP_DEST", raw_audio_fwd_dest))
if (cfg().getValue(name(), "RAW_AUDIO_UDP_DEST", raw_audio_fwd_dest))
{
AudioSplitter *raw_audio_splitter = new AudioSplitter;
prev_src->registerSink(raw_audio_splitter, true);
@ -348,9 +354,9 @@ bool LocalRxBase::initialize(void)
prev_src->registerSink(siglevdet_splitter, true);
// Create the signal level detector
siglevdet = SigLevDetFactoryBase::createNamedSigLevDet(cfg, name());
siglevdet = SigLevDetFactoryBase::createNamedSigLevDet(cfg(), name());
if ((siglevdet == 0) ||
(!siglevdet->initialize(cfg, name(), INTERNAL_SAMPLE_RATE)))
(!siglevdet->initialize(cfg(), name(), INTERNAL_SAMPLE_RATE)))
{
cout << "*** ERROR: Could not initialize the signal level detector for "
<< "receiver " << name() << endl;
@ -404,7 +410,7 @@ bool LocalRxBase::initialize(void)
// Create the configured squelch detector and initialize it
string sql_det_str;
if (!cfg.getValue(name(), "SQL_DET", sql_det_str))
if (!cfg().getValue(name(), "SQL_DET", sql_det_str))
{
cerr << "*** ERROR: Config variable " << name() << "/SQL_DET not set\n";
return false;
@ -459,7 +465,7 @@ bool LocalRxBase::initialize(void)
return false;
}
if (!squelch_det->initialize(cfg, name()))
if (!squelch_det->initialize(cfg(), name()))
{
cerr << "*** ERROR: Squelch detector initialization failed for RX \""
<< name() << "\"\n";
@ -471,13 +477,13 @@ bool LocalRxBase::initialize(void)
readyStateChanged.connect(mem_fun(*this, &LocalRxBase::rxReadyStateChanged));
if (cfg.getValue(name(), "SQL_HANGTIME", sql_hangtime))
if (cfg().getValue(name(), CFG_SQL_HANGTIME, sql_hangtime))
{
squelch_det->setHangtime(sql_hangtime);
}
cfg.getValue(name(), "SQL_EXTENDED_HANGTIME", sql_extended_hangtime);
cfg.getValue(name(), "SQL_EXTENDED_HANGTIME_THRESH",
sql_extended_hangtime_thresh);
cfg().getValue(name(), CFG_SQL_EXTENDED_HANGTIME, sql_extended_hangtime);
cfg().getValue(name(), CFG_SQL_EXTENDED_HANGTIME_THRESH,
sql_extended_hangtime_thresh);
squelch_det->squelchOpen.connect(mem_fun(*this, &LocalRxBase::onSquelchOpen));
fullband_splitter->addSink(squelch_det, true);
@ -488,16 +494,16 @@ bool LocalRxBase::initialize(void)
// Set up out of band AFSK demodulator if configured
float voice_gain = 0.0f;
bool ob_afsk_enable = false;
if (cfg.getValue(name(), "OB_AFSK_ENABLE", ob_afsk_enable) && ob_afsk_enable)
if (cfg().getValue(name(), "OB_AFSK_ENABLE", ob_afsk_enable) && ob_afsk_enable)
{
unsigned fc = 5500;
//cfg.getValue(name(), "OB_AFSK_CENTER_FQ", fc);
//cfg().getValue(name(), "OB_AFSK_CENTER_FQ", fc);
unsigned shift = 170;
//cfg.getValue(name(), "OB_AFSK_SHIFT", shift);
//cfg().getValue(name(), "OB_AFSK_SHIFT", shift);
unsigned baudrate = 300;
//cfg.getValue(name(), "OB_AFSK_BAUDRATE", baudrate);
//cfg().getValue(name(), "OB_AFSK_BAUDRATE", baudrate);
voice_gain = 6.0f;
cfg.getValue(name(), "OB_AFSK_VOICE_GAIN", voice_gain);
cfg().getValue(name(), "OB_AFSK_VOICE_GAIN", voice_gain);
// Frequency sampling filter with passband center 5500Hz, about 400Hz
// wide and about 40dB stop band attenuation
@ -532,14 +538,14 @@ bool LocalRxBase::initialize(void)
}
bool ib_afsk_enable = false;
if (cfg.getValue(name(), "IB_AFSK_ENABLE", ib_afsk_enable) && ib_afsk_enable)
if (cfg().getValue(name(), "IB_AFSK_ENABLE", ib_afsk_enable) && ib_afsk_enable)
{
unsigned fc = 1700;
//cfg.getValue(name(), "IB_AFSK_CENTER_FQ", fc);
//cfg().getValue(name(), "IB_AFSK_CENTER_FQ", fc);
unsigned shift = 1000;
//cfg.getValue(name(), "IB_AFSK_SHIFT", shift);
//cfg().getValue(name(), "IB_AFSK_SHIFT", shift);
unsigned baudrate = 1200;
//cfg.getValue(name(), "IB_AFSK_BAUDRATE", baudrate);
//cfg().getValue(name(), "IB_AFSK_BAUDRATE", baudrate);
AfskDemodulator *fsk_demod =
new AfskDemodulator(fc - shift/2, fc + shift/2, baudrate);
@ -580,10 +586,10 @@ bool LocalRxBase::initialize(void)
// Create the configured type of DTMF decoder and add it to the splitter
string dtmf_dec_type("NONE");
cfg.getValue(name(), "DTMF_DEC_TYPE", dtmf_dec_type);
cfg().getValue(name(), "DTMF_DEC_TYPE", dtmf_dec_type);
if (dtmf_dec_type != "NONE")
{
DtmfDecoder *dtmf_dec = DtmfDecoder::create(this, cfg, name());
DtmfDecoder *dtmf_dec = DtmfDecoder::create(this, cfg(), name());
if ((dtmf_dec == 0) || !dtmf_dec->initialize())
{
// FIXME: Cleanup?
@ -597,7 +603,7 @@ bool LocalRxBase::initialize(void)
voiceband_splitter->addSink(dtmf_dec, true);
bool dtmf_muting = false;
cfg.getValue(name(), "DTMF_MUTING", dtmf_muting);
cfg().getValue(name(), "DTMF_MUTING", dtmf_muting);
if (dtmf_muting)
{
dtmf_muting_pre = dtmf_dec->detectionTime();
@ -607,10 +613,10 @@ bool LocalRxBase::initialize(void)
// Create a selective multiple tone detector object
string sel5_dec_type("NONE");
cfg.getValue(name(), "SEL5_DEC_TYPE", sel5_dec_type);
cfg().getValue(name(), "SEL5_DEC_TYPE", sel5_dec_type);
if (sel5_dec_type != "NONE")
{
Sel5Decoder *sel5_dec = Sel5Decoder::create(cfg, name());
Sel5Decoder *sel5_dec = Sel5Decoder::create(cfg(), name());
if (sel5_dec == 0 || !sel5_dec->initialize())
{
cerr << "*** ERROR: Sel5 decoder initialization failed for RX \""
@ -673,7 +679,7 @@ bool LocalRxBase::initialize(void)
// the LocalRxBase class
setHandler(prev_src);
cfg.getValue(name(), "AUDIO_DEV_KEEP_OPEN", audio_dev_keep_open);
cfg().getValue(name(), "AUDIO_DEV_KEEP_OPEN", audio_dev_keep_open);
// Open the audio device for reading
if (!audioOpen())
@ -692,8 +698,10 @@ bool LocalRxBase::initialize(void)
//cout << "### Enabling 1750Hz muting\n";
}
cfg().valueUpdated.connect(sigc::mem_fun(*this, &LocalRxBase::cfgUpdated));
return true;
} /* LocalRxBase:initialize */
@ -999,6 +1007,40 @@ void LocalRxBase::publishSquelchState(void)
} /* LocalRxBase::publishSquelchState */
void LocalRxBase::cfgUpdated(const std::string& section, const std::string& tag)
{
//std::cout << "### LocalRxBase::cfgUpdated: "
// << section << "/" << tag << "=" << cfg().getValue(section, tag)
// << std::endl;
if (section == name())
{
if (tag == CFG_SQL_HANGTIME)
{
if (cfg().getValue(name(), CFG_SQL_HANGTIME, sql_hangtime))
{
squelch_det->setHangtime(sql_hangtime);
}
std::cout << "Setting " << CFG_SQL_HANGTIME << " to " << sql_hangtime
<< " for receiver " << name() << std::endl;
}
else if (tag == CFG_SQL_EXTENDED_HANGTIME)
{
cfg().getValue(name(), CFG_SQL_EXTENDED_HANGTIME, sql_extended_hangtime);
std::cout << "Setting " << CFG_SQL_EXTENDED_HANGTIME << " to "
<< sql_extended_hangtime
<< " for receiver " << name() << std::endl;
}
else if (tag == CFG_SQL_EXTENDED_HANGTIME_THRESH)
{
cfg().getValue(name(), CFG_SQL_EXTENDED_HANGTIME_THRESH,
sql_extended_hangtime_thresh);
std::cout << "Setting " << CFG_SQL_EXTENDED_HANGTIME_THRESH << " to "
<< sql_extended_hangtime_thresh
<< " for receiver " << name() << std::endl;
}
}
} /* LocalRxBase::cfgUpdated */
/*
* This file has not been truncated

View File

@ -237,7 +237,6 @@ class LocalRxBase : public Rx
virtual Async::AudioSource *audioSource(void) = 0;
private:
Async::Config &cfg;
MuteState mute_state;
Squelch *squelch_det;
SigLevDet *siglevdet;
@ -269,6 +268,7 @@ class LocalRxBase : public Rx
void setSqlHangtimeFromSiglev(float siglev);
void rxReadyStateChanged(void);
void publishSquelchState(void);
void cfgUpdated(const std::string& section, const std::string& tag);
}; /* class LocalRxBase */

View File

@ -152,7 +152,13 @@ class Rx : public sigc::trackable, public Async::AudioSource
* @brief Destructor
*/
virtual ~Rx(void);
/**
* @brief The config object
* @returns Returns a reference to the configuration object
*/
Async::Config& cfg(void) { return m_cfg; }
/**
* @brief Initialize the receiver object
* @return Return \em true on success, or \em false on failure
@ -303,11 +309,11 @@ class Rx : public sigc::trackable, public Async::AudioSource
private:
std::string m_name;
bool m_verbose;
bool m_sql_open;
Async::Config m_cfg;
Async::Timer *m_sql_tmo_timer;
std::string m_name;
bool m_verbose;
bool m_sql_open;
Async::Config& m_cfg;
Async::Timer* m_sql_tmo_timer;
void sqlTimeout(Async::Timer *t);

View File

@ -211,7 +211,7 @@ class Sel5Decoder : public sigc::trackable, public Async::AudioSink
const std::string &name(void) const { return m_name; }
private:
Async::Config m_cfg;
Async::Config& m_cfg;
std::string m_name;
}; /* class Sel5Decoder */

View File

@ -8,10 +8,10 @@ QTEL=1.2.4
LIBECHOLIB=1.3.3
# Version for the Async library
LIBASYNC=1.6.0.99.3
LIBASYNC=1.6.0.99.4
# SvxLink versions
SVXLINK=1.7.99.20
SVXLINK=1.7.99.21
MODULE_HELP=1.0.0
MODULE_PARROT=1.1.1
MODULE_ECHO_LINK=1.5.99.0