Use -S <dB> for setting RF level or use -S auto for auto level. When squelch closes, audio is muted. If squelch is closed for some seconds (depending on network), call is released. (RF loss condition) The previous loss detection has been removedpull/1/head
parent
f7a0e4622b
commit
b32e0ab602
@ -1,6 +1,6 @@ |
||||
|
||||
void dsp_init(void); |
||||
int dsp_init_sender(bnetz_t *bnetz); |
||||
int dsp_init_sender(bnetz_t *bnetz, double squelch_db); |
||||
void dsp_cleanup_sender(bnetz_t *bnetz); |
||||
void bnetz_set_dsp_mode(bnetz_t *bnetz, enum dsp_mode mode); |
||||
|
||||
|
@ -1,93 +0,0 @@ |
||||
/* Loss detection
|
||||
* |
||||
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu> |
||||
* All Rights Reserved |
||||
* |
||||
* This program 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. |
||||
* |
||||
* This program 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 <stdint.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <errno.h> |
||||
#include <math.h> |
||||
#include "../common/debug.h" |
||||
#include "loss.h" |
||||
|
||||
/* initialize detector
|
||||
* |
||||
* interval: number of detector calls for one interval of one second |
||||
* threshold: intervals may differ by this factor, to be declared as similar |
||||
* 0 to disable, e.g. 1.3 for 30 percent change |
||||
*/ |
||||
void audio_init_loss(loss_t *loss, int interval, double threshold, int seconds) |
||||
{ |
||||
memset(loss, 0, sizeof(*loss)); |
||||
|
||||
loss->interval = interval; |
||||
loss->threshold = threshold; |
||||
loss->interval_num = seconds; |
||||
} |
||||
|
||||
/* call this when tones/telegrams are detected */ |
||||
void audio_reset_loss(loss_t *loss) |
||||
{ |
||||
if (loss->interval_count > 0) { |
||||
PDEBUG(DDSP, DEBUG_DEBUG, "Signal is recovered (loss is gone).\n"); |
||||
loss->interval_count = 0; |
||||
} |
||||
loss->level = 0; |
||||
loss->level_count = 0; |
||||
} |
||||
|
||||
#define LOSS_MAX_DIFF 1.2 /* 20 % difference */ |
||||
|
||||
/* call this for every interval */ |
||||
int audio_detect_loss(loss_t *loss, double level) |
||||
{ |
||||
double diff; |
||||
|
||||
/* disabled */ |
||||
if (loss->threshold == 0.0) |
||||
return 0; |
||||
|
||||
/* calculate a total level to detect loss */ |
||||
loss->level += level; |
||||
|
||||
if (++loss->level_count < loss->interval)
|
||||
return 0; |
||||
|
||||
/* normalize level */ |
||||
loss->level = loss->level / loss->level_count; |
||||
|
||||
PDEBUG(DDSP, DEBUG_DEBUG, "Noise level = %.0f%%\n", loss->level * 100); |
||||
|
||||
diff = loss->level / loss->level_last; |
||||
if (diff < 1.0) |
||||
diff = 1.0 / diff; |
||||
loss->level_last = loss->level; |
||||
loss->level = 0; |
||||
loss->level_count = 0; |
||||
if (diff < LOSS_MAX_DIFF && loss->level_last > loss->threshold) { |
||||
loss->interval_count++; |
||||
PDEBUG(DDSP, DEBUG_DEBUG, "Detected signal loss %d for intervals level change %.0f%% (below %.0f%%).\n", loss->interval_count, diff * 100 - 100, LOSS_MAX_DIFF * 100 - 100); |
||||
} else if (loss->interval_count > 0) { |
||||
audio_reset_loss(loss); |
||||
} |
||||
|
||||
if (loss->interval_count == loss->interval_num) |
||||
return 1; |
||||
return 0; |
||||
} |
||||
|
@ -1,15 +0,0 @@ |
||||
|
||||
typedef struct loss { |
||||
int interval; /* levels in one interval */ |
||||
int interval_num; /* number of similar intervals until loss */ |
||||
double threshold; /* how much volume change is accedped during loss */ |
||||
double level_last; /* received level of last block */ |
||||
double level; /* received level of current block */ |
||||
int level_count; /* counter of levels inside interval */ |
||||
int interval_count; /* counter of cosecutive intervals with loss */ |
||||
} loss_t; |
||||
|
||||
void audio_init_loss(loss_t *loss, int interval, double threshold, int seconds); |
||||
void audio_reset_loss(loss_t *loss); |
||||
int audio_detect_loss(loss_t *loss, double level); |
||||
|
@ -0,0 +1,139 @@ |
||||
/* Squelch functions
|
||||
* |
||||
* (C) 2017 by Andreas Eversberg <jolly@eversberg.eu> |
||||
* All Rights Reserved |
||||
* |
||||
* This program 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. |
||||
* |
||||
* This program 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 <string.h> |
||||
#include <math.h> |
||||
#include "debug.h" |
||||
#include "squelch.h" |
||||
|
||||
#define CHAN squelch->chan |
||||
|
||||
/* How does it work:
|
||||
* |
||||
* After init, squelch() is called with the RF level and duration of each chunk. |
||||
* Normally quelch() returns SQUELCH_OPEN. If the RF level is below the |
||||
* threshold level for multe_time, it returns SQUELCH_MUTE. If the RF level is |
||||
* below the threshold level for loss_time, it returns SQUELCH_LOSS, which |
||||
* measns that the carrier was loss. |
||||
* |
||||
* This is done by a counter. Whenever the RF level is below threshold, the mute |
||||
* counter is incremented, whenever the RF level is above threshodl, the mute |
||||
* counter is decremented. When the mute counter reaches mute_time, the mute |
||||
* state is set and the 'mute' condition is returned. When the mute counter |
||||
* rechers 0, the mute state is unset and the 'open' condition is returned. |
||||
* |
||||
* If the mute state is set, the loss counter is incremented. If the mute state |
||||
* is not set, the loss counter is reset. When the loss counter reaches |
||||
* loss_time, the 'loss' condition is returned. |
||||
*/ |
||||
|
||||
#define SQUELCH_INIT_TIME 1.0 /* wait some time before performing squelch */ |
||||
#define SQUELCH_AUTO_TIME 1.0 /* duration of squelch quelch calibration */ |
||||
#define SQUELCH_AUTO_OFFSET 6.0 /* auto calibration: offset above noise floor */ |
||||
|
||||
void squelch_init(squelch_t *squelch, int chan, double threshold_db, double mute_time, double loss_time) |
||||
{ |
||||
memset(squelch, 0, sizeof(*squelch)); |
||||
squelch->chan = chan; |
||||
squelch->threshold_db = threshold_db; |
||||
/* wait for init condition */ |
||||
squelch->init_count = 0.0; |
||||
/* measure noise floor for auto threshold mode */ |
||||
if (threshold_db == 0.0) { |
||||
/* automatic threshold */ |
||||
PDEBUG_CHAN(DDSP, DEBUG_INFO, "RF signal squelch: Use automatic threshold\n"); |
||||
squelch->auto_state = 1; |
||||
} else if (!isinf(threshold_db)) { |
||||
/* preset threshold */ |
||||
PDEBUG_CHAN(DDSP, DEBUG_INFO, "RF signal squelch: Use preset threshold of %.1f dB\n", threshold_db); |
||||
} |
||||
/* squelch is mute on init */ |
||||
squelch->mute_time = mute_time; |
||||
squelch->mute_count = mute_time; |
||||
squelch->mute_state = 1; |
||||
/* loss condition met on init */ |
||||
squelch->loss_time = loss_time; |
||||
squelch->loss_state = 1; |
||||
} |
||||
|
||||
enum squelch_result squelch(squelch_t *squelch, double rf_level_db, double duration) |
||||
{ |
||||
/* squelch disabled */ |
||||
if (isinf(squelch->threshold_db)) |
||||
return SQUELCH_OPEN; |
||||
|
||||
/* count until start quelch processing */ |
||||
squelch->init_count += duration; |
||||
if (squelch->init_count < SQUELCH_INIT_TIME) |
||||
return SQUELCH_MUTE; |
||||
|
||||
/* measure noise floor and calibrate threashold_db */ |
||||
if (squelch->auto_state) { |
||||
squelch->auto_count += duration; |
||||
squelch->auto_level_sum += rf_level_db; |
||||
squelch->auto_level_count++; |
||||
if (squelch->auto_count < SQUELCH_AUTO_TIME) |
||||
return SQUELCH_MUTE; |
||||
squelch->threshold_db = squelch->auto_level_sum / (double) squelch->auto_level_count; |
||||
PDEBUG_CHAN(DDSP, DEBUG_INFO, "RF signal measurement: %.1f dB noise floor, using threshold of %.1f dB\n", squelch->threshold_db, squelch->threshold_db + SQUELCH_AUTO_OFFSET); |
||||
squelch->threshold_db += SQUELCH_AUTO_OFFSET; |
||||
squelch->auto_state = 0; |
||||
} |
||||
|
||||
/* enough RF level, so we unmute when mute_count reched 0 */ |
||||
if (rf_level_db >= squelch->threshold_db) { |
||||
squelch->mute_count -= duration; |
||||
if (squelch->mute_count <= 0.0) { |
||||
if (squelch->mute_state) { |
||||
PDEBUG_CHAN(DDSP, DEBUG_INFO, "RF signal strong: Unmuting audio (RF %.1f >= %.1f dB)\n", rf_level_db, squelch->threshold_db); |
||||
squelch->mute_state = 0; |
||||
} |
||||
squelch->mute_count = 0.0; |
||||
} |
||||
} else { |
||||
/* RF level too low, so we mute when mute_count reached mute_time */ |
||||
squelch->mute_count += duration; |
||||
if (squelch->mute_count >= squelch->mute_time) { |
||||
if (!squelch->mute_state) { |
||||
PDEBUG_CHAN(DDSP, DEBUG_INFO, "RF signal weak: Muting audio (RF %.1f < %.1f dB)\n", rf_level_db, squelch->threshold_db); |
||||
squelch->mute_state = 1; |
||||
} |
||||
squelch->mute_count = squelch->mute_time; |
||||
} |
||||
} |
||||
|
||||
if (squelch->mute_state) { |
||||
/* at 'mute' condition, count and check for loss */ |
||||
squelch->loss_count += duration; |
||||
if (squelch->loss_count >= squelch->loss_time) { |
||||
if (!squelch->loss_state) { |
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "RF signal loss detected after %.1f seconds\n", squelch->loss_time); |
||||
squelch->loss_state = 1; |
||||
return SQUELCH_LOSS; |
||||
} |
||||
} |
||||
return SQUELCH_MUTE; |
||||
} else { |
||||
/* at unmute condition, reset loss counter */ |
||||
squelch->loss_state = 0; |
||||
squelch->loss_count = 0.0; |
||||
return SQUELCH_OPEN; |
||||
} |
||||
} |
||||
|
@ -0,0 +1,26 @@ |
||||
|
||||
typedef struct squelch { |
||||
int chan; /* channel number */ |
||||
double threshold_db; /* threshold level to mute or loss of signal */ |
||||
double init_count; /* duration counter for starting squelch process */ |
||||
int auto_state; /* set if auto threshold calibration is performed */ |
||||
double auto_count; /* duration counter for calibration process */ |
||||
double auto_level_sum; /* sum of rf level while calibrating */ |
||||
int auto_level_count; /* counter for rf levels that are summed */ |
||||
double mute_time; /* time to indicate mute after being below threshold */ |
||||
int mute_state; /* set, if we are currently at mute condition */ |
||||
double mute_count; /* duration counter for mute condition */ |
||||
double loss_time; /* time to indicate loss after being below threshold */ |
||||
int loss_state; /* set, if we are currently at 'signal loss' condition */ |
||||
double loss_count; /* duration counter for 'signal loss' condition */ |
||||
} squelch_t; |
||||
|
||||
enum squelch_result { |
||||
SQUELCH_OPEN, |
||||
SQUELCH_MUTE, |
||||
SQUELCH_LOSS, |
||||
}; |
||||
|
||||
void squelch_init(squelch_t *squelch, int chan, double threshold_db, double mute_time, double loss_time); |
||||
enum squelch_result squelch(squelch_t *squelch, double rf_level_db, double duration); |
||||
|