aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/a-netz.html6
-rw-r--r--docs/b-netz.html58
-rw-r--r--src/amps/amps.c2
-rw-r--r--src/amps/dsp.c2
-rw-r--r--src/anetz/anetz.c10
-rw-r--r--src/anetz/anetz.h6
-rw-r--r--src/anetz/dsp.c32
-rw-r--r--src/anetz/dsp.h2
-rw-r--r--src/anetz/main.c23
-rw-r--r--src/bnetz/bnetz.c10
-rw-r--r--src/bnetz/bnetz.h9
-rw-r--r--src/bnetz/dsp.c62
-rw-r--r--src/bnetz/dsp.h2
-rw-r--r--src/bnetz/main.c24
-rw-r--r--src/cnetz/cnetz.c2
-rw-r--r--src/cnetz/dsp.c2
-rw-r--r--src/common/Makefile.am4
-rw-r--r--src/common/call.c3
-rw-r--r--src/common/loss.c93
-rw-r--r--src/common/loss.h15
-rw-r--r--src/common/sdr.c3
-rw-r--r--src/common/sdr.h2
-rw-r--r--src/common/sender.c10
-rw-r--r--src/common/sender.h11
-rw-r--r--src/common/sound.h2
-rw-r--r--src/common/sound_alsa.c3
-rw-r--r--src/common/squelch.c139
-rw-r--r--src/common/squelch.h26
-rw-r--r--src/nmt/dsp.c2
-rw-r--r--src/nmt/nmt.c2
-rw-r--r--src/r2000/dsp.c2
-rw-r--r--src/r2000/r2000.c2
-rw-r--r--src/tv/main.c2
33 files changed, 310 insertions, 263 deletions
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
<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>
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.
<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>
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(&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;
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 | <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;
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 <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;
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 <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;
-}
-
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 <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;
+ }
+}
+
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;