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

@ -259,6 +259,9 @@ Be sure that the phone turns off the transmitter and indicates the (green) light
<p>
Level adjustment:
</p>
<p>
We see a receive level of around 140%.
Then start the base station using '-l 2' option for loop-back and tune receiver to the transmitter.
The base station generates a 1750 Hz test signal, just like the mobile phone.
@ -308,6 +311,9 @@ Be sure to check: Does your transmitter has enough frequency deviation (15 KHz i
<p>
Detecting loss of carrier signal:
</p>
<p>
To automatically release the call, when the carrier signal gets lost, look at the B-Netz page.
It is the same principle.
</p>

View File

@ -638,6 +638,9 @@ The base station returns to idle.
<p>
Level adjustment:
</p>
<p>
We see a receive level of around 85%.
Tune your receiver to the up-link frequency, so you get loop-back of base station broadcast.
Use the variable resistor (connecting your transmitter) to adjust the volume until the received level matches the same level of your previously received message.
@ -703,53 +706,38 @@ dsp.c:159 info : Detecting continous tone: 2070:Level= 104% Quality=100%
<p>
Detecting loss of carrier signal:
We do not have any RSSI (Received Signal Strength Indicator) signal from our radio, so we cannot directly find out if the signal is lost.
But we have a constant noise level when the signal is lost.
Be sure to have squelch on your receiver all the way open, so that noise reaches the base station.
In order to see this level, use command line option '-L 100 -v 0' or '--loss 100 --verbose 0'.
The noise level (relative to the sound card's input level) is shown:
</p>
<p>
This works with SDR only, because we do not have any RSSI (Received Signal Strength Indicator) signal from a radio connected to the sound card.
With SDR we know the RX level, so we can define a threashold value for a lost signal.
Use '-S &lt;db&gt;' or '--squelch &lt;db&gt;' to define the squelch threshold level.
To measure the noise floor, use the 'm' key to get a bar graph of the current RSSI. (RF level)
Add some dB to the noise floor for the squelch threshold value.
An easier way is to use '-S auto' or '--squelch auto' to automatically measure the noise floor level and then automatically use a threshold level that is some dB above this measured level.
This level is then used to detect loss of carrier.
Also this level is used to mute the audio path, whenever the signal gets lost for a short time.
After about 12 seconds of signal loss, a call is released - similar to the real network.
</p>
<pre>
...
loss.c:74 debug : Noise level = 22%
squelch.c:94 info : RF signal measurement: -69.2 dB noise floor, using threshold of -63.2 dB
...
</pre>
<p>
Since we have a noise level of about 20%, we can use a threshold of 10%.
Use command line option '-L 10' in this case.
To see the process, keep debugging on by using command line option '-v 0'.
Whenever the noise level is above the given percentage, loss of carrier is assumed, if the noise level is constant.
If the noise level changes (due to speech), the noise is ignored and the loss counter is reset.
After a system specific duration of signal loss, the call is released.
</p>
<p>
In this example I cut the power off the phone and waited for the base station to time out.
In the following example I cut off the power of the phone beeing in a call and waited 12 seconds for the base station to time out:
</p>
<pre>
...
loss.c:74 debug : Noise level = 1%
loss.c:74 debug : Noise level = 2%
loss.c:74 debug : Noise level = 1%
loss.c:74 debug : Noise level = 22%
loss.c:74 debug : Noise level = 21%
loss.c:84 debug : Detected signal loss 1 for intervals level change 6% (below 10%).
loss.c:74 debug : Noise level = 21%
loss.c:84 debug : Detected signal loss 2 for intervals level change 2% (below 10%).
...
loss.c:74 debug : Noise level = 22%
loss.c:84 debug : Detected signal loss 11 for intervals level change 7% (below 10%).
loss.c:74 debug : Noise level = 21%
loss.c:84 debug : Detected signal loss 12 for intervals level change 3% (below 10%).
bnetz.c:448 notice : Detected loss of signal, releasing.
bnetz.c:363 info : Entering release state, sending 'Trennsignal'.
call.c:706 info : Call has been released with cause=41
bnetz.c:439 debug : Sending telegramm 'Trennsignal/Schlusssignal'.
bnetz.c:439 debug : Sending telegramm 'Trennsignal/Schlusssignal'.
bnetz.c:439 debug : Sending telegramm 'Trennsignal/Schlusssignal'.
squelch.c:114 info : RF signal weak: Muting audio (RF -77.6 dB &lt; -70.7 db)
bnetz.c:392 notice : Detected loss of signal after 12 seconds, releasing.
bnetz.c:297 info : Entering release state, sending 'Trennsignal' (4 times).
call.c:933 info : Call has been released with cause=41
bnetz.c:279 info : Entering IDLE state on channel 17, sending 'Gruppenfreisignal' 2.
call.c:637 info : Call hangup
...
</pre>

View File

@ -566,7 +566,7 @@ int amps_create(int channel, enum amps_chan_type chan_type, const char *audiodev
PDEBUG(DAMPS, DEBUG_DEBUG, "Creating 'AMPS' instance for channel = %d of band %s (sample rate %d).\n", channel, band, samplerate);
/* init general part of transceiver */
rc = sender_create(&amps->sender, channel, amps_channel2freq(channel, 0), amps_channel2freq(channel, 1), audiodev, use_sdr, samplerate, rx_gain, 0, 0, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, 0, PAGING_SIGNAL_NONE);
rc = sender_create(&amps->sender, channel, amps_channel2freq(channel, 0), amps_channel2freq(channel, 1), audiodev, use_sdr, samplerate, rx_gain, 0, 0, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, PAGING_SIGNAL_NONE);
if (rc < 0) {
PDEBUG(DAMPS, DEBUG_ERROR, "Failed to init transceiver process!\n");
goto error;

View File

@ -851,7 +851,7 @@ static void sender_receive_audio(amps_t *amps, sample_t *samples, int length)
}
/* 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 __attribute__((unused)) rf_level_db)
{
amps_t *amps = (amps_t *) sender;

View File

@ -186,7 +186,7 @@ static void anetz_timeout(struct timer *timer);
static void anetz_go_idle(anetz_t *anetz);
/* Create transceiver instance and link to a list. */
int anetz_create(int kanal, const char *audiodev, int use_sdr, int samplerate, double rx_gain, double page_gain, int page_sequence, 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_volume)
int anetz_create(int kanal, const char *audiodev, int use_sdr, int samplerate, double rx_gain, double page_gain, int page_sequence, 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)
{
anetz_t *anetz;
int rc;
@ -205,14 +205,14 @@ int anetz_create(int kanal, const char *audiodev, int use_sdr, int samplerate, d
PDEBUG(DANETZ, DEBUG_DEBUG, "Creating 'A-Netz' instance for 'Kanal' = %d (sample rate %d).\n", kanal, samplerate);
/* init general part of transceiver */
rc = sender_create(&anetz->sender, kanal, anetz_kanal2freq(kanal, 0), anetz_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_volume, PAGING_SIGNAL_NONE);
rc = sender_create(&anetz->sender, kanal, anetz_kanal2freq(kanal, 0), anetz_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_NONE);
if (rc < 0) {
PDEBUG(DANETZ, DEBUG_ERROR, "Failed to init 'Sender' processing!\n");
goto error;
}
/* init audio processing */
rc = dsp_init_sender(anetz, page_gain, page_sequence);
rc = dsp_init_sender(anetz, page_gain, page_sequence, squelch_db);
if (rc < 0) {
PDEBUG(DANETZ, DEBUG_ERROR, "Failed to init signal processing!\n");
goto error;
@ -282,10 +282,10 @@ static void anetz_page(anetz_t *anetz, const char *dial_string, double *freq)
}
/* Loss of signal was detected, release active call. */
void anetz_loss_indication(anetz_t *anetz)
void anetz_loss_indication(anetz_t *anetz, double loss_time)
{
if (anetz->state == ANETZ_GESPRAECH) {
PDEBUG_CHAN(DANETZ, DEBUG_NOTICE, "Detected loss of signal, releasing.\n");
PDEBUG_CHAN(DANETZ, DEBUG_NOTICE, "Detected loss of signal after %.1f seconds, releasing.\n", loss_time);
anetz_release(anetz);
call_in_release(anetz->callref, CAUSE_TEMPFAIL);
anetz->callref = 0;

View File

@ -1,3 +1,4 @@
#include "../common/squelch.h"
#include "../common/goertzel.h"
#include "../common/sender.h"
@ -46,13 +47,14 @@ typedef struct anetz {
int paging_tone; /* current tone (0..3) in sequenced mode */
int paging_count; /* current sample count of tone in seq. mode */
int paging_transition; /* set to number of samples during transition */
squelch_t squelch; /* squelch detection process */
} anetz_t;
double anetz_kanal2freq(int kanal, int unterband);
int anetz_init(void);
int anetz_create(int kanal, const char *audiodev, int use_sdr, int samplerate, double rx_gain, double page_gain, int page_sequence, 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_volume);
int anetz_create(int kanal, const char *audiodev, int use_sdr, int samplerate, double rx_gain, double page_gain, int page_sequence, 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);
void anetz_destroy(sender_t *sender);
void anetz_loss_indication(anetz_t *anetz);
void anetz_loss_indication(anetz_t *anetz, double loss_time);
void anetz_receive_tone(anetz_t *anetz, int bit);

View File

@ -49,8 +49,8 @@
#define TONE_DETECT_TH 8 /* chunk intervals to detect continuous tone */
/* carrier loss detection */
#define LOSS_INTERVAL 100 /* filter steps (chunk durations) 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.0 /* duration of signal loss before release (what was the actual duration ???) */
/* two signaling tones */
static double fsk_tones[2] = {
@ -77,7 +77,7 @@ void dsp_init(void)
}
/* Init transceiver instance. */
int dsp_init_sender(anetz_t *anetz, double page_gain, int page_sequence)
int dsp_init_sender(anetz_t *anetz, double page_gain, int page_sequence, double squelch_db)
{
sample_t *spl;
int i;
@ -85,14 +85,15 @@ int dsp_init_sender(anetz_t *anetz, double page_gain, int page_sequence)
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Init DSP for 'Sender'.\n");
/* init squelch */
squelch_init(&anetz->squelch, anetz->sender.kanal, squelch_db, MUTE_TIME, LOSS_TIME);
/* set modulation parameters */
sender_set_fm(&anetz->sender, MAX_DEVIATION * page_gain, MAX_MODULATION, DBM0_DEVIATION, MAX_DISPLAY);
anetz->page_gain = page_gain;
anetz->page_sequence = page_sequence;
audio_init_loss(&anetz->sender.loss, LOSS_INTERVAL, anetz->sender.loss_volume, LOSS_TIME);
anetz->samples_per_chunk = anetz->sender.samplerate * CHUNK_DURATION;
PDEBUG(DDSP, DEBUG_DEBUG, "Using %d samples per chunk duration.\n", anetz->samples_per_chunk);
spl = calloc(anetz->samples_per_chunk, sizeof(sample_t));
@ -147,24 +148,19 @@ static void fsk_receive_tone(anetz_t *anetz, int tone, int goodtone, double leve
anetz->tone_count++;
if (anetz->tone_count >= TONE_DETECT_TH)
audio_reset_loss(&anetz->sender.loss);
if (anetz->tone_count == TONE_DETECT_TH) {
PDEBUG_CHAN(DDSP, DEBUG_INFO, "Detecting continuous %.0f Hz tone. (level = %.0f%%, quality =%.0f%%)\n", fsk_tones[anetz->tone_detected], level * 100.0, quality * 100.0);
anetz_receive_tone(anetz, anetz->tone_detected);
}
}
/* Filter one chunk of audio an detect tone, quality and loss of signal. */
/* Filter one chunk of audio an detect tone and quality of signal. */
static void fsk_decode_chunk(anetz_t *anetz, sample_t *spl, int max)
{
double level, result[2], quality[2];
level = audio_level(spl, max);
if (audio_detect_loss(&anetz->sender.loss, level))
anetz_loss_indication(anetz);
audio_goertzel(anetz->fsk_tone_goertzel, spl, max, 0, result, 2);
/* normalize quality of tones and level */
@ -189,13 +185,25 @@ static void fsk_decode_chunk(anetz_t *anetz, sample_t *spl, int max)
}
/* 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)
{
anetz_t *anetz = (anetz_t *) sender;
sample_t *spl;
int max, pos;
int i;
/* process signal mute/loss, also for signalling tone */
switch (squelch(&anetz->squelch, rf_level_db, (double)length / (double)anetz->sender.samplerate)) {
case SQUELCH_LOSS:
anetz_loss_indication(anetz, LOSS_TIME);
// fall through:
case SQUELCH_MUTE:
memset(samples, 0, sizeof(*samples) * length);
break;
default:
break;
}
/* write received samples to decode buffer */
max = anetz->samples_per_chunk;
pos = anetz->fsk_filter_pos;

View File

@ -1,6 +1,6 @@
void dsp_init(void);
int dsp_init_sender(anetz_t *anetz, double page_gain, int page_seqeuence);
int dsp_init_sender(anetz_t *anetz, double page_gain, int page_seqeuence, double squelch_db);
void dsp_cleanup_sender(anetz_t *anetz);
void dsp_set_paging(anetz_t *anetz, double *freq);
void anetz_set_dsp_mode(anetz_t *anetz, enum dsp_mode mode, int detect_reset);

View File

@ -39,7 +39,7 @@
/* settings */
double page_gain = 1;
int page_sequence = 0;
double lossdetect = 0;
double squelch_db = -INFINITY;
void print_help(const char *arg0)
{
@ -55,9 +55,11 @@ void print_help(const char *arg0)
printf(" -P --page-sequence 0 | <ms>\n");
printf(" Cycle paging tones, rather than sending simultaniously. Try 100.\n");
printf(" (default = '%d')\n", page_sequence);
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> | auto\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 (last) 5 digits of station-id, you don't need to enter it\n");
printf(" for every start of this program.\n");
main_mobile_print_hotkeys();
@ -73,11 +75,11 @@ static int handle_options(int argc, char **argv)
{"geo", 1, 0, 'G'},
{"page-gain", 1, 0, 'V'},
{"page-sequence", 1, 0, 'P'},
{"loss", 1, 0, 'L'},
{"squelch", 1, 0, 'S'},
{0, 0, 0, 0}
};
main_mobile_set_options("G:V:P:L:", long_options_special);
main_mobile_set_options("G:V:P:S:", long_options_special);
while (1) {
int option_index = 0, c;
@ -109,8 +111,11 @@ static int handle_options(int argc, char **argv)
page_sequence = atoi(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:
@ -179,7 +184,7 @@ int main(int argc, char *argv[])
/* create transceiver instance */
for (i = 0; i < num_kanal; i++) {
rc = anetz_create(kanal[i], audiodev[i], use_sdr, samplerate, rx_gain, page_gain, page_sequence, do_pre_emphasis, do_de_emphasis, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, lossdetect / 100.0);
rc = anetz_create(kanal[i], audiodev[i], use_sdr, samplerate, rx_gain, page_gain, page_sequence, do_pre_emphasis, do_de_emphasis, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, squelch_db);
if (rc < 0) {
fprintf(stderr, "Failed to create \"Sender\" instance. Quitting!\n");
goto fail;

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;

View File

@ -306,7 +306,7 @@ int cnetz_create(int kanal, enum cnetz_chan_type chan_type, const char *audiodev
/* init general part of transceiver */
/* do not enable emphasis, since it is done by cnetz code, not by common sender code */
rc = sender_create(&cnetz->sender, kanal, cnetz_kanal2freq(kanal, 0), cnetz_kanal2freq(kanal, 1), audiodev, use_sdr, samplerate, rx_gain, 0, 0, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, 0, PAGING_SIGNAL_NONE);
rc = sender_create(&cnetz->sender, kanal, cnetz_kanal2freq(kanal, 0), cnetz_kanal2freq(kanal, 1), audiodev, use_sdr, samplerate, rx_gain, 0, 0, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, PAGING_SIGNAL_NONE);
if (rc < 0) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to init transceiver process!\n");
goto error;

View File

@ -550,7 +550,7 @@ static int fsk_distributed_encode(cnetz_t *cnetz, const char *bits)
/* decode samples and hut for bit changes
* use deviation to find greatest slope of the signal (bit change)
*/
void sender_receive(sender_t *sender, sample_t *samples, int length)
void sender_receive(sender_t *sender, sample_t *samples, int length, double __attribute__((unused)) rf_level_db)
{
cnetz_t *cnetz = (cnetz_t *) sender;

View File

@ -10,7 +10,6 @@ libcommon_a_SOURCES = \
wave.c \
goertzel.c \
jitter.c \
loss.c \
iir_filter.c \
dtmf.c \
samplerate.c \
@ -20,7 +19,8 @@ libcommon_a_SOURCES = \
fm_modulation.c \
fsk.c \
display_wave.c \
display_measurements.c
display_measurements.c \
squelch.c
libmobile_a_SOURCES = \
sender.c \

View File

@ -709,6 +709,7 @@ void process_call(int c)
/* handle audio, if sound device is used */
sample_t samples[call.latspl + 10], *samples_list[1];
uint8_t *power_list[1];
double rf_level_db[1];
int count;
int rc;
@ -754,7 +755,7 @@ void process_call(int c)
}
}
samples_list[0] = samples;
count = sound_read(call.sound, samples_list, call.latspl, 1);
count = sound_read(call.sound, samples_list, call.latspl, 1, rf_level_db);
if (count < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to read from sound device (rc = %d)!\n", count);
if (count == -EPIPE)

View File

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

View File

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

View File

@ -700,7 +700,7 @@ int sdr_write(void *inst, sample_t **samples, uint8_t **power, int num, enum pag
return sent;
}
int sdr_read(void *inst, sample_t **samples, int num, int channels)
int sdr_read(void *inst, sample_t **samples, int num, int channels, double *rf_level_db)
{
sdr_t *sdr = (sdr_t *)inst;
float *buff = NULL;
@ -794,6 +794,7 @@ int sdr_read(void *inst, sample_t **samples, int num, int channels)
avg = sqrt(avg /(double)count); /* RMS */
avg = log10(avg) * 20;
display_measurements_update(sdr->chan[c].dmp_rf_level, avg, 0.0);
rf_level_db[c] = avg;
min = 0.0;
max = 0.0;
avg = 0.0;

View File

@ -3,6 +3,6 @@ int sdr_start(void *inst);
void *sdr_open(const char *audiodev, double *tx_frequency, double *rx_frequency, int channels, double paging_frequency, int samplerate, int latspl, double bandwidth, double sample_deviation);
void sdr_close(void *inst);
int sdr_write(void *inst, sample_t **samples, uint8_t **power, int num, enum paging_signal *paging_signal, int *on, int channels);
int sdr_read(void *inst, sample_t **samples, int num, int channels);
int sdr_read(void *inst, sample_t **samples, int num, int channels, double *rf_level_db);
int sdr_get_tosend(void *inst, int latspl);

View File

@ -37,7 +37,7 @@ static sender_t **sender_tailp = &sender_head;
int cant_recover = 0;
/* Init transceiver instance and link to list of transceivers. */
int sender_create(sender_t *sender, int kanal, double sendefrequenz, double empfangsfrequenz, const char *audiodev, int use_sdr, int samplerate, double rx_gain, 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_volume, enum paging_signal paging_signal)
int sender_create(sender_t *sender, int kanal, double sendefrequenz, double empfangsfrequenz, const char *audiodev, int use_sdr, int samplerate, double rx_gain, 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, enum paging_signal paging_signal)
{
sender_t *master, *slave;
int rc = 0;
@ -51,7 +51,6 @@ int sender_create(sender_t *sender, int kanal, double sendefrequenz, double empf
sender->pre_emphasis = pre_emphasis;
sender->de_emphasis = de_emphasis;
sender->loopback = loopback;
sender->loss_volume = loss_volume;
sender->paging_signal = paging_signal;
sender->write_rx_wave = write_rx_wave;
sender->write_tx_wave = write_tx_wave;
@ -298,6 +297,7 @@ void process_sender_audio(sender_t *sender, int *quit, int latspl)
uint8_t pbuff[num_chan][latspl], *power[num_chan];
enum paging_signal paging_signal[num_chan];
int on[num_chan];
double rf_level_db[num_chan];
for (i = 0; i < num_chan; i++) {
samples[i] = buff[i];
power[i] = pbuff[i];
@ -337,7 +337,7 @@ cant_recover:
/* internal loopback: loop back TX audio to RX */
if (inst->loopback == 1) {
display_wave(inst, samples[i], count, inst->max_display);
sender_receive(inst, samples[i], count);
sender_receive(inst, samples[i], count, 0.0);
}
/* do pre emphasis towards radio */
if (inst->pre_emphasis)
@ -372,7 +372,7 @@ cant_recover:
t3 = get_time();
#endif
count = sender->audio_read(sender->audio, samples, latspl, num_chan);
count = sender->audio_read(sender->audio, samples, latspl, num_chan, rf_level_db);
if (count < 0) {
/* special case when audio_read wants us to quit */
if (count == -EPERM) {
@ -410,7 +410,7 @@ cant_recover:
}
if (inst->loopback != 1) {
display_wave(inst, samples[i], count, inst->max_display);
sender_receive(inst, samples[i], count);
sender_receive(inst, samples[i], count, rf_level_db[i]);
}
if (inst->loopback == 3)
jitter_save(&inst->dejitter, samples[i], count);

View File

@ -5,7 +5,6 @@
#include "wave.h"
#include "samplerate.h"
#include "jitter.h"
#include "loss.h"
#include "emphasis.h"
#include "display.h"
@ -45,7 +44,7 @@ typedef struct sender {
int (*audio_start)(void *);
void (*audio_close)(void *);
int (*audio_write)(void *, sample_t **, uint8_t **, int, enum paging_signal *, int *, int);
int (*audio_read)(void *, sample_t **, int, int);
int (*audio_read)(void *, sample_t **, int, int, double *);
int (*audio_get_tosend)(void *, int);
int samplerate;
samplerate_t srstate; /* sample rate conversion state */
@ -74,10 +73,6 @@ typedef struct sender {
sample_t rxbuf[160];
int rxbuf_pos; /* current fill of buffer */
/* loss of carrier detection */
double loss_volume;
loss_t loss;
/* paging tone */
enum paging_signal paging_signal; /* if paging signal is used and how it is performed */
int paging_on; /* 1 or 0 for on or off */
@ -93,14 +88,14 @@ typedef struct sender {
extern sender_t *sender_head;
extern int cant_recover;
int sender_create(sender_t *sender, int kanal, double sendefrequenz, double empfangsfrequenz, const char *audiodev, int use_sdr, int samplerate, double rx_gain, 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_volume, enum paging_signal paging_signal);
int sender_create(sender_t *sender, int kanal, double sendefrequenz, double empfangsfrequenz, const char *audiodev, int use_sdr, int samplerate, double rx_gain, 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, enum paging_signal paging_signal);
void sender_destroy(sender_t *sender);
void sender_set_fm(sender_t *sender, double max_deviation, double max_modulation, double dBm0_deviation, double max_display);
int sender_open_audio(int latspl);
int sender_start_audio(void);
void process_sender_audio(sender_t *sender, int *quit, int latspl);
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int count);
void sender_receive(sender_t *sender, sample_t *samples, int count);
void sender_receive(sender_t *sender, sample_t *samples, int count, double rf_level_db);
void sender_paging(sender_t *sender, int on);
sender_t *get_sender_by_empfangsfrequenz(double freq);

View File

@ -5,6 +5,6 @@ void *sound_open(const char *audiodev, double *tx_frequency, double *rx_frequenc
int sound_start(void *inst);
void sound_close(void *inst);
int sound_write(void *inst, sample_t **samples, uint8_t **power, int num, enum paging_signal *paging_signal, int *on, int channels);
int sound_read(void *inst, sample_t **samples, int num, int channels);
int sound_read(void *inst, sample_t **samples, int num, int channels, double *rf_level_db);
int sound_get_tosend(void *inst, int latspl);

View File

@ -353,7 +353,7 @@ int sound_write(void *inst, sample_t **samples, uint8_t __attribute__((unused))
#define KEEP_FRAMES 8 /* minimum frames not to read, due to bug in ALSA */
int sound_read(void *inst, sample_t **samples, int num, int channels)
int sound_read(void *inst, sample_t **samples, int num, int channels, double *rf_level_db)
{
sound_t *sound = (sound_t *)inst;
double spl_deviation = sound->spl_deviation;
@ -431,6 +431,7 @@ int sound_read(void *inst, sample_t **samples, int num, int channels)
if (!sender)
continue;
display_measurements_update(sound->dmp[i], log10((double)max[i] / 32768.0) * 20, 0.0);
rf_level_db[i] = 0.0;
}
return rc;

139
src/common/squelch.c Normal file
View File

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

26
src/common/squelch.h Normal file
View File

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

View File

@ -303,7 +303,7 @@ void super_reset(nmt_t *nmt)
}
/* 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 __attribute__((unused)) rf_level_db)
{
nmt_t *nmt = (nmt_t *) sender;
sample_t *spl;

View File

@ -283,7 +283,7 @@ int nmt_create(int nmt_system, const char *country, int channel, enum nmt_chan_t
PDEBUG(DNMT, DEBUG_DEBUG, "Creating 'NMT' instance for channel = %d (sample rate %d).\n", channel, samplerate);
/* init general part of transceiver */
rc = sender_create(&nmt->sender, channel, nmt_channel2freq(nmt_system, country, channel, 0, NULL, NULL, NULL), nmt_channel2freq(nmt_system, country, channel, 1, NULL, NULL, NULL), audiodev, use_sdr, samplerate, rx_gain, pre_emphasis, de_emphasis, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, 0, PAGING_SIGNAL_NONE);
rc = sender_create(&nmt->sender, channel, nmt_channel2freq(nmt_system, country, channel, 0, NULL, NULL, NULL), nmt_channel2freq(nmt_system, country, channel, 1, NULL, NULL, NULL), 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_NONE);
if (rc < 0) {
PDEBUG(DNMT, DEBUG_ERROR, "Failed to init transceiver process!\n");
goto error;

View File

@ -241,7 +241,7 @@ static void super_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 __attribute__((unused)) rf_level_db)
{
r2000_t *r2000 = (r2000_t *) sender;
sample_t *spl;

View File

@ -441,7 +441,7 @@ int r2000_create(int band, int channel, enum r2000_chan_type chan_type, const ch
PDEBUG(DR2000, DEBUG_DEBUG, "Creating 'Radiocom 2000' instance for channel = %d (sample rate %d).\n", channel, samplerate);
/* init general part of transceiver */
rc = sender_create(&r2000->sender, channel, r2000_channel2freq(band, channel, 0), r2000_channel2freq(band, channel, 1), audiodev, use_sdr, samplerate, rx_gain, 0, 0, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, 0, PAGING_SIGNAL_NONE);
rc = sender_create(&r2000->sender, channel, r2000_channel2freq(band, channel, 0), r2000_channel2freq(band, channel, 1), audiodev, use_sdr, samplerate, rx_gain, 0, 0, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, PAGING_SIGNAL_NONE);
if (rc < 0) {
PDEBUG(DR2000, DEBUG_ERROR, "Failed to init transceiver process!\n");
goto error;

View File

@ -324,7 +324,7 @@ static void tx_bas(sample_t *sample_bas, __attribute__((__unused__)) sample_t *s
int s, ss, tosend;
while (!quit) {
usleep(1000);
sdr_read(sdr, (void *)sendbuff, latspl, 0);
sdr_read(sdr, (void *)sendbuff, latspl, 0, NULL);
tosend = sdr_get_tosend(sdr, latspl);
if (tosend > latspl / 10)
tosend = latspl / 10;