From b32e0ab6025a025850dc0079fa6596a96d7295a6 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 9 Oct 2017 20:49:14 +0200 Subject: Implementation of RX level squelch (for A-Netz and B-Netz) Use -S 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 --- docs/a-netz.html | 6 +++ docs/b-netz.html | 58 ++++++++------------ src/amps/amps.c | 2 +- src/amps/dsp.c | 2 +- src/anetz/anetz.c | 10 ++-- src/anetz/anetz.h | 6 ++- src/anetz/dsp.c | 32 ++++++----- src/anetz/dsp.h | 2 +- src/anetz/main.c | 23 ++++---- src/bnetz/bnetz.c | 10 ++-- src/bnetz/bnetz.h | 9 ++-- src/bnetz/dsp.c | 62 +++++++-------------- src/bnetz/dsp.h | 2 +- src/bnetz/main.c | 24 +++++---- src/cnetz/cnetz.c | 2 +- src/cnetz/dsp.c | 2 +- src/common/Makefile.am | 4 +- src/common/call.c | 3 +- src/common/loss.c | 93 -------------------------------- src/common/loss.h | 15 ------ src/common/sdr.c | 3 +- src/common/sdr.h | 2 +- src/common/sender.c | 10 ++-- src/common/sender.h | 11 ++-- src/common/sound.h | 2 +- src/common/sound_alsa.c | 3 +- src/common/squelch.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++ src/common/squelch.h | 26 +++++++++ src/nmt/dsp.c | 2 +- src/nmt/nmt.c | 2 +- src/r2000/dsp.c | 2 +- src/r2000/r2000.c | 2 +- src/tv/main.c | 2 +- 33 files changed, 310 insertions(+), 263 deletions(-) delete mode 100644 src/common/loss.c delete mode 100644 src/common/loss.h create mode 100644 src/common/squelch.c create mode 100644 src/common/squelch.h diff --git a/docs/a-netz.html b/docs/a-netz.html index d91534c..b0bf9ee 100644 --- a/docs/a-netz.html +++ b/docs/a-netz.html @@ -259,6 +259,9 @@ Be sure that the phone turns off the transmitter and indicates the (green) light

Level adjustment: +

+ +

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

Detecting loss of carrier signal: +

+ +

To automatically release the call, when the carrier signal gets lost, look at the B-Netz page. It is the same principle.

diff --git a/docs/b-netz.html b/docs/b-netz.html index 4dfaf19..6bdb18e 100644 --- a/docs/b-netz.html +++ b/docs/b-netz.html @@ -638,6 +638,9 @@ The base station returns to idle.

Level adjustment: +

+ +

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%

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: +

+ +

+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 <db>' or '--squelch <db>' 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.

 ...
-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
 ...
 

-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. -

- -

-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:

 ...
-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 < -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
 ...
 
