From ef7ff26793b290d5ee3725a5490f6e5bfeb7b5da Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Wed, 17 May 2017 12:21:19 +0200 Subject: [PATCH] C-Netz: SDR now uses zero-crossing to detect level changes. This does not work with analog radio, but with SDR it works. The quality should be better and the process is faster. --- src/cnetz/Makefile.am | 2 +- src/cnetz/cnetz.c | 4 +- src/cnetz/cnetz.h | 2 +- src/cnetz/dsp.c | 4 +- src/cnetz/dsp.h | 2 +- src/cnetz/{fsk_fm_demod.c => fsk_demod.c} | 205 ++++++++++++++++------ src/cnetz/{fsk_fm_demod.h => fsk_demod.h} | 8 +- 7 files changed, 168 insertions(+), 59 deletions(-) rename src/cnetz/{fsk_fm_demod.c => fsk_demod.c} (76%) rename src/cnetz/{fsk_fm_demod.h => fsk_demod.h} (90%) diff --git a/src/cnetz/Makefile.am b/src/cnetz/Makefile.am index 79d008f5..2177f1d4 100644 --- a/src/cnetz/Makefile.am +++ b/src/cnetz/Makefile.am @@ -10,7 +10,7 @@ cnetz_SOURCES = \ sysinfo.c \ telegramm.c \ dsp.c \ - fsk_fm_demod.c \ + fsk_demod.c \ scrambler.c \ image.c \ ansage.c \ diff --git a/src/cnetz/cnetz.c b/src/cnetz/cnetz.c index 3da44669..2286e46f 100644 --- a/src/cnetz/cnetz.c +++ b/src/cnetz/cnetz.c @@ -140,7 +140,7 @@ #include "telegramm.h" #include "dsp.h" -/* uncomment this to do echo debugging (-L) on Speech Channel */ +/* uncomment this to do echo debugging (-l) on Speech Channel */ //#define DEBUG_SPK #define CUT_OFF_EMPHASIS_CNETZ 796.0 /* 200 uS time constant */ @@ -290,7 +290,7 @@ int cnetz_create(int kanal, enum cnetz_chan_type chan_type, const char *audiodev #endif /* init audio processing */ - rc = dsp_init_sender(cnetz, measure_speed, clock_speed); + rc = dsp_init_sender(cnetz, measure_speed, clock_speed, use_sdr); if (rc < 0) { PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to init signal processing!\n"); goto error; diff --git a/src/cnetz/cnetz.h b/src/cnetz/cnetz.h index 73e837b2..b5be7554 100644 --- a/src/cnetz/cnetz.h +++ b/src/cnetz/cnetz.h @@ -1,6 +1,6 @@ #include "../common/compandor.h" #include "../common/sender.h" -#include "fsk_fm_demod.h" +#include "fsk_demod.h" #include "scrambler.h" #include "transaction.h" diff --git a/src/cnetz/dsp.c b/src/cnetz/dsp.c index 675ff9ea..c719bf92 100644 --- a/src/cnetz/dsp.c +++ b/src/cnetz/dsp.c @@ -83,7 +83,7 @@ static void dsp_init_ramp(cnetz_t *cnetz) } /* Init transceiver instance. */ -int dsp_init_sender(cnetz_t *cnetz, int measure_speed, double clock_speed[2]) +int dsp_init_sender(cnetz_t *cnetz, int measure_speed, double clock_speed[2], int use_sdr) { int rc = 0; double size; @@ -136,7 +136,7 @@ int dsp_init_sender(cnetz_t *cnetz, int measure_speed, double clock_speed[2]) /* reinit the sample rate to shrink/expand audio */ init_samplerate(&cnetz->sender.srstate, 8000.0, (double)cnetz->sender.samplerate / 1.1, 3300.0); /* 66 <-> 60 */ - rc = fsk_fm_init(&cnetz->fsk_demod, cnetz, cnetz->sender.samplerate, (double)BITRATE / (1.0 + clock_speed[0] / 1000000.0)); + rc = fsk_fm_init(&cnetz->fsk_demod, cnetz, cnetz->sender.samplerate, (double)BITRATE / (1.0 + clock_speed[0] / 1000000.0), (use_sdr) ? FSK_DEMOD_LEVEL : FSK_DEMOD_SLOPE); if (rc < 0) goto error; diff --git a/src/cnetz/dsp.h b/src/cnetz/dsp.h index 649737f3..7528e18b 100644 --- a/src/cnetz/dsp.h +++ b/src/cnetz/dsp.h @@ -1,6 +1,6 @@ void dsp_init(void); -int dsp_init_sender(cnetz_t *cnetz, int measure_speed, double clock_speed[2]); +int dsp_init_sender(cnetz_t *cnetz, int measure_speed, double clock_speed[2], int use_sdr); void dsp_cleanup_sender(cnetz_t *cnetz); void calc_clock_speed(cnetz_t *cnetz, double samples, int tx, int result); void unshrink_speech(cnetz_t *cnetz, sample_t *speech_buffer, int count); diff --git a/src/cnetz/fsk_fm_demod.c b/src/cnetz/fsk_demod.c similarity index 76% rename from src/cnetz/fsk_fm_demod.c rename to src/cnetz/fsk_demod.c index 6005b3bb..99d3dc49 100644 --- a/src/cnetz/fsk_fm_demod.c +++ b/src/cnetz/fsk_demod.c @@ -24,15 +24,24 @@ * 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: + * Detecting level change (from SDR): + * + * Whenever we cross zero, we detect a level change. Also we know the level + * of the bit then. If we don't get another level change within 1.5 of bit + * duration, we will sample the next bit with the current level. From then + * we will sample the next bit 1.0 bit duration later, if there is still no + * level change. If we get another level change, we take that bit and wait + * 1.5 bit duration for next change... + * + * Detect level change (from analog radio): * * 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 + * 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 + * reversed, if we find out, that we received a sync sequence in reversed * polarity.) If there is no significant change in level, we keep the value of * last change, regardless of what level we actually receive. * @@ -88,9 +97,9 @@ * 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. + * since the mobile phone is perfectly synced to us. * - * After receiving and decording of a frame, we use the time of received sync + * After receiving and decoding 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 @@ -107,8 +116,12 @@ * of the sample graph. */ -/* use to debug decoder */ +/* use to debug decoder + * if debug is set to 0, debugging will start from SPK_V signalling, + * if debug is set to 1, debugging will start at program start + */ //#define DEBUG_DECODER +//static int debug = 0; #include #include @@ -122,7 +135,7 @@ #include "dsp.h" #include "telegramm.h" -int fsk_fm_init(fsk_fm_demod_t *fsk, cnetz_t *cnetz, int samplerate, double bitrate) +int fsk_fm_init(fsk_fm_demod_t *fsk, cnetz_t *cnetz, int samplerate, double bitrate, enum demod_type demod_type) { int len, half; @@ -133,6 +146,12 @@ int fsk_fm_init(fsk_fm_demod_t *fsk, cnetz_t *cnetz, int samplerate, double bitr } fsk->cnetz = cnetz; + fsk->demod_type = demod_type; + + if (demod_type == FSK_DEMOD_SLOPE) + PDEBUG(DDSP, DEBUG_INFO, "Detecting level change by looking at slope (good for sound cards)\n"); + if (demod_type == FSK_DEMOD_LEVEL) + PDEBUG(DDSP, DEBUG_INFO, "Detecting level change by looking zero crosssing (good for SDR)\n"); len = (int)((double)samplerate / bitrate + 0.5); half = (int)((double)samplerate / bitrate / 2.0 + 0.5); @@ -157,7 +176,7 @@ int fsk_fm_init(fsk_fm_demod_t *fsk, cnetz_t *cnetz, int samplerate, double bitr #ifdef DEBUG_DECODER char debug_filename[256]; - sprintf(debug_filename, "/tmp/debug_decoder_channel_%d.txt\n", cnetz->sender.kanal); + sprintf(debug_filename, "/tmp/debug_decoder_channel_%d.txt", cnetz->sender.kanal); fsk->debug_fp = fopen(debug_filename, "w"); if (!fsk->debug_fp) { fprintf(stderr, "Failed to open decoder debug file '%s'!\n", debug_filename); @@ -204,7 +223,7 @@ static inline void get_levels(fsk_fm_demod_t *fsk, double *_min, double *_max, d /* 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) + if (level <= 0.0) continue; /* in spk mode, we skip the voice part (62 bits) */ @@ -262,7 +281,7 @@ static inline void get_levels(fsk_fm_demod_t *fsk, double *_min, double *_max, d /* get jitter of received changes */ for (i = 0; i < num; i++) { level = fsk->change_levels[(fsk->change_pos - 1 - i) & 0xff]; - if (level <= 0) + if (level <= 0.0) continue; /* in spk mode, we skip the voice part (62 bits) */ @@ -289,7 +308,7 @@ static inline void got_bit(fsk_fm_demod_t *fsk, int bit, double change_level) /* for first bit, we have only half of the modulation deviation, so we multiply level by two */ if (fsk->bit_count == 0) change_level *= 2.0; - if (fsk->bit_count == 4) + if (fsk->bit_count >= 4) return; } fsk->bit_count++; @@ -319,14 +338,15 @@ static inline void got_bit(fsk_fm_demod_t *fsk, int bit, double change_level) fsk->sync = FSK_SYNC_POSITIVE; got_sync: #ifdef DEBUG_DECODER - fprintf(fsk->debug_fp, " SYNC!"); + if (debug) + fprintf(fsk->debug_fp, " SYNC!"); #endif get_levels(fsk, &min, &max, &avg, &probes, 30, &fsk->sync_time, &fsk->sync_jitter); - fsk->sync_level = avg / 2.0; + fsk->sync_level = avg; if (fsk->sync == FSK_SYNC_NEGATIVE) fsk->sync_level = -fsk->sync_level; // printf("sync (change min=%.0f%% max=%.0f%% avg=%.0f%% sync_time=%.2f jitter=%.2f probes=%d)\n", min * 100, max * 100, avg * 100, fsk->sync_time, fsk->sync_jitter, probes); - fsk->level_threshold = (double)avg / 2.0; + fsk->level_threshold = (double)avg; fsk->rx_sync = 0; fsk->rx_buffer_count = 0; break; @@ -344,7 +364,8 @@ got_sync: if (++fsk->rx_buffer_count == 150) { fsk->sync = FSK_SYNC_NONE; #ifdef DEBUG_DECODER - fprintf(fsk->debug_fp, " FRAME DONE!"); + if (debug) + fprintf(fsk->debug_fp, " FRAME DONE!"); #endif if (fsk->cnetz->dsp_mode != DSP_MODE_SPK_V) { /* received 40 bits after start of block */ @@ -359,8 +380,8 @@ got_sync: } } -/* DOC TBD: find change for bit change */ -static inline void find_change(fsk_fm_demod_t *fsk) +/* find bit change by checking slope within a window */ +static inline void find_change_slope(fsk_fm_demod_t *fsk) { sample_t level_min = 0, level_max = 0, change_max = -1; int change_at = -1, change_positive = -1; @@ -368,10 +389,16 @@ static inline void find_change(fsk_fm_demod_t *fsk) sample_t threshold; int i; - /* levels at total reverse */ - change_max = -1; - change_at = -1; - change_positive = -1; +#ifdef DEBUG_DECODER + /* show deviation of middle sample in windows (in a range of bandwidth) */ + if (debug) { + fprintf(fsk->debug_fp, "%s", + debug_amplitude( + fsk->bit_buffer_spl[(fsk->bit_buffer_pos + fsk->bit_buffer_half) % fsk->bit_buffer_len] + ) + ); + } +#endif /* get level range (level_min and level_max) and also * get maximum slope (change_max) and where it was @@ -412,26 +439,115 @@ static inline void find_change(fsk_fm_demod_t *fsk) * and then all subsequent bits after 1.0 bits */ if (level_max - level_min > threshold && change_at == fsk->bit_buffer_half) { #ifdef DEBUG_DECODER - fprintf(fsk->debug_fp, " CHANGE %d->%d (level=%.3f, threshold=%.3f)", + if (debug) { + fprintf(fsk->debug_fp, " CHANGE %d->%d (level=%.3f, threshold=%.3f)", fsk->last_change_positive, change_positive, level_max - level_min, threshold); + } #endif fsk->last_change_positive = change_positive; if (!fsk->sync) { fsk->next_bit = 1.5; - got_bit(fsk, change_positive, level_max - level_min); + got_bit(fsk, change_positive, (level_max - level_min) / 2); } } if (fsk->next_bit <= 0.0) { #ifdef DEBUG_DECODER - fprintf(fsk->debug_fp, " SAMPLING %d", fsk->last_change_positive); + if (debug) + fprintf(fsk->debug_fp, " SAMPLING %d", fsk->last_change_positive); #endif fsk->next_bit += 1.0; - got_bit(fsk, fsk->last_change_positive, 0); +#ifdef DEBUG_DECODER + if (debug && fsk->cnetz->dsp_mode == DSP_MODE_SPK_V && fsk->bit_count >= 4) + fprintf(fsk->debug_fp, " (ignoring)"); +#endif + got_bit(fsk, fsk->last_change_positive, 0.0); } fsk->next_bit -= fsk->bits_per_sample; + +#ifdef DEBUG_DECODER + if (debug) + fprintf(fsk->debug_fp, "\n"); +#endif +} + +/* find bit change by looking at zero crossing */ +static inline void find_change_level(fsk_fm_demod_t *fsk) +{ + int change_positive = -1; + sample_t s; + + /* get bit in the middle of the buffer */ + s = fsk->bit_buffer_spl[(fsk->bit_buffer_pos + fsk->bit_buffer_half) % fsk->bit_buffer_len]; + +#ifdef DEBUG_DECODER + /* show deviation */ + if (debug) + fprintf(fsk->debug_fp, "%s", debug_amplitude(s)); +#endif + + /* just sample first bit in distributed mode */ + if (fsk->cnetz->dsp_mode == DSP_MODE_SPK_V && fsk->bit_count == 0) { + if (fmod(fsk->bit_time, BITS_PER_SPK_BLOCK) < 1.5) + goto done; + +#ifdef DEBUG_DECODER + if (debug) + fprintf(fsk->debug_fp, " (First bit of data chunk)"); +#endif + /* use current level for first bit to sample */ + fsk->last_change_positive = (s > 0); + fsk->next_bit = 0.0; + } else { + /* see if we have a level change */ + if (!fsk->last_change_positive && s > 0) + change_positive = 1; + if (fsk->last_change_positive && s < 0) + change_positive = 0; + } + + /* 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 */ + if (change_positive >= 0) { +#ifdef DEBUG_DECODER + if (debug) + fprintf(fsk->debug_fp, " CHANGE %d->%d", fsk->last_change_positive, change_positive); +#endif + fsk->last_change_positive = change_positive; + if (!fsk->sync) { + fsk->next_bit = 1.5; + /* if bit change is inside window, we can get level from end of window */ + s = fsk->bit_buffer_spl[(fsk->bit_buffer_pos + fsk->bit_buffer_len - 1) % fsk->bit_buffer_len]; + got_bit(fsk, change_positive, fabs(s)); + } + } + if (fsk->next_bit <= 0.0) { +#ifdef DEBUG_DECODER + if (debug) + fprintf(fsk->debug_fp, " SAMPLING %d", fsk->last_change_positive); +#endif + fsk->next_bit += 1.0; +#ifdef DEBUG_DECODER + if (debug && fsk->cnetz->dsp_mode == DSP_MODE_SPK_V && fsk->bit_count >= 4) + fprintf(fsk->debug_fp, " (ignoring)"); +#endif + got_bit(fsk, fsk->last_change_positive, 0.0); + } + fsk->next_bit -= fsk->bits_per_sample; + +done: +#ifdef DEBUG_DECODER + if (debug) + fprintf(fsk->debug_fp, "\n"); +#endif + return; } /* receive FM signal from receiver */ @@ -448,22 +564,17 @@ void fsk_fm_demod(fsk_fm_demod_t *fsk, sample_t *samples, int length) /* for each sample process buffer */ if (fsk->cnetz->dsp_mode != DSP_MODE_SPK_V) { -#ifdef DEBUG_DECODER - /* show deviation of middle sample in windows (in a range of +-5000 Hz) */ - fprintf(fsk->debug_fp, "%s", - debug_amplitude( - fsk->bit_buffer_spl[(fsk->bit_buffer_pos + fsk->bit_buffer_half) % fsk->bit_buffer_len] / 5000.0 - ) - ); -#endif - find_change(fsk); -#ifdef DEBUG_DECODER - fprintf(fsk->debug_fp, "\n"); -#endif + if (fsk->demod_type == FSK_DEMOD_SLOPE) + find_change_slope(fsk); + else + find_change_level(fsk); } else { +#ifdef DEBUG_DECODER + /* start debugging */ + debug = 1; +#endif /* in distributed signaling, 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 */ @@ -471,24 +582,16 @@ void fsk_fm_demod(fsk_fm_demod_t *fsk, sample_t *samples, int length) if (t < 0.5) { fsk->next_bit = 1.0 - fsk->bits_per_sample; #ifdef DEBUG_DECODER - if (fsk->bit_count) + if (debug && fsk->bit_count) fprintf(fsk->debug_fp, "---- SPK(V) BLOCK START ----\n"); #endif fsk->bit_count = 0; } else if (t >= 0.5 && t < 5.5) { -#ifdef DEBUG_DECODER - /* show deviation of middle sample in windows (in a range of +-5000 Hz) */ - fprintf(fsk->debug_fp, "%s", - debug_amplitude( - fsk->bit_buffer_spl[(fsk->bit_buffer_pos + fsk->bit_buffer_half) % fsk->bit_buffer_len] / 5000.0 - ) - ); -#endif - find_change(fsk); -#ifdef DEBUG_DECODER - fprintf(fsk->debug_fp, "\n"); -#endif + if (fsk->demod_type == FSK_DEMOD_SLOPE) + find_change_slope(fsk); + else + find_change_level(fsk); } else if (t >= 5.5 && t < 65.5) { /* get audio for the duration of 60 bits */ diff --git a/src/cnetz/fsk_fm_demod.h b/src/cnetz/fsk_demod.h similarity index 90% rename from src/cnetz/fsk_fm_demod.h rename to src/cnetz/fsk_demod.h index 6d198547..d8d645fa 100644 --- a/src/cnetz/fsk_fm_demod.h +++ b/src/cnetz/fsk_demod.h @@ -10,6 +10,11 @@ enum fsk_sync { FSK_SYNC_NEGATIVE, }; +enum demod_type { + FSK_DEMOD_SLOPE, /* check for highest slope (good for sound cards) */ + FSK_DEMOD_LEVEL, /* check for zero crossing (good for SDR) */ +}; + typedef struct cnetz cnetz_t; typedef struct fsk_fm_demod { @@ -20,6 +25,7 @@ typedef struct fsk_fm_demod { double bit_time_uncorrected; /* same as above, but not corrected by sync */ /* bit detection */ + enum demod_type demod_type; /* how to demodulate bits */ sample_t *bit_buffer_spl; /* samples ring buffer */ int bit_buffer_len; /* number of samples in ring buffer */ int bit_buffer_half; /* half of ring buffer */ @@ -53,7 +59,7 @@ typedef struct fsk_fm_demod { FILE *debug_fp; /* file pointer for debugging output */ } fsk_fm_demod_t; -int fsk_fm_init(fsk_fm_demod_t *fsk, cnetz_t *cnetz, int samplerate, double bitrate); +int fsk_fm_init(fsk_fm_demod_t *fsk, cnetz_t *cnetz, int samplerate, double bitrate, enum demod_type); void fsk_fm_exit(fsk_fm_demod_t *fsk); void fsk_fm_demod(fsk_fm_demod_t *fsk, sample_t *samples, int length); void fsk_correct_sync(fsk_fm_demod_t *fsk, double offset);