Implementation of C-Netz (German mobile telephone system)
This commit is contained in:
parent
4b3e3385b5
commit
16acdbf59d
|
@ -22,6 +22,7 @@ m4
|
|||
src/common/libcommon.a
|
||||
src/anetz/anetz
|
||||
src/bnetz/bnetz
|
||||
src/cnetz/cnetz
|
||||
src/nmt/nmt
|
||||
test/test_compander
|
||||
test/test_emphasis
|
||||
|
|
|
@ -31,6 +31,7 @@ AC_OUTPUT(
|
|||
src/common/Makefile
|
||||
src/anetz/Makefile
|
||||
src/bnetz/Makefile
|
||||
src/cnetz/Makefile
|
||||
src/nmt/Makefile
|
||||
src/test/Makefile
|
||||
src/Makefile
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
AUTOMAKE_OPTIONS = foreign
|
||||
SUBDIRS = common anetz bnetz nmt test
|
||||
SUBDIRS = common anetz bnetz cnetz nmt test
|
||||
|
||||
|
|
|
@ -635,7 +635,7 @@ void bnetz_receive_telegramm(bnetz_t *bnetz, uint16_t telegramm, double quality,
|
|||
PDEBUG(DBNETZ, DEBUG_INFO, "Setup call to network.\n");
|
||||
rc = call_in_setup(callref, bnetz->station_id, dialing);
|
||||
if (rc < 0) {
|
||||
PDEBUG(DBNETZ, DEBUG_NOTICE, "Call rejected (cause %d), releasing.\n", rc);
|
||||
PDEBUG(DBNETZ, DEBUG_NOTICE, "Call rejected (cause %d), releasing.\n", -rc);
|
||||
bnetz_release(bnetz);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
DEBUG_DECODER
|
||||
printf("receive bit change to %d (level=%d, threshold=%d)\n", change_positive, level_max - level_min, threshold);
|
||||
#endif
|
||||
last_change_positive = change_positive;
|
||||
if (!sync) {
|
||||
next_bit = 1.5;
|
||||
got_bit(fsk, change_positive, level_max - level_min);
|
||||
}
|
||||
}
|
||||
if (next_bit <= 0.0) {
|
||||
#ifdef DEBUG_DECODER
|
||||
DEBUG_DECODER {
|
||||
if (sync)
|
||||
printf("sampling here bit %d\n", last_change_positive);
|
||||
else
|
||||
printf("no bit change\n");
|
||||
}
|
||||
#endif
|
||||
next_bit += 1.0;
|
||||
got_bit(fsk, last_change_positive, 0);
|
||||
}
|
||||
next_bit -= bits_per_sample;
|
||||
}
|
||||
|
||||
/* receive FM signal from receiver */
|
||||
void fsk_fm_demod(fsk_fm_demod_t *fsk, int16_t *samples, int length)
|
||||
{
|
||||
int i;
|
||||
double t;
|
||||
|
||||
len = fsk->bit_buffer_len;
|
||||
half = fsk->bit_buffer_half;
|
||||
spl = fsk->bit_buffer_spl;
|
||||
speech_buffer = fsk->speech_buffer;
|
||||
speech_size = fsk->speech_size;
|
||||
speech_count = fsk->speech_count;
|
||||
bits_per_sample = fsk->bits_per_sample;
|
||||
level_threshold = fsk->level_threshold;
|
||||
pos = fsk->bit_buffer_pos;
|
||||
next_bit = fsk->next_bit;
|
||||
sync = fsk->sync;
|
||||
last_change_positive = fsk->last_change_positive;
|
||||
sync_level = fsk->sync_level;
|
||||
sync_time = fsk->sync_time;
|
||||
sync_jitter = fsk->sync_jitter;
|
||||
bit_time = fsk->bit_time;
|
||||
bit_time_uncorrected = fsk->bit_time_uncorrected;
|
||||
bit_count = fsk->bit_count;
|
||||
|
||||
/* process signalling block, sample by sample */
|
||||
for (i = 0; i < length; i++) {
|
||||
spl[pos++] = samples[i];
|
||||
if (pos == len)
|
||||
pos = 0;
|
||||
/* for each sample process buffer */
|
||||
if (fsk->cnetz->dsp_mode != DSP_MODE_SPK_V) {
|
||||
#ifdef DEBUG_DECODER
|
||||
DEBUG_DECODER
|
||||
fsk_show_level((double)samples[i] / 32768.0);
|
||||
#endif
|
||||
find_change(fsk);
|
||||
} else {
|
||||
/* in distributed signalling, measure over 5 bits, but ignore 5th bit.
|
||||
* also reset next_bit, as soon as we reach the window */
|
||||
|
||||
/* note that we start from 0.5, because we detect change 0.5 bits later,
|
||||
* because the detector of the change is in the middle of the 1 bit
|
||||
* search window */
|
||||
t = fmod(bit_time, BITS_PER_SPK_BLOCK);
|
||||
if (t < 0.5) {
|
||||
next_bit = 1.0 - bits_per_sample;
|
||||
#ifdef DEBUG_DECODER
|
||||
if (bit_count) {
|
||||
DEBUG_DECODER
|
||||
printf("start spk_block bit count=%d\n", bit_count);
|
||||
}
|
||||
#endif
|
||||
bit_count = 0;
|
||||
} else
|
||||
if (t >= 0.5 && t < 5.5) {
|
||||
#ifdef DEBUG_DECODER
|
||||
DEBUG_DECODER
|
||||
fsk_show_level((double)samples[i] / 32768.0);
|
||||
#endif
|
||||
find_change(fsk);
|
||||
} else
|
||||
if (t >= 5.5 && t < 65.5) {
|
||||
/* get audio for the duration of 60 bits */
|
||||
if (speech_count <= speech_size)
|
||||
speech_buffer[speech_count++] = samples[i];
|
||||
} else
|
||||
if (t >= 65.5) {
|
||||
if (speech_count) {
|
||||
unshrink_speech(fsk->cnetz);
|
||||
speech_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
bit_time += bits_per_sample;
|
||||
if (bit_time >= BITS_PER_SUPERFRAME) {
|
||||
bit_time -= BITS_PER_SUPERFRAME;
|
||||
}
|
||||
/* another clock is used to measure actual super frame time */
|
||||
bit_time_uncorrected += bits_per_sample;
|
||||
if (bit_time_uncorrected >= BITS_PER_SUPERFRAME) {
|
||||
bit_time_uncorrected -= BITS_PER_SUPERFRAME;
|
||||
calc_clock_speed(fsk->cnetz, fsk->cnetz->sender.samplerate * 24 / 10, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
fsk->level_threshold = level_threshold;
|
||||
fsk->bit_buffer_pos = pos;
|
||||
fsk->speech_count = speech_count;
|
||||
fsk->next_bit = next_bit;
|
||||
fsk->sync = sync;
|
||||
fsk->last_change_positive = last_change_positive;
|
||||
fsk->sync_level = sync_level;
|
||||
fsk->sync_time = sync_time;
|
||||
fsk->sync_jitter = sync_jitter;
|
||||
fsk->bit_time = bit_time;
|
||||
fsk->bit_time_uncorrected = bit_time_uncorrected;
|
||||
fsk->bit_count = bit_count;
|
||||
}
|
||||
|
||||
void fsk_correct_sync(cnetz_t *cnetz, double offset)
|
||||
{
|
||||
bit_time = fmod(bit_time - offset + BITS_PER_SUPERFRAME, BITS_PER_SUPERFRAME);
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
#define BITS_PER_SUPERFRAME 12672.0 /* super frame (Oberrahmen) has duration of excactly 2.4 seconds */
|
||||
#define BITS_PER_BLOCK 198.0 /* block has duration of excactly 37.5 milli seconds */
|
||||
#define BITS_PER_SPK_BLOCK 66.0 /* spk block has a duration of exactly 12.5 milli seconds */
|
||||
|
||||
/* fsk rx sync state */
|
||||
enum fsk_sync {
|
||||
FSK_SYNC_NONE = 0,
|
||||
FSK_SYNC_POSITIVE,
|
||||
FSK_SYNC_NEGATIVE,
|
||||
};
|
||||
|
||||
typedef struct cnetz cnetz_t;
|
||||
|
||||
typedef struct fsk_fm_demod {
|
||||
cnetz_t *cnetz; /* pointer back to cnetz instance */
|
||||
|
||||
/* clock */
|
||||
double bit_time; /* current time in bits inside superframe */
|
||||
double bit_time_uncorrected; /* same as above, but not corrected by sync */
|
||||
|
||||
/* bit detection */
|
||||
int16_t bit_buffer_spl[40]; /* samples ring buffer */
|
||||
int bit_buffer_len; /* number of samples in ring buffer */
|
||||
int bit_buffer_half; /* half of ring buffer */
|
||||
int bit_buffer_pos; /* current position to write next sample */
|
||||
int level_threshold; /* threshold for detection of next level change */
|
||||
double bits_per_sample; /* duration of one sample in bits */
|
||||
double next_bit; /* count time to detect bits */
|
||||
int bit_count; /* counts bits, to match 4 bits at distributed signalling */
|
||||
int last_change_positive; /* flags last level change direction */
|
||||
enum fsk_sync sync; /* set, if we are in sync and what polarity we receive */
|
||||
double sync_level; /* what was the level, when we received the sync */
|
||||
double sync_time; /* when did we receive sync, relative to super frame */
|
||||
double sync_jitter; /* what was the jitter of the sync */
|
||||
|
||||
/* speech */
|
||||
int16_t speech_buffer[3000]; /* holds one chunk of 12.5ms */
|
||||
int speech_size;
|
||||
int speech_count;
|
||||
|
||||
/* bit decoder */
|
||||
uint64_t rx_sync; /* sync shift register */
|
||||
char rx_buffer[151]; /* 150 bits + termination */
|
||||
int rx_buffer_count; /* counter when receiving bits */
|
||||
|
||||
/* statistics */
|
||||
int change_levels[256]; /* ring buffer to store levels */
|
||||
double change_when[256]; /* ring buffer to store time when level has changed */
|
||||
uint8_t change_pos; /* index for next write */
|
||||
} fsk_fm_demod_t;
|
||||
|
||||
int fsk_fm_init(fsk_fm_demod_t *fsk, cnetz_t *cnetz, int samplerate, double bitrate);
|
||||
void fsk_fm_demod(fsk_fm_demod_t *fsk, int16_t *samples, int length);
|
||||
void fsk_correct_sync(cnetz_t *cnetz, double offset);
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "image.h"
|
||||
|
||||
const char *image[] = {
|
||||
"@g _\n"
|
||||
" @y______________@g ( )\n"
|
||||
" @y/ \\@g / /\n"
|
||||
" @y(@w Die Mauer ist@y )@g / /\n"
|
||||
" @y\\@w gefallen!@y /@g / /\n"
|
||||
" @y\\_______ __/@g / /\n"
|
||||
" @y\\ |@g / /\n"
|
||||
" @wC-NETZ@g @y\\|@g / /\n"
|
||||
" __________________/_/_\n"
|
||||
" / oo /|\n"
|
||||
" / o o o / |\n"
|
||||
" / oo / |\n"
|
||||
" / ________________ / |\n"
|
||||
" / / @G021250993@g / / /\n"
|
||||
" / /_______________/ / /\n"
|
||||
" / @b______ ______@g / / @c___@g\n"
|
||||
" / @b/_@G(@b_@G)@b_/ /_@r(@b_@r)@b_/@g / / @c\\ \\__ @r___/@g\n"
|
||||
" / @b____ ____ ____@g / / @c_ ) / @r__/ )@g\n"
|
||||
" / @b/_@w1@b_/ /_@w2@b_/ /_@w3@b_/@g / / @c( \\/ \\@r/ |@g\n"
|
||||
" / @b____ ____ ____@g / / @c| @r| \\@g\n"
|
||||
" / @b/_@w4@b_/ /_@w5@b_/ /_@w6@b_/@g / / @c/ @r\\ |@g\n"
|
||||
" / @b____ ____ ____@g / / @c| BRD @r/ DDR )@g\n"
|
||||
" / @b/_@w7@b_/ /_@w8@b_/ /_@w9@b_/@g / / @c_| @r/ |@g\n"
|
||||
" / @b____ ____ ____@g / / @c\\ @r| |@g\n"
|
||||
" / @b/_@w*@b_/ /_@w0@b_/ /_@w#@b_/@g / / @c/ @r/ ___/@g\n"
|
||||
" / / / @c| @r\\________/@g\n"
|
||||
" / o o / / @c\\ \\@g\n"
|
||||
" /_____________________/ / @c| \\@g\n"
|
||||
" | | / @c\\___ \\_@g\n"
|
||||
" | = = | / @c/ /@g\n"
|
||||
" | = = | / @c/ __ (@g\n"
|
||||
" |______________________|/ @c|___________/ \\)@g\n"
|
||||
"@w",
|
||||
NULL
|
||||
};
|
||||
|
||||
void print_image(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; image[i]; i++) {
|
||||
for (j = 0; j < strlen(image[i]); j++) {
|
||||
if (image[i][j] == '@') {
|
||||
j++;
|
||||
switch(image[i][j]) {
|
||||
case 'g': /* gray */
|
||||
printf("\033[0;37m");
|
||||
break;
|
||||
case 'G': /* green */
|
||||
printf("\033[0;32m");
|
||||
break;
|
||||
case 'c': /* cyan */
|
||||
printf("\033[0;36m");
|
||||
break;
|
||||
case 'w': /* white */
|
||||
printf("\033[1;37m");
|
||||
break;
|
||||
case 'y': /* yellow */
|
||||
printf("\033[0;33m");
|
||||
break;
|
||||
case 'r': /* red */
|
||||
printf("\033[0;31m");
|
||||
break;
|
||||
case 'b': /* blue */
|
||||
printf("\033[1;34m");
|
||||
break;
|
||||
}
|
||||
} else
|
||||
printf("%c", image[i][j]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf("\033[0;39m");
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
void print_image(void);
|
||||
|
|
@ -0,0 +1,250 @@
|
|||
/* C-Netz main
|
||||
*
|
||||
* (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 <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sched.h>
|
||||
#include "../common/main.h"
|
||||
#include "../common/debug.h"
|
||||
#include "../common/timer.h"
|
||||
#include "../common/call.h"
|
||||
#include "../common/mncc_sock.h"
|
||||
#include "../common/freiton.h"
|
||||
#include "../common/besetztton.h"
|
||||
#include "cnetz.h"
|
||||
#include "sysinfo.h"
|
||||
#include "dsp.h"
|
||||
#include "telegramm.h"
|
||||
#include "image.h"
|
||||
#include "ansage.h"
|
||||
|
||||
/* settings */
|
||||
int measure_speed = 0;
|
||||
double clock_speed[2] = { 0.0, 0.0 };
|
||||
int set_clock_speed = 0;
|
||||
double deviation = 0.5, noise = 0.0;
|
||||
int ms_power = 3; /* 0..3 */
|
||||
int auth = 0;
|
||||
|
||||
void print_help(const char *arg0)
|
||||
{
|
||||
print_help_common(arg0, "");
|
||||
/* - - */
|
||||
printf(" -M --measure-speed\n");
|
||||
printf(" Measures clock speed. See documentation!\n");
|
||||
printf(" -S --clock-speed <rx ppm>,<tx ppm>\n");
|
||||
printf(" Correct speed of sound card's clock. Use '-M' to measure speed for\n");
|
||||
printf(" some hours after temperature has settled. The use these results to\n");
|
||||
printf(" correct signal processing speed. After adjustment, the clock must match\n");
|
||||
printf(" +- 1ppm or better. See documentation on how to measure correct value.\n");
|
||||
printf(" -F --flip-polarity\n");
|
||||
printf(" Adjust, so the transmitter increases frequency, when positive levels\n");
|
||||
printf(" are sent to sound device\n");
|
||||
printf(" -N --noise 0.0 .. 1.0 (default = %.1f)\n", noise);
|
||||
printf(" Between frames on OgK, send noise at given level. Use 0.0 for Silence.\n");
|
||||
printf(" -P --ms-power <power level>\n");
|
||||
printf(" Give power level of the mobile station 0..3. (default = '%d')\n", ms_power);
|
||||
printf(" 0 = 50-125 mW; 1 = 0.5-1 W; 2 = 4-8 W; 3 = 10-20 W\n");
|
||||
printf(" -A --authentication\n");
|
||||
printf(" Enable authentication on the base station. Since we cannot\n");
|
||||
printf(" authenticate, because we don't know the secret key and the algorithm,\n");
|
||||
printf(" we just accept any card. With this we get the vendor IDs of the phone.\n");
|
||||
}
|
||||
|
||||
static int handle_options(int argc, char **argv)
|
||||
{
|
||||
int skip_args = 0;
|
||||
const char *p;
|
||||
|
||||
static struct option long_options_special[] = {
|
||||
{"measure-speed", 0, 0, 'M'},
|
||||
{"clock-speed", 1, 0, 'S'},
|
||||
{"flip-polarity", 0, 0, 'F'},
|
||||
{"noise", 1, 0, 'N'},
|
||||
{"ms-power", 1, 0, 'P'},
|
||||
{"authentication", 0, 0, 'A'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
set_options_common("MS:FN:P:A", long_options_special);
|
||||
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
|
||||
c = getopt_long(argc, argv, optstring, long_options, &option_index);
|
||||
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'M':
|
||||
measure_speed = 1;
|
||||
skip_args++;
|
||||
break;
|
||||
case 'S':
|
||||
p = strchr(optarg, ',');
|
||||
if (!p) {
|
||||
fprintf(stderr, "Illegal clock speed, use two values, seperated by comma and no spaces!\n");
|
||||
exit(0);
|
||||
}
|
||||
clock_speed[0] = strtold(optarg, NULL);
|
||||
clock_speed[1] = strtold(p + 1, NULL);
|
||||
set_clock_speed = 1;
|
||||
skip_args += 2;
|
||||
break;
|
||||
case 'F':
|
||||
deviation = -deviation;
|
||||
skip_args += 1;
|
||||
break;
|
||||
case 'N':
|
||||
noise = strtold(optarg, NULL);
|
||||
skip_args += 2;
|
||||
break;
|
||||
case 'P':
|
||||
ms_power = atoi(optarg);
|
||||
if (ms_power > 3)
|
||||
ms_power = 3;
|
||||
if (ms_power < 0)
|
||||
ms_power = 0;
|
||||
skip_args += 2;
|
||||
break;
|
||||
case 'A':
|
||||
auth = 1;
|
||||
skip_args += 1;
|
||||
break;
|
||||
default:
|
||||
opt_switch_common(c, argv[0], &skip_args);
|
||||
}
|
||||
}
|
||||
|
||||
free(long_options);
|
||||
|
||||
return skip_args;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int rc;
|
||||
int skip_args;
|
||||
const char *station_id = "";
|
||||
int mandatory = 0;
|
||||
|
||||
/* init common tones */
|
||||
init_freiton();
|
||||
init_besetzton();
|
||||
init_ansage();
|
||||
|
||||
skip_args = handle_options(argc, argv);
|
||||
argc -= skip_args;
|
||||
argv += skip_args;
|
||||
|
||||
if (argc > 1) {
|
||||
}
|
||||
|
||||
if (!kanal) {
|
||||
printf("No channel (\"Kanal\") is specified, I suggest channel %d.\n\n", CNETZ_OGK_KANAL);
|
||||
mandatory = 1;
|
||||
}
|
||||
|
||||
if (!set_clock_speed && !measure_speed) {
|
||||
printf("No clock speed given. You need to measure clock using '-M' and later correct clock using '-S <rx ppm>,<tx ppm>'. See documentation for help!\n\n");
|
||||
mandatory = 1;
|
||||
}
|
||||
|
||||
if (mandatory) {
|
||||
print_help(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!loopback)
|
||||
print_image();
|
||||
|
||||
/* init functions */
|
||||
if (use_mncc_sock) {
|
||||
rc = mncc_init("/tmp/bsc_mncc");
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to setup MNCC socket. Quitting!\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
scrambler_init();
|
||||
init_sysinfo();
|
||||
dsp_init();
|
||||
init_telegramm();
|
||||
init_coding();
|
||||
cnetz_init();
|
||||
rc = call_init(station_id, call_sounddev, samplerate, latency, 7, loopback);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to create call control instance. Quitting!\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* create transceiver instance */
|
||||
rc = cnetz_create(sounddev, samplerate, do_pre_emphasis, do_de_emphasis, write_wave, read_wave, kanal, auth, ms_power, measure_speed, clock_speed, deviation, noise, loopback);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to create \"Sender\" instance. Quitting!\n");
|
||||
goto fail;
|
||||
}
|
||||
printf("Base station ready, please tune transmitter to %.3f MHz and receiver to %.3f MHz.\n", cnetz_kanal2freq(CNETZ_OGK_KANAL, 0), cnetz_kanal2freq(CNETZ_OGK_KANAL, 1));
|
||||
if (kanal != CNETZ_OGK_KANAL)
|
||||
printf("When switching to speech channel %d, be sure that transmitter switches to %.3f MHz and receiver to %.3f MHz. (using pilot signal)\n", kanal, cnetz_kanal2freq(kanal, 0), cnetz_kanal2freq(kanal, 1));
|
||||
|
||||
signal(SIGINT,sighandler);
|
||||
signal(SIGHUP,sighandler);
|
||||
signal(SIGTERM,sighandler);
|
||||
signal(SIGPIPE,sighandler);
|
||||
|
||||
if (rt_prio > 0) {
|
||||
struct sched_param schedp;
|
||||
int rc;
|
||||
|
||||
memset(&schedp, 0, sizeof(schedp));
|
||||
schedp.sched_priority = rt_prio;
|
||||
rc = sched_setscheduler(0, SCHED_RR, &schedp);
|
||||
if (rc)
|
||||
fprintf(stderr, "Error setting SCHED_RR with prio %d\n", rt_prio);
|
||||
}
|
||||
|
||||
main_loop(&quit, latency);
|
||||
|
||||
if (rt_prio > 0) {
|
||||
struct sched_param schedp;
|
||||
|
||||
memset(&schedp, 0, sizeof(schedp));
|
||||
schedp.sched_priority = 0;
|
||||
sched_setscheduler(0, SCHED_OTHER, &schedp);
|
||||
}
|
||||
|
||||
fail:
|
||||
/* cleanup functions */
|
||||
call_cleanup();
|
||||
if (use_mncc_sock)
|
||||
mncc_exit();
|
||||
|
||||
/* destroy transceiver instance */
|
||||
while (sender_head)
|
||||
cnetz_destroy(sender_head);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/* C-Netz audio spectrum inversion (Sprachverschleierung)
|
||||
*
|
||||
* (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 <math.h>
|
||||
#include "scrambler.h"
|
||||
|
||||
#define PI M_PI
|
||||
|
||||
/* FTZ 171 TR 60 Clause 6.2
|
||||
* Carrier frequency, that is the spectrum that is mirrored */
|
||||
#define CARRIER_HZ 3300.0
|
||||
#define FILTER_BELOW 500.0
|
||||
#define FILTER_TURNS 1
|
||||
|
||||
/* FTZ 171 TR 60 Clause 6.3
|
||||
* How much must the carrier frequency be lower than a 1000 HZ tone that passes the inversion.
|
||||
* The filter must be tuned to get that dampening. */
|
||||
#define TEST_1000HZ_DB 55.0
|
||||
|
||||
/* sine wave for carrier to modulate to */
|
||||
static double carrier[256];
|
||||
|
||||
void scrambler_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
carrier[i] = sin((double)i / 256.0 * 2 * PI);
|
||||
}
|
||||
}
|
||||
|
||||
void scrambler_setup(scrambler_t *scrambler, int samplerate)
|
||||
{
|
||||
biquad_init(&scrambler->bq, CARRIER_HZ - FILTER_BELOW, samplerate);
|
||||
scrambler->carrier_phaseshift256 = 256.0 / ((double)samplerate / CARRIER_HZ);
|
||||
}
|
||||
|
||||
/* Modulate samples to carriere that is twice the mirror frequency.
|
||||
* Then we got spectrum above carrier and mirrored spectrum below carrier.
|
||||
* Afterwards we cut off carrier frequency and frequencies above carrier.
|
||||
*/
|
||||
void scrambler(scrambler_t *scrambler, int16_t *samples, int length)
|
||||
{
|
||||
double spl[length];
|
||||
int32_t sample;
|
||||
double phaseshift, phase;
|
||||
int i;
|
||||
|
||||
phaseshift = scrambler->carrier_phaseshift256;
|
||||
phase = scrambler->carrier_phase256;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
/* modulate samples to carrier */
|
||||
spl[i] = (double)samples[i] / 32768.0 * carrier[((uint8_t)phase) & 0xff];
|
||||
phase += phaseshift;
|
||||
if (phase >= 256.0)
|
||||
phase -= 256.0;
|
||||
}
|
||||
|
||||
scrambler->carrier_phase256 = phase;
|
||||
|
||||
/* cut off carrier frequency and modulation above carrier frequency */
|
||||
biquad_process(&scrambler->bq, spl, length, FILTER_TURNS);
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
/* store result */
|
||||
sample = spl[i] * 2.0 * 32768.0;
|
||||
if (sample > 32767)
|
||||
sample = 32767;
|
||||
else if (sample < -32768)
|
||||
sample = -32768;
|
||||
*samples++ = sample;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#include "../common/filter.h"
|
||||
|
||||
typedef struct scrambler {
|
||||
double carrier_phaseshift256; /* carrier phase shift per sample */
|
||||
double carrier_phase256; /* current phase of carrier frequency */
|
||||
biquad_low_pass_t bq; /* filter to remove carrier frequency */
|
||||
} scrambler_t;
|
||||
|
||||
void scrambler_init(void);
|
||||
void scrambler_setup(scrambler_t *scrambler, int samplerate);
|
||||
void scrambler(scrambler_t *scrambler, int16_t *samples, int length);
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "sysinfo.h"
|
||||
|
||||
cnetz_si si;
|
||||
|
||||
void init_sysinfo(void)
|
||||
{
|
||||
memset(&si, 0, sizeof(si));
|
||||
|
||||
si.ogk_timeslot_mask = 0x01010101; /* 4 slots per super frame */
|
||||
si.fuz_nat = 1;
|
||||
si.fuz_fuvst = 1;
|
||||
si.fuz_rest = 38;
|
||||
si.mittel_umschalten = 5;
|
||||
si.grenz_umschalten = 0;
|
||||
si.mittel_ausloesen = 5;
|
||||
si.grenz_ausloesen = 0;
|
||||
si.sperre = 0;
|
||||
si.genauigkeit = 1; /* bedingte Genauigkeit */
|
||||
si.entfernung = 3;
|
||||
si.grenz_einbuchen = 1; /* worst case */
|
||||
si.fufst_prio = 1; /* normal pio */
|
||||
si.nachbar_prio = 0;
|
||||
si.bewertung = 1; /* pegel */
|
||||
si.reduzierung = 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
typedef struct system_information {
|
||||
uint32_t ogk_timeslot_mask; /* each bit defines an assigned time slot */
|
||||
uint8_t fuz_nat; /* national network ID */
|
||||
uint8_t fuz_fuvst; /* id of switching center */
|
||||
uint8_t fuz_rest; /* rest of base station id */
|
||||
uint8_t mittel_umschalten;
|
||||
uint8_t grenz_umschalten;
|
||||
uint8_t mittel_ausloesen;
|
||||
uint8_t grenz_ausloesen;
|
||||
uint8_t sperre;
|
||||
uint8_t genauigkeit;
|
||||
uint8_t entfernung;
|
||||
uint8_t grenz_einbuchen;
|
||||
uint8_t fufst_prio; /* prio of base station */
|
||||
uint8_t nachbar_prio;
|
||||
uint8_t bewertung;
|
||||
uint8_t reduzierung;
|
||||
} cnetz_si;
|
||||
|
||||
extern cnetz_si si;
|
||||
|
||||
void init_sysinfo(void);
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,129 @@
|
|||
|
||||
#define OPCODE_EM_R 0
|
||||
#define OPCODE_UM_R 1
|
||||
#define OPCODE_UWG_R 2
|
||||
#define OPCODE_ATO_R 3
|
||||
#define OPCODE_VWG_R 4
|
||||
#define OPCODE_SRG_R 5
|
||||
#define OPCODE_NUG_R 6
|
||||
#define OPCODE_UWK_R 7
|
||||
#define OPCODE_MFT_M 8
|
||||
#define OPCODE_WUE_M 10
|
||||
#define OPCODE_BEL_K 16
|
||||
#define OPCODE_VH_K 17
|
||||
#define OPCODE_RTAQ_K 18
|
||||
#define OPCODE_AH_K 19
|
||||
#define OPCODE_VH_V 20
|
||||
#define OPCODE_AT_K 21
|
||||
#define OPCODE_AT_V 22
|
||||
#define OPCODE_DSQ_K 23
|
||||
#define OPCODE_USAI_V 24
|
||||
#define OPCODE_USAE_V 25
|
||||
#define OPCODE_USTLN_K 26
|
||||
#define OPCODE_ZFZQ_K 27
|
||||
#define OPCODE_AP_K 28
|
||||
#define OPCODE_MA_M 32
|
||||
#define OPCODE_VAK_R 33
|
||||
#define OPCODE_EBQ_R 35
|
||||
#define OPCODE_UBQ_R 36
|
||||
#define OPCODE_WSK_R 37
|
||||
#define OPCODE_MLR_M 38
|
||||
#define OPCODE_LR_R 39
|
||||
#define OPCODE_ATQ_R 40
|
||||
#define OPCODE_SAR_R 41
|
||||
#define OPCODE_WAF_M 42
|
||||
#define OPCODE_WBP_R 43
|
||||
#define OPCODE_WBN_R 44
|
||||
#define OPCODE_WWBP_R 45
|
||||
#define OPCODE_VAG_R 46
|
||||
#define OPCODE_VA_R 47
|
||||
#define OPCODE_BQ_K 48
|
||||
#define OPCODE_VHQ_K 49
|
||||
#define OPCODE_RTA_K 50
|
||||
#define OPCODE_AHQ_K 51
|
||||
#define OPCODE_VHQ1_V 52
|
||||
#define OPCODE_VHQ2_V 53
|
||||
#define OPCODE_AF_K 54
|
||||
#define OPCODE_AF_V 55
|
||||
#define OPCODE_DSB_K 56
|
||||
#define OPCODE_DSBI_V 57
|
||||
#define OPCODE_USF_K 58
|
||||
#define OPCODE_USBE_V 59
|
||||
#define OPCODE_ZFZ_K 60
|
||||
|
||||
#define BLOCK_I 0
|
||||
#define BLOCK_R 1
|
||||
#define BLOCK_M 2
|
||||
#define BLOCK_K 3
|
||||
#define BLOCK_V 4
|
||||
|
||||
/* data structor of one cnetz-message */
|
||||
typedef struct telegramm {
|
||||
double level; /* average level of received sync sequence */
|
||||
double sync_time; /* when did we receive the sync for this frame */
|
||||
double jitter; /* phase jitter of sync sequence */
|
||||
uint8_t opcode;
|
||||
/* used parameters depend on opcode */
|
||||
uint8_t fuz_fuvst_nr;
|
||||
uint8_t betriebs_art;
|
||||
uint8_t ankuendigung_gespraechsende;
|
||||
uint8_t teilnehmersperre;
|
||||
uint8_t anzahl_gesperrter_teilnehmergruppen;
|
||||
uint8_t fuz_rest_nr;
|
||||
uint16_t gebuehren_stand;
|
||||
uint16_t ogk_vorschlag;
|
||||
uint8_t fuz_nationalitaet;
|
||||
uint8_t sendeleistungsanpassung;
|
||||
uint16_t frequenz_nr;
|
||||
uint8_t art_der_signalisierung_im_ogk;
|
||||
uint8_t ogk_verkehrsanteil;
|
||||
uint8_t futln_nationalitaet;
|
||||
uint8_t max_sendeleistung;
|
||||
uint8_t kartenkennung;
|
||||
uint8_t durchfuehrung_der_ueberlastbehandlung;
|
||||
uint8_t sonderruf;
|
||||
uint16_t futln_rest_nr;
|
||||
uint8_t futln_heimat_fuvst_nr;
|
||||
uint16_t sicherungs_code;
|
||||
uint8_t ws_kennung;
|
||||
char wahlziffern[17];
|
||||
uint8_t zeitschlitz_nr;
|
||||
uint8_t grenze_fuer_ausloesen;
|
||||
uint8_t chipkarten_futelg_bit;
|
||||
uint8_t ausloesegrund;
|
||||
uint8_t bedingte_genauigkeit_der_fufst;
|
||||
uint8_t entfernung;
|
||||
uint8_t grenzwert_fuer_einbuchen_und_umbuchen;
|
||||
uint8_t nachbarschafts_prioritaets_bit;
|
||||
uint8_t herstellerkennung;
|
||||
uint8_t hardware_des_futelg;
|
||||
uint8_t software_des_futelg;
|
||||
uint8_t kennung_fufst;
|
||||
uint8_t authentifikationsbit;
|
||||
uint8_t mittelungsfaktor_fuer_ausloesen;
|
||||
uint8_t mittelungsfaktor_fuer_umschalten;
|
||||
uint16_t zufallszahl;
|
||||
uint8_t bewertung_nach_pegel_und_entfernung;
|
||||
uint64_t authorisierungsparameter;
|
||||
uint8_t entfernungsangabe_der_fufst;
|
||||
uint8_t gueltigkeit_des_gebuehrenstandes;
|
||||
uint8_t test_telefonteilnehmer_geraet;
|
||||
uint8_t grenzwert_fuer_umschalten;
|
||||
uint8_t vermittlungstechnische_sperren;
|
||||
uint8_t reduzierungsfaktor;
|
||||
uint64_t illegaler_opcode;
|
||||
} telegramm_t;
|
||||
|
||||
int init_telegramm(void);
|
||||
int init_coding(void);
|
||||
const char *telegramm_name(uint8_t opcode);
|
||||
|
||||
const char *telegramm2rufnummer(telegramm_t *telegramm);
|
||||
const char *transaction2rufnummer(transaction_t *trans);
|
||||
int match_fuz(telegramm_t *telegramm);
|
||||
int match_futln(telegramm_t *telegramm, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest);
|
||||
|
||||
int detect_sync(uint64_t bitstream);
|
||||
void cnetz_decode_telegramm(cnetz_t *cnetz, const char *bits, double level, double sync_time, double jitter);
|
||||
const char *cnetz_encode_telegramm(cnetz_t *cnetz);
|
||||
|
|
@ -38,6 +38,7 @@ struct debug_cat {
|
|||
{ "dsp", "\033[0;31m" },
|
||||
{ "anetz", "\033[1;34m" },
|
||||
{ "bnetz", "\033[1;34m" },
|
||||
{ "cnetz", "\033[1;34m" },
|
||||
{ "nmt", "\033[1;34m" },
|
||||
{ "frame", "\033[0;36m" },
|
||||
{ "call", "\033[1;37m" },
|
||||
|
|
|
@ -9,10 +9,11 @@
|
|||
#define DDSP 2
|
||||
#define DANETZ 3
|
||||
#define DBNETZ 4
|
||||
#define DNMT 5
|
||||
#define DFRAME 6
|
||||
#define DCALL 7
|
||||
#define DMNCC 8
|
||||
#define DCNETZ 5
|
||||
#define DNMT 6
|
||||
#define DFRAME 7
|
||||
#define DCALL 8
|
||||
#define DMNCC 9
|
||||
|
||||
#define PDEBUG(cat, level, fmt, arg...) _printdebug(__FILE__, __FUNCTION__, __LINE__, cat, level, fmt, ## arg)
|
||||
void _printdebug(const char *file, const char *function, int line, int cat, int level, const char *fmt, ...);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#ifndef _FILTER_H
|
||||
#define _FILTER_H
|
||||
|
||||
typedef struct biquad_low_pass {
|
||||
double a0, a1, a2, b1, b2;
|
||||
|
@ -7,3 +9,4 @@ typedef struct biquad_low_pass {
|
|||
void biquad_init(biquad_low_pass_t *bq, double frequency, int samplerate);
|
||||
void biquad_process(biquad_low_pass_t *bq, double *samples, int length, int iterations);
|
||||
|
||||
#endif /* _FILTER_H */
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
sender_t *sender_head = NULL;
|
||||
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, const char *sounddev, int samplerate, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int kanal, int loopback, double loss_volume, int use_pilot_signal)
|
||||
|
@ -139,7 +140,7 @@ static void gen_pilotton(sender_t *sender, int16_t *samples, int length)
|
|||
}
|
||||
|
||||
/* Handle audio streaming of one transceiver. */
|
||||
void process_sender(sender_t *sender, int latspl)
|
||||
void process_sender(sender_t *sender, int *quit, int latspl)
|
||||
{
|
||||
int16_t samples[latspl], pilot[latspl];
|
||||
int rc, count;
|
||||
|
@ -147,8 +148,15 @@ void process_sender(sender_t *sender, int latspl)
|
|||
count = sound_get_inbuffer(sender->sound);
|
||||
if (count < 0) {
|
||||
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to get samples in buffer (rc = %d)!\n", count);
|
||||
if (count == -EPIPE)
|
||||
if (count == -EPIPE) {
|
||||
if (cant_recover) {
|
||||
cant_recover:
|
||||
PDEBUG(DSENDER, DEBUG_ERROR, "Cannot recover due to measurements, quitting!\n");
|
||||
*quit = 1;
|
||||
return;
|
||||
}
|
||||
PDEBUG(DSENDER, DEBUG_ERROR, "Trying to recover!\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (count < latspl) {
|
||||
|
@ -189,8 +197,11 @@ void process_sender(sender_t *sender, int latspl)
|
|||
}
|
||||
if (rc < 0) {
|
||||
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to write TX data to sound device (rc = %d)\n", rc);
|
||||
if (rc == -EPIPE)
|
||||
if (rc == -EPIPE) {
|
||||
if (cant_recover)
|
||||
goto cant_recover;
|
||||
PDEBUG(DSENDER, DEBUG_ERROR, "Trying to recover!\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (sender->loopback == 1) {
|
||||
|
@ -204,8 +215,11 @@ void process_sender(sender_t *sender, int latspl)
|
|||
//printf("count=%d time= %.4f\n", count, (double)count * 1000 / sender->samplerate);
|
||||
if (count < 0) {
|
||||
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to read from sound device (rc = %d)!\n", count);
|
||||
if (count == -EPIPE)
|
||||
if (count == -EPIPE) {
|
||||
if (cant_recover)
|
||||
goto cant_recover;
|
||||
PDEBUG(DSENDER, DEBUG_ERROR, "Trying to recover!\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (count) {
|
||||
|
@ -236,7 +250,7 @@ void main_loop(int *quit, int latency)
|
|||
sender = sender_head;
|
||||
while (sender) {
|
||||
latspl = sender->samplerate * latency / 1000;
|
||||
process_sender(sender, latspl);
|
||||
process_sender(sender, quit, latspl);
|
||||
sender = sender->next;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ typedef struct sender {
|
|||
|
||||
/* list of all senders */
|
||||
extern sender_t *sender_head;
|
||||
extern int cant_recover;
|
||||
|
||||
int sender_create(sender_t *sender, const char *sounddev, int samplerate, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int kanal, int loopback, double loss_volume, int use_pilot_signal);
|
||||
void sender_destroy(sender_t *sender);
|
||||
|
|
|
@ -249,7 +249,10 @@ int sound_get_inbuffer(void *inst)
|
|||
|
||||
rc = snd_pcm_delay(sound->phandle, &delay);
|
||||
if (rc < 0) {
|
||||
PDEBUG(DSOUND, DEBUG_ERROR, "failed to get delay from interface (%s)\n", snd_strerror(rc));
|
||||
if (rc == -32)
|
||||
PDEBUG(DSOUND, DEBUG_ERROR, "Buffer underrun: Please use higher latency and enable real time scheduling\n");
|
||||
else
|
||||
PDEBUG(DSOUND, DEBUG_ERROR, "failed to get delay from interface (%s)\n", snd_strerror(rc));
|
||||
if (rc == -EPIPE)
|
||||
snd_pcm_prepare(sound->phandle);
|
||||
return rc;
|
||||
|
|
Loading…
Reference in New Issue