parent
4b3e3385b5
commit
16acdbf59d
@ -1,3 +1,3 @@ |
||||
AUTOMAKE_OPTIONS = foreign
|
||||
SUBDIRS = common anetz bnetz nmt test
|
||||
SUBDIRS = common anetz bnetz cnetz nmt test
|
||||
|
||||
|
@ -0,0 +1,21 @@ |
||||
AM_CPPFLAGS = -Wall -g $(all_includes)
|
||||
|
||||
bin_PROGRAMS = \
|
||||
cnetz
|
||||
|
||||
cnetz_SOURCES = \
|
||||
cnetz.c \
|
||||
sysinfo.c \
|
||||
telegramm.c \
|
||||
dsp.c \
|
||||
fsk_fm_demod.c \
|
||||
scrambler.c \
|
||||
image.c \
|
||||
ansage.c \
|
||||
main.c
|
||||
cnetz_LDADD = \
|
||||
$(COMMON_LA) \
|
||||
$(ALSA_LIBS) \
|
||||
$(top_builddir)/src/common/libcommon.a \
|
||||
-lm
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,3 @@ |
||||
|
||||
void init_ansage(void); |
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,151 @@ |
||||
#include "../common/compander.h" |
||||
#include "../common/sender.h" |
||||
#include "fsk_fm_demod.h" |
||||
#include "scrambler.h" |
||||
|
||||
#define CNETZ_OGK_KANAL 131 |
||||
|
||||
/* dsp modes of transmission */ |
||||
enum dsp_mode { |
||||
DSP_SCHED_NONE = 0, /* use for sheduling: nothing to shedule */ |
||||
DSP_MODE_OGK, /* send "Telegramm" on OgK */ |
||||
DSP_MODE_SPK_K, /* send concentrated "Telegramm" SpK */ |
||||
DSP_MODE_SPK_V, /* send distributed "Telegramm" SpK */ |
||||
}; |
||||
|
||||
/* current state of c-netz sender */ |
||||
enum cnetz_state { |
||||
CNETZ_IDLE, /* broadcasting LR/MLR on Ogk */ |
||||
CNETZ_BUSY, /* currently processing a call, no other transaction allowed */ |
||||
}; |
||||
|
||||
/* login to the network */ |
||||
#define TRANS_EM (1 << 0) /* attach request received, sending reply */ |
||||
/* roaming to different base station/network */ |
||||
#define TRANS_UM (1 << 1) /* roaming request received, sending reply */ |
||||
/* check if phone is still on */ |
||||
#define TRANS_MA (1 << 2) /* periodic online check sent, waiting for reply */ |
||||
/* mobile originated call */ |
||||
#define TRANS_VWG (1 << 3) /* received dialing request, waiting for time slot to send dial order */ |
||||
#define TRANS_WAF (1 << 4) /* dial order sent, waiting for dialing */ |
||||
#define TRANS_WBP (1 << 5) /* dialing received, waiting for time slot to acknowledge call */ |
||||
#define TRANS_WBN (1 << 6) /* dialing received, waiting for time slot to reject call */ |
||||
#define TRANS_VAG (1 << 7) /* establishment of call sent, switching channel */ |
||||
/* mobile terminated call */ |
||||
#define TRANS_VAK (1 << 8) /* establishment of call sent, switching channel */ |
||||
/* traffic channel */ |
||||
#define TRANS_BQ (1 << 9) /* accnowledge channel */ |
||||
#define TRANS_VHQ (1 << 10) /* hold call */ |
||||
#define TRANS_RTA (1 << 11) /* hold call and make the phone ring */ |
||||
#define TRANS_DS (1 << 12) /* establish speech connection */ |
||||
#define TRANS_AHQ (1 << 13) /* establish speech connection after answer */ |
||||
/* release */ |
||||
#define TRANS_AF (1 << 14) /* release connection by base station */ |
||||
#define TRANS_AT (1 << 15) /* release connection by mobile station */ |
||||
|
||||
/* timers */ |
||||
#define F_BQ 8 /* number of not received frames at BQ state */ |
||||
#define F_VHQK 16 /* number of not received frames at VHQ state during concentrated signalling */ |
||||
#define F_VHQ 16 /* number of not received frames at VHQ state during distributed signalling */ |
||||
#define F_DS 16 /* number of not received frames at DS state */ |
||||
#define F_RTA 16 /* number of not received frames at RTA state */ |
||||
#define N_AFKT 6 /* number of release frames to send during concentrated signalling */ |
||||
#define N_AFV 4 /* number of release frames to send during distributed signalling */ |
||||
|
||||
/* clear causes */ |
||||
#define CNETZ_CAUSE_TEILNEHMERBESETZT 0 /* subscriber busy */ |
||||
#define CNETZ_CAUSE_GASSENBESETZT 1 /* network congested */ |
||||
#define CNETZ_CAUSE_FUNKTECHNISCH 2 /* radio transmission fault */ |
||||
|
||||
struct cnetz; |
||||
struct telegramm; |
||||
|
||||
typedef struct transaction { |
||||
struct transaction *next; /* pointer to next node in list */ |
||||
struct cnetz *cnetz; /* pointer to cnetz instance */ |
||||
uint8_t futln_nat; /* current station ID (3 values) */ |
||||
uint8_t futln_fuvst; |
||||
uint16_t futln_rest; |
||||
char dialing[17]; /* number dialed by the phone */ |
||||
int32_t state; /* state of transaction */ |
||||
int8_t release_cause; /* reason for release, (c-netz coding) */ |
||||
int count; /* counts resending things */ |
||||
struct timer timer; /* for varous timeouts */ |
||||
int mo_call; /* flags a moile originating call */ |
||||
int mt_call; /* flags a moile terminating call */ |
||||
} transaction_t; |
||||
|
||||
struct clock_speed { |
||||
double meas_ti; /* time stamp for measurement interval */ |
||||
double start_ti[4]; /* time stamp for start of counting */ |
||||
double last_ti[4]; /* time stamp of last received time */ |
||||
uint64_t spl_count[4]; /* sample counter for sound card */ |
||||
}; |
||||
|
||||
/* instance of cnetz sender */ |
||||
typedef struct cnetz { |
||||
sender_t sender; |
||||
scrambler_t scrambler_tx; /* mirror what we transmit to MS */ |
||||
scrambler_t scrambler_rx; /* mirror what we receive from MS */ |
||||
compander_t cstate; |
||||
int pre_emphasis; /* use pre_emphasis by this instance */ |
||||
int de_emphasis; /* use de_emphasis by this instance */ |
||||
emphasis_t estate; |
||||
|
||||
/* cell config */ |
||||
int ms_power; /* power level of MS, use 0..3 */ |
||||
int auth; /* authentication support of the cell */ |
||||
|
||||
/* all cnetz states */ |
||||
enum cnetz_state state; /* main state of sender */ |
||||
|
||||
/* scheduler */ |
||||
int sched_ts; /* current time slot */ |
||||
int last_tx_timeslot; /* last timeslot we transmitted, so we can match MS timeslot */ |
||||
int sched_r_m; /* Rufblock (0) / Meldeblock (1) */ |
||||
int sched_switch_mode; /* counts slots until mode is switched */ |
||||
enum dsp_mode sched_dsp_mode; /* what mode shall be switched to */ |
||||
|
||||
/* dsp states */ |
||||
enum dsp_mode dsp_mode; /* current mode: audio, "Telegramm", .... */ |
||||
fsk_fm_demod_t fsk_demod; /* demod process */ |
||||
int16_t fsk_deviation; /* deviation used for digital signal */ |
||||
int16_t fsk_ramp_up[256]; /* samples of upward ramp shape */ |
||||
int16_t fsk_ramp_down[256]; /* samples of downward ramp shape */ |
||||
double fsk_noise; /* send static between OgK frames */ |
||||
double fsk_bitduration; /* duration of a bit in samples */ |
||||
int16_t *fsk_tx_buffer; /* tx buffer for one data block */ |
||||
int fsk_tx_buffer_size; /* size of tx buffer (in samples) */ |
||||
int fsk_tx_buffer_length; /* usage of buffer (in samples) */ |
||||
int fsk_tx_buffer_pos; /* current position sending buffer */ |
||||
double fsk_tx_bitstep; /* fraction of a bit each sample */ |
||||
double fsk_tx_phase; /* current bit position */ |
||||
int scrambler; /* 0 = normal speech, 1 = scrambled speech */ |
||||
int16_t *dsp_speech_buffer; /* samples in one chunk */ |
||||
int dsp_speech_length; /* number of samples */ |
||||
int dsp_speech_pos; /* current position in buffer */ |
||||
|
||||
/* audio offset removal */ |
||||
double offset_removal_factor; /* how much to remove every sample */ |
||||
int16_t offset_last_sample; /* last sample of last audio chunk */ |
||||
|
||||
/* measurements */ |
||||
int measure_speed; /* measure clock speed */ |
||||
struct clock_speed clock_speed; |
||||
|
||||
transaction_t *trans_list; /* list of transactions */ |
||||
} cnetz_t; |
||||
|
||||
double cnetz_kanal2freq(int kanal, int unterband); |
||||
int cnetz_init(void); |
||||
int cnetz_create(const char *sounddev, int samplerate, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int kanal, int auth, int ms_power, int measure_speed, double clock_speed[2], double deviation, double noise, int loopback); |
||||
void cnetz_destroy(sender_t *sender); |
||||
void cnetz_sync_frame(cnetz_t *cnetz, double sync, int ts); |
||||
const struct telegramm *cnetz_transmit_telegramm_rufblock(cnetz_t *cnetz); |
||||
const struct telegramm *cnetz_transmit_telegramm_meldeblock(cnetz_t *cnetz); |
||||
void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, struct telegramm *telegramm, int block); |
||||
const struct telegramm *cnetz_transmit_telegramm_spk_k(cnetz_t *cnetz); |
||||
void cnetz_receive_telegramm_spk_k(cnetz_t *cnetz, struct telegramm *telegramm); |
||||
const struct telegramm *cnetz_transmit_telegramm_spk_v(cnetz_t *cnetz); |
||||
void cnetz_receive_telegramm_spk_v(cnetz_t *cnetz, struct telegramm *telegramm); |
||||
|
@ -0,0 +1,690 @@ |
||||
/* C-Netz audio processing
|
||||
* |
||||
* (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 <stdio.h> |
||||
#include <stdint.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <math.h> |
||||
#include <errno.h> |
||||
#include "../common/debug.h" |
||||
#include "../common/timer.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 PI M_PI |
||||
|
||||
#define BITRATE 5280.0 /* bits per second */ |
||||
#define BLOCK_BITS 198 /* duration of one time slot including pause at beginning and end */ |
||||
|
||||
#ifdef TEST_SCRABLE |
||||
jitter_t scrambler_test_jb; |
||||
scrambler_t scrambler_test_scrambler1; |
||||
scrambler_t scrambler_test_scrambler2; |
||||
#endif |
||||
|
||||
static int16_t ramp_up[256], ramp_down[256]; |
||||
|
||||
void dsp_init(void) |
||||
{ |
||||
} |
||||
|
||||
static void dsp_init_ramp(cnetz_t *cnetz) |
||||
{ |
||||
double c; |
||||
int i; |
||||
int16_t deviation = cnetz->fsk_deviation; |
||||
|
||||
PDEBUG(DDSP, DEBUG_DEBUG, "Generating smooth ramp table.\n"); |
||||
for (i = 0; i < 256; i++) { |
||||
c = cos((double)i / 256.0 * PI); |
||||
#if 0 |
||||
if (c < 0) |
||||
c = -sqrt(-c); |
||||
else |
||||
c = sqrt(c); |
||||
#endif |
||||
ramp_down[i] = (int)(c * (double)deviation); |
||||
ramp_up[i] = -ramp_down[i]; |
||||
} |
||||
} |
||||
|
||||
/* Init transceiver instance. */ |
||||
int dsp_init_sender(cnetz_t *cnetz, int measure_speed, double clock_speed[2], double deviation, double noise) |
||||
{ |
||||
int rc = 0; |
||||
double size; |
||||
|
||||
PDEBUG(DDSP, DEBUG_DEBUG, "Init FSK for 'Sender'.\n"); |
||||
|
||||
if (measure_speed) { |
||||
cnetz->measure_speed = measure_speed; |
||||
cant_recover = 1; |
||||
} |
||||
|
||||
if (clock_speed[0] > 1000 || clock_speed[0] < -1000 || clock_speed[1] > 1000 || clock_speed[1] < -1000) { |
||||
PDEBUG(DDSP, DEBUG_ERROR, "Clock speed %.1f,%.1f ppm out of range! Plese use range between +-1000 ppm!\n", clock_speed[0], clock_speed[1]); |
||||
return -EINVAL; |
||||
} |
||||
PDEBUG(DDSP, DEBUG_INFO, "Using clock speed of %.1f ppm (RX) and %.1f ppm (TX) to correct sound card's clock.\n", clock_speed[0], clock_speed[1]); |
||||
|
||||
cnetz->fsk_bitduration = (double)cnetz->sender.samplerate / ((double)BITRATE / (1.0 + clock_speed[1] / 1000000.0)); |
||||
cnetz->fsk_tx_bitstep = 1.0 / cnetz->fsk_bitduration; |
||||
PDEBUG(DDSP, DEBUG_DEBUG, "Use %.4f samples for one bit duration @ %d.\n", cnetz->fsk_bitduration, cnetz->sender.samplerate); |
||||
|
||||
size = cnetz->fsk_bitduration * (double)BLOCK_BITS * 16.0; /* 16 blocks for distributed frames */ |
||||
cnetz->fsk_tx_buffer_size = size * 1.1; /* more to compensate clock speed */ |
||||
cnetz->fsk_tx_buffer = calloc(sizeof(int16_t), cnetz->fsk_tx_buffer_size); |
||||
if (!cnetz->fsk_tx_buffer) { |
||||
PDEBUG(DDSP, DEBUG_DEBUG, "No memory!\n"); |
||||
rc = -ENOMEM; |
||||
goto error; |
||||
} |
||||
|
||||
/* create devation and ramp */ |
||||
if (deviation > 1.0) |
||||
deviation = 1.0; |
||||
cnetz->fsk_deviation = (int16_t)(deviation * 32766.9); /* be sure not to overflow -32767 .. 32767 */ |
||||
dsp_init_ramp(cnetz); |
||||
cnetz->fsk_noise = noise; |
||||
|
||||
/* create speech buffer */ |
||||
cnetz->dsp_speech_buffer = calloc(sizeof(int16_t), cnetz->sender.samplerate); /* buffer is greater than sr/1.1, just to be secure */ |
||||
if (!cnetz->dsp_speech_buffer) { |
||||
PDEBUG(DDSP, DEBUG_DEBUG, "No memory!\n"); |
||||
rc = -ENOMEM; |
||||
goto error; |
||||
} |
||||
|
||||
/* reinit the sample rate to shrink/expand audio */ |
||||
init_samplerate(&cnetz->sender.srstate, (double)cnetz->sender.samplerate / 1.1); /* 66 <-> 60 */ |
||||
|
||||
rc = fsk_fm_init(&cnetz->fsk_demod, cnetz, cnetz->sender.samplerate, (double)BITRATE / (1.0 + clock_speed[0] / 1000000.0)); |
||||
if (rc < 0) |
||||
goto error; |
||||
|
||||
/* init scrambler for shrinked audio */ |
||||
scrambler_setup(&cnetz->scrambler_tx, (double)cnetz->sender.samplerate / 1.1);
|
||||
scrambler_setup(&cnetz->scrambler_rx, (double)cnetz->sender.samplerate / 1.1); |
||||
|
||||
/* reinit jitter buffer for 8000 kHz */ |
||||
jitter_destroy(&cnetz->sender.audio); |
||||
rc = jitter_create(&cnetz->sender.audio, 8000 / 5); |
||||
if (rc < 0) |
||||
goto error; |
||||
|
||||
/* 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); |
||||
|
||||
#ifdef TEST_SCRABLE |
||||
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"); |
||||
exit(0); |
||||
} |
||||
scrambler_setup(&scrambler_test_scrambler1, cnetz->sender.samplerate); |
||||
scrambler_setup(&scrambler_test_scrambler2, cnetz->sender.samplerate); |
||||
#endif |
||||
|
||||
return 0; |
||||
|
||||
error: |
||||
dsp_cleanup_sender(cnetz); |
||||
|
||||
return rc; |
||||
} |
||||
|
||||
void dsp_cleanup_sender(cnetz_t *cnetz) |
||||
{ |
||||
PDEBUG(DDSP, DEBUG_DEBUG, "Cleanup FSK for 'Sender'.\n"); |
||||
|
||||
if (cnetz->fsk_tx_buffer) |
||||
free(cnetz->fsk_tx_buffer); |
||||
if (cnetz->dsp_speech_buffer) |
||||
free(cnetz->dsp_speech_buffer); |
||||
} |
||||
|
||||
/* receive sample time and calculate speed against system clock
|
||||
* tx: indicates transmit stream |
||||
* result: if set the actual signal speed is used (instead of sample rate) */ |
||||
void calc_clock_speed(cnetz_t *cnetz, uint64_t samples, int tx, int result) |
||||
{ |
||||
struct clock_speed *cs = &cnetz->clock_speed; |
||||
double ti; |
||||
double speed_ppm_rx[2], speed_ppm_tx[2]; |
||||
|
||||
if (!cnetz->measure_speed) |
||||
return; |
||||
|
||||
if (result) |
||||
tx += 2; |
||||
|
||||
ti = get_time(); |
||||
|
||||
/* skip some time to avoid false mesurement due to filling of buffers */ |
||||
if (cs->meas_ti == 0.0) { |
||||
cs->meas_ti = ti + 1.0; |
||||
return; |
||||
} |
||||
if (cs->meas_ti > ti) |
||||
return; |
||||
|
||||
/* start sample counting */ |
||||
if (cs->start_ti[tx] == 0.0) { |
||||
cs->start_ti[tx] = ti; |
||||
cs->spl_count[tx] = 0; |
||||
return; |
||||
} |
||||
|
||||
/* add elapsed time */ |
||||
cs->last_ti[tx] = ti; |
||||
cs->spl_count[tx] += samples; |
||||
|
||||
/* only calculate speed, if one second has elapsed */ |
||||
if (ti - cs->meas_ti <= 1.0) |
||||
return; |
||||
cs->meas_ti += 1.0; |
||||
|
||||
if (!cs->spl_count[2] || !cs->spl_count[3]) |
||||
return; |
||||
speed_ppm_rx[0] = ((double)cs->spl_count[0] / (double)cnetz->sender.samplerate) / (cs->last_ti[0] - cs->start_ti[0]) * 1000000.0 - 1000000.0; |
||||
speed_ppm_tx[0] = ((double)cs->spl_count[1] / (double)cnetz->sender.samplerate) / (cs->last_ti[1] - cs->start_ti[1]) * 1000000.0 - 1000000.0; |
||||
speed_ppm_rx[1] = ((double)cs->spl_count[2] / (double)cnetz->sender.samplerate) / (cs->last_ti[2] - cs->start_ti[2]) * 1000000.0 - 1000000.0; |
||||
speed_ppm_tx[1] = ((double)cs->spl_count[3] / (double)cnetz->sender.samplerate) / (cs->last_ti[3] - cs->start_ti[3]) * 1000000.0 - 1000000.0; |
||||
PDEBUG(DDSP, DEBUG_NOTICE, "Clock: RX=%.2f TX=%.2f; Signal: TX=%.2f RX=%.2f ppm\n", speed_ppm_rx[0], speed_ppm_tx[0], speed_ppm_rx[1], speed_ppm_tx[1]); |
||||
} |
||||
|
||||
static int fsk_nothing_encode(cnetz_t *cnetz) |
||||
{ |
||||
int16_t *spl; |
||||
double phase, bitstep, r; |
||||
int i, count; |
||||
|
||||
spl = cnetz->fsk_tx_buffer; |
||||
phase = cnetz->fsk_tx_phase; |
||||
bitstep = cnetz->fsk_tx_bitstep * 256.0; |
||||
|
||||
if (cnetz->fsk_noise) { |
||||
r = cnetz->fsk_noise; |
||||
/* add 198 bits of noise */ |
||||
for (i = 0; i < 198; i++) { |
||||
do { |
||||
*spl++ = (double)((int16_t)(random() & 0xffff)) * r; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} |
||||
} else { |
||||
/* add 198 bits of silence */ |
||||
for (i = 0; i < 198; i++) { |
||||
do { |
||||
*spl++ = 0; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} |
||||
} |
||||
|
||||
/* depending on the number of samples, return the number */ |
||||
count = ((uintptr_t)spl - (uintptr_t)cnetz->fsk_tx_buffer) / sizeof(*spl); |
||||
|
||||
cnetz->fsk_tx_phase = phase; |
||||
cnetz->fsk_tx_buffer_length = count; |
||||
|
||||
return count; |
||||
} |
||||
|
||||
/* encode one data block into samples
|
||||
* input: 184 data bits (including barker code) |
||||
* output: samples |
||||
* return number of samples */ |
||||
static int fsk_block_encode(cnetz_t *cnetz, const char *bits) |
||||
{ |
||||
/* alloc samples, add 1 in case there is a rest */ |
||||
int16_t *spl; |
||||
double phase, bitstep, deviation; |
||||
int i, count; |
||||
char last; |
||||
|
||||
deviation = cnetz->fsk_deviation; |
||||
spl = cnetz->fsk_tx_buffer; |
||||
phase = cnetz->fsk_tx_phase; |
||||
bitstep = cnetz->fsk_tx_bitstep * 256.0; |
||||
|
||||
/* add 7 bits of pause */ |
||||
for (i = 0; i < 7; i++) { |
||||
do { |
||||
*spl++ = 0; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} |
||||
/* add 184 bits */ |
||||
last = ' '; |
||||
for (i = 0; i < 184; i++) { |
||||
switch (last) { |
||||
case ' ': |
||||
if (bits[i] == '1') { |
||||
/* ramp up from 0 */ |
||||
do { |
||||
*spl++ = ramp_up[(int)phase] / 2 + deviation / 2; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} else { |
||||
/* ramp down from 0 */ |
||||
do { |
||||
*spl++ = ramp_down[(int)phase] / 2 - deviation / 2; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} |
||||
break; |
||||
case '1': |
||||
if (bits[i] == '1') { |
||||
/* stay up */ |
||||
do { |
||||
*spl++ = deviation; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} else { |
||||
/* ramp down */ |
||||
do { |
||||
*spl++ = ramp_down[(int)phase]; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} |
||||
break; |
||||
case '0': |
||||
if (bits[i] == '1') { |
||||
/* ramp up */ |
||||
do { |
||||
*spl++ = ramp_up[(int)phase]; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} else { |
||||
/* stay down */ |
||||
do { |
||||
*spl++ = -deviation; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} |
||||
break; |
||||
} |
||||
last = bits[i]; |
||||
} |
||||
/* add 7 bits of pause */ |
||||
if (last == '0') { |
||||
/* ramp up to 0 */ |
||||
do { |
||||
*spl++ = ramp_up[(int)phase] / 2 - deviation / 2; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} else { |
||||
/* ramp down to 0 */ |
||||
do { |
||||
*spl++ = ramp_down[(int)phase] / 2 + deviation / 2; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} |
||||
for (i = 1; i < 7; i++) { |
||||
do { |
||||
*spl++ = 0; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} |
||||
|
||||
/* depending on the number of samples, return the number */ |
||||
count = ((uintptr_t)spl - (uintptr_t)cnetz->fsk_tx_buffer) / sizeof(*spl); |
||||
|
||||
cnetz->fsk_tx_phase = phase; |
||||
cnetz->fsk_tx_buffer_length = count; |
||||
|
||||
return count; |
||||
} |
||||
|
||||
/* encode one distributed data block into samples
|
||||
* input: 184 data bits (including barker code) |
||||
* output: samples |
||||
* if a sample contains 0x8000, it indicates where to insert speech block |
||||
* return number of samples */ |
||||
static int fsk_distributed_encode(cnetz_t *cnetz, const char *bits) |
||||
{ |
||||
/* alloc samples, add 1 in case there is a rest */ |
||||
int16_t *spl, *marker; |
||||
double phase, bitstep, deviation; |
||||
int i, j, count; |
||||
char last; |
||||
|
||||
deviation = cnetz->fsk_deviation; |
||||
spl = cnetz->fsk_tx_buffer; |
||||
phase = cnetz->fsk_tx_phase; |
||||
bitstep = cnetz->fsk_tx_bitstep * 256.0; |
||||
|
||||
/* add 2 * (1+4+1 + 60) bits of pause / for speech */ |
||||
for (i = 0; i < 2; i++) { |
||||
for (j = 0; j < 6; j++) { |
||||
do { |
||||
*spl++ = 0; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} |
||||
marker = spl; |
||||
for (j = 0; j < 60; j++) { |
||||
do { |
||||
*spl++ = 0; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} |
||||
*marker = -32768; /* indicator for inserting speech */ |
||||
} |
||||
/* add 46 * (1+4+1 + 60) bits */ |
||||
for (i = 0; i < 46; i++) { |
||||
/* unmodulated bit */ |
||||
do { |
||||
*spl++ = 0; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
last = ' '; |
||||
for (j = 0; j < 4; j++) { |
||||
switch (last) { |
||||
case ' ': |
||||
if (bits[i * 4 + j] == '1') { |
||||
/* ramp up from 0 */ |
||||
do { |
||||
*spl++ = ramp_up[(int)phase] / 2 + deviation / 2; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} else { |
||||
/* ramp down from 0 */ |
||||
do { |
||||
*spl++ = ramp_down[(int)phase] / 2 - deviation / 2; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} |
||||
break; |
||||
case '1': |
||||
if (bits[i * 4 + j] == '1') { |
||||
/* stay up */ |
||||
do { |
||||
*spl++ = deviation; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} else { |
||||
/* ramp down */ |
||||
do { |
||||
*spl++ = ramp_down[(int)phase]; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} |
||||
break; |
||||
case '0': |
||||
if (bits[i * 4 + j] == '1') { |
||||
/* ramp up */ |
||||
do { |
||||
*spl++ = ramp_up[(int)phase]; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} else { |
||||
/* stay down */ |
||||
do { |
||||
*spl++ = -deviation; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} |
||||
break; |
||||
} |
||||
last = bits[i * 4 + j]; |
||||
} |
||||
/* unmodulated bit */ |
||||
if (last == '0') { |
||||
/* ramp up to 0 */ |
||||
do { |
||||
*spl++ = ramp_up[(int)phase] / 2 - deviation / 2; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} else { |
||||
/* ramp down to 0 */ |
||||
do { |
||||
*spl++ = ramp_down[(int)phase] / 2 + deviation / 2; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} |
||||
marker = spl; |
||||
for (j = 0; j < 60; j++) { |
||||
do { |
||||
*spl++ = 0; |
||||
phase += bitstep; |
||||
} while (phase < 256.0); |
||||
phase -= 256.0; |
||||
} |
||||
*marker = -32768; /* indicator for inserting speech */ |
||||
} |
||||
|
||||
/* depending on the number of samples, return the number */ |
||||
count = ((uintptr_t)spl - (uintptr_t)cnetz->fsk_tx_buffer) / sizeof(*spl); |
||||
|
||||
cnetz->fsk_tx_phase = phase; |
||||
cnetz->fsk_tx_buffer_length = count; |
||||
|
||||
return count; |
||||
} |
||||
|
||||
void show_level(double level) |
||||
{ |
||||
char text[42] = " "; |
||||
|
||||
if (level > 1.0) |
||||
level = 1.0; |
||||
if (level < -1.0) |
||||
level = -1.0; |
||||
text[20 - (int)(level * 20)] = '*'; |
||||
printf("%s\n", text); |
||||
} |
||||
|
||||
/* decode samples and hut for bit changes
|
||||
* use deviation to find greatest slope of the signal (bit change) |
||||
*/ |
||||
void sender_receive(sender_t *sender, int16_t *samples, int length) |
||||
{ |
||||
cnetz_t *cnetz = (cnetz_t *) sender; |
||||
|
||||
/* measure rx sample speed */ |
||||
calc_clock_speed(cnetz, length, 0, 0); |
||||
|
||||
#ifdef TEST_SCRABLE |
||||
#ifdef TEST_UNSCRABLE |
||||
scrambler(&scrambler_test_scrambler1, samples, length); |
||||
#endif |
||||
jitter_save(&scrambler_test_jb, samples, length); |
||||
return; |
||||
#endif |
||||
|
||||
fsk_fm_demod(&cnetz->fsk_demod, samples, length); |
||||
return; |
||||
} |
||||
|
||||
static int fsk_telegramm(cnetz_t *cnetz, int16_t *samples, int length) |
||||
{ |
||||
int count = 0, pos, copy, i, speech_length, speech_pos; |
||||
int16_t *spl, *speech_buffer; |
||||
const char *bits; |
||||
|
||||
speech_buffer = cnetz->dsp_speech_buffer; |
||||
speech_length = cnetz->dsp_speech_length; |
||||
speech_pos = cnetz->dsp_speech_pos; |
||||
|
||||
again: |
||||
/* there must be length, otherwise we would skip blocks */ |
||||
if (!length) |
||||
return count; |
||||
|
||||
pos = cnetz->fsk_tx_buffer_pos; |
||||
spl = cnetz->fsk_tx_buffer + pos; |
||||
|
||||
/* start new telegramm, so we generate one */ |
||||
if (pos == 0) { |
||||
/* measure actual signal speed */ |
||||
if (cnetz->sched_ts == 0 && cnetz->sched_r_m == 0) |
||||
calc_clock_speed(cnetz, cnetz->sender.samplerate * 24 / 10, 1, 1); |
||||
|
||||
/* switch to speech channel */ |
||||
if (cnetz->sched_switch_mode && cnetz->sched_r_m == 0) { |
||||
if (--cnetz->sched_switch_mode == 0) { |
||||
/* OgK / SpK(K) / SpK(V) */ |
||||
PDEBUG(DDSP, DEBUG_INFO, "Switching channel (mode)\n"); |
||||
cnetz->dsp_mode = cnetz->sched_dsp_mode; |
||||
} |
||||
} |
||||
|
||||
switch (cnetz->dsp_mode) { |
||||
case DSP_MODE_OGK: |
||||
if (((1 << cnetz->sched_ts) & si.ogk_timeslot_mask)) { |
||||
if (cnetz->sched_r_m == 0) { |
||||
/* set last time slot, so we can match received message from mobile station */ |
||||
cnetz->last_tx_timeslot = cnetz->sched_ts; |
||||
PDEBUG(DDSP, DEBUG_DEBUG, "Transmitting 'Rufblock' at timeslot %d\n", cnetz->sched_ts); |
||||
bits = cnetz_encode_telegramm(cnetz); |
||||
} else { |
||||
PDEBUG(DDSP, DEBUG_DEBUG, "Transmitting 'Meldeblock' at timeslot %d\n", cnetz->sched_ts); |
||||
bits = cnetz_encode_telegramm(cnetz); |
||||
} |
||||
fsk_block_encode(cnetz, bits); |
||||
} else { |
||||
fsk_nothing_encode(cnetz); |
||||
} |
||||
break; |
||||
case DSP_MODE_SPK_K: |
||||
PDEBUG(DDSP, DEBUG_DEBUG, "Transmitting 'Konzentrierte Signalisierung'\n"); |
||||
bits = cnetz_encode_telegramm(cnetz); |
||||
fsk_block_encode(cnetz, bits); |
||||
break; |
||||
case DSP_MODE_SPK_V: |
||||
PDEBUG(DDSP, DEBUG_DEBUG, "Transmitting 'Verteilte Signalisierung'\n"); |
||||
bits = cnetz_encode_telegramm(cnetz); |
||||
fsk_distributed_encode(cnetz, bits); |
||||
break; |
||||
default: |
||||
fsk_nothing_encode(cnetz); |
||||
} |
||||
|
||||
if (cnetz->dsp_mode == DSP_MODE_SPK_V) { |
||||
/* count sub frame */ |
||||
cnetz->sched_ts += 8; |
||||
} else { |
||||
/* count slot */ |
||||
if (cnetz->sched_r_m == 0) |
||||
cnetz->sched_r_m = 1; |
||||
else { |
||||
cnetz->sched_r_m = 0; |
||||
cnetz->sched_ts++; |
||||
} |
||||
} |
||||
if (cnetz->sched_ts == 32) |
||||
cnetz->sched_ts = 0; |
||||
} |
||||
|
||||
copy = cnetz->fsk_tx_buffer_length - pos; |
||||
if (length < copy) |
||||
copy = length; |
||||
for (i = 0; i < copy; i++) { |
||||
if (*spl == -32768) { |
||||
/* marker found to insert new chunk of audio */ |
||||
jitter_load(&cnetz->sender.audio, speech_buffer, 100); |
||||
compress_audio(&cnetz->cstate, speech_buffer, 100); |
||||
speech_length = samplerate_upsample(&cnetz->sender.srstate, speech_buffer, 100, speech_buffer); |
||||
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 */ |
||||
if (cnetz->pre_emphasis && !cnetz->scrambler) |
||||
pre_emphasis(&cnetz->estate, speech_buffer, speech_length); |
||||
speech_pos = 0; |
||||
} |
||||
/* copy speech as long as we have something left in buffer */ |
||||
if (speech_pos < speech_length) |
||||
*samples++ = speech_buffer[speech_pos++]; |
||||
else |
||||
*samples++ = *spl; |
||||
spl++; |
||||
} |
||||
cnetz->dsp_speech_length = speech_length; |
||||
cnetz->dsp_speech_pos = speech_pos; |
||||
pos += copy; |
||||
count += copy; |
||||
length -= copy; |
||||
if (pos == cnetz->fsk_tx_buffer_length) { |
||||
cnetz->fsk_tx_buffer_pos = 0; |
||||
goto again; |
||||
} |
||||
|
||||
cnetz->fsk_tx_buffer_pos = pos; |
||||
|
||||
return count; |
||||
} |
||||
|
||||
/* Provide stream of audio toward radio unit */ |
||||
void sender_send(sender_t *sender, int16_t *samples, int length) |
||||
{ |
||||
cnetz_t *cnetz = (cnetz_t *) sender; |
||||
int count; |
||||
|
||||
/* measure tx sample speed */ |
||||
calc_clock_speed(cnetz, length, 1, 0); |
||||
|
||||
#ifdef TEST_SCRABLE |
||||
jitter_load(&scrambler_test_jb, samples, length); |
||||
scrambler(&scrambler_test_scrambler2, samples, length); |
||||
return; |
||||
#endif |
||||
|
||||
count = fsk_telegramm(cnetz, samples, length); |
||||
if (count < length) { |
||||
printf("length=%d < count=%d\n", length, count); |
||||
printf("this shall not happen, so please fix!\n"); |
||||
exit(0); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,6 @@ |
||||
|
||||
void dsp_init(void); |
||||
int dsp_init_sender(cnetz_t *cnetz, int measure_speed, double clock_speed[2], double deviation, double noise); |
||||
void dsp_cleanup_sender(cnetz_t *cnetz); |
||||
void calc_clock_speed(cnetz_t *cnetz, uint64_t samples, int tx, int result); |
||||
|
@ -0,0 +1,557 @@ |
||||
/* FSK decoder of carrier FSK signals received by simple FM receiver
|
||||
* |
||||
* (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/>.
|
||||
*/ |
||||
|
||||
/* How does it work:
|
||||
* ----------------- |
||||
* |
||||
* C-Netz modulates the carrier frequency. If it is 2.4 kHz above, it is high |
||||
* level, if it is 2.4 kHz below, it is low level. Look at FTZ 171 TR 60 |
||||
* Chapter 5 (data exchange) for closer information. |
||||
* |
||||
* Detect level change: |
||||
* |
||||
* We don't just look for high/low level, because we don't know what the actual |
||||
* 0-level of the phone's transmitter is. (level of carrier frequency) Also we |
||||
* use receiver and sound card that cause any level to return to 0 after some |
||||
* time, even if the transmitter still transmits a level above or below the |
||||
* carrier frequnecy. Insted we look at the change of the received signal. An |
||||
* upward change indicates 1. An downward change indicates 0. (This may also be |
||||
* reversed, it we find out, that we received a sync sequence in received |
||||
* polarity.) If there is no significant change in level, we keep the value of |
||||
* last change, regardless of what level we actually receive. |
||||
* |
||||
* To determine a change from noise, we use a theshold. This is set to half of |
||||
* the level of last received change. This means that the next change may be |
||||
* down to a half lower. There is a special case during distributed signalling. |
||||
* The first level change of each data chunk raises or falls from 0-level |
||||
* (unmodulated carrier), so the threshold for this bit is only a quarter of the |
||||
* last received change. |
||||
* |
||||
* While searching for a sync sequence, the threshold for the next change is set |
||||
* after each change. After synchronization, the the threshold is locked to half |
||||
* of the average change level of the sync sequence. |
||||
* |
||||
* Search window |
||||
* |
||||
* We use a window of one bit length (9 samples at 48 kHz sample rate) and look |
||||
* for a change that is higher than the threshold and has its highest slope in |
||||
* the middle of the window. To determine the level, the min and max value |
||||
* inside the window is searched. The differece is the change level. To |
||||
* determine the highest slope, the highest difference between subsequent |
||||
* samples is used. For every sample we move the window one bit to the right |
||||
* (next sample), check if change level matches the threshold and highest slope |
||||
* is in the middle and so forth. Only if the highes slope is exactly in the |
||||
* middle, we declare a change. This means that we detect a slope about half of |
||||
* a bit duration later. |
||||
* |
||||
* When we are not synced: |
||||
*
|
||||
* For every change we record a bit. A positive change is 1 and a negative 0. If |
||||
* it turns out that the receiver or sound card is reversed, we reverse bits. |
||||
* After every change we wait up to 1.5 bit duration for next change. If there |
||||
* is a change, we record our next bit. If there is no change, we record the |
||||
* state of the last bit. After we had no change, we wait 1 bit duration, since |
||||
* we already 0.5 behind the start of the recently recorded bit. |
||||
* |
||||
* When we are synced: |
||||
* |
||||
* After we recorded the time of all level changes during the sync sequence, we |
||||
* calulate an average and use it as a time base for sampling the subsequent 150 |
||||
* bit of a message. From now on, a bit change does not cause any resync. We |
||||
* just remember what change we received. Later we use it for sampling the 150 |
||||
* bits. |
||||
* |
||||
* We wait a duration of 1.5 bits after the sync sequence and the start of the |
||||
* bit that follows the sync sequence. We record what we received as last |
||||
* change. For all following 149 bits we wait 1 bit duration and record what we |
||||
* received as last change. |
||||
* |
||||
* Sync clock |
||||
* |
||||
* Because we transmit and receive chunks of sample from buffers of different |
||||
* drivers, we cannot determine the exact latency between received and |
||||
* transmitted samples. Also some sound cards may have different RX and TX |
||||
* speed. One (pure software) solution is to sync ourself to the mobile phone, |
||||
* since the mobile phone is perfectly synced to use. |
||||
* |
||||
* After receiving and decording of a frame, we use the time of received sync |
||||
* sequence to synchronize the reciever to the mobile phone. If we receive a |
||||
* message on the OgK (control channel), we know that this is a response to a |
||||
* message of a specific time slot we recently sent. Then we can fully sync the |
||||
* receiver's clock. For any other frame, we cannot determine the absolute |
||||
* clock. We just correct the receiver's clock, as the clock differs only |
||||
* slightly from the time the message was received. |
||||
*
|
||||
*/ |
||||
|
||||
#include <stdio.h> |
||||
#include <stdint.h> |
||||
#include <string.h> |
||||
#include <math.h> |
||||
#include "../common/timer.h" |
||||
#include "../common/debug.h" |
||||
#include "../common/call.h" |
||||
#include "cnetz.h" |
||||
#include "dsp.h" |
||||
#include "telegramm.h" |
||||
|
||||
/* use to debug decoder */ |
||||
//#define DEBUG_DECODER if (1)
|
||||
//#define DEBUG_DECODER if (fsk->cnetz->dsp_mode == DSP_MODE_SPK_V)
|
||||
//#define DEBUG_DECODER if (fsk->cnetz->dsp_mode == DSP_MODE_SPK_V && sync)
|
||||
|
||||
static int len, half; |
||||
static int16_t *spl; |
||||
static int pos; |
||||
static double bits_per_sample, next_bit; |
||||
static int level_threshold; |
||||
static double bit_time, bit_time_uncorrected; |
||||
static enum fsk_sync sync; |
||||
static int last_change_positive; |
||||
static double sync_level; |
||||
static double sync_time; |
||||
static double sync_jitter; |
||||
static int bit_count; |
||||
static int16_t *speech_buffer; |
||||
static int speech_size, speech_count; |
||||
|
||||
int fsk_fm_init(fsk_fm_demod_t *fsk, cnetz_t *cnetz, int samplerate, double bitrate) |
||||
{ |
||||
memset(fsk, 0, sizeof(*fsk)); |
||||
if (samplerate < 48000) { |
||||
PDEBUG(DDSP, DEBUG_ERROR, "Sample rate must be at least 48000 Hz!\n"); |
||||
return -1; |
||||
} |
||||
|
||||
fsk->cnetz = cnetz; |
||||
|
||||
len = (int)((double)samplerate / bitrate + 0.5); |
||||
half = (int)((double)samplerate / bitrate / 2.0 + 0.5); |
||||
if (len > sizeof(fsk->bit_buffer_spl) / sizeof(fsk->bit_buffer_spl[0])) { |
||||
PDEBUG(DDSP, DEBUG_ERROR, "Sample rate too high for buffer, please use lower rate, like 192000 Hz!\n"); |
||||
return -1; |
||||
} |
||||
|
||||
fsk->bit_buffer_len = len; |
||||
fsk->bit_buffer_half = half; |
||||
fsk->bits_per_sample = bitrate / (double)samplerate; |
||||
|
||||
fsk->speech_size = sizeof(fsk->speech_buffer) / sizeof(fsk->speech_buffer[0]); |
||||
|
||||
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) |
||||
{ |
||||
int min = 32767, max = -32768, avg = 0, count = 0, level; |
||||
double time = 0, t, sync_average, sync_time, jitter = 0; |
||||
int bit_offset; |
||||
int i; |
||||
|
||||
/* get levels an the average receive time */ |
||||
for (i = 0; i < num; i++) { |
||||
level = fsk->change_levels[(fsk->change_pos - 1 - i) & 0xff]; |
||||
if (level <= 0) |
||||
continue; |
||||
|
||||
/* in spk mode, we skip the voice part (62 bits) */ |
||||
if (fsk->cnetz->dsp_mode == DSP_MODE_SPK_V) |
||||
bit_offset = i + ((i + 2) >> 2) * 62; |
||||
else |
||||
bit_offset = i; |
||||
t = fmod(fsk->change_when[(fsk->change_pos - 1 - i) & 0xff] - bit_time + (double)bit_offset + BITS_PER_SUPERFRAME, BITS_PER_SUPERFRAME); |
||||
if (t > BITS_PER_SUPERFRAME / 2) |
||||
t -= BITS_PER_SUPERFRAME; |
||||
//if (fsk->cnetz->dsp_mode == DSP_MODE_SPK_V)
|
||||
// printf("%d: level=%d%% @%.2f difference=%.2f\n", bit_offset, level * 100 / 65536, fsk->change_when[(fsk->change_pos - 1 - i) & 0xff], t);
|
||||
time += t; |
||||
|
||||
if (level < min) |
||||
min = level; |
||||
if (level > max) |
||||
max = level; |
||||
avg += level; |
||||
count++; |
||||
} |
||||
|
||||
if (!count) { |
||||
*_min = *_max = *_avg = 0; |
||||
return; |
||||
} |
||||
|
||||
/* when did we received the sync?
|
||||
* sync_average is the average about how early (negative) or |
||||
* late (positive) we received the sync relative to current bit_time. |
||||
* sync_time is the absolute time within the super frame. |
||||
*/ |
||||
sync_average = time / (double)count; |
||||
sync_time = fmod(sync_average + bit_time + BITS_PER_SUPERFRAME, BITS_PER_SUPERFRAME); |
||||
|
||||
*_probes = count; |
||||
*_min = min; |
||||
*_max = max; |
||||
*_avg = avg / count; |
||||
|
||||
if (_time) { |
||||
// if (fsk->cnetz->dsp_mode == DSP_MODE_SPK_V)
|
||||
// printf("sync at distributed mode\n");
|
||||
// printf("sync at bit_time=%.2f (sync_average = %.2f)\n", sync_time, sync_average);
|
||||
/* if our average sync is later (greater) than the current
|
||||
* bit_time, we must wait longer (next_bit above 1.5) |
||||
* for the time to sample the bit. |
||||
* if sync is earlier, bit_time is already too late, so |
||||
* we must wait less than 1.5 bits */ |
||||
next_bit = 1.5 + sync_average; |
||||
*_time = sync_time; |
||||
} |
||||
if (_jitter) { |
||||
/* get jitter of received changes */ |
||||
for (i = 0; i < num; i++) { |
||||
level = fsk->change_levels[(fsk->change_pos - 1 - i) & 0xff]; |
||||
if (level <= 0) |
||||
continue; |
||||
|
||||
/* in spk mode, we skip the voice part (62 bits) */ |
||||
if (fsk->cnetz->dsp_mode == DSP_MODE_SPK_V) |
||||
bit_offset = i + ((i + 2) >> 2) * 62; |
||||
else |
||||
bit_offset = i; |
||||
t = fmod(fsk->change_when[(fsk->change_pos - 1 - i) & 0xff] - sync_time + (double)bit_offset + BITS_PER_SUPERFRAME, BITS_PER_SUPERFRAME); |
||||
if (t > BITS_PER_SUPERFRAME / 2) |
||||
t = BITS_PER_SUPERFRAME - t; /* turn negative into positive */ |
||||
jitter += t; |
||||
} |
||||
*_jitter = jitter / (double)count; |
||||
} |
||||
} |
||||
|
||||
static inline void got_bit(fsk_fm_demod_t *fsk, int bit, int change_level) |
||||
{ |
||||
int min, max, avg, probes; |
||||
|
||||
/* count bits, but do not exceed 4 bits per SPK block */ |
||||
if (fsk->cnetz->dsp_mode == DSP_MODE_SPK_V) { |
||||
/* for first bit, we have only half of the modulation deviation, so we multiply level by two */ |
||||
if (bit_count == 0) |
||||
change_level *= 2; |
||||
if (bit_count == 4) |
||||
return; |
||||
} |
||||
bit_count++; |
||||
|
||||
//printf("bit %d\n", bit);
|
||||
fsk->change_levels[fsk->change_pos] = change_level; |
||||
fsk->change_when[fsk->change_pos++] = bit_time; |
||||
|
||||
|
||||
switch (sync) { |
||||
case FSK_SYNC_NONE: |
||||
fsk->rx_sync = (fsk->rx_sync << 1) | bit; |
||||
/* use half level of last change for threshold change detection.
|
||||
* if there is no change detected for 5 bits, set theshold to |
||||
* 1 percent, so the 7 pause bits before a frame will make sure |
||||
* that the change is below noise level, so the first sync |
||||
* bit is detected. then the change is set and adjusted |
||||
* for all other bits in the sync sequence. |
||||
* after sync, the theshold is set to half of the average of |
||||
* all changes in the sync sequence */ |
||||
if (change_level) { |
||||
level_threshold = (double)change_level / 2.0; |
||||
} else if ((fsk->rx_sync & 0x1f) == 0x00 || (fsk->rx_sync & 0x1f) == 0x1f) { |
||||
if (fsk->cnetz->dsp_mode != DSP_MODE_SPK_V) |
||||
level_threshold = 655; |
||||
} |
||||
if (detect_sync(fsk->rx_sync)) { |
||||
sync = FSK_SYNC_POSITIVE; |
||||
got_sync: |
||||
get_levels(fsk, &min, &max, &avg, &probes, 30, &sync_time, &sync_jitter); |
||||
sync_level = (double)avg / 65535.0; |
||||
if (sync == FSK_SYNC_NEGATIVE) |
||||
sync_level = -sync_level; |
||||
// printf("sync (change min=%d%% max=%d%% avg=%d%% sync_time=%.2f jitter=%.2f probes=%d)\n", min * 100 / 65535, max * 100 / 65535, avg * 100 / 65535, sync_time, sync_jitter, probes);
|
||||
level_threshold = (double)avg / 2.0; |
||||
fsk->rx_sync = 0; |
||||
fsk->rx_buffer_count = 0; |
||||
break; |
||||
} |
||||
if (detect_sync(fsk->rx_sync ^ 0xfffffffff)) { |
||||
sync = FSK_SYNC_NEGATIVE; |
||||
goto got_sync; |
||||
} |
||||
break; |
||||
case FSK_SYNC_NEGATIVE: |
||||
bit = 1 - bit; |
||||
/* fall through */ |
||||
case FSK_SYNC_POSITIVE: |
||||
fsk->rx_buffer[fsk->rx_buffer_count] = bit + '0'; |
||||
if (++fsk->rx_buffer_count == 150) { |
||||
sync = FSK_SYNC_NONE; |
||||
if (fsk->cnetz->dsp_mode != DSP_MODE_SPK_V) { |
||||
/* received 40 bits after start of block */ |
||||
sync_time = fmod(sync_time - (7+33) + BITS_PER_SUPERFRAME, BITS_PER_SUPERFRAME); |
||||
} else { |
||||
/* received 662 bits after start of block (10 SPK blocks + 1 bit (== 2 level changes)) */ |
||||
sync_time = fmod(sync_time - (66*10+2) + BITS_PER_SUPERFRAME, BITS_PER_SUPERFRAME); |
||||
} |
||||
cnetz_decode_telegramm(fsk->cnetz, fsk->rx_buffer, sync_level, sync_time, sync_jitter); |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
|
||||
#ifdef DEBUG_DECODER |
||||
static void fsk_show_level(double level) |
||||
{ |
||||
if (level > 1.0) |
||||
level = 1.0; |
||||
if (level < -1.0) |
||||
level = -1.0; |
||||
printf(" *\n" + 10 - (int)(level * 10)); |
||||
} |
||||
#endif |
||||
|
||||
/* DOC TBD: find change for bit change */ |
||||
static inline void find_change(fsk_fm_demod_t *fsk) |
||||
{ |
||||
int32_t level_min, level_max, change_max; |
||||
int change_at, change_positive; |
||||
int16_t s, last_s = 0; |
||||
int threshold; |
||||
int i; |
||||
|
||||
/* levels at total reverse */ |
||||
level_min = 32767; |
||||
level_max = -32768; |
||||
change_max = -1; |
||||
change_at = -1; |
||||
change_positive = -1; |
||||
|
||||
for (i = 0; i < len; i++) { |
||||
last_s = s; |
||||
s = spl[pos++]; |
||||
if (pos == len) |
||||
pos = 0; |
||||
if (i > 0) { |
||||
if (s - last_s > change_max) { |
||||
change_max = s - last_s; |
||||
change_at = i; |
||||
change_positive = 1; |
||||
} else if (last_s - s > change_max) { |
||||
change_max = last_s - s; |
||||
change_at = i; |
||||
change_positive = 0; |
||||
} |
||||
} |
||||
if (s > level_max) |
||||
level_max = s; |
||||
if (s < level_min) |
||||
level_min = s; |
||||
} |
||||
/* for first bit, we have only half of the modulation deviation, so we divide the threshold by two */ |
||||
if (fsk->cnetz->dsp_mode == DSP_MODE_SPK_V && bit_count == 0) |
||||
threshold = level_threshold / 2; |
||||
else |
||||
threshold = level_threshold; |
||||
/* if we are not in sync, for every detected change we set
|
||||
* next_bit to 1.5, so we wait 1.5 bits for next change |
||||
* if it is not received within this time, there is no change, |
||||
* so the bit does not change. |
||||
* if we are in sync, we remember last change. after 1.5 |
||||
* bits after sync average, we measure the first bit |
||||
* and then all subsequent bits after 1.0 bits */ |
||||
//DEBUG_DECODER printf("next_bit=%.4f\n", next_bit);
|
||||
if (level_max - level_min > threshold && change_at == half) { |
||||
#ifdef DEBUG_DECODER |
||||