diff options
Diffstat (limited to 'src/golay/dsp.c')
-rw-r--r-- | src/golay/dsp.c | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/src/golay/dsp.c b/src/golay/dsp.c new file mode 100644 index 0000000..224ecbd --- /dev/null +++ b/src/golay/dsp.c @@ -0,0 +1,261 @@ +/* GSC signal processing + * + * (C) 2022 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 gsc->sender.kanal + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include <sys/param.h> +#include "../libsample/sample.h" +#include "../liblogging/logging.h" +#include "golay.h" +#include "dsp.h" + +#define MAX_DISPLAY 1.4 /* something above speech level, no emphasis */ +#define VOICE_BANDWIDTH 3000 /* just guessing */ + +static void dsp_init_ramp(gsc_t *gsc) +{ + double c; + int i; + + LOGP_CHAN(DDSP, LOGL_DEBUG, "Generating cosine shaped ramp table.\n"); + for (i = 0; i < 256; i++) { + /* This is mathematically incorrect... */ + if (i < 64) + c = 1.0; + else if (i >= 192) + c = -1.0; + else + c = cos((double)(i - 64) / 128.0 * M_PI); + gsc->fsk_ramp_down[i] = c * gsc->fsk_deviation * gsc->fsk_polarity; + gsc->fsk_ramp_up[i] = -gsc->fsk_ramp_down[i]; + } +} + +/* Init transceiver instance. */ +int dsp_init_sender(gsc_t *gsc, int samplerate, double deviation, double polarity) +{ + int rc; + + LOGP_CHAN(DDSP, LOGL_DEBUG, "Init DSP for transceiver.\n"); + + /* set modulation parameters */ + // NOTE: baudrate equals modulation, because we have a raised cosine ramp of beta = 0.5 + sender_set_fm(&gsc->sender, deviation, 600.0, deviation, MAX_DISPLAY); + + gsc->fsk_bitduration = (double)samplerate / 600.0; + gsc->fsk_bitstep = 1.0 / gsc->fsk_bitduration; + LOGP_CHAN(DDSP, LOGL_DEBUG, "Use %.4f samples for one bit duration @ %d.\n", gsc->fsk_bitduration, gsc->sender.samplerate); + + gsc->fsk_tx_buffer_size = gsc->fsk_bitduration + 10; /* 1 bit, add some extra to prevent short buffer due to rounding */ + gsc->fsk_tx_buffer = calloc(sizeof(sample_t), gsc->fsk_tx_buffer_size); + if (!gsc->fsk_tx_buffer) { + LOGP_CHAN(DDSP, LOGL_ERROR, "No memory!\n"); + rc = -ENOMEM; + goto error; + } + + /* create deviation and ramp */ + gsc->fsk_deviation = 1.0; // equals what we st at sender_set_fm() + gsc->fsk_polarity = polarity; + dsp_init_ramp(gsc); + + return 0; + +error: + dsp_cleanup_sender(gsc); + + return -rc; + +} + +/* Cleanup transceiver instance. */ +void dsp_cleanup_sender(gsc_t *gsc) +{ + LOGP_CHAN(DDSP, LOGL_DEBUG, "Cleanup DSP for transceiver.\n"); + + if (gsc->fsk_tx_buffer) { + free(gsc->fsk_tx_buffer); + gsc->fsk_tx_buffer = NULL; + } +} + + +/* encode one bit into samples + * input: bit + * output: samples + * return number of samples */ +static int fsk_bit_encode(gsc_t *gsc, uint8_t bit) +{ + /* alloc samples, add 1 in case there is a rest */ + sample_t *spl; + double phase, bitstep, devpol; + int count; + uint8_t lastbit; + + devpol = gsc->fsk_deviation * gsc->fsk_polarity; + spl = gsc->fsk_tx_buffer; + phase = gsc->fsk_tx_phase; + lastbit = gsc->fsk_tx_lastbit; + bitstep = gsc->fsk_bitstep * 256.0; + + if (lastbit) { + if (bit) { + /* stay up */ + do { + *spl++ = devpol; + phase += bitstep; + } while (phase < 256.0); + phase -= 256.0; + } else { + /* ramp down */ + do { + *spl++ = gsc->fsk_ramp_down[(uint8_t)phase]; + phase += bitstep; + } while (phase < 256.0); + phase -= 256.0; + lastbit = 0; + } + } else { + if (bit) { + /* ramp up */ + do { + *spl++ = gsc->fsk_ramp_up[(uint8_t)phase]; + phase += bitstep; + } while (phase < 256.0); + phase -= 256.0; + lastbit = 1; + } else { + /* stay down */ + do { + *spl++ = -devpol; + phase += bitstep; + } while (phase < 256.0); + phase -= 256.0; + } + } + + /* depending on the number of samples, return the number */ + count = ((uintptr_t)spl - (uintptr_t)gsc->fsk_tx_buffer) / sizeof(*spl); + + gsc->fsk_tx_phase = phase; + gsc->fsk_tx_lastbit = lastbit; + + return count; +} + +/* Process received audio stream from radio unit. */ +void sender_receive(sender_t __attribute__((unused)) *sender, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) length, double __attribute__((unused)) rf_level_db) +{ +} + +/* Provide stream of audio toward radio unit */ +void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length) +{ + gsc_t *gsc = (gsc_t *) sender; + int rc; + +again: + /* play 2 seconds of pause */ + if (gsc->wait_2_sec) { + int tosend = MIN(length, gsc->wait_2_sec); + memset(power, 1, tosend); + memset(samples, 0, sizeof(samples) * tosend); + power += tosend; + samples += tosend; + gsc->wait_2_sec -= tosend; + if (gsc->wait_2_sec) + return; + } + + /* play wave file, if open */ + if (gsc->wave_tx_play.left) { + int wave_num, s; + wave_num = samplerate_upsample_input_num(&gsc->wave_tx_upsample, length); + sample_t buffer[wave_num * 2], *wave_samples[2] = { buffer, buffer + wave_num }; + wave_read(&gsc->wave_tx_play, wave_samples, wave_num); + if (gsc->wave_tx_channels == 2) { + for (s = 0; s < wave_num; s++) { + wave_samples[0][s] += wave_samples[1][s]; + } + } + samplerate_upsample(&gsc->wave_tx_upsample, wave_samples[0], wave_num, samples, length); + if (!gsc->wave_tx_play.left) { + LOGP_CHAN(DDSP, LOGL_INFO, "Voice message sent.\n"); + wave_destroy_playback(&gsc->wave_tx_play); + return; + } + return; + } + + + /* get FSK bits or start playing wave file */ + if (!gsc->fsk_tx_buffer_length) { + int8_t bit = get_bit(gsc); + + /* bit == 2 means voice transmission. */ + if (bit == 2) { + if (gsc->wave_tx_filename[0]) { + gsc->wave_tx_samplerate = gsc->wave_tx_channels = 0; + rc = wave_create_playback(&gsc->wave_tx_play, gsc->wave_tx_filename, &gsc->wave_tx_samplerate, &gsc->wave_tx_channels, gsc->fsk_deviation); + if (rc < 0) { + gsc->wave_tx_play.left = 0; + LOGP_CHAN(DDSP, LOGL_ERROR, "Failed to open wave file '%s' for voice message.\n", gsc->wave_tx_filename); + } else { + LOGP_CHAN(DDSP, LOGL_INFO, "Sending wave file '%s' for voice message after 2 seconds.\n", gsc->wave_tx_filename); + init_samplerate(&gsc->wave_tx_upsample, gsc->wave_tx_samplerate, gsc->sender.samplerate, VOICE_BANDWIDTH); + } + } + gsc->wait_2_sec = gsc->sender.samplerate * 2.0; + goto again; + } + + /* no message, power is off */ + if (bit < 0) { + memset(samples, 0, sizeof(samples) * length); + memset(power, 0, length); + return; + } + + /* encode */ + gsc->fsk_tx_buffer_length = fsk_bit_encode(gsc, bit); + gsc->fsk_tx_buffer_pos = 0; + } + + /* send encoded bit until end of source or destination buffer is reached */ + while (length) { + *power++ = 1; + *samples++ = gsc->fsk_tx_buffer[gsc->fsk_tx_buffer_pos++]; + length--; + if (gsc->fsk_tx_buffer_pos == gsc->fsk_tx_buffer_length) { + gsc->fsk_tx_buffer_length = 0; + break; + } + } + + /* do again, if destination buffer is not yet full */ + if (length) + goto again; +} + |