diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/Makefile.am | 1 | ||||
-rw-r--r-- | src/common/ffsk.c | 256 | ||||
-rw-r--r-- | src/common/ffsk.h | 27 |
3 files changed, 284 insertions, 0 deletions
diff --git a/src/common/Makefile.am b/src/common/Makefile.am index abdb4b5..f719c6e 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -24,6 +24,7 @@ libcommon_a_SOURCES = \ compandor.c \ fft.c \ fm_modulation.c \ + ffsk.c \ sender.c \ display_wave.c \ display_status.c \ diff --git a/src/common/ffsk.c b/src/common/ffsk.c new file mode 100644 index 0000000..fdbf255 --- /dev/null +++ b/src/common/ffsk.c @@ -0,0 +1,256 @@ +/* FFSK audio processing (NMT / Radiocom 2000) + * + * (C) 2017 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/>. + */ + +#define CHAN ffsk->channel + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include "../common/sample.h" +#include "../common/debug.h" +#include "ffsk.h" + +#define PI M_PI + +#define BIT_RATE 1200 /* baud rate */ +#define FILTER_STEPS 0.1 /* step every 1/12000 sec */ + +/* two signaling tones */ +static double ffsk_freq[2] = { + 1800.0, + 1200.0, +}; + +static sample_t dsp_tone_bit[2][2][65536]; /* polarity, bit, phase */ + +/* global init for FFSK */ +void ffsk_global_init(double peak_fsk) +{ + int i; + double s; + + PDEBUG(DDSP, DEBUG_DEBUG, "Generating sine table for FFSK tones.\n"); + for (i = 0; i < 65536; i++) { + s = sin((double)i / 65536.0 * 2.0 * PI); + /* bit(1) 1 cycle */ + dsp_tone_bit[0][1][i] = s * peak_fsk; + dsp_tone_bit[1][1][i] = -s * peak_fsk; + /* bit(0) 1.5 cycles */ + s = sin((double)i / 65536.0 * 3.0 * PI); + dsp_tone_bit[0][0][i] = s * peak_fsk; + dsp_tone_bit[1][0][i] = -s * peak_fsk; + } +} + +/* Init FFSK */ +int ffsk_init(ffsk_t *ffsk, void *inst, void (*receive_bit)(void *inst, int bit, double quality, double level), int channel, int samplerate) +{ + sample_t *spl; + int i; + + /* a symbol rate of 1200 Hz, times check interval of FILTER_STEPS */ + if (samplerate < (double)BIT_RATE / (double)FILTER_STEPS) { + PDEBUG(DDSP, DEBUG_ERROR, "Sample rate must be at least 12000 Hz to process FSK+supervisory signal.\n"); + return -EINVAL; + } + + memset(ffsk, 0, sizeof(*ffsk)); + ffsk->inst = inst; + ffsk->receive_bit = receive_bit; + ffsk->channel = channel; + ffsk->samplerate = samplerate; + + ffsk->samples_per_bit = (double)ffsk->samplerate / (double)BIT_RATE; + ffsk->bits_per_sample = 1.0 / ffsk->samples_per_bit; + PDEBUG(DDSP, DEBUG_DEBUG, "Use %.4f samples for full bit duration @ %d.\n", ffsk->samples_per_bit, ffsk->samplerate); + + /* allocate ring buffers, one bit duration */ + ffsk->filter_size = floor(ffsk->samples_per_bit); /* buffer holds one bit (rounded down) */ + spl = calloc(1, ffsk->filter_size * sizeof(*spl)); + if (!spl) { + PDEBUG(DDSP, DEBUG_ERROR, "No memory!\n"); + ffsk_cleanup(ffsk); + return -ENOMEM; + } + ffsk->filter_spl = spl; + ffsk->filter_bit = -1; + + /* count symbols */ + for (i = 0; i < 2; i++) + audio_goertzel_init(&ffsk->goertzel[i], ffsk_freq[i], ffsk->samplerate); + ffsk->phaseshift65536 = 65536.0 / ffsk->samples_per_bit; + PDEBUG(DDSP, DEBUG_DEBUG, "fsk_phaseshift = %.4f\n", ffsk->phaseshift65536); + + return 0; +} + +/* Cleanup transceiver instance. */ +void ffsk_cleanup(ffsk_t *ffsk) +{ + PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Cleanup DSP for Transceiver.\n"); + + if (ffsk->filter_spl) { + free(ffsk->filter_spl); + ffsk->filter_spl = NULL; + } +} + +//#define DEBUG_MODULATOR +//#define DEBUG_FILTER +//#define DEBUG_QUALITY + +/* Filter one chunk of audio an detect tone, quality and loss of signal. + * The chunk is a window of 1/1200s. This window slides over audio stream + * and is processed every 1/12000s. (one step) */ +static inline void ffsk_decode_step(ffsk_t *ffsk, int pos) +{ + double level, result[2], softbit, quality; + int max; + sample_t *spl; + int bit; + + max = ffsk->filter_size; + spl = ffsk->filter_spl; + + level = audio_level(spl, max); + /* limit level to prevent division by zero */ + if (level < 0.001) + level = 0.001; + + audio_goertzel(ffsk->goertzel, spl, max, pos, result, 2); + + /* calculate soft bit from both frequencies */ + softbit = (result[1] / level - result[0] / level + 1.0) / 2.0; +//printf("%.3f: %.3f\n", level, softbit); + /* scale it, since both filters overlap by some percent */ +#define MIN_QUALITY 0.33 + softbit = (softbit - MIN_QUALITY) / (1.0 - MIN_QUALITY - MIN_QUALITY); +#ifdef DEBUG_FILTER +// printf("|%s", debug_amplitude(result[0]/level)); +// printf("|%s| low=%.3f high=%.3f level=%d\n", debug_amplitude(result[1]/level), result[0]/level, result[1]/level, (int)level); + printf("|%s| softbit=%.3f\n", debug_amplitude(softbit), softbit); +#endif + if (softbit > 1) + softbit = 1; + if (softbit < 0) + softbit = 0; + if (softbit > 0.5) + bit = 1; + else + bit = 0; + + if (ffsk->filter_bit != bit) { + /* If we have a bit change, move sample counter towards one half bit duration. + * We may have noise, so the bit change may be wrong or not at the correct place. + * This can cause bit slips. + * Therefore we change the sample counter only slightly, so bit slips may not + * happen so quickly. + * */ +#ifdef DEBUG_FILTER + puts("bit change"); +#endif + ffsk->filter_bit = bit; + if (ffsk->filter_sample < 5) + ffsk->filter_sample++; + if (ffsk->filter_sample > 5) + ffsk->filter_sample--; + } else if (--ffsk->filter_sample == 0) { + /* if sample counter bit reaches 0, we reset sample counter to one bit duration */ +#ifdef DEBUG_FILTER + puts("sample"); +#endif +// quality = result[bit] / level; + if (softbit > 0.5) + quality = softbit * 2.0 - 1.0; + else + quality = 1.0 - softbit * 2.0; +#ifdef DEBUG_QUALITY + printf("|%s| quality=%.2f ", debug_amplitude(softbit), quality); + printf("|%s|\n", debug_amplitude(quality)); +#endif + /* adjust level, so a peak level becomes 100% */ + ffsk->receive_bit(ffsk->inst, bit, quality, level / 0.63662); + ffsk->filter_sample = 10; + } +} + +void ffsk_receive(ffsk_t *ffsk, sample_t *sample, int length) +{ + sample_t *spl; + int max, pos; + double step, bps; + int i; + + /* write received samples to decode buffer */ + max = ffsk->filter_size; + pos = ffsk->filter_pos; + step = ffsk->filter_step; + bps = ffsk->bits_per_sample; + spl = ffsk->filter_spl; + for (i = 0; i < length; i++) { +#ifdef DEBUG_MODULATOR + printf("|%s|\n", debug_amplitude((double)samples[i] / 2333.0 /*fsk peak*/ / 2.0)); +#endif + /* write into ring buffer */ + spl[pos++] = sample[i]; + if (pos == max) + pos = 0; + /* if 1/10th of a bit duration is reached, decode buffer */ + step += bps; + if (step >= FILTER_STEPS) { + step -= FILTER_STEPS; + ffsk_decode_step(ffsk, pos); + } + } + ffsk->filter_step = step; + ffsk->filter_pos = pos; +} + +/* render frame */ +int ffsk_render_frame(ffsk_t *ffsk, const char *frame, int length, sample_t *sample) +{ + int bit, polarity; + double phaseshift, phase; + int count = 0, i; + + polarity = ffsk->polarity; + phaseshift = ffsk->phaseshift65536; + phase = ffsk->phase65536; + for (i = 0; i < length; i++) { + bit = (frame[i] == '1'); + do { + *sample++ = dsp_tone_bit[polarity][bit][(uint16_t)phase]; + count++; + phase += phaseshift; + } while (phase < 65536.0); + phase -= 65536.0; + /* flip polarity when we have 1.5 sine waves */ + if (bit == 0) + polarity = 1 - polarity; + } + ffsk->phase65536 = phase; + ffsk->polarity = polarity; + + /* return number of samples created for frame */ + return count; +} + diff --git a/src/common/ffsk.h b/src/common/ffsk.h new file mode 100644 index 0000000..84fc52a --- /dev/null +++ b/src/common/ffsk.h @@ -0,0 +1,27 @@ +#include "../common/goertzel.h" + +typedef struct ffsk { + void *inst; + void (*receive_bit)(void *inst, int bit, double quality, double level); + int channel; /* channel number */ + int samplerate; /* current sample rate */ + double samples_per_bit; /* number of samples for one bit (1200 Baud) */ + double bits_per_sample; /* fraction of a bit per sample */ + goertzel_t goertzel[2]; /* filter for fsk decoding */ + int polarity; /* current polarity state of bit */ + sample_t *filter_spl; /* array to hold ring buffer for bit decoding */ + int filter_size; /* size of ring buffer */ + int filter_pos; /* position to write next sample */ + double filter_step; /* counts bit duration, to trigger decoding every 10th bit */ + int filter_bit; /* last bit state, so we detect a bit change */ + int filter_sample; /* count until it is time to sample bit */ + double phaseshift65536; /* how much the phase of fsk synbol changes per sample */ + double phase65536; /* current phase */ +} ffsk_t; + +void ffsk_global_init(double peak_fsk); +int ffsk_init(ffsk_t *ffsk, void *inst, void (*receive_bit)(void *inst, int bit, double quality, double level), int channel, int samplerate); +void ffsk_cleanup(ffsk_t *ffsk); +void ffsk_receive(ffsk_t *ffsk, sample_t *sample, int lenght); +int ffsk_render_frame(ffsk_t *ffsk, const char *frame, int length, sample_t *sample); + |