aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2020-02-22 21:07:00 +0100
committerAndreas Eversberg <jolly@eversberg.eu>2020-12-21 08:38:44 +0100
commit6a18c924fb6f24b8690c7abf6a096c44c209ce67 (patch)
treee0463ed6eaf676a56e112aebb5af7c4723f51586
parent5070bc70bd6308926e17eee78268da56ff8d7dd7 (diff)
V.27ter Modem emulation (partly)
-rw-r--r--.gitignore2
-rw-r--r--configure.ac1
-rw-r--r--src/Makefile.am3
-rw-r--r--src/libv27/Makefile.am9
-rw-r--r--src/libv27/modem.c89
-rw-r--r--src/libv27/modem.h18
-rw-r--r--src/libv27/psk.c459
-rw-r--r--src/libv27/psk.h48
-rw-r--r--src/libv27/scrambler.c163
-rw-r--r--src/libv27/scrambler.h13
-rw-r--r--src/test/Makefile.am10
-rw-r--r--src/test/test_v27scrambler.c133
12 files changed, 946 insertions, 2 deletions
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 <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 <string.h>
+#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 <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 <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#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 <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/>.
+ */
+
+/* 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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#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;
+}
+