Implement support for multitone CTCSS SNR offset
New CTCSS configuration variable CTCSS_SNR_OFFSETS (note the plural S) which replace the old variable with a similar name. The new variable take a comma separated list of frequency:offset pairs to support the use of multiple CTCSS tone frequencies. The siglevdetcal utility has also been modified to output the new variable format. If you are using just one CTCSS frequency with a previously calibrated offet value you can keep using that. The older configuration variable will not be removed and it still work in the same way.
This commit is contained in:
parent
68a653ef9a
commit
d5cd8ce81b
|
|
@ -1,4 +1,4 @@
|
|||
1.6.1 -- ?? ??? 2021
|
||||
1.6.1 -- ?? ??? 2022
|
||||
----------------------
|
||||
|
||||
* ASYNC_AUDIO_ALSA_ZEROFILL is now enabled by default.
|
||||
|
|
@ -44,6 +44,12 @@
|
|||
will be written instead. Enable this behavior by setting the environment
|
||||
variable ASYNC_AUDIO_UDP_ZEROFILL=1.
|
||||
|
||||
* The Config class now also accept associative containers for getValue. This
|
||||
for example make it possible to use a std::map<float,float> to read a
|
||||
configuration variable with a list of values on the form
|
||||
VARNAME=88.5:-1,136.5:1. It is also possible to use other key/value
|
||||
separators.
|
||||
|
||||
|
||||
|
||||
1.6.0 -- 01 Sep 2019
|
||||
|
|
|
|||
|
|
@ -325,6 +325,7 @@ class Config
|
|||
return true;
|
||||
}
|
||||
std::stringstream ssval(str_val);
|
||||
ssval.imbue(std::locale(ssval.getloc(), new csv_whitespace));
|
||||
while (!ssval.eof())
|
||||
{
|
||||
Key tmp;
|
||||
|
|
@ -342,6 +343,77 @@ class Config
|
|||
return true;
|
||||
} /* Config::getValue */
|
||||
|
||||
/**
|
||||
* @brief Get value of given config variable into associative container
|
||||
* @param section The name of the section where the configuration
|
||||
* variable is located
|
||||
* @param tag The name of the configuration variable to get
|
||||
* @param c The value is returned in this argument.
|
||||
* Successful completion overwrites previous contents
|
||||
* @param sep The character used to separate key and value
|
||||
* @param missing_ok If set to \em true, return \em true if the
|
||||
* configuration variable is missing
|
||||
* @return Returns \em true on success or else \em false on failure.
|
||||
*
|
||||
* This function is used to get the value of a configuraiton variable. The
|
||||
* config variable is read into an associative container (e.g. std::map or
|
||||
* std::multimap). It's a template function meaning that it can take any
|
||||
* key and value type that supports the operator>> function.
|
||||
* Normally a missing configuration variable is seen as an error and the
|
||||
* function returns \em false. If the missing_ok parameter is set to \em
|
||||
* true, this function returns \em true for a missing variable but still
|
||||
* returns \em false if an illegal value is specified.
|
||||
*/
|
||||
template <template <typename, typename, typename, typename> class Container,
|
||||
class Key, class T, class Compare=std::less<Key>,
|
||||
class Allocator=std::allocator<std::pair<const Key, T>>>
|
||||
bool getValue(const std::string& section, const std::string& tag,
|
||||
Container<Key, T, Compare, Allocator>& c,
|
||||
char sep = ':', bool missing_ok = false) const
|
||||
{
|
||||
std::string str_val;
|
||||
if (!getValue(section, tag, str_val))
|
||||
{
|
||||
return missing_ok;
|
||||
}
|
||||
if (str_val.empty())
|
||||
{
|
||||
c.clear();
|
||||
return true;
|
||||
}
|
||||
std::stringstream ssval(str_val);
|
||||
ssval.imbue(std::locale(ssval.getloc(), new csv_whitespace));
|
||||
while (!ssval.eof())
|
||||
{
|
||||
std::string entry;
|
||||
ssval >> entry;
|
||||
std::string::size_type seppos = entry.find(sep);
|
||||
if (seppos == std::string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
std::string keystr(entry.substr(0, seppos));
|
||||
std::string valuestr(entry.substr(seppos+1));
|
||||
Key key;
|
||||
T value;
|
||||
if (!setValueFromString(key, keystr) ||
|
||||
!setValueFromString(value, valuestr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(!ssval.eof())
|
||||
{
|
||||
ssval >> std::ws;
|
||||
}
|
||||
if (ssval.fail())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
c.insert(std::pair<Key, T>(key, value));
|
||||
}
|
||||
return true;
|
||||
} /* Config::getValue */
|
||||
|
||||
/**
|
||||
* @brief Get a range checked variable value
|
||||
* @param section The name of the section where the configuration
|
||||
|
|
@ -487,7 +559,19 @@ class Config
|
|||
char *parseValue(char *value);
|
||||
char *translateEscapedChars(char *val);
|
||||
|
||||
}; /* class Config */
|
||||
template <class T>
|
||||
bool setValueFromString(T& val, const std::string &str) const
|
||||
{
|
||||
std::istringstream ss(str);
|
||||
ss >> std::noskipws >> val;
|
||||
if(!ss.eof())
|
||||
{
|
||||
ss >> std::ws;
|
||||
}
|
||||
return !ss.fail() && ss.eof();
|
||||
}
|
||||
|
||||
}; /* class Config */
|
||||
|
||||
|
||||
} /* namespace */
|
||||
|
|
|
|||
|
|
@ -951,7 +951,7 @@ The CTCSS squelch detector checks for the presence of a tone with the specified
|
|||
frequency. The tone frequency is specified using the CTCSS_FQ config variable.
|
||||
The thresholds are specified using the CTCSS_OPEN_THRESH and CTCSS_CLOSE_THRESH
|
||||
config variables. Other config variables that effect the CTCSS squelch is:
|
||||
CTCSS_MODE, CTCSS_SNR_OFFSET, CTCSS_BPF_LOW, CTCSS_BPF_HIGH.
|
||||
CTCSS_MODE, CTCSS_SNR_OFFSETS, CTCSS_BPF_LOW, CTCSS_BPF_HIGH.
|
||||
|
||||
The SERIAL squelch detector use a pin in a serial port to detect if the squelch
|
||||
is open. This squelch detector can be used if the receiver have an external
|
||||
|
|
@ -1125,36 +1125,45 @@ frequency range (67.0 to 254.1Hz) need to be reasonable flat. When choosing
|
|||
tones, try to select tones that are close to each other. However, selecting
|
||||
tones too close may cause both to trigger. Also do not select tones that are a
|
||||
multiple of another tone since overtones may trigger the wrong one.
|
||||
|
||||
If multiple tones are configured when running siglevdetcal, the first one will
|
||||
be used for the calibration.
|
||||
.TP
|
||||
.B CTCSS_OPEN_THRESH
|
||||
If CTCSS (PL, subtone) squelch is used (SQL_DET is set to CTCSS), this config
|
||||
variable sets the required tone level to indicate squelch open. The value is
|
||||
some kind of estimated signal to noise dB value. If using CTCSS mode 2 or 3 it
|
||||
is helpful to set up the CTCSS_SNR_OFFSET config variable. This will make the
|
||||
SNR estimation pretty good. Default threshold is 15dB.
|
||||
some kind of estimated signal to noise dB value. If using CTCSS mode 2, 3 or 4
|
||||
it is helpful to set up the CTCSS_SNR_OFFSETS config variable. This will make
|
||||
the SNR estimation pretty good. Default threshold is 15dB.
|
||||
.TP
|
||||
.B CTCSS_CLOSE_THRESH
|
||||
If CTCSS (PL, subtone) squelch is used (SQL_DET is set to CTCSS), this config
|
||||
variable sets the required tone level to indicate squelch close. The value is
|
||||
some kind of estimated signal to noise dB value. If using CTCSS mode 2 or 3 it
|
||||
is helpful to set up the CTCSS_SNR_OFFSET config variable. This will make the
|
||||
SNR estimation pretty good. Default threshold is 9dB.
|
||||
some kind of estimated signal to noise dB value. If using CTCSS mode 2, 3 or 4
|
||||
it is helpful to set up the CTCSS_SNR_OFFSETS config variable. This will make
|
||||
the SNR estimation pretty good. Default threshold is 9dB.
|
||||
.TP
|
||||
.B CTCSS_SNR_OFFSET
|
||||
This config variable is used when CTCSS_MODE is set to 0, 2 or 3. It will
|
||||
adjust the estimated SNR value so that it becomes very close to a real SNR
|
||||
This configuration variable should not be used unless you know why you use it.
|
||||
Using CTCSS_SNR_OFFSETS is often more appropriate.
|
||||
.TP
|
||||
.B CTCSS_SNR_OFFSETS
|
||||
This config variable is used when CTCSS_MODE is set to 0, 2, 3 or 4. It will
|
||||
adjust the estimated SNR values so that they become very close to a real SNR
|
||||
value. This value will have to be adjusted if CTCSS_FQ, CTCSS_MODE,
|
||||
CTCSS_BPF_LOW or CTCSS_BPF_HIGH changes.
|
||||
|
||||
Use the siglevdetcal utility to find out what to set this config variable to.
|
||||
There is no requirement to set this config variable up. The downside is that
|
||||
you will then need to experiment more with the CTCSS_OPEN_THRESH and
|
||||
CTCSS_CLOSE_THRESH config variables to find the correct squelch level.
|
||||
CTCSS_CLOSE_THRESH config variables to find the correct squelch level. If using
|
||||
multiple CTCSS frequencies it may not be possible to find good threshold values
|
||||
due to uneven frequency characteristics for the receiver.
|
||||
|
||||
The format for this config variable is a comma separated list of
|
||||
frequency:offset pairs. The CTCSS_SNR_OFFSET variable may be used as a form of
|
||||
fallback value that is used if a specific tone frequency in use is missing from
|
||||
this list.
|
||||
.TP
|
||||
.B CTCSS_BPF_LOW
|
||||
When CTCSS_MODE is set to 0, 2 or 3, this config variable will set the low
|
||||
When CTCSS_MODE is set to 0, 2, 3 or 4, this config variable will set the low
|
||||
cutoff frequency for the passband filter. It normally should not have to be
|
||||
adjusted but could improve the detector if some interference falls within the
|
||||
passband (e.g. mains hum). Note however that the more narrow you make the
|
||||
|
|
@ -1163,13 +1172,13 @@ increasing the open/close thresholds or by setting up SQL_DELAY and
|
|||
SQL_HANGTIME. Default is 60Hz.
|
||||
.TP
|
||||
.B CTCSS_BPF_HIGH
|
||||
When CTCSS_MODE is set to 0, 2 or 3, this config variable will set the high
|
||||
When CTCSS_MODE is set to 0, 2, 3 or 4, this config variable will set the high
|
||||
cutoff frequency for the passband filter. It normally should not have to be
|
||||
adjusted but could improve the detector if some interference falls within the
|
||||
passband. Note however that the more narrow you make the
|
||||
passband, the less stable the detector will be. You may need to compensate by
|
||||
increasing the open/close thresholds or by setting up SQL_DELAY and
|
||||
SQL_HANGTIME. Default is 270Hz.
|
||||
passband. Note however that the more narrow you make the passband, the less
|
||||
stable the detector will be. You may need to compensate by increasing the
|
||||
open/close thresholds or by setting up SQL_DELAY and SQL_HANGTIME. Default is
|
||||
270Hz.
|
||||
.TP
|
||||
.B CTCSS_DEBUG
|
||||
Set this to 1 to enable continuously printing SNR values for the CTCSS tone
|
||||
|
|
@ -1277,7 +1286,7 @@ Below is a simple example of how to set up a combined squelch.
|
|||
[Rx1:CTCSS]
|
||||
SQL_DET=CTCSS
|
||||
CTCSS_FQ=77.0
|
||||
CTCSS_SNR_OFFSET=-3.22
|
||||
CTCSS_SNR_OFFSETS=77.0:-3.22
|
||||
|
||||
[Rx1:SIGLEV]
|
||||
SQL_DET=SIGLEV
|
||||
|
|
@ -2729,7 +2738,7 @@ Before starting the siglevdetcal application, pull the PTT cable since the PTT
|
|||
might get triggered during the calibration procedure.
|
||||
|
||||
The siglevdetcal utility will also measure the CTCSS tone SNR offset so that
|
||||
the CTCSS_SNR_OFFSET config variable can be set up to a proper value.
|
||||
the CTCSS_SNR_OFFSETS config variable can be set up to a proper value.
|
||||
|
||||
If the siglevdetcal application cannot be used for some reason, the manual
|
||||
procedure below might be used. This procedure will only work for a
|
||||
|
|
|
|||
|
|
@ -182,6 +182,15 @@
|
|||
|
||||
* Improved squelch state logging with more details about the detection.
|
||||
|
||||
* New CTCSS configuration variable CTCSS_SNR_OFFSETS (note the plural S) which
|
||||
replace the old variable with a similar name. The new variable take a comma
|
||||
separated list of frequency:offset pairs to support the use of multiple
|
||||
CTCSS tone frequencies. The siglevdetcal utility has also been modified to
|
||||
output the new variable format. If you are using just one CTCSS frequency
|
||||
with a previously calibrated offet value you can keep using that. The older
|
||||
configuration variable will not be removed and it still work in the same
|
||||
way.
|
||||
|
||||
|
||||
|
||||
1.7.0 -- 01 Sep 2019
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ VOX_THRESH=1000
|
|||
#CTCSS_MODE=2
|
||||
CTCSS_FQ=136.5
|
||||
#CTCSS_SNR_OFFSET=0
|
||||
#CTCSS_SNR_OFFSETS=88.5:-1.0,136.5:-0.5
|
||||
#CTCSS_OPEN_THRESH=15
|
||||
#CTCSS_CLOSE_THRESH=9
|
||||
#CTCSS_BPF_LOW=60
|
||||
|
|
@ -167,6 +168,7 @@ VOX_THRESH=1000
|
|||
#CTCSS_MODE=2
|
||||
CTCSS_FQ=136.5
|
||||
#CTCSS_SNR_OFFSET=0
|
||||
#CTCSS_SNR_OFFSETS=88.5:-1.0,136.5:-0.5
|
||||
#CTCSS_OPEN_THRESH=15
|
||||
#CTCSS_CLOSE_THRESH=9
|
||||
#CTCSS_BPF_LOW=60
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <map>
|
||||
|
||||
#include <AsyncCppApplication.h>
|
||||
#include <AsyncConfig.h>
|
||||
|
|
@ -17,6 +18,12 @@ using namespace Async;
|
|||
|
||||
#define PROGRAM_NAME "SigLevDetCal"
|
||||
|
||||
struct CtcssMeasurement
|
||||
{
|
||||
double sum = 0.0;
|
||||
size_t count = 0;
|
||||
};
|
||||
|
||||
static const int INTERVAL = 100;
|
||||
static const int ITERATIONS = 150;
|
||||
|
||||
|
|
@ -26,10 +33,9 @@ static double open_sum = 0.0f;
|
|||
static double close_sum = 0.0f;
|
||||
static float siglev_slope = 10.0;
|
||||
static float siglev_offset = 0.0;
|
||||
static double ctcss_snr_sum = 0.0;
|
||||
static unsigned ctcss_snr_cnt = 0;
|
||||
static float ctcss_open_snr = 0.0f;
|
||||
static float ctcss_close_snr = 0.0f;
|
||||
static std::map<float, CtcssMeasurement> ctcss_snr_sum;
|
||||
static std::map<float, float> ctcss_open_snr;
|
||||
static std::map<float, float> ctcss_close_snr;
|
||||
|
||||
|
||||
#if 0
|
||||
|
|
@ -89,34 +95,48 @@ void sample_squelch_close(Timer *t)
|
|||
|
||||
float new_siglev_slope = 100.0 / open_close_mean;
|
||||
float new_siglev_offset = -close_mean * new_siglev_slope;
|
||||
if (ctcss_snr_cnt > 0)
|
||||
for (const auto& entry : ctcss_snr_sum)
|
||||
{
|
||||
ctcss_close_snr = ctcss_snr_sum / ctcss_snr_cnt;
|
||||
ctcss_close_snr[entry.first] = entry.second.sum / entry.second.count;
|
||||
}
|
||||
|
||||
cout << endl;
|
||||
cout << "--- Results\n";
|
||||
printf("Mean SNR for the CTCSS tone : ");
|
||||
if (ctcss_snr_cnt > 0)
|
||||
printf("Mean SNR for the CTCSS tones : ");
|
||||
if (!ctcss_close_snr.empty())
|
||||
{
|
||||
printf("%.1fdB\n",
|
||||
ctcss_open_snr - ctcss_close_snr);
|
||||
printf("\n");
|
||||
for (const auto& entry : ctcss_close_snr)
|
||||
{
|
||||
float snr = ctcss_open_snr[entry.first] - entry.second;
|
||||
printf(" %5.1f : %+5.1fdB\n", entry.first, snr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("N/A (CTCSS not enabled)\n");
|
||||
}
|
||||
printf("Dynamic range for the siglev measurement : %.1fdB\n",
|
||||
10.0 * open_close_mean);
|
||||
//printf("Dynamic range for the siglev measurement : %.1fdB\n",
|
||||
// 10.0 * open_close_mean);
|
||||
|
||||
cout << endl;
|
||||
cout << "--- Put the config variables below in the configuration file\n";
|
||||
cout << "--- section for " << rx->name() << ".\n";
|
||||
printf("SIGLEV_SLOPE=%.2f\n", new_siglev_slope);
|
||||
printf("SIGLEV_OFFSET=%.2f\n", new_siglev_offset);
|
||||
if (ctcss_snr_cnt > 0)
|
||||
if (!ctcss_close_snr.empty())
|
||||
{
|
||||
printf("CTCSS_SNR_OFFSET=%.2f\n", ctcss_close_snr);
|
||||
printf("CTCSS_SNR_OFFSETS=");
|
||||
for (auto it = ctcss_close_snr.begin();
|
||||
it != ctcss_close_snr.end(); ++it)
|
||||
{
|
||||
if (it->first != ctcss_close_snr.begin()->first)
|
||||
{
|
||||
printf(",");
|
||||
}
|
||||
printf("%.1f:%+.2f", it->first, it->second);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
cout << endl;
|
||||
|
||||
|
|
@ -140,12 +160,9 @@ void start_squelch_close_measurement(FdWatch *w)
|
|||
cout << "--- Starting squelch close measurement\n";
|
||||
delete w;
|
||||
|
||||
ctcss_snr_sum = 0.0;
|
||||
ctcss_snr_cnt = 0;
|
||||
ctcss_snr_sum.clear();
|
||||
|
||||
Timer *timer = new Timer(INTERVAL);
|
||||
// must explicitly specify name space for ptr_fun() to avoid conflict
|
||||
// with ptr_fun() in /usr/include/c++/4.5/bits/stl_function.h
|
||||
timer->expired.connect(sigc::ptr_fun(&sample_squelch_close));
|
||||
}
|
||||
} /* start_squelch_close_measurement */
|
||||
|
|
@ -163,9 +180,9 @@ void sample_squelch_open(Timer *t)
|
|||
{
|
||||
delete t;
|
||||
|
||||
if (ctcss_snr_cnt > 0)
|
||||
for (const auto& entry : ctcss_snr_sum)
|
||||
{
|
||||
ctcss_open_snr = ctcss_snr_sum / ctcss_snr_cnt;
|
||||
ctcss_open_snr[entry.first] = entry.second.sum / entry.second.count;
|
||||
}
|
||||
|
||||
FdWatch *w = new FdWatch(0, FdWatch::FD_WATCH_RD);
|
||||
|
|
@ -195,8 +212,7 @@ void start_squelch_open_measurement(FdWatch *w)
|
|||
{
|
||||
cout << "--- Starting squelch open measurement\n";
|
||||
delete w;
|
||||
ctcss_snr_sum = 0.0;
|
||||
ctcss_snr_cnt = 0;
|
||||
ctcss_snr_sum.clear();
|
||||
Timer *timer = new Timer(INTERVAL);
|
||||
// must explicitly specify name space for ptr_fun() to avoid conflict
|
||||
// with ptr_fun() in /usr/include/c++/4.5/bits/stl_function.h
|
||||
|
|
@ -208,8 +224,9 @@ void start_squelch_open_measurement(FdWatch *w)
|
|||
|
||||
void ctcss_snr_updated(float snr, float fq)
|
||||
{
|
||||
ctcss_snr_sum += snr;
|
||||
ctcss_snr_cnt += 1;
|
||||
auto& measurement = ctcss_snr_sum[fq];
|
||||
measurement.sum += snr;
|
||||
measurement.count += 1;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -201,6 +201,7 @@ VOX_THRESH=1000
|
|||
#CTCSS_MODE=2
|
||||
CTCSS_FQ=136.5
|
||||
#CTCSS_SNR_OFFSET=0
|
||||
#CTCSS_SNR_OFFSETS=88.5:-1.0,136.5:-0.5
|
||||
#CTCSS_OPEN_THRESH=15
|
||||
#CTCSS_CLOSE_THRESH=9
|
||||
#CTCSS_BPF_LOW=60
|
||||
|
|
|
|||
|
|
@ -395,7 +395,7 @@ bool LocalRxBase::initialize(void)
|
|||
}
|
||||
|
||||
// Create a splitter to distribute full bandwidth audio to all consumers
|
||||
AudioSplitter *fullband_splitter = new AudioSplitter;
|
||||
fullband_splitter = new AudioSplitter;
|
||||
prev_src->registerSink(fullband_splitter, true);
|
||||
prev_src = fullband_splitter;
|
||||
|
||||
|
|
@ -774,6 +774,17 @@ void LocalRxBase::reset(void)
|
|||
} /* LocalRxBase::reset */
|
||||
|
||||
|
||||
void LocalRxBase::registerFullbandSink(Async::AudioSink* sink)
|
||||
{
|
||||
fullband_splitter->addSink(sink);
|
||||
} /* LocalRxBase::registerFullbandSink */
|
||||
|
||||
|
||||
void LocalRxBase::unregisterFullbandSink(Async::AudioSink* sink)
|
||||
{
|
||||
fullband_splitter->removeSink(sink);
|
||||
} /* LocalRxBase::unregisterFullbandSink */
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
|
|
|
|||
|
|
@ -186,6 +186,32 @@ class LocalRxBase : public Rx
|
|||
*/
|
||||
virtual void reset(void);
|
||||
|
||||
/**
|
||||
* @brief Register a fullband (unfiltered) audio sink
|
||||
* @param sink The sink to register
|
||||
*
|
||||
* Use this function to register a sink for handling fullband audio, that
|
||||
* is receiver audio that is not filtered. This can be used to get access
|
||||
* to audio outside of the voice band, in theory 0 to 8000 Hz.
|
||||
*/
|
||||
void registerFullbandSink(Async::AudioSink* sink);
|
||||
|
||||
/**
|
||||
* @brief Unregister a fullband (unfiltered) audio sink
|
||||
* @param sink The sink to unregister
|
||||
*
|
||||
* Use this function to unregister a sink for handling fullband audio.
|
||||
* See the documentation for the registerFullbandSink() function for more
|
||||
* information.
|
||||
*/
|
||||
void unregisterFullbandSink(Async::AudioSink* sink);
|
||||
|
||||
/**
|
||||
* @bried Get the squelch detector associated with this receiver
|
||||
* @returns Returns a pointer to the squelch detector or nullptr if none
|
||||
*/
|
||||
Squelch* squelchDetector(void) { return squelch_det; }
|
||||
|
||||
/**
|
||||
* @brief A signal that is emitted when the CTCSS tone SNR has changed
|
||||
* @param snr The current SNR
|
||||
|
|
@ -255,6 +281,7 @@ class LocalRxBase : public Rx
|
|||
HdlcDeframer * ob_afsk_deframer;
|
||||
HdlcDeframer * ib_afsk_deframer;
|
||||
bool audio_dev_keep_open;
|
||||
Async::AudioSplitter * fullband_splitter;
|
||||
|
||||
int audioRead(float *samples, int count);
|
||||
void dtmfDigitActivated(char digit);
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||
#include <iomanip>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
|
|
@ -158,9 +159,11 @@ class SquelchCtcss : public Squelch
|
|||
|
||||
int ctcss_mode = 0;
|
||||
cfg.getValue(rx_name, "CTCSS_MODE", ctcss_mode);
|
||||
|
||||
cfg.getValue(rx_name, "CTCSS_SNR_OFFSET", m_ctcss_snr_offset);
|
||||
|
||||
|
||||
float ctcss_snr_offset = 0.0f;
|
||||
cfg.getValue(rx_name, "CTCSS_SNR_OFFSET", ctcss_snr_offset);
|
||||
cfg.getValue(rx_name, "CTCSS_SNR_OFFSETS", m_ctcss_snr_offsets);
|
||||
|
||||
float open_thresh = 15.0f;
|
||||
float close_thresh = 9.0f;
|
||||
if (cfg.getValue(rx_name, "CTCSS_THRESH", open_thresh))
|
||||
|
|
@ -179,9 +182,21 @@ class SquelchCtcss : public Squelch
|
|||
close_thresh = open_thresh;
|
||||
}
|
||||
|
||||
open_thresh += m_ctcss_snr_offset;
|
||||
close_thresh += m_ctcss_snr_offset;
|
||||
|
||||
std::map<float, float> open_threshs;
|
||||
std::map<float, float> close_threshs;
|
||||
for (auto ctcss_fq : ctcss_fqs)
|
||||
{
|
||||
if (m_ctcss_snr_offsets.count(ctcss_fq) == 0)
|
||||
{
|
||||
m_ctcss_snr_offsets[ctcss_fq] = ctcss_snr_offset;
|
||||
}
|
||||
//std::cout << "### f=" << ctcss_fq
|
||||
// << " offset=" << m_ctcss_snr_offsets[ctcss_fq]
|
||||
// << std::endl;
|
||||
open_threshs[ctcss_fq] = open_thresh + m_ctcss_snr_offsets[ctcss_fq];
|
||||
close_threshs[ctcss_fq] = close_thresh + m_ctcss_snr_offsets[ctcss_fq];
|
||||
}
|
||||
|
||||
unsigned bpf_low = 60;
|
||||
cfg.getValue(rx_name, "CTCSS_BPF_LOW", bpf_low);
|
||||
if ((bpf_low < 50) || (bpf_low > 300))
|
||||
|
|
@ -233,8 +248,8 @@ class SquelchCtcss : public Squelch
|
|||
case 1:
|
||||
{
|
||||
//std::cout << "### CTCSS mode: Neighbour bins\n";
|
||||
det->setDetectPeakThresh(open_thresh);
|
||||
det->setUndetectPeakThresh(close_thresh);
|
||||
det->setDetectPeakThresh(open_threshs[ctcss_fq]);
|
||||
det->setUndetectPeakThresh(close_threshs[ctcss_fq]);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -245,7 +260,7 @@ class SquelchCtcss : public Squelch
|
|||
det->setDetectBw(16.0f);
|
||||
det->setDetectPeakThresh(0.0f);
|
||||
//det->setDetectPeakToTotPwrThresh(0.6f);
|
||||
det->setDetectSnrThresh(open_thresh, bpf_high - bpf_low);
|
||||
det->setDetectSnrThresh(open_threshs[ctcss_fq], bpf_high - bpf_low);
|
||||
det->setDetectStableCountThresh(1);
|
||||
det->setDetectPhaseBwThresh(2.0f, 2.0f);
|
||||
|
||||
|
|
@ -253,7 +268,7 @@ class SquelchCtcss : public Squelch
|
|||
det->setUndetectUseWindowing(false);
|
||||
det->setUndetectPeakThresh(0.0f);
|
||||
//det->setUndetectPeakToTotPwrThresh(0.3f);
|
||||
det->setUndetectSnrThresh(close_thresh, bpf_high - bpf_low);
|
||||
det->setUndetectSnrThresh(close_threshs[ctcss_fq], bpf_high - bpf_low);
|
||||
det->setUndetectStableCountThresh(2);
|
||||
//det->setUndetectPhaseBwThresh(4.0f, 16.0f);
|
||||
|
||||
|
|
@ -280,14 +295,14 @@ class SquelchCtcss : public Squelch
|
|||
det->setDetectToneFrequencyTolerancePercent(TONE_FQ_TOLERANCE);
|
||||
det->setDetectUseWindowing(USE_WINDOWING);
|
||||
det->setDetectPeakThresh(0.0f);
|
||||
det->setDetectSnrThresh(open_thresh, bpf_high - bpf_low);
|
||||
det->setDetectSnrThresh(open_threshs[ctcss_fq], bpf_high - bpf_low);
|
||||
|
||||
det->setUndetectBw(8.0f);
|
||||
det->setUndetectOverlapPercent(OVERLAP_PERCENT);
|
||||
det->setUndetectDelay(100);
|
||||
det->setUndetectUseWindowing(USE_WINDOWING);
|
||||
det->setUndetectPeakThresh(0.0f);
|
||||
det->setUndetectSnrThresh(close_thresh, bpf_high - bpf_low);
|
||||
det->setUndetectSnrThresh(close_threshs[ctcss_fq], bpf_high - bpf_low);
|
||||
|
||||
// Set up CTCSS band pass filter
|
||||
Async::AudioFilter *filter =
|
||||
|
|
@ -305,14 +320,14 @@ class SquelchCtcss : public Squelch
|
|||
det->setDetectUseWindowing(false);
|
||||
det->setDetectPeakThresh(0.0f);
|
||||
//det->setDetectPeakToTotPwrThresh(0.6f);
|
||||
det->setDetectSnrThresh(open_thresh, bpf_high - bpf_low);
|
||||
det->setDetectSnrThresh(open_threshs[ctcss_fq], bpf_high - bpf_low);
|
||||
det->setDetectStableCountThresh(1);
|
||||
|
||||
//det->setUndetectBw(8.0f);
|
||||
det->setUndetectUseWindowing(false);
|
||||
det->setUndetectPeakThresh(0.0f);
|
||||
//det->setUndetectPeakToTotPwrThresh(0.3f);
|
||||
det->setUndetectSnrThresh(close_thresh, bpf_high - bpf_low);
|
||||
det->setUndetectSnrThresh(close_threshs[ctcss_fq], bpf_high - bpf_low);
|
||||
det->setUndetectStableCountThresh(2);
|
||||
|
||||
// Set up CTCSS band pass filter
|
||||
|
|
@ -408,7 +423,7 @@ class SquelchCtcss : public Squelch
|
|||
DetList m_dets;
|
||||
Async::AudioSplitter* m_splitter = nullptr;
|
||||
ToneDetector* m_active_det = nullptr;
|
||||
float m_ctcss_snr_offset = 0.0f;
|
||||
std::map<float, float> m_ctcss_snr_offsets;
|
||||
bool m_debug = false;
|
||||
std::unique_ptr<Async::Timer> m_dbg_timer = nullptr;
|
||||
|
||||
|
|
@ -430,7 +445,7 @@ class SquelchCtcss : public Squelch
|
|||
ss << std::showpos << fq_err;
|
||||
}
|
||||
ss << ":" << static_cast<int>(
|
||||
std::roundf(det->lastSnr() - m_ctcss_snr_offset));
|
||||
std::roundf(det->lastSnr() - m_ctcss_snr_offsets[det->toneFq()]));
|
||||
if (is_detected)
|
||||
{
|
||||
if (m_active_det == 0)
|
||||
|
|
@ -466,16 +481,17 @@ class SquelchCtcss : public Squelch
|
|||
void printDebug(void)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << rxName() << ": ";
|
||||
os << rxName() << ":";
|
||||
for (auto det : m_dets)
|
||||
{
|
||||
float snr = det->lastSnr() - m_ctcss_snr_offset;
|
||||
float snr = det->lastSnr() - m_ctcss_snr_offsets[det->toneFq()];
|
||||
os << std::setw(4) << static_cast<int>(roundf(snr))
|
||||
<< ":" << std::fixed << std::setprecision(1) << det->toneFq();
|
||||
<< ":" << std::fixed << std::setprecision(1) << std::noshowpos
|
||||
<< det->toneFq();
|
||||
if (det->toneFqEstimate() > 0.0)
|
||||
{
|
||||
float fq_err = det->toneFqEstimate() - det->toneFq();
|
||||
os << std::showpos << fq_err;
|
||||
os << std::showpos << std::setw(5) << fq_err;
|
||||
}
|
||||
}
|
||||
std::cout << os.str() << std::endl;
|
||||
|
|
|
|||
10
src/versions
10
src/versions
|
|
@ -8,10 +8,10 @@ QTEL=1.2.4.99.5
|
|||
LIBECHOLIB=1.3.3.99.0
|
||||
|
||||
# Version for the Async library
|
||||
LIBASYNC=1.6.99.16
|
||||
LIBASYNC=1.6.99.17
|
||||
|
||||
# SvxLink versions
|
||||
SVXLINK=1.7.99.54
|
||||
SVXLINK=1.7.99.55
|
||||
MODULE_HELP=1.0.0
|
||||
MODULE_PARROT=1.1.1
|
||||
MODULE_ECHO_LINK=1.5.99.2
|
||||
|
|
@ -25,13 +25,13 @@ MODULE_FRN=1.1.0
|
|||
MODULE_TRX=1.0.0
|
||||
|
||||
# Version for the RemoteTrx application
|
||||
REMOTE_TRX=1.3.99.6
|
||||
REMOTE_TRX=1.3.99.7
|
||||
|
||||
# Version for the signal level calibration utility
|
||||
SIGLEV_DET_CAL=1.0.7.99.3
|
||||
SIGLEV_DET_CAL=1.0.7.99.4
|
||||
|
||||
# Version for the deviation calibration utility
|
||||
DEVCAL=1.0.2.99.4
|
||||
DEVCAL=1.0.2.99.5
|
||||
|
||||
# Version for svxserver
|
||||
SVXSERVER=0.0.6
|
||||
|
|
|
|||
Loading…
Reference in New Issue