diff options
-rw-r--r-- | docs/a-netz.html | 6 | ||||
-rw-r--r-- | docs/b-netz.html | 58 | ||||
-rw-r--r-- | src/amps/amps.c | 2 | ||||
-rw-r--r-- | src/amps/dsp.c | 2 | ||||
-rw-r--r-- | src/anetz/anetz.c | 10 | ||||
-rw-r--r-- | src/anetz/anetz.h | 6 | ||||
-rw-r--r-- | src/anetz/dsp.c | 32 | ||||
-rw-r--r-- | src/anetz/dsp.h | 2 | ||||
-rw-r--r-- | src/anetz/main.c | 23 | ||||
-rw-r--r-- | src/bnetz/bnetz.c | 10 | ||||
-rw-r--r-- | src/bnetz/bnetz.h | 9 | ||||
-rw-r--r-- | src/bnetz/dsp.c | 62 | ||||
-rw-r--r-- | src/bnetz/dsp.h | 2 | ||||
-rw-r--r-- | src/bnetz/main.c | 24 | ||||
-rw-r--r-- | src/cnetz/cnetz.c | 2 | ||||
-rw-r--r-- | src/cnetz/dsp.c | 2 | ||||
-rw-r--r-- | src/common/Makefile.am | 4 | ||||
-rw-r--r-- | src/common/call.c | 3 | ||||
-rw-r--r-- | src/common/loss.c | 93 | ||||
-rw-r--r-- | src/common/loss.h | 15 | ||||
-rw-r--r-- | src/common/sdr.c | 3 | ||||
-rw-r--r-- | src/common/sdr.h | 2 | ||||
-rw-r--r-- | src/common/sender.c | 10 | ||||
-rw-r--r-- | src/common/sender.h | 11 | ||||
-rw-r--r-- | src/common/sound.h | 2 | ||||
-rw-r--r-- | src/common/sound_alsa.c | 3 | ||||
-rw-r--r-- | src/common/squelch.c | 139 | ||||
-rw-r--r-- | src/common/squelch.h | 26 | ||||
-rw-r--r-- | src/nmt/dsp.c | 2 | ||||
-rw-r--r-- | src/nmt/nmt.c | 2 | ||||
-rw-r--r-- | src/r2000/dsp.c | 2 | ||||
-rw-r--r-- | src/r2000/r2000.c | 2 | ||||
-rw-r--r-- | src/tv/main.c | 2 |
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 <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. </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 < -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(&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 | <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; |