C-Netz: Calibration/fix processing of tone levels on TX and RX side
FSK and compander levels are adjusted. Eliminate offsets between subsequent speech chunks. This is done by high-pass filter. An offset is not passed to the filter. Do correct audio processing chain: time compress -> compressor -> scrambler / pre-emphasis -> TX RX -> de-scrambler / de-emphasis -> expander -> time expand
This commit is contained in:
parent
5a9c9c7401
commit
46fa72894e
|
@ -144,8 +144,8 @@ typedef struct cnetz {
|
|||
double frame_last_phase; /* master's bit phase of last frame sync */
|
||||
|
||||
/* audio offset removal */
|
||||
double offset_removal_factor; /* how much to remove every sample */
|
||||
int16_t offset_last_sample; /* last sample of last audio chunk */
|
||||
double offset_factor; /* filer alpha of high-pass filter */
|
||||
double offset_y_last; /* last stored sample */
|
||||
|
||||
/* measurements */
|
||||
int measure_speed; /* measure clock speed */
|
||||
|
|
|
@ -25,23 +25,26 @@
|
|||
#include <errno.h>
|
||||
#include "../common/debug.h"
|
||||
#include "../common/timer.h"
|
||||
#include "../common/call.h"
|
||||
#include "cnetz.h"
|
||||
#include "sysinfo.h"
|
||||
#include "telegramm.h"
|
||||
#include "dsp.h"
|
||||
|
||||
/* test function to mirror received audio from ratio back to radio */
|
||||
//#define TEST_SCRABLE
|
||||
/* test the audio quality after cascading two scramblers (TEST_SCRABLE must be defined) */
|
||||
//#define TEST_UNSCRABLE
|
||||
//#define TEST_SCRAMBLE
|
||||
/* test the audio quality after cascading two scramblers (TEST_SCRAMBLE must be defined) */
|
||||
//#define TEST_UNSCRAMBLE
|
||||
|
||||
#define PI M_PI
|
||||
|
||||
#define FSK_DEVIATION 16384
|
||||
#define FSK_DEVIATION 10000
|
||||
#define COMPANDER_0DB 20000
|
||||
#define BITRATE 5280.0 /* bits per second */
|
||||
#define BLOCK_BITS 198 /* duration of one time slot including pause at beginning and end */
|
||||
#define CUT_OFF_OFFSET 300.0 /* cut off frequency for offset filter (level correction between subsequent audio chunks) */
|
||||
|
||||
#ifdef TEST_SCRABLE
|
||||
#ifdef TEST_SCRAMBLE
|
||||
jitter_t scrambler_test_jb;
|
||||
scrambler_t scrambler_test_scrambler1;
|
||||
scrambler_t scrambler_test_scrambler2;
|
||||
|
@ -77,6 +80,7 @@ int dsp_init_sender(cnetz_t *cnetz, int measure_speed, double clock_speed[2], do
|
|||
{
|
||||
int rc = 0;
|
||||
double size;
|
||||
double RC, dt;
|
||||
|
||||
PDEBUG(DDSP, DEBUG_DEBUG, "Init FSK for 'Sender'.\n");
|
||||
|
||||
|
@ -136,9 +140,14 @@ int dsp_init_sender(cnetz_t *cnetz, int measure_speed, double clock_speed[2], do
|
|||
|
||||
/* init compander, according to C-Netz specs, attack and recovery time
|
||||
* shall not exceed according to ITU G.162 */
|
||||
init_compander(&cnetz->cstate, 8000, 5.0, 22.5, 32767);
|
||||
init_compander(&cnetz->cstate, 8000, 5.0, 22.5, COMPANDER_0DB);
|
||||
|
||||
#ifdef TEST_SCRABLE
|
||||
/* use this filter to compensate level changes between two subsequent audio chunks */
|
||||
RC = 1.0 / (CUT_OFF_OFFSET * 2.0 *3.14);
|
||||
dt = 1.0 / cnetz->sender.samplerate;
|
||||
cnetz->offset_factor = RC / (RC + dt);
|
||||
|
||||
#ifdef TEST_SCRAMBLE
|
||||
rc = jitter_create(&scrambler_test_jb, cnetz->sender.samplerate / 5);
|
||||
if (rc < 0) {
|
||||
PDEBUG(DDSP, DEBUG_ERROR, "Failed to init jitter buffer for scrambler test!\n");
|
||||
|
@ -565,8 +574,8 @@ void sender_receive(sender_t *sender, int16_t *samples, int length)
|
|||
/* measure rx sample speed */
|
||||
calc_clock_speed(cnetz, length, 0, 0);
|
||||
|
||||
#ifdef TEST_SCRABLE
|
||||
#ifdef TEST_UNSCRABLE
|
||||
#ifdef TEST_SCRAMBLE
|
||||
#ifdef TEST_UNSCRAMBLE
|
||||
scrambler(&scrambler_test_scrambler1, samples, length);
|
||||
#endif
|
||||
jitter_save(&scrambler_test_jb, samples, length);
|
||||
|
@ -711,12 +720,15 @@ again:
|
|||
if (*spl == -32768) {
|
||||
/* marker found to insert new chunk of audio */
|
||||
jitter_load(&cnetz->sender.audio, speech_buffer, 100);
|
||||
/* 1. compress dynamics */
|
||||
compress_audio(&cnetz->cstate, speech_buffer, 100);
|
||||
/* 2. upsample */
|
||||
speech_length = samplerate_upsample(&cnetz->sender.srstate, speech_buffer, 100, speech_buffer);
|
||||
/* 3. scramble */
|
||||
if (cnetz->scrambler)
|
||||
scrambler(&cnetz->scrambler_tx, speech_buffer, speech_length);
|
||||
/* pre-emphasis is done by cnetz code, not by common code */
|
||||
/* pre-emphasis makes bad sound in conjunction with scrambler, so we disable */
|
||||
/* 4. pre-emphasis is done by cnetz code, not by common code */
|
||||
/* pre-emphasis is only used when scrambler is off, see FTZ 171 TR 60 Clause 4 */
|
||||
if (cnetz->pre_emphasis && !cnetz->scrambler)
|
||||
pre_emphasis(&cnetz->estate, speech_buffer, speech_length);
|
||||
speech_pos = 0;
|
||||
|
@ -751,7 +763,7 @@ void sender_send(sender_t *sender, int16_t *samples, int length)
|
|||
/* measure tx sample speed */
|
||||
calc_clock_speed(cnetz, length, 1, 0);
|
||||
|
||||
#ifdef TEST_SCRABLE
|
||||
#ifdef TEST_SCRAMBLE
|
||||
jitter_load(&scrambler_test_jb, samples, length);
|
||||
scrambler(&scrambler_test_scrambler2, samples, length);
|
||||
return;
|
||||
|
@ -775,3 +787,55 @@ void sender_send(sender_t *sender, int16_t *samples, int length)
|
|||
#endif
|
||||
}
|
||||
|
||||
/* unshrink audio segment from the duration of 60 bits to 12.5 ms */
|
||||
void unshrink_speech(cnetz_t *cnetz, int16_t *speech_buffer, int count)
|
||||
{
|
||||
int16_t *spl;
|
||||
int32_t value;
|
||||
int pos, i;
|
||||
double x, y, x_last, y_last, factor;
|
||||
|
||||
/* fix offset between speech blocks by using high pass filter */
|
||||
/* use first sample as previous sample, so we don't have a level jump between two subsequent audio chunks */
|
||||
x_last = speech_buffer[0];
|
||||
y_last = cnetz->offset_y_last;
|
||||
factor = cnetz->offset_factor;
|
||||
for (i = 0; i < count; i++) {
|
||||
x = (double)speech_buffer[i];
|
||||
/* high-pass to remove low level frequencies, caused by level jump between audio chunks */
|
||||
y = factor * (y_last + x - x_last);
|
||||
x_last = x;
|
||||
y_last = y;
|
||||
value = (int32_t)y;
|
||||
if (value < -32768.0)
|
||||
value = -32768.0;
|
||||
else if (value > 32767)
|
||||
value = 32767;
|
||||
speech_buffer[i] = value;
|
||||
}
|
||||
cnetz->offset_y_last = y_last;
|
||||
|
||||
/* 4. de-emphasis is done by cnetz code, not by common code */
|
||||
/* de-emphasis is only used when scrambler is off, see FTZ 171 TR 60 Clause 4 */
|
||||
if (cnetz->de_emphasis && !cnetz->scrambler)
|
||||
de_emphasis(&cnetz->estate, speech_buffer, count);
|
||||
/* 3. descramble */
|
||||
if (cnetz->scrambler)
|
||||
scrambler(&cnetz->scrambler_rx, speech_buffer, count);
|
||||
/* 2. decompress time */
|
||||
count = samplerate_downsample(&cnetz->sender.srstate, speech_buffer, count, speech_buffer);
|
||||
/* 1. expand dynamics */
|
||||
expand_audio(&cnetz->cstate, speech_buffer, count);
|
||||
/* to call control */
|
||||
spl = cnetz->sender.rxbuf;
|
||||
pos = cnetz->sender.rxbuf_pos;
|
||||
for (i = 0; i < count; i++) {
|
||||
spl[pos++] = speech_buffer[i];
|
||||
if (pos == 160) {
|
||||
call_tx_audio(cnetz->sender.callref, spl, 160);
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
cnetz->sender.rxbuf_pos = pos;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,4 +3,5 @@ void dsp_init(void);
|
|||
int dsp_init_sender(cnetz_t *cnetz, int measure_speed, double clock_speed[2], double noise);
|
||||
void dsp_cleanup_sender(cnetz_t *cnetz);
|
||||
void calc_clock_speed(cnetz_t *cnetz, uint64_t samples, int tx, int result);
|
||||
void unshrink_speech(cnetz_t *cnetz, int16_t *speech_buffer, int count);
|
||||
|
||||
|
|
|
@ -106,7 +106,6 @@
|
|||
#include <math.h>
|
||||
#include "../common/timer.h"
|
||||
#include "../common/debug.h"
|
||||
#include "../common/call.h"
|
||||
#include "cnetz.h"
|
||||
#include "dsp.h"
|
||||
#include "telegramm.h"
|
||||
|
@ -156,54 +155,9 @@ int fsk_fm_init(fsk_fm_demod_t *fsk, cnetz_t *cnetz, int samplerate, double bitr
|
|||
|
||||
fsk->level_threshold = 655;
|
||||
|
||||
/* reduce half of DC after about 3ms */
|
||||
cnetz->offset_removal_factor = pow(0.5, 1.0 / ((double)samplerate / 333.0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* unshrink audio segment from the duration of 60 bits to 12.5 ms */
|
||||
static inline void unshrink_speech(cnetz_t *cnetz)
|
||||
{
|
||||
int16_t *spl;
|
||||
int32_t value;
|
||||
int pos, i, count;
|
||||
double offset, factor;
|
||||
|
||||
/* fix offset between speech blocks */
|
||||
offset = (double)(speech_buffer[0] - cnetz->offset_last_sample);
|
||||
factor = cnetz->offset_removal_factor;
|
||||
for (i = 0; i < speech_count; i++) {
|
||||
value = (int32_t)speech_buffer[i] - (int)offset;
|
||||
if (value < -32768.0)
|
||||
value = -32768.0;
|
||||
else if (value > 32767)
|
||||
value = 32767;
|
||||
speech_buffer[i] = value;
|
||||
offset = offset * factor;
|
||||
}
|
||||
cnetz->offset_last_sample = speech_buffer[speech_count-1];
|
||||
|
||||
/* de-emphasis is done by cnetz code, not by common code */
|
||||
/* de-emphasis makes bad sound in conjunction with scrambler, so we disable */
|
||||
if (cnetz->de_emphasis && !cnetz->scrambler)
|
||||
de_emphasis(&cnetz->estate, speech_buffer, speech_count);
|
||||
if (cnetz->scrambler)
|
||||
scrambler(&cnetz->scrambler_rx, speech_buffer, speech_count);
|
||||
count = samplerate_downsample(&cnetz->sender.srstate, speech_buffer, speech_count, speech_buffer);
|
||||
expand_audio(&cnetz->cstate, speech_buffer, count);
|
||||
spl = cnetz->sender.rxbuf;
|
||||
pos = cnetz->sender.rxbuf_pos;
|
||||
for (i = 0; i < count; i++) {
|
||||
spl[pos++] = speech_buffer[i];
|
||||
if (pos == 160) {
|
||||
call_tx_audio(cnetz->sender.callref, spl, 160);
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
cnetz->sender.rxbuf_pos = pos;
|
||||
}
|
||||
|
||||
/* get levels, sync time and jitter from sync sequence or frame data */
|
||||
static inline void get_levels(fsk_fm_demod_t *fsk, int *_min, int *_max, int *_avg, int *_probes, int num, double *_time, double *_jitter)
|
||||
{
|
||||
|
@ -514,15 +468,18 @@ void fsk_fm_demod(fsk_fm_demod_t *fsk, int16_t *samples, int length)
|
|||
} else
|
||||
if (t >= 5.5 && t < 65.5) {
|
||||
/* get audio for the duration of 60 bits */
|
||||
/* prevent overflow, if speech_size != 0 and SPK_V
|
||||
* has been restarted. */
|
||||
if (speech_count <= speech_size)
|
||||
speech_buffer[speech_count++] = samples[i];
|
||||
} else
|
||||
if (t >= 65.5) {
|
||||
if (speech_count) {
|
||||
unshrink_speech(fsk->cnetz);
|
||||
unshrink_speech(fsk->cnetz, speech_buffer, speech_count);
|
||||
speech_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
bit_time += bits_per_sample;
|
||||
if (bit_time >= BITS_PER_SUPERFRAME) {
|
||||
|
|
Loading…
Reference in New Issue