forked from cellular-infrastructure/osmocom-analog
B-Netz: Refactoring tone and quality detection
This commit is contained in:
parent
3b58408691
commit
f4988297a6
|
@ -399,6 +399,8 @@ void bnetz_receive_tone(bnetz_t *bnetz, int bit)
|
||||||
bnetz->dial_mode = DIAL_MODE_START;
|
bnetz->dial_mode = DIAL_MODE_START;
|
||||||
bnetz_set_dsp_mode(bnetz, DSP_MODE_1);
|
bnetz_set_dsp_mode(bnetz, DSP_MODE_1);
|
||||||
timer_start(&bnetz->timer, DIALING_TO);
|
timer_start(&bnetz->timer, DIALING_TO);
|
||||||
|
/* must reset, so we will not get corrupt first digit */
|
||||||
|
bnetz->rx_telegramm = bnetz->tone_detected * 0xffff;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -431,17 +433,11 @@ void bnetz_receive_tone(bnetz_t *bnetz, int bit)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A digit was received. */
|
/* A digit was received. */
|
||||||
void bnetz_receive_telegramm(bnetz_t *bnetz, uint16_t telegramm, double level_avg, double level_stddev, double quality_avg)
|
void bnetz_receive_telegramm(bnetz_t *bnetz, uint16_t telegramm)
|
||||||
{
|
{
|
||||||
struct impulstelegramm *it;
|
struct impulstelegramm *it;
|
||||||
int digit = 0;
|
int digit = 0;
|
||||||
|
|
||||||
/* drop any telegramm that is too bad */
|
|
||||||
if (level_stddev / level_avg > 0.2)
|
|
||||||
return;
|
|
||||||
|
|
||||||
PDEBUG_CHAN(DDSP, DEBUG_INFO, "RX Level: average=%.0f%% standard deviation=%.0f%% Quality: %.0f%%\n", level_avg * 100.0, level_stddev / level_avg * 100.0, quality_avg * 100.0);
|
|
||||||
|
|
||||||
it = bnetz_telegramm2digit(telegramm);
|
it = bnetz_telegramm2digit(telegramm);
|
||||||
if (it) {
|
if (it) {
|
||||||
digit = it->digit;
|
digit = it->digit;
|
||||||
|
|
|
@ -79,6 +79,7 @@ typedef struct bnetz {
|
||||||
|
|
||||||
/* display measurements */
|
/* display measurements */
|
||||||
dispmeasparam_t *dmp_tone_level;
|
dispmeasparam_t *dmp_tone_level;
|
||||||
|
dispmeasparam_t *dmp_tone_stddev;
|
||||||
dispmeasparam_t *dmp_tone_quality;
|
dispmeasparam_t *dmp_tone_quality;
|
||||||
dispmeasparam_t *dmp_frame_level;
|
dispmeasparam_t *dmp_frame_level;
|
||||||
dispmeasparam_t *dmp_frame_stddev;
|
dispmeasparam_t *dmp_frame_stddev;
|
||||||
|
@ -91,8 +92,13 @@ typedef struct bnetz {
|
||||||
double rx_telegramm_quality[16];/* quality of each bit in telegramm */
|
double rx_telegramm_quality[16];/* quality of each bit in telegramm */
|
||||||
double rx_telegramm_level[16]; /* level of each bit in telegramm */
|
double rx_telegramm_level[16]; /* level of each bit in telegramm */
|
||||||
int rx_telegramm_qualidx; /* index of quality array above */
|
int rx_telegramm_qualidx; /* index of quality array above */
|
||||||
|
uint16_t rx_tone; /* rx shift register for receiveing continous tone */
|
||||||
|
double rx_tone_quality[16]; /* quality of tone fragment (100th of second) */
|
||||||
|
double rx_tone_level[16]; /* level of tone fragment (100th of second) */
|
||||||
|
int rx_tone_qualidx; /* index of quality array above */
|
||||||
int tone_detected; /* what tone has been detected */
|
int tone_detected; /* what tone has been detected */
|
||||||
int tone_count; /* how long has that tone been detected */
|
int tone_count; /* how long has that tone been detected */
|
||||||
|
int tone_duration; /* how long has that tone been detected */
|
||||||
const char *tx_telegramm; /* carries bits of one frame to transmit */
|
const char *tx_telegramm; /* carries bits of one frame to transmit */
|
||||||
int tx_telegramm_pos;
|
int tx_telegramm_pos;
|
||||||
double meter_phaseshift65536; /* how much the phase of sine wave changes per sample */
|
double meter_phaseshift65536; /* how much the phase of sine wave changes per sample */
|
||||||
|
@ -110,6 +116,6 @@ int bnetz_create(int kanal, const char *audiodev, int use_sdr, int samplerate, d
|
||||||
void bnetz_destroy(sender_t *sender);
|
void bnetz_destroy(sender_t *sender);
|
||||||
void bnetz_loss_indication(bnetz_t *bnetz, double loss_time);
|
void bnetz_loss_indication(bnetz_t *bnetz, double loss_time);
|
||||||
void bnetz_receive_tone(bnetz_t *bnetz, int bit);
|
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);
|
void bnetz_receive_telegramm(bnetz_t *bnetz, uint16_t telegramm);
|
||||||
const char *bnetz_get_telegramm(bnetz_t *bnetz);
|
const char *bnetz_get_telegramm(bnetz_t *bnetz);
|
||||||
|
|
||||||
|
|
158
src/bnetz/dsp.c
158
src/bnetz/dsp.c
|
@ -53,7 +53,9 @@
|
||||||
#define F0 2070.0
|
#define F0 2070.0
|
||||||
#define F1 1950.0
|
#define F1 1950.0
|
||||||
#define METERING_HZ 2900 /* metering pulse frequency */
|
#define METERING_HZ 2900 /* metering pulse frequency */
|
||||||
#define TONE_DETECT_TH 7 /* 70 milliseconds to detect continuous tone */
|
#define TONE_DETECT_CNT 7 /* 70 milliseconds to detect continuous tone */
|
||||||
|
#define TONE_LOST_CNT 14 /* we use twice of the detect time, so we bridge a loss of "TONE_DETECT_CNT duration" */
|
||||||
|
#define TONE_STDDEV_TH 0.2 /* threshold of bad quality (standard deviation) to reject tone */
|
||||||
|
|
||||||
/* carrier loss detection */
|
/* carrier loss detection */
|
||||||
#define MUTE_TIME 0.1 /* time to mute after loosing signal */
|
#define MUTE_TIME 0.1 /* time to mute after loosing signal */
|
||||||
|
@ -80,6 +82,11 @@ int dsp_init_sender(bnetz_t *bnetz, double squelch_db)
|
||||||
{
|
{
|
||||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Init DSP for 'Sender'.\n");
|
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Init DSP for 'Sender'.\n");
|
||||||
|
|
||||||
|
if (TONE_DETECT_CNT > sizeof(bnetz->rx_tone_quality) / sizeof(bnetz->rx_tone_quality[0])) {
|
||||||
|
PDEBUG_CHAN(DDSP, DEBUG_ERROR, "buffer for tone quality is too small, please fix!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/* init squelch */
|
/* init squelch */
|
||||||
squelch_init(&bnetz->squelch, bnetz->sender.kanal, squelch_db, MUTE_TIME, LOSS_TIME);
|
squelch_init(&bnetz->squelch, bnetz->sender.kanal, squelch_db, MUTE_TIME, LOSS_TIME);
|
||||||
|
|
||||||
|
@ -101,6 +108,7 @@ int dsp_init_sender(bnetz_t *bnetz, double squelch_db)
|
||||||
PDEBUG(DDSP, DEBUG_DEBUG, "dial_phaseshift = %.4f\n", bnetz->meter_phaseshift65536);
|
PDEBUG(DDSP, DEBUG_DEBUG, "dial_phaseshift = %.4f\n", bnetz->meter_phaseshift65536);
|
||||||
|
|
||||||
bnetz->dmp_tone_level = display_measurements_add(&bnetz->sender.dispmeas, "Tone Level", "%.1f %%", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 150.0, 100.0);
|
bnetz->dmp_tone_level = display_measurements_add(&bnetz->sender.dispmeas, "Tone Level", "%.1f %%", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 150.0, 100.0);
|
||||||
|
bnetz->dmp_tone_stddev = display_measurements_add(&bnetz->sender.dispmeas, "Tone Stddev", "%.1f %%", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 100.0, 100.0);
|
||||||
bnetz->dmp_tone_quality = display_measurements_add(&bnetz->sender.dispmeas, "Tone Quality", "%.1f %%", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 100.0, 100.0);
|
bnetz->dmp_tone_quality = display_measurements_add(&bnetz->sender.dispmeas, "Tone Quality", "%.1f %%", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 100.0, 100.0);
|
||||||
bnetz->dmp_frame_level = display_measurements_add(&bnetz->sender.dispmeas, "Frame Level", "%.1f %% (last)", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 150.0, 100.0);
|
bnetz->dmp_frame_level = display_measurements_add(&bnetz->sender.dispmeas, "Frame Level", "%.1f %% (last)", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 150.0, 100.0);
|
||||||
bnetz->dmp_frame_stddev = display_measurements_add(&bnetz->sender.dispmeas, "Frame Stddev", "%.1f %% (last)", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 100.0, 100.0);
|
bnetz->dmp_frame_stddev = display_measurements_add(&bnetz->sender.dispmeas, "Frame Stddev", "%.1f %% (last)", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 100.0, 100.0);
|
||||||
|
@ -117,31 +125,38 @@ void dsp_cleanup_sender(bnetz_t *bnetz)
|
||||||
fsk_cleanup(&bnetz->fsk);
|
fsk_cleanup(&bnetz->fsk);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Count duration of tone and indicate detection/loss to protocol handler. */
|
/* If good tone is received, we just set this tone, if not already and reset counters.
|
||||||
static void fsk_receive_tone(bnetz_t *bnetz, int bit, int goodtone, double level, double quality)
|
* If band tone was received, we just unset this tone, if not already.
|
||||||
|
* Count duration of tone and indicate detection/loss to protocol handler.
|
||||||
|
*/
|
||||||
|
static void fsk_receive_tone(bnetz_t *bnetz, int tone, int goodtone, double level_avg, double level_stddev, double quality_avg)
|
||||||
{
|
{
|
||||||
/* lost tone because it is not good anymore or has changed */
|
/* count duration of the tone being detected */
|
||||||
if (!goodtone || bit != bnetz->tone_detected) {
|
bnetz->tone_duration++;
|
||||||
if (bnetz->tone_count >= TONE_DETECT_TH) {
|
|
||||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Lost F%d tone after %d ms.\n", bnetz->tone_detected, bnetz->tone_count);
|
|
||||||
bnetz_receive_tone(bnetz, -1);
|
|
||||||
}
|
|
||||||
if (goodtone)
|
|
||||||
bnetz->tone_detected = bit;
|
|
||||||
else
|
|
||||||
bnetz->tone_detected = -1;
|
|
||||||
bnetz->tone_count = 0;
|
|
||||||
|
|
||||||
return;
|
/* check if we have a good tone that was not previously detected
|
||||||
|
* in the previous function we already checked for TONE_DETECT_CNT intervals so we directly mark the tone as detected */
|
||||||
|
if (goodtone) {
|
||||||
|
bnetz->tone_count = 0;
|
||||||
|
/* if tone was undetected or has changed, we set new detected tone */
|
||||||
|
if (tone != bnetz->tone_detected) {
|
||||||
|
/* set duration to TONE_DETECT_CNT, because it took that long to detect the tone */
|
||||||
|
bnetz->tone_duration = TONE_DETECT_CNT;
|
||||||
|
bnetz->tone_detected = tone;
|
||||||
|
PDEBUG_CHAN(DDSP, DEBUG_INFO, "Detecting continuous tone: F%d Level=%3.0f%% standard deviation=%.0f%% Quality=%3.0f%%\n", bnetz->tone_detected, level_avg * 100.0, level_stddev / level_avg * 100.0, quality_avg * 100.0);
|
||||||
|
bnetz_receive_tone(bnetz, bnetz->tone_detected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bnetz->tone_count++;
|
/* lost detected tone because it is not good anymore or has changed */
|
||||||
|
if (!goodtone && bnetz->tone_detected > -1) {
|
||||||
if (bnetz->tone_count == TONE_DETECT_TH) {
|
bnetz->tone_count++;
|
||||||
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);
|
if (bnetz->tone_count == TONE_LOST_CNT) {
|
||||||
/* must reset, so we will not get corrupt first digit */
|
/* substract TONE_LOST_CNT from duration, because it took that long to detect loss of tone */
|
||||||
bnetz->rx_telegramm = bnetz->tone_detected * 0xffff;
|
PDEBUG_CHAN(DDSP, DEBUG_INFO, "Lost F%d tone after %.2f seconds.\n", bnetz->tone_detected, (double)(bnetz->tone_duration - TONE_LOST_CNT) / 100.0);
|
||||||
bnetz_receive_tone(bnetz, bnetz->tone_detected);
|
bnetz->tone_detected = -1;
|
||||||
|
bnetz_receive_tone(bnetz, -1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,53 +170,96 @@ static void fsk_receive_bit(void *inst, int bit, double quality, double level)
|
||||||
/* normalize FSK level */
|
/* normalize FSK level */
|
||||||
level /= TX_PEAK_FSK;
|
level /= TX_PEAK_FSK;
|
||||||
|
|
||||||
/* update measurements */
|
/* store level and quality to tone buffer */
|
||||||
display_measurements_update(bnetz->dmp_tone_level, level * 100.0 , 0.0);
|
bnetz->rx_tone_quality[bnetz->rx_tone_qualidx] = quality;
|
||||||
display_measurements_update(bnetz->dmp_tone_quality, quality * 100.0, 0.0);
|
bnetz->rx_tone_level[bnetz->rx_tone_qualidx] = level;
|
||||||
|
if (++bnetz->rx_tone_qualidx == TONE_DETECT_CNT)
|
||||||
|
bnetz->rx_tone_qualidx = 0;
|
||||||
|
|
||||||
/* continuous tone detection */
|
/* average level and quality of tone */
|
||||||
if (level > 0.10 && quality > 0.10) {
|
level_avg = level_stddev = quality_avg = 0;
|
||||||
fsk_receive_tone(bnetz, bit, 1, level, quality);
|
for (i = 0; i < TONE_DETECT_CNT; i++) {
|
||||||
} else
|
level_avg += bnetz->rx_tone_level[i];
|
||||||
fsk_receive_tone(bnetz, bit, 0, level, quality);
|
quality_avg += bnetz->rx_tone_quality[i];
|
||||||
|
}
|
||||||
|
level_avg /= TONE_DETECT_CNT; quality_avg /= TONE_DETECT_CNT;
|
||||||
|
for (i = 0; i < TONE_DETECT_CNT; i++) {
|
||||||
|
level = bnetz->rx_tone_level[i];
|
||||||
|
level_stddev += (level - level_avg) * (level - level_avg);
|
||||||
|
}
|
||||||
|
level_stddev = sqrt(level_stddev / TONE_DETECT_CNT);
|
||||||
|
|
||||||
/* collect bits */
|
/* update tone measurements */
|
||||||
if (level < 0.05)
|
display_measurements_update(bnetz->dmp_tone_level, level_avg * 100.0, 0.0);
|
||||||
return;
|
display_measurements_update(bnetz->dmp_tone_stddev, level_stddev / level_avg * 100.0, 0.0);
|
||||||
bnetz->rx_telegramm = (bnetz->rx_telegramm << 1) | bit;
|
display_measurements_update(bnetz->dmp_tone_quality, quality_avg * 100.0, 0.0);
|
||||||
|
|
||||||
|
/* collect bits, and check for level and continous tone */
|
||||||
|
bnetz->rx_tone = (bnetz->rx_tone << 1) | bit;
|
||||||
|
for (i = 0; i < TONE_DETECT_CNT; i++) {
|
||||||
|
if (((bnetz->rx_tone >> i) & 1) != bit)
|
||||||
|
break;
|
||||||
|
if (bnetz->rx_tone_level[i] < 0.05)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* continuous tone detection:
|
||||||
|
* 1. The quality must be good enough.
|
||||||
|
* 2. All bits must be the same (continuous tone).
|
||||||
|
*/
|
||||||
|
if (level_stddev / level_avg > TONE_STDDEV_TH || i < TONE_DETECT_CNT)
|
||||||
|
fsk_receive_tone(bnetz, bit, 0, level_avg, level_stddev, quality_avg);
|
||||||
|
else
|
||||||
|
fsk_receive_tone(bnetz, bit, 1, level_avg, level_stddev, quality_avg);
|
||||||
|
|
||||||
|
/* store level and quality to telegram buffer */
|
||||||
bnetz->rx_telegramm_quality[bnetz->rx_telegramm_qualidx] = quality;
|
bnetz->rx_telegramm_quality[bnetz->rx_telegramm_qualidx] = quality;
|
||||||
bnetz->rx_telegramm_level[bnetz->rx_telegramm_qualidx] = level;
|
bnetz->rx_telegramm_level[bnetz->rx_telegramm_qualidx] = level;
|
||||||
if (++bnetz->rx_telegramm_qualidx == 16)
|
if (++bnetz->rx_telegramm_qualidx == 16)
|
||||||
bnetz->rx_telegramm_qualidx = 0;
|
bnetz->rx_telegramm_qualidx = 0;
|
||||||
|
|
||||||
|
/* average level and quality of frame */
|
||||||
|
level_avg = level_stddev = quality_avg = 0;
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
level_avg += bnetz->rx_telegramm_level[i];
|
||||||
|
quality_avg += bnetz->rx_telegramm_quality[i];
|
||||||
|
}
|
||||||
|
level_avg /= 16.0; quality_avg /= 16.0;
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
level = bnetz->rx_telegramm_level[i];
|
||||||
|
level_stddev += (level - level_avg) * (level - level_avg);
|
||||||
|
}
|
||||||
|
level_stddev = sqrt(level_stddev / 16.0);
|
||||||
|
|
||||||
|
/* collect bits */
|
||||||
|
bnetz->rx_telegramm = (bnetz->rx_telegramm << 1) | bit;
|
||||||
|
|
||||||
/* check if pattern 01110xxxxxxxxxxx matches */
|
/* check if pattern 01110xxxxxxxxxxx matches */
|
||||||
if ((bnetz->rx_telegramm & 0xf800) != 0x7000)
|
if ((bnetz->rx_telegramm & 0xf800) != 0x7000)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* average level and quality */
|
/* collect bits, and check for level */
|
||||||
level_avg = level_stddev = quality_avg = 0;
|
|
||||||
for (i = 0; i < 16; i++) {
|
for (i = 0; i < 16; i++) {
|
||||||
level_avg += bnetz->rx_telegramm_level[bnetz->rx_telegramm_qualidx];
|
if (bnetz->rx_telegramm_level[i] < 0.05)
|
||||||
quality_avg += bnetz->rx_telegramm_quality[bnetz->rx_telegramm_qualidx];
|
break;
|
||||||
if (++bnetz->rx_telegramm_qualidx == 16)
|
|
||||||
bnetz->rx_telegramm_qualidx = 0;
|
|
||||||
}
|
}
|
||||||
level_avg /= 16.0; quality_avg /= 16.0;
|
|
||||||
for (i = 0; i < 16; i++) {
|
|
||||||
level = bnetz->rx_telegramm_level[bnetz->rx_telegramm_qualidx];
|
|
||||||
level_stddev += (level - level_avg) * (level - level_avg);
|
|
||||||
if (++bnetz->rx_telegramm_qualidx == 16)
|
|
||||||
bnetz->rx_telegramm_qualidx = 0;
|
|
||||||
}
|
|
||||||
level_stddev = sqrt(level_stddev / 16.0);
|
|
||||||
|
|
||||||
/* update measurements */
|
if (i == 16)
|
||||||
|
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "FSK Valid bits: %d / %d Level: %.0f%% Stddev: %.0f%%\n", i, 16, level_avg * 100.0, level_stddev / level_avg * 100.0);
|
||||||
|
|
||||||
|
/* drop any telegramm that is too bad */
|
||||||
|
if (level_stddev / level_avg > TONE_STDDEV_TH || i < 16)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* update telegramm measurements */
|
||||||
display_measurements_update(bnetz->dmp_frame_level, level_avg * 100.0 , 0.0);
|
display_measurements_update(bnetz->dmp_frame_level, level_avg * 100.0 , 0.0);
|
||||||
display_measurements_update(bnetz->dmp_frame_stddev, level_stddev / level_avg * 100.0, 0.0);
|
display_measurements_update(bnetz->dmp_frame_stddev, level_stddev / level_avg * 100.0, 0.0);
|
||||||
display_measurements_update(bnetz->dmp_frame_quality, quality_avg * 100.0, 0.0);
|
display_measurements_update(bnetz->dmp_frame_quality, quality_avg * 100.0, 0.0);
|
||||||
|
|
||||||
|
PDEBUG_CHAN(DDSP, DEBUG_INFO, "Telegramm RX Level: average=%.0f%% standard deviation=%.0f%% Quality: %.0f%%\n", level_avg * 100.0, level_stddev / level_avg * 100.0, quality_avg * 100.0);
|
||||||
|
|
||||||
/* receive telegramm */
|
/* receive telegramm */
|
||||||
bnetz_receive_telegramm(bnetz, bnetz->rx_telegramm, level_avg, level_stddev, quality_avg);
|
bnetz_receive_telegramm(bnetz, bnetz->rx_telegramm);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process received audio stream from radio unit. */
|
/* Process received audio stream from radio unit. */
|
||||||
|
|
Loading…
Reference in New Issue