Implementation of RX level squelch (for A-Netz and B-Netz)

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 removed
This commit is contained in:
Andreas Eversberg
2017-10-09 20:49:14 +02:00
parent f7a0e4622b
commit b32e0ab602
33 changed files with 310 additions and 263 deletions

View File

@@ -162,7 +162,7 @@ static void bnetz_timeout(struct timer *timer);
static void bnetz_go_idle(bnetz_t *bnetz);
/* Create transceiver instance and link to a list. */
int bnetz_create(int kanal, const char *audiodev, int use_sdr, int samplerate, double rx_gain, int gfs, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback, double loss_factor, const char *paging, int metering)
int bnetz_create(int kanal, const char *audiodev, int use_sdr, int samplerate, double rx_gain, int gfs, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback, double squelch_db, const char *paging, int metering)
{
bnetz_t *bnetz;
enum paging_signal paging_signal = PAGING_SIGNAL_NONE;
@@ -223,7 +223,7 @@ error_paging:
PDEBUG(DBNETZ, DEBUG_DEBUG, "Creating 'B-Netz' instance for 'Kanal' = %d 'Gruppenfreisignal' = %d (sample rate %d).\n", kanal, gfs, samplerate);
/* init general part of transceiver */
rc = sender_create(&bnetz->sender, kanal, bnetz_kanal2freq(kanal, 0), bnetz_kanal2freq(kanal, 1), audiodev, use_sdr, samplerate, rx_gain, pre_emphasis, de_emphasis, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, loss_factor, paging_signal);
rc = sender_create(&bnetz->sender, kanal, bnetz_kanal2freq(kanal, 0), bnetz_kanal2freq(kanal, 1), audiodev, use_sdr, samplerate, rx_gain, pre_emphasis, de_emphasis, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, paging_signal);
if (rc < 0) {
PDEBUG(DBNETZ, DEBUG_ERROR, "Failed to init transceiver process!\n");
goto error;
@@ -231,7 +231,7 @@ error_paging:
bnetz->sender.ruffrequenz = bnetz_kanal2freq(19, 0);
/* init audio processing */
rc = dsp_init_sender(bnetz);
rc = dsp_init_sender(bnetz, squelch_db);
if (rc < 0) {
PDEBUG(DBNETZ, DEBUG_ERROR, "Failed to init audio processing!\n");
goto error;
@@ -385,11 +385,11 @@ const char *bnetz_get_telegramm(bnetz_t *bnetz)
}
/* Loss of signal was detected, release active call. */
void bnetz_loss_indication(bnetz_t *bnetz)
void bnetz_loss_indication(bnetz_t *bnetz, double loss_time)
{
if (bnetz->state == BNETZ_GESPRAECH
|| bnetz->state == BNETZ_RUFHALTUNG) {
PDEBUG_CHAN(DBNETZ, DEBUG_NOTICE, "Detected loss of signal, releasing.\n");
PDEBUG_CHAN(DBNETZ, DEBUG_NOTICE, "Detected loss of signal after %.1f seconds, releasing.\n", loss_time);
bnetz_release(bnetz, TRENN_COUNT);
call_in_release(bnetz->callref, CAUSE_TEMPFAIL);
bnetz->callref = 0;

View File

@@ -1,3 +1,4 @@
#include "../common/squelch.h"
#include "../common/fsk.h"
#include "../common/sender.h"
@@ -93,11 +94,9 @@ typedef struct bnetz {
int tone_count; /* how long has that tone been detected */
const char *tx_telegramm; /* carries bits of one frame to transmit */
int tx_telegramm_pos;
int samples_per_chunk; /* samples per loss detection interval */
sample_t *chunk_spl; /* chunk sample */
int chunk_pos; /* current received sample of chunk */
double meter_phaseshift65536; /* how much the phase of sine wave changes per sample */
double meter_phase65536; /* current phase */
squelch_t squelch; /* squelch detection process */
/* loopback test for latency */
int loopback_count; /* count digits from 0 to 9 */
@@ -106,9 +105,9 @@ typedef struct bnetz {
double bnetz_kanal2freq(int kanal, int unterband);
int bnetz_init(void);
int bnetz_create(int kanal, const char *audiodev, int use_sdr, int samplerate, double rx_gain, int gfs, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback, double loss_factor, const char *paging, int metering);
int bnetz_create(int kanal, const char *audiodev, int use_sdr, int samplerate, double rx_gain, int gfs, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback, double squelch_db, const char *paging, int metering);
void bnetz_destroy(sender_t *sender);
void bnetz_loss_indication(bnetz_t *bnetz);
void bnetz_loss_indication(bnetz_t *bnetz, double loss_time);
void bnetz_receive_tone(bnetz_t *bnetz, int bit);
void bnetz_receive_telegramm(bnetz_t *bnetz, uint16_t telegramm, double level_avg, double level_dev, double quality_avg);
const char *bnetz_get_telegramm(bnetz_t *bnetz);

View File

@@ -57,9 +57,8 @@
#define TONE_DETECT_TH 7 /* 70 milliseconds to detect continuous tone */
/* carrier loss detection */
#define CHUNK_DURATION 0.010 /* 10 ms */
#define LOSS_INTERVAL 100 /* filter steps (milliseconds) for one second interval */
#define LOSS_TIME 12 /* duration of signal loss before release */
#define MUTE_TIME 0.1 /* time to mute after loosing signal */
#define LOSS_TIME 12.5 /* duration of signal loss before release (according to FTZ 1727 Pfl 32 Clause 3.2.3.2) */
/* table for fast sine generation */
static sample_t dsp_metering[65536];
@@ -78,17 +77,16 @@ static int fsk_send_bit(void *inst);
static void fsk_receive_bit(void *inst, int bit, double quality, double level);
/* Init transceiver instance. */
int dsp_init_sender(bnetz_t *bnetz)
int dsp_init_sender(bnetz_t *bnetz, double squelch_db)
{
sample_t *spl;
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Init DSP for 'Sender'.\n");
/* init squelch */
squelch_init(&bnetz->squelch, bnetz->sender.kanal, squelch_db, MUTE_TIME, LOSS_TIME);
/* set modulation parameters */
sender_set_fm(&bnetz->sender, MAX_DEVIATION, MAX_MODULATION, DBM0_DEVIATION, MAX_DISPLAY);
audio_init_loss(&bnetz->sender.loss, LOSS_INTERVAL, bnetz->sender.loss_volume, LOSS_TIME);
PDEBUG(DDSP, DEBUG_DEBUG, "Using FSK level of %.3f (%.3f KHz deviation @ 2000 Hz)\n", TX_PEAK_FSK, 4.0);
/* init fsk */
@@ -99,15 +97,6 @@ int dsp_init_sender(bnetz_t *bnetz)
bnetz->tone_detected = -1;
bnetz->samples_per_chunk = (double)bnetz->sender.samplerate * CHUNK_DURATION;
PDEBUG(DDSP, DEBUG_DEBUG, "Using %d samples per chunk duration.\n", bnetz->samples_per_chunk);
spl = calloc(bnetz->samples_per_chunk, sizeof(sample_t));
if (!spl) {
PDEBUG(DDSP, DEBUG_ERROR, "No memory!\n");
return -ENOMEM;
}
bnetz->chunk_spl = spl;
/* metering tone */
bnetz->meter_phaseshift65536 = 65536.0 / ((double)bnetz->sender.samplerate / METERING_HZ);
PDEBUG(DDSP, DEBUG_DEBUG, "dial_phaseshift = %.4f\n", bnetz->meter_phaseshift65536);
@@ -127,11 +116,6 @@ void dsp_cleanup_sender(bnetz_t *bnetz)
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Cleanup DSP for 'Sender'.\n");
fsk_cleanup(&bnetz->fsk);
if (bnetz->chunk_spl) {
free(bnetz->chunk_spl);
bnetz->chunk_spl = NULL;
}
}
/* Count duration of tone and indicate detection/loss to protocol handler. */
@@ -154,8 +138,6 @@ static void fsk_receive_tone(bnetz_t *bnetz, int bit, int goodtone, double level
bnetz->tone_count++;
if (bnetz->tone_count >= TONE_DETECT_TH)
audio_reset_loss(&bnetz->sender.loss);
if (bnetz->tone_count == TONE_DETECT_TH) {
PDEBUG_CHAN(DDSP, DEBUG_INFO, "Detecting continuous tone: F%d Level=%3.0f%% Quality=%3.0f%%\n", bnetz->tone_detected, level * 100.0, quality * 100.0);
/* must reset, so we will not get corrupt first digit */
@@ -224,32 +206,28 @@ static void fsk_receive_bit(void *inst, int bit, double quality, double level)
}
/* Process received audio stream from radio unit. */
void sender_receive(sender_t *sender, sample_t *samples, int length)
void sender_receive(sender_t *sender, sample_t *samples, int length, double rf_level_db)
{
bnetz_t *bnetz = (bnetz_t *) sender;
sample_t *spl;
int max, pos;
double level;
int pos;
int i;
/* write received samples to decode buffer */
max = bnetz->samples_per_chunk;
pos = bnetz->chunk_pos;
spl = bnetz->chunk_spl;
for (i = 0; i < length; i++) {
spl[pos++] = samples[i];
if (pos == max) {
pos = 0;
level = audio_level(spl, max);
if (audio_detect_loss(&bnetz->sender.loss, level))
bnetz_loss_indication(bnetz);
}
}
bnetz->chunk_pos = pos;
/* fsk/tone signal */
fsk_receive(&bnetz->fsk, samples, length);
/* process signal mute/loss, without signalling tone / FSK frames */
switch (squelch(&bnetz->squelch, rf_level_db, (double)length / (double)bnetz->sender.samplerate)) {
case SQUELCH_LOSS:
bnetz_loss_indication(bnetz, LOSS_TIME);
// fall through:
case SQUELCH_MUTE:
memset(samples, 0, sizeof(*samples) * length);
break;
default:
break;
}
if ((bnetz->dsp_mode == DSP_MODE_AUDIO
|| bnetz->dsp_mode == DSP_MODE_AUDIO_METER) && bnetz->callref) {
int count;

View File

@@ -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);

View File

@@ -22,6 +22,7 @@
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "../common/sample.h"
#include "../common/debug.h"
#include "../common/timer.h"
@@ -39,7 +40,7 @@
int gfs = 2;
int metering = 20;
const char *paging = "tone";
double lossdetect = 0;
double squelch_db = -INFINITY;
void print_help(const char *arg0)
{
@@ -70,9 +71,11 @@ void print_help(const char *arg0)
printf(" Example: /sys/class/gpio/gpio17/value=1:0 writes a '1' to\n");
printf(" /sys/class/gpio/gpio17/value to switching to channel 19 and a '0' to\n");
printf(" switch back. (default = %s)\n", paging);
printf(" -L --loss <volume>\n");
printf(" Detect loss of carrier by detecting steady noise above given volume in\n");
printf(" percent. (disabled by default)\n");
printf(" -S --squelch <dB>\n");
printf(" Use given RF level to detect loss of signal. When the signal gets lost\n");
printf(" and stays below this level, the connection is released.\n");
printf(" Use 'auto' to do automatic noise floor calibration to detect loss.\n");
printf(" Only works with SDR! (disabled by default)\n");
printf("\nstation-id: Give 5 digit station-id, you don't need to enter it for every\n");
printf(" start of this program.\n");
main_mobile_print_hotkeys();
@@ -87,11 +90,11 @@ static int handle_options(int argc, char **argv)
{"gfs", 1, 0, 'G'},
{"gebuehrenimpuls", 1, 0, 'M'},
{"paging", 1, 0, 'P'},
{"loss", 1, 0, 'L'},
{"squelch", 1, 0, 'S'},
{0, 0, 0, 0},
};
main_mobile_set_options("G:M:P:L:", long_options_special);
main_mobile_set_options("G:M:P:S:", long_options_special);
while (1) {
int option_index = 0, c;
@@ -123,8 +126,11 @@ static int handle_options(int argc, char **argv)
paging = strdup(optarg);
skip_args += 2;
break;
case 'L':
lossdetect = atoi(optarg);
case 'S':
if (!strcasecmp(optarg, "auto"))
squelch_db = 0.0;
else
squelch_db = atof(optarg);
skip_args += 2;
break;
default:
@@ -194,7 +200,7 @@ int main(int argc, char *argv[])
/* create transceiver instance */
for (i = 0; i < num_kanal; i++) {
rc = bnetz_create(kanal[i], audiodev[i], use_sdr, samplerate, rx_gain, gfs, do_pre_emphasis, do_de_emphasis, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, (double)lossdetect / 100.0, paging, metering);
rc = bnetz_create(kanal[i], audiodev[i], use_sdr, samplerate, rx_gain, gfs, do_pre_emphasis, do_de_emphasis, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, squelch_db, paging, metering);
if (rc < 0) {
fprintf(stderr, "Failed to create \"Sender\" instance. Quitting!\n");
goto fail;