diff --git a/src/amps/amps.c b/src/amps/amps.c index 3bc22b6..9fe0c4e 100644 --- a/src/amps/amps.c +++ b/src/amps/amps.c @@ -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(&s->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(&s->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; diff --git a/src/amps/dsp.c b/src/amps/dsp.c index 2415f0a..f3eb35b 100644 --- a/src/amps/dsp.c +++ b/src/amps/dsp.c @@ -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; diff --git a/src/anetz/anetz.c b/src/anetz/anetz.c index 1d58efe..27eb1b0 100644 --- a/src/anetz/anetz.c +++ b/src/anetz/anetz.c @@ -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; diff --git a/src/anetz/anetz.h b/src/anetz/anetz.h index b43c5be..7f0cac5 100644 --- a/src/anetz/anetz.h +++ b/src/anetz/anetz.h @@ -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); diff --git a/src/anetz/dsp.c b/src/anetz/dsp.c index 266f4d6..4d60110 100644 --- a/src/anetz/dsp.c +++ b/src/anetz/dsp.c @@ -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; diff --git a/src/anetz/dsp.h b/src/anetz/dsp.h index aff93a9..04ea2a8 100644 --- a/src/anetz/dsp.h +++ b/src/anetz/dsp.h @@ -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); diff --git a/src/anetz/main.c b/src/anetz/main.c index e144f8a..ab386ec 100644 --- a/src/anetz/main.c +++ b/src/anetz/main.c @@ -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 | \n"); printf(" Cycle paging tones, rather than sending simultaniously. Try 100.\n"); printf(" (default = '%d')\n", page_sequence); - printf(" -L --loss \n"); - printf(" Detect loss of carrier by detecting steady noise above given volume in\n"); - printf(" percent. (disabled by default)\n"); + printf(" -S --squelch | 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; diff --git a/src/bnetz/bnetz.c b/src/bnetz/bnetz.c index a1ff011..a136f85 100644 --- a/src/bnetz/bnetz.c +++ b/src/bnetz/bnetz.c @@ -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; diff --git a/src/bnetz/bnetz.h b/src/bnetz/bnetz.h index 608f067..95d6581 100644 --- a/src/bnetz/bnetz.h +++ b/src/bnetz/bnetz.h @@ -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); diff --git a/src/bnetz/dsp.c b/src/bnetz/dsp.c index 8d830be..368ee32 100644 --- a/src/bnetz/dsp.c +++ b/src/bnetz/dsp.c @@ -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; diff --git a/src/bnetz/dsp.h b/src/bnetz/dsp.h index 1114799..48f3bb5 100644 --- a/src/bnetz/dsp.h +++ b/src/bnetz/dsp.h @@ -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); diff --git a/src/bnetz/main.c b/src/bnetz/main.c index 04466b3..d927649 100644 --- a/src/bnetz/main.c +++ b/src/bnetz/main.c @@ -22,6 +22,7 @@ #include #include #include +#include #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 \n"); - printf(" Detect loss of carrier by detecting steady noise above given volume in\n"); - printf(" percent. (disabled by default)\n"); + printf(" -S --squelch \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; diff --git a/src/cnetz/cnetz.c b/src/cnetz/cnetz.c index 856d6ce..0ec5f09 100644 --- a/src/cnetz/cnetz.c +++ b/src/cnetz/cnetz.c @@ -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; diff --git a/src/cnetz/dsp.c b/src/cnetz/dsp.c index f34b798..af15a68 100644 --- a/src/cnetz/dsp.c +++ b/src/cnetz/dsp.c @@ -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; diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 0edd221..0ae7f9c 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -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 \ diff --git a/src/common/call.c b/src/common/call.c index cdda131..1bfc233 100644 --- a/src/common/call.c +++ b/src/common/call.c @@ -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) diff --git a/src/common/loss.c b/src/common/loss.c deleted file mode 100644 index 0e37f95..0000000 --- a/src/common/loss.c +++ /dev/null @@ -1,93 +0,0 @@ -/* Loss detection - * - * (C) 2016 by Andreas Eversberg - * 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 . - */ - -#include -#include -#include -#include -#include -#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; -} - diff --git a/src/common/loss.h b/src/common/loss.h deleted file mode 100644 index be3cc48..0000000 --- a/src/common/loss.h +++ /dev/null @@ -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); - diff --git a/src/common/sdr.c b/src/common/sdr.c index c2684e3..b461f7a 100644 --- a/src/common/sdr.c +++ b/src/common/sdr.c @@ -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; diff --git a/src/common/sdr.h b/src/common/sdr.h index 1bbfa7b..360f424 100644 --- a/src/common/sdr.h +++ b/src/common/sdr.h @@ -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); diff --git a/src/common/sender.c b/src/common/sender.c index 9cd8c0b..a3b90e2 100644 --- a/src/common/sender.c +++ b/src/common/sender.c @@ -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); diff --git a/src/common/sender.h b/src/common/sender.h index ca53de4..4ca728f 100644 --- a/src/common/sender.h +++ b/src/common/sender.h @@ -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); diff --git a/src/common/sound.h b/src/common/sound.h index f5ac621..71293c0 100644 --- a/src/common/sound.h +++ b/src/common/sound.h @@ -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); diff --git a/src/common/sound_alsa.c b/src/common/sound_alsa.c index a9c3ae1..c18f123 100644 --- a/src/common/sound_alsa.c +++ b/src/common/sound_alsa.c @@ -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; diff --git a/src/common/squelch.c b/src/common/squelch.c new file mode 100644 index 0000000..97188c7 --- /dev/null +++ b/src/common/squelch.c @@ -0,0 +1,139 @@ +/* Squelch functions + * + * (C) 2017 by Andreas Eversberg + * 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 . + */ + +#include +#include +#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; + } +} + diff --git a/src/common/squelch.h b/src/common/squelch.h new file mode 100644 index 0000000..45de690 --- /dev/null +++ b/src/common/squelch.h @@ -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); + diff --git a/src/nmt/dsp.c b/src/nmt/dsp.c index b95191b..9ff6d67 100644 --- a/src/nmt/dsp.c +++ b/src/nmt/dsp.c @@ -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; diff --git a/src/nmt/nmt.c b/src/nmt/nmt.c index 40d8727..73dcdc7 100644 --- a/src/nmt/nmt.c +++ b/src/nmt/nmt.c @@ -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; diff --git a/src/r2000/dsp.c b/src/r2000/dsp.c index ca61122..d689971 100644 --- a/src/r2000/dsp.c +++ b/src/r2000/dsp.c @@ -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; diff --git a/src/r2000/r2000.c b/src/r2000/r2000.c index 8d14775..7eda9f7 100644 --- a/src/r2000/r2000.c +++ b/src/r2000/r2000.c @@ -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; diff --git a/src/tv/main.c b/src/tv/main.c index 7e133da..911d1e5 100644 --- a/src/tv/main.c +++ b/src/tv/main.c @@ -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; -- cgit v1.2.3