From 6a18c924fb6f24b8690c7abf6a096c44c209ce67 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 22 Feb 2020 21:07:00 +0100 Subject: [PATCH] V.27ter Modem emulation (partly) --- .gitignore | 2 + configure.ac | 1 + src/Makefile.am | 3 +- src/libv27/Makefile.am | 9 + src/libv27/modem.c | 89 +++++++ src/libv27/modem.h | 18 ++ src/libv27/psk.c | 459 +++++++++++++++++++++++++++++++++++ src/libv27/psk.h | 48 ++++ src/libv27/scrambler.c | 163 +++++++++++++ src/libv27/scrambler.h | 13 + src/test/Makefile.am | 10 +- src/test/test_v27scrambler.c | 133 ++++++++++ 12 files changed, 946 insertions(+), 2 deletions(-) create mode 100644 src/libv27/Makefile.am create mode 100644 src/libv27/modem.c create mode 100644 src/libv27/modem.h create mode 100644 src/libv27/psk.c create mode 100644 src/libv27/psk.h create mode 100644 src/libv27/scrambler.c create mode 100644 src/libv27/scrambler.h create mode 100644 src/test/test_v27scrambler.c diff --git a/.gitignore b/.gitignore index 9c57ee7..77a278f 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,7 @@ src/libsample/libsample.a src/libam/libam.a src/libclipper/libclipper.a src/libserial/libserial.a +src/libv27/libv27.a src/anetz/libgermanton.a src/anetz/anetz src/bnetz/bnetz @@ -80,3 +81,4 @@ src/test/test_dms src/test/test_sms src/test/test_performance src/test/test_hagelbarger +src/test/test_v27scrambler diff --git a/configure.ac b/configure.ac index 42921eb..315561c 100644 --- a/configure.ac +++ b/configure.ac @@ -85,6 +85,7 @@ AC_OUTPUT( src/libsample/Makefile src/libclipper/Makefile src/libserial/Makefile + src/libv27/Makefile src/anetz/Makefile src/bnetz/Makefile src/cnetz/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 40271e5..57f4d7c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,7 +26,8 @@ SUBDIRS = \ libfft \ libmncc \ libclipper \ - libserial + libserial \ + libv27 if HAVE_ALSA SUBDIRS += \ diff --git a/src/libv27/Makefile.am b/src/libv27/Makefile.am new file mode 100644 index 0000000..69873d1 --- /dev/null +++ b/src/libv27/Makefile.am @@ -0,0 +1,9 @@ +AM_CPPFLAGS = -Wall -Wextra -g $(all_includes) + +noinst_LIBRARIES = libv27.a + +libv27_a_SOURCES = \ + scrambler.c \ + psk.c \ + modem.c + diff --git a/src/libv27/modem.c b/src/libv27/modem.c new file mode 100644 index 0000000..fbcd448 --- /dev/null +++ b/src/libv27/modem.c @@ -0,0 +1,89 @@ +/* V27 Modem + * + * (C) 2020 by Andreas Eversberg + * 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 . + */ + +#include +#include +#include +#include "../libdebug/debug.h" +#include "../libsample/sample.h" +#include "modem.h" + +static int psk_send_bit(void *inst) +{ + v27modem_t *modem = (v27modem_t *)inst; + uint8_t bit; + + bit = modem->send_bit(modem->inst); + bit = v27_scrambler_bit(&modem->scrambler, bit); + + return bit; +} + +static void psk_receive_bit(void *inst, int bit) +{ + v27modem_t *modem = (v27modem_t *)inst; + + bit = v27_scrambler_bit(&modem->descrambler, bit); + modem->receive_bit(modem->inst, bit); +} + +/* init psk */ +int v27_modem_init(v27modem_t *modem, void *inst, int (*send_bit)(void *inst), void (*receive_bit)(void *inst, int bit), int samplerate, int bis) +{ + int rc = 0; + + memset(modem, 0, sizeof(*modem)); + + modem->send_bit = send_bit; + modem->receive_bit = receive_bit; + modem->inst = inst; + + /* V.27bis/ter with 4800 bps */ + rc = psk_mod_init(&modem->psk_mod, modem, psk_send_bit, samplerate, 1600.0); + if (rc) + goto error; + rc = psk_demod_init(&modem->psk_demod, modem, psk_receive_bit, samplerate, 1600.0); + if (rc) + goto error; + v27_scrambler_init(&modem->scrambler, bis, 0); + v27_scrambler_init(&modem->descrambler, bis, 1); + + return 0; + +error: + v27_modem_exit(modem); + return rc; +} + +void v27_modem_exit(v27modem_t *modem) +{ + psk_mod_exit(&modem->psk_mod); + psk_demod_exit(&modem->psk_demod); +} + +void v27_modem_send(v27modem_t *modem, sample_t *sample, int length) +{ + psk_mod(&modem->psk_mod, sample, length); +} + +void v27_modem_receive(v27modem_t *modem, sample_t *sample, int length) +{ + psk_demod(&modem->psk_demod, sample, length); +} + diff --git a/src/libv27/modem.h b/src/libv27/modem.h new file mode 100644 index 0000000..e21b569 --- /dev/null +++ b/src/libv27/modem.h @@ -0,0 +1,18 @@ +#include "psk.h" +#include "scrambler.h" + +typedef struct v27modem { + int (*send_bit)(void *inst); + void (*receive_bit)(void *inst, int bit); + void *inst; + + v27scrambler_t scrambler, descrambler; + psk_mod_t psk_mod; + psk_demod_t psk_demod; +} v27modem_t; + +int v27_modem_init(v27modem_t *modem, void *inst, int (*send_bit)(void *inst), void (*receive_bit)(void *inst, int bit), int samplerate, int bis); +void v27_modem_exit(v27modem_t *modem); +void v27_modem_send(v27modem_t *modem, sample_t *sample, int length); +void v27_modem_receive(v27modem_t *modem, sample_t *sample, int length); + diff --git a/src/libv27/psk.c b/src/libv27/psk.c new file mode 100644 index 0000000..c20f286 --- /dev/null +++ b/src/libv27/psk.c @@ -0,0 +1,459 @@ +/* Jolly's Version of PSK + * + * (C) 2020 by Andreas Eversberg + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include "../libdebug/debug.h" +#include "../libsample/sample.h" +#include "psk.h" + +/* bits to phase change */ +double phase_change8[8] = { + 2.0 * M_PI * 1.0 / 8.0, + 2.0 * M_PI * 0.0 / 8.0, + 2.0 * M_PI * 2.0 / 8.0, + 2.0 * M_PI * 3.0 / 8.0, + 2.0 * M_PI * 6.0 / 8.0, + 2.0 * M_PI * 7.0 / 8.0, + 2.0 * M_PI * 5.0 / 8.0, + 2.0 * M_PI * 4.0 / 8.0, +}; + +/* phase change to bits */ +uint8_t phase2bits[8] = { 1, 0, 2, 3, 7, 6, 4, 5 }; + +/* debug decoder */ +//#define DEBUG_DECODER + +/* use SIT original encoder */ +//#define SIT_ENCODER + +/* may be different for testing purpose */ +#define TX_CARRIER 1800.0 +#define RX_CARRIER 1800.0 + +#ifdef SIT_ENCODER +static int8_t symbols_int[8][30] = { + { 0xff, 0xfd, 0x00, 0x02, 0xfe, 0x02, 0x0d, 0x05, 0xf9, 0x05, 0x02, 0xd2, 0xc9, 0x23, 0x64, 0x23, + 0xc9, 0xd2, 0x02, 0x05, 0xf9, 0x05, 0x0d, 0x02, 0xfe, 0x02, 0x00, 0xfd, 0xff, 0x00 }, + { 0xfe, 0xff, 0x03, 0x02, 0xff, 0x07, 0x0a, 0xfa, 0xf7, 0x03, 0xef, 0xd0, 0xfe, 0x56, 0x47, 0xdb, + 0xb4, 0xef, 0x14, 0x03, 0xff, 0x0e, 0x09, 0xfc, 0xfe, 0x01, 0xfd, 0xfd, 0x00, 0x00 }, + { 0xff, 0x01, 0x04, 0x01, 0x01, 0x08, 0x00, 0xf2, 0xfa, 0x00, 0xe6, 0xea, 0x34, 0x57, 0x00, 0xa9, + 0xcc, 0x16, 0x1a, 0x00, 0x06, 0x0e, 0x00, 0xf8, 0xff, 0xff, 0xfc, 0xff, 0x01, 0x00 }, + { 0x00, 0x03, 0x03, 0xff, 0x02, 0x04, 0xf7, 0xf2, 0x01, 0xfd, 0xec, 0x11, 0x4c, 0x25, 0xb9, 0xaa, + 0x02, 0x30, 0x11, 0xfd, 0x09, 0x06, 0xf6, 0xf9, 0x01, 0xfe, 0xfd, 0x01, 0x02, 0x00 }, + { 0x01, 0x03, 0x00, 0xfe, 0x02, 0xfe, 0xf3, 0xfb, 0x07, 0xfb, 0xfe, 0x2e, 0x37, 0xdd, 0x9c, 0xdd, + 0x37, 0x2e, 0xfe, 0xfb, 0x07, 0xfb, 0xf3, 0xfe, 0x02, 0xfe, 0x00, 0x03, 0x01, 0x00 }, + { 0x02, 0x01, 0xfd, 0xfe, 0x01, 0xf9, 0xf6, 0x06, 0x09, 0xfd, 0x11, 0x30, 0x02, 0xaa, 0xb9, 0x25, + 0x4c, 0x11, 0xec, 0xfd, 0x01, 0xf2, 0xf7, 0x04, 0x02, 0xff, 0x03, 0x03, 0x00, 0x00 }, + { 0x01, 0xff, 0xfc, 0xff, 0xff, 0xf8, 0x00, 0x0e, 0x06, 0x00, 0x1a, 0x16, 0xcc, 0xa9, 0x00, 0x57, + 0x34, 0xea, 0xe6, 0x00, 0xfa, 0xf2, 0x00, 0x08, 0x01, 0x01, 0x04, 0x01, 0xff, 0x00 }, + { 0x00, 0xfd, 0xfd, 0x01, 0xfe, 0xfc, 0x09, 0x0e, 0xff, 0x03, 0x14, 0xef, 0xb4, 0xdb, 0x47, 0x56, + 0xfe, 0xd0, 0xef, 0x03, 0xf7, 0xfa, 0x0a, 0x07, 0xff, 0x02, 0x03, 0xff, 0xfe, 0x00 }, +}; + +static sample_t symbols[8][150]; + +/* indexes are rotated by 45 degrees, because the phase change during one symbol is 360+45 degrees */ +static int bits2index[8] = { 2, 1, 3, 4, 7, 0, 6, 5 }; +#endif + +/* init psk */ +int psk_mod_init(psk_mod_t *psk, void *inst, int (*send_bit)(void *inst), int samplerate, double symbolrate) +{ + double cutoff, transitionband; + + memset(psk, 0, sizeof(*psk)); + + psk->send_bit = send_bit; + psk->inst = inst; + +#ifdef SIT_ENCODER + int i, j, s; + sample_t spl; + + if (samplerate != 48000) { + PDEBUG(DDSP, DEBUG_NOTICE, "Sampling rate for PSK encoder must be exactly 48000 Hz!\n"); + return -EINVAL; + } + if (symbolrate != 1600) { + PDEBUG(DDSP, DEBUG_NOTICE, "Symbol rate for PSK encoder must be exactly 1600 Hz!\n"); + return -EINVAL; + } + + cutoff = 3300.0; + transitionband = 200; + psk->lp[0] = fir_lowpass_init((double)samplerate, cutoff, transitionband); + PDEBUG(DDSP, DEBUG_DEBUG, "Cut off frequency is at %.1f Hz and %.1f Hz.\n", TX_CARRIER + cutoff, TX_CARRIER - cutoff); + + /* interpolate symbol table from 9600 Hz to 48000 Hz */ + for (i = 0; i < 8; i++) { + for (j = 0, s = 0; j < 30; j++) { + spl = (double)symbols_int[i][j] / 128.0; + symbols[i][s++] = spl; + symbols[i][s++] = spl; + symbols[i][s++] = spl; + symbols[i][s++] = spl; + symbols[i][s++] = spl; + } + } +#else + if (samplerate < 48000) { + PDEBUG(DDSP, DEBUG_NOTICE, "Sampling rate for PSK encoder must be 48000 Hz minimum!\n"); + return -EINVAL; + } + + /* fixme: make correct filter */ + cutoff = RX_CARRIER - 100; + transitionband = 200; + psk->lp[0] = fir_lowpass_init((double)samplerate, cutoff, transitionband); + psk->lp[1] = fir_lowpass_init((double)samplerate, cutoff, transitionband); + PDEBUG(DDSP, DEBUG_DEBUG, "Cut off frequency is at %.1f Hz and %.1f Hz.\n", TX_CARRIER + cutoff, TX_CARRIER - cutoff); + + psk->symbols_per_sample = symbolrate / (double)samplerate; + PDEBUG(DDSP, DEBUG_DEBUG, "Symbol duration of %.4f symbols per sample @ %d.\n", psk->symbols_per_sample, samplerate); + + psk->carrier_phaseshift = 2.0 * M_PI * TX_CARRIER / (double)samplerate; + PDEBUG(DDSP, DEBUG_DEBUG, "Carrier phase shift of %.4f per sample @ %d.\n", psk->carrier_phaseshift, samplerate); +#endif + + return 0; +} + +void psk_mod_exit(psk_mod_t *psk) +{ + if (psk->lp[0]) { + fir_exit(psk->lp[0]); + psk->lp[0] = NULL; + } + if (psk->lp[1]) { + fir_exit(psk->lp[1]); + psk->lp[1] = NULL; + } +} + +void psk_mod(psk_mod_t *psk, sample_t *sample, int length) +{ + uint8_t bits; + int s; +#ifdef SIT_ENCODER + int index; + + for (s = 0; s < length; s++) { + if (psk->spl_count == 0) { + bits = (psk->send_bit(psk->inst) & 1) << 2; + bits |= (psk->send_bit(psk->inst) & 1) << 1; + bits |= (psk->send_bit(psk->inst) & 1); + +#ifdef DEBUG_DECODER + static int nextbit = 0; + if (++nextbit == 8) + nextbit = 0; + bits = nextbit; +#endif + + index = psk->sym_list[psk->sym_count]; + psk->sym_count = (psk->sym_count + 1) % 5; + psk->sym_list[psk->sym_count] = (index + bits2index[bits]) & 7; + } + + sample[s] = symbols[psk->sym_list[psk->sym_count]][psk->spl_count]; + sample[s] += symbols[psk->sym_list[(psk->sym_count + 4) % 5]][30 + psk->spl_count]; + sample[s] += symbols[psk->sym_list[(psk->sym_count + 3) % 5]][60 + psk->spl_count]; + sample[s] += symbols[psk->sym_list[(psk->sym_count + 2) % 5]][90 + psk->spl_count]; + sample[s] += symbols[psk->sym_list[(psk->sym_count + 1) % 5]][120 + psk->spl_count]; + + if (++psk->spl_count == 30) + psk->spl_count = 0; + } + fir_process(psk->lp[0], sample, length); +#else + sample_t I[length], Q[length]; + + /* count symbol and get new bits for next symbol */ + for (s = 0; s < length; s++) { + psk->symbol_pos += psk->symbols_per_sample; + if (psk->symbol_pos >= 1.0) { + psk->symbol_pos -= 1.0; + /* get tree bits */ + bits = (psk->send_bit(psk->inst) & 1) << 2; + bits |= (psk->send_bit(psk->inst) & 1) << 1; + bits |= (psk->send_bit(psk->inst) & 1); + +#ifdef DEBUG_DECODER + static int nextbit = 0; + if (++nextbit == 8) + nextbit = 0; + bits = nextbit; +#endif + + /* change phase_shift */ + psk->phase_shift += phase_change8[bits]; + if (psk->phase_shift > M_PI) + psk->phase_shift -= 2.0 * M_PI; + } + + I[s] = cos(psk->phase_shift); + Q[s] = sin(psk->phase_shift); + } + + /* filter phase_shift to limit bandwidth */ + fir_process(psk->lp[0], I, length); + fir_process(psk->lp[1], Q, length); + + /* modulate with carrier frequency */ + for (s = 0; s < length; s++) { + /* compensate overshooting of filter */ + *sample++ = (I[s] * cos(psk->carrier_phase) - Q[s] * sin(psk->carrier_phase)) * 0.7; + psk->carrier_phase += psk->carrier_phaseshift; + if (psk->carrier_phase >= 2.0 * M_PI) + psk->carrier_phase -= 2.0 * M_PI; + } +#endif +} + +int psk_demod_init(psk_demod_t *psk, void *inst, void (*receive_bit)(void *inst, int bit), int samplerate, double symbolrate) +{ + double cutoff, transitionband; + + if (samplerate < 48000) { + PDEBUG(DDSP, DEBUG_NOTICE, "Sampling rate for PSK decoder must be 48000 Hz minimum!\n"); + return -EINVAL; + } + + memset(psk, 0, sizeof(*psk)); + + psk->receive_bit = receive_bit; + psk->inst = inst; + + /* fixme: make correct filter */ +// cutoff = symbolrate / 2.0 * 1.5; + cutoff = RX_CARRIER - 100; + transitionband = 200; + psk->lp[0] = fir_lowpass_init((double)samplerate, cutoff, transitionband); + psk->lp[1] = fir_lowpass_init((double)samplerate, cutoff, transitionband); + iir_lowpass_init(&psk->lp_error[0], 50.0, samplerate, 2); + iir_lowpass_init(&psk->lp_error[1], 50.0, samplerate, 2); + iir_bandpass_init(&psk->lp_clock, symbolrate, samplerate, 40); + psk->sample_delay = (int)floor((double)samplerate / symbolrate * 0.25); /* percent of sine duration behind zero crossing */ + PDEBUG(DDSP, DEBUG_DEBUG, "Cut off frequency is at %.1f Hz and %.1f Hz.\n", RX_CARRIER + cutoff, RX_CARRIER - cutoff); + + psk->carrier_phaseshift = 2.0 * M_PI * -RX_CARRIER / (double)samplerate; + PDEBUG(DDSP, DEBUG_DEBUG, "Carrier phase shift of %.4f per sample @ %d.\n", psk->carrier_phaseshift, samplerate); + + return 0; +} + +void psk_demod_exit(psk_demod_t *psk) +{ + if (psk->lp[0]) { + fir_exit(psk->lp[0]); + psk->lp[0] = NULL; + } + if (psk->lp[1]) { + fir_exit(psk->lp[1]); + psk->lp[1] = NULL; + } +} + +#ifdef DEBUG_DECODER +static void debug_phase(double phase, double amplitude, double error, double amplitude2) +{ + int x, y, xx = 100, yy = 50; + char buffer[yy][xx + 1]; + double p; + int i; + + if (amplitude > 1.0) + amplitude = 1.0; + if (amplitude < -0.0) + amplitude = -0.0; + if (amplitude2 > 0.5) + amplitude2 = 0.5; + if (amplitude2 < -0.5) + amplitude2 = -0.5; + amplitude2 += 0.5; + + /* clear (EOL) and fill spaces with border */ + memset(buffer, '\0', sizeof(buffer)); + memset(buffer[0], '#', xx); + for (y = 1; y < yy - 1; y++) { + memset(buffer[y], ' ', xx); + buffer[y][0] = '|'; + buffer[y][xx - 1] = '|'; + } + memset(buffer[yy - 1], '-', xx); + buffer[0][0] = '+'; + buffer[0][xx - 1] = '+'; + buffer[yy - 1][0] = '+'; + buffer[yy - 1][xx - 1] = '+'; + + /* plot target angles on buffer */ + for (i = 0, p = 0.0; i < 8; i++, p = p + M_PI / 4.0) { + y = -(sin(p + error) * (double)yy / 1.1) + (double)yy; + x = (cos(p + error) * (double)xx / 2.2) + (double)xx / 2.0; + buffer[y >> 1][x] = '0' + i; + } + + /* plot angle on buffer */ + y = -(amplitude * 1.1 * sin(phase) * (double)yy / 1.1) + (double)yy; + x = (amplitude * 1.1 * cos(phase) * (double)xx / 2.2) + (double)xx / 2.0; + if ((y & 1)) + buffer[y >> 1][x] = '.'; + else + buffer[y >> 1][x] = '\''; + + /* plot amplitude on buffer */ + y = -(amplitude * (double)yy * 2.0 / 1.1) + (double)yy * 2.0 / 1.1; + if ((y & 1)) + buffer[y >> 1][1] = '.'; + else + buffer[y >> 1][1] = '\''; + y = -(amplitude2 * (double)yy * 2.0 / 1.1) + (double)yy * 2.0 / 1.1; + if ((y & 1)) + buffer[y >> 1][2] = '.'; + else + buffer[y >> 1][2] = '\''; + + /* display on screen */ + for (y = 0; y < yy; y++) + printf("%s\n", buffer[y]); +} +#endif + +void psk_demod(psk_demod_t *psk, sample_t *sample, int length) +{ + sample_t I[length], Q[length], i; + sample_t Ip[length], Qp[length]; + double phases[length]; + sample_t amplitudes[length], amplitudes2[length]; + double phaseshift, phase, phase_error, angle_error; + uint16_t phase_error_int, offset; + int s, ss; + uint8_t sector, rotation, bits; + + /* demodulate phase from carrier */ + phaseshift = psk->carrier_phaseshift; + phase = psk->carrier_phase; + for (s = 0, ss = 0; s < length; s++) { + phase += phaseshift; + i = sample[ss++]; + if (phase < 0) + phase += 2.0 * M_PI; + else if (phase >= 2.0 * M_PI) + phase -= 2.0 * M_PI; + I[s] = i * cos(phase); + Q[s] = i * sin(phase); + } + psk->carrier_phase = phase; + fir_process(psk->lp[0], I, length); + fir_process(psk->lp[1], Q, length); + + /* get phase error */ + for (s = 0, ss = 0; s < length; s++) { + phases[s] = atan2(Q[s], I[s]); + amplitudes[s] = sqrt(Q[s] * Q[s] + I[s] * I[s]) * 2.0; + amplitudes2[s] = amplitudes[s]; + Ip[s] = amplitudes[s] * cos(phases[s] * 8.0) * 2.0; + Qp[s] = amplitudes[s] * sin(phases[s] * 8.0) * 2.0; + } + iir_process(&psk->lp_error[0], Ip, length); + iir_process(&psk->lp_error[1], Qp, length); + + /* filter amplitude to get symbol clock */ + /* NOTE: the filter biases the amplitude, so that we have positive and negative peaks. + positive peak is the sample point */ + iir_process(&psk->lp_clock, amplitudes2, length); + + for (s = 0; s < length; s++) { + /* calculate change of phase error angle within one sample */ + phase_error_int = (int)floor(atan2(Qp[s], Ip[s]) / (2.0 * M_PI) * 65536.0); + offset = phase_error_int - psk->last_phase_error; + psk->last_phase_error = phase_error_int; + + /* apply change to current phase error value */ + psk->phase_error += (int16_t)offset; + if (psk->phase_error >= 65536 * 8) + psk->phase_error -= 65536 * 8; + if (psk->phase_error < 0) + psk->phase_error += 65536 * 8; + + phase_error = (double)psk->phase_error / 65536.0 * (2.0 * M_PI) / 8.0; + + /* if we have reached a zero crossing of the amplitude signal, wait for sample point */ + if (psk->sample_timer && --psk->sample_timer == 0) { + /* sample point reached */ + phase = fmod(phases[s] - phase_error + 4.0 * M_PI, 2.0 * M_PI); + if (phase < 2.0 * M_PI / 16.0 * 1.0) + sector = 0; + else if (phase < 2.0 * M_PI / 16.0 * 3.0) + sector = 1; + else if (phase < 2.0 * M_PI / 16.0 * 5.0) + sector = 2; + else if (phase < 2.0 * M_PI / 16.0 * 7.0) + sector = 3; + else if (phase < 2.0 * M_PI / 16.0 * 9.0) + sector = 4; + else if (phase < 2.0 * M_PI / 16.0 * 11.0) + sector = 5; + else if (phase < 2.0 * M_PI / 16.0 * 13.0) + sector = 6; + else if (phase < 2.0 * M_PI / 16.0 * 15.0) + sector = 7; + else + sector = 0; + angle_error = fmod(phase / 2.0 / M_PI * 8.0, 1.0); + if (angle_error > 0.5) + angle_error -= 1.0; + + rotation = (sector - psk->last_sector) & 7; // might be negative, so we use AND! + bits = phase2bits[rotation]; +#ifdef DEBUG_DECODER + printf("sector=%d last_sector=%d rotation=%d bits=%d angle_error=%.2f\n", sector, psk->last_sector, rotation, bits, angle_error); +#endif + psk->last_sector = sector; + /* report bits */ +#ifndef DEBUG_DECODER + psk->receive_bit(psk->inst, bits >> 2); + psk->receive_bit(psk->inst, (bits >> 1) & 1); + psk->receive_bit(psk->inst, bits & 1); +#endif + } + if (psk->last_amplitude <= 0.0 && amplitudes2[s] > 0.0) + psk->sample_timer = psk->sample_delay; + psk->last_amplitude = amplitudes2[s]; + +#ifdef DEBUG_DECODER + static int when = 0; + if (++when > 10000) { + printf("\0337\033[H"); + /* display amplitude between 0.0 and 1.0, aplitude2 between -0.5 and 0.5 */ + debug_phase(phases[s], amplitudes[s], phase_error, amplitudes2[s]); + printf("phase2 = %.4f offset = %d, error = %d (error & 0xffff = %d)\n", phase_error, offset, psk->phase_error, psk->phase_error & 0xffff); + printf("\033[0;39m\0338"); fflush(stdout); + usleep(50000); + } +#endif + } +} + diff --git a/src/libv27/psk.h b/src/libv27/psk.h new file mode 100644 index 0000000..1dbaa07 --- /dev/null +++ b/src/libv27/psk.h @@ -0,0 +1,48 @@ +#include "../libfilter/iir_filter.h" +#include "../libfilter/fir_filter.h" + +typedef struct psk_mod { + int (*send_bit)(void *inst); + void *inst; + + double symbol_pos; /* current position in symbol */ + double symbols_per_sample; /* change of position per sample */ + double phase_shift; /* carrier phase shift */ + double carrier_phase; /* current carrier phase */ + double carrier_phaseshift; /* shift of phase per sample */ + + fir_filter_t *lp[2]; /* filter for limiting spectrum */ + + int spl_count; /* SIT: counter for 30 samples (symbol duration) */ + int sym_list[5]; /* SIT: list of 5 symbols */ + int sym_count; /* SIT: current list index */ +} psk_mod_t; + +typedef struct psk_demod { + void (*receive_bit)(void *inst, int bit); + void *inst; + + double carrier_phase; /* current carrier phase */ + double carrier_phaseshift; /* shift of phase per sample */ + + fir_filter_t *lp[2]; /* filter for limiting spectrum */ + iir_filter_t lp_error[2]; /* filter for phase correction */ + iir_filter_t lp_clock; /* filter for symbol clock */ + + uint16_t last_phase_error; /* error phase of last sample */ + int32_t phase_error; /* current phase error */ + + sample_t last_amplitude; /* clock amplitude of last sample */ + int sample_delay; /* delay of quarter symbol length in samples */ + int sample_timer; /* counter to wait for the symbol's sample point */ + + uint8_t last_sector; /* sector of last symbol */ +} psk_demod_t; + +int psk_mod_init(psk_mod_t *psk, void *inst, int (*send_bit)(void *inst), int samplerate, double symbolrate); +void psk_mod_exit(psk_mod_t *psk); +void psk_mod(psk_mod_t *psk, sample_t *sample, int length); +int psk_demod_init(psk_demod_t *psk, void *inst, void (*receive_bit)(void *inst, int bit), int samplerate, double symbolrate); +void psk_demod_exit(psk_demod_t *psk); +void psk_demod(psk_demod_t *psk, sample_t *sample, int length); + diff --git a/src/libv27/scrambler.c b/src/libv27/scrambler.c new file mode 100644 index 0000000..481bf83 --- /dev/null +++ b/src/libv27/scrambler.c @@ -0,0 +1,163 @@ +/* V.27(bis) Scrambler / Descrambler + * + * (C) 2020 by Andreas Eversberg + * 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 . + */ + +/* Based on orignal Scrambler code from SIT-Rom: + + r6 already has input bit at position 0. + r6 (low) and r7 (high) are the shift register. + The register is shifed during process, so that compare at 00BE and + 00C5 refers to the already shifted register and not to the original + position. + +00A4 L00A4: +00A4 : FE mov a,r6 +00A5 : ED AF djnz r5,L00AF +00A7 : D3 01 xrl a,#001H +00A9 : BD 22 mov r5,#022H +00AB L00AB: +00AB : D2 B3 jb6 L00B3 +00AD : 04 B5 jmp L00B5 + ; +00AF L00AF: +00AF : 00 nop +00B0 : 00 nop +00B1 : 04 AB jmp L00AB + ; +00B3 L00B3: +00B3 : D3 01 xrl a,#001H +00B5 L00B5: +00B5 : F2 B9 jb7 L00B9 +00B7 : 04 BB jmp L00BB + ; +00B9 L00B9: +00B9 : D3 01 xrl a,#001H +00BB L00BB: +00BB : 97 clr c +00BC : F7 rlc a +00BD : AE mov r6,a +00BE : 32 CB jb1 L00CB +00C0 : FF mov a,r7 +00C1 : F7 rlc a +00C2 : AF mov r7,a +00C3 : 37 cpl a +00C4 : 00 nop +00C5 L00C5: +00C5 : 53 26 anl a,#026H +00C7 : C6 D0 jz L00D0 +00C9 : 04 D2 jmp L00D2 + ; +00CB L00CB: +00CB : FF mov a,r7 +00CC : F7 rlc a +00CD : AF mov r7,a +00CE : 04 C5 jmp L00C5 + ; +00D0 00D0: +00D0 : BD 22 mov r5,#022H +00D2 00D2: +00D2 : 83 ret + +*/ + +#include +#include +#include +#include "scrambler.h" + +#define GUARD_COUNT 34 + +/* init scrambler */ +void v27_scrambler_init(v27scrambler_t *scram, int bis, int descramble) +{ + memset(scram, 0, sizeof(*scram)); + + scram->descramble = descramble; + + /* set bits 9 and 12 (and 8 for V.27bis) */ + if (bis) + scram->resetmask = 0x1300; + else + scram->resetmask = 0x1200; + + /* guard counter */ + scram->counter = GUARD_COUNT; +} + +/* scramble/descramble one bit */ +uint8_t v27_scrambler_bit(v27scrambler_t *scram, uint8_t in) +{ + uint8_t bit0 = in & 1; + uint16_t shift = scram->shift; + + + /* the descrambler stores the input bit */ + if (scram->descramble) { + /* put bit 0 into shift register and shift */ + shift |= bit0; + scram->shift = shift << 1; + } + + /* process guaard counter */ + if (--scram->counter == 0) { + /* restart counter */ + scram->counter = GUARD_COUNT; + /* invert this time */ + bit0 ^= 1; + } + + /* xor bit 0 with bits 6 and 7: polynome 1 + x^-6 + x^-7 */ + bit0 ^= ((shift >> 6) & 1); + bit0 ^= ((shift >> 7) & 1); + + /* the scrambler stores the output bit */ + if (!scram->descramble) { + /* put bit 0 into shift register and shift */ + shift |= bit0; + scram->shift = shift << 1; + } + + /* check if bits (8),9,12 are repitions of bit 0 in shift register (prior shift) */ + if (!(shift & 1)) + shift ^= ~0; + if (!(shift & scram->resetmask)) { + /* any repetition is not true, reset counter */ + scram->counter = GUARD_COUNT; + } + + return bit0; +} + +/* scramble/descramble block of bytes (LSB first) */ +void v27_scrambler_block(v27scrambler_t *scram, uint8_t *data, int len) +{ + int i, j; + uint8_t in, out = 0; + + for (i = 0; i < len; i++) { + in = data[i]; + for (j = 0; j < 8; j++) { + out >>= 1; + // Note: 'in' will be masked to bit 0 only + out |= v27_scrambler_bit(scram, in) << 7; + in >>= 1; + } + data[i] = out; + } +} + diff --git a/src/libv27/scrambler.h b/src/libv27/scrambler.h new file mode 100644 index 0000000..56851a2 --- /dev/null +++ b/src/libv27/scrambler.h @@ -0,0 +1,13 @@ + +typedef struct v27scrambler { + int descramble; /* set if we descramble */ + + uint16_t shift; /* shift register to hold 13 bits */ + int counter; /* counter to guard against repetitions */ + uint16_t resetmask; /* bit mask for repition check */ +} v27scrambler_t; + +void v27_scrambler_init(v27scrambler_t *scram, int bis, int descramble); +uint8_t v27_scrambler_bit(v27scrambler_t *scram, uint8_t in); +void v27_scrambler_block(v27scrambler_t *scram, uint8_t *data, int len); + diff --git a/src/test/Makefile.am b/src/test/Makefile.am index dd65ec6..409a638 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -9,7 +9,8 @@ noinst_PROGRAMS = \ test_dms \ test_sms \ test_performance \ - test_hagelbarger + test_hagelbarger \ + test_v27scrambler test_filter_SOURCES = test_filter.c dummy.c @@ -136,3 +137,10 @@ test_hagelbarger_LDADD = \ $(top_builddir)/src/libhagelbarger/libhagelbarger.a \ -lm +test_v27scrambler_SOURCES = dummy.c test_v27scrambler.c + +test_v27scrambler_LDADD = \ + $(COMMON_LA) \ + $(top_builddir)/src/libv27/libv27.a \ + -lm + diff --git a/src/test/test_v27scrambler.c b/src/test/test_v27scrambler.c new file mode 100644 index 0000000..68d96da --- /dev/null +++ b/src/test/test_v27scrambler.c @@ -0,0 +1,133 @@ +#include "stdio.h" +#include "stdint.h" +#include "string.h" +#include "../libv27/scrambler.h" + +static int show_bin(uint8_t *data1, uint8_t *data2, int len) +{ + int i, j, error = 0;; + uint8_t bit1, bit2; + + for (i = 0; i < len; i++) { + printf("."); + for (j = 0; j < 8; j++) { + bit1 = (data1[i] >> j) & 1; + bit2 = (data2[i] >> j) & 1; + if (bit1 == bit2) + printf("%d", bit1); + else { + printf("X"); + error++; + } + } + } + + printf("\n"); + + return error; +} + +static int check_repetition(uint8_t *data, int len, int repeat, int start) +{ + int i; + uint8_t b1, b2; + + for (i = start; i < (len * 8 - repeat); i++) { + b1 = (data[i >> 3] >> (i & 7)) & 1; + b2 = (data[(i+repeat) >> 3] >> ((i+repeat) & 7)) & 1; + if (b1 != b2) + return i - start + repeat; + } + + return 0; +} + +int main(void) +{ + v27scrambler_t scram, descram; + + char message[] = "Jolly Roger~~~~"; + int len = strlen(message); + uint8_t data[len]; + int ret; + + printf("Message: %s\n", message); + + memcpy(data, message, len); + show_bin(data, (uint8_t *)message, len); + + v27_scrambler_init(&scram, 1, 0); + v27_scrambler_block(&scram, data, len); + + printf("Scrambled:\n"); + show_bin(data, data, len); + + v27_scrambler_init(&descram, 1, 1); + v27_scrambler_block(&descram, data, len); + + printf("Descramble without corruption?\n"); + + ret = show_bin(data, (uint8_t *)message, len); + if (ret) { + printf("Descrambling failed!\n"); + return 1; + } + printf("Yes!\n"); + + printf("\n"); + + v27_scrambler_init(&scram, 1, 0); + v27_scrambler_block(&scram, data, len); + + data[0] = 'B'; + data[1] = 'U'; + data[2] = 'G'; + + v27_scrambler_init(&descram, 1, 1); + v27_scrambler_block(&descram, data, len); + + printf("Descramble with 3 bytes corruption: (should fix itself after 4 bytes)\n"); + + show_bin(data, (uint8_t *)message, len); + + printf("\n"); + + printf("Descramble a scrambled sequence of 8 bit repetitions with V.27: 01111110\n"); + + memset(data, 0x7e, len); + + v27_scrambler_init(&descram, 0, 1); + v27_scrambler_block(&descram, data, len); + + show_bin(data, (uint8_t *)data, len); + + /* note at position 6 we have no more change towards 8 bit offset */ + ret = check_repetition(data, len, 8, 6); + if (ret) { + printf("Theres is a change of repetition after %d bits after start %d, please fix!\n", ret, 6); + return 1; + } + printf("Repetition not detected, good!\n"); + + printf("\n"); + + printf("Descramble a scrambled sequence of 8 bit repetitions with V.27bis/ter: 01111110\n"); + + memset(data, 0x7e, len); + + v27_scrambler_init(&descram, 1, 1); + v27_scrambler_block(&descram, data, len); + + show_bin(data, (uint8_t *)data, len); + + /* note at position 6 we have no more change towards 8 bit offset */ + ret = check_repetition(data, len, 8, 6); + if (ret != 34) { + printf("Theres is NO change of repetition after 34 bits, but after %d bits, which should not happen!\n", ret); + return 1; + } + printf("Repetition detected after %d bits from start %d, good!\n", ret, 6); + + return 0; +} +