V.27ter Modem emulation (partly)

This commit is contained in:
Andreas Eversberg 2020-02-22 21:07:00 +01:00
parent 5070bc70bd
commit 6a18c924fb
12 changed files with 946 additions and 2 deletions

2
.gitignore vendored
View File

@ -48,6 +48,7 @@ src/libsample/libsample.a
src/libam/libam.a src/libam/libam.a
src/libclipper/libclipper.a src/libclipper/libclipper.a
src/libserial/libserial.a src/libserial/libserial.a
src/libv27/libv27.a
src/anetz/libgermanton.a src/anetz/libgermanton.a
src/anetz/anetz src/anetz/anetz
src/bnetz/bnetz src/bnetz/bnetz
@ -80,3 +81,4 @@ src/test/test_dms
src/test/test_sms src/test/test_sms
src/test/test_performance src/test/test_performance
src/test/test_hagelbarger src/test/test_hagelbarger
src/test/test_v27scrambler

View File

@ -85,6 +85,7 @@ AC_OUTPUT(
src/libsample/Makefile src/libsample/Makefile
src/libclipper/Makefile src/libclipper/Makefile
src/libserial/Makefile src/libserial/Makefile
src/libv27/Makefile
src/anetz/Makefile src/anetz/Makefile
src/bnetz/Makefile src/bnetz/Makefile
src/cnetz/Makefile src/cnetz/Makefile

View File

@ -26,7 +26,8 @@ SUBDIRS = \
libfft \ libfft \
libmncc \ libmncc \
libclipper \ libclipper \
libserial libserial \
libv27
if HAVE_ALSA if HAVE_ALSA
SUBDIRS += \ SUBDIRS += \

9
src/libv27/Makefile.am Normal file
View File

@ -0,0 +1,9 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libv27.a
libv27_a_SOURCES = \
scrambler.c \
psk.c \
modem.c

89
src/libv27/modem.c Normal file
View File

@ -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);
}

18
src/libv27/modem.h Normal file
View File

@ -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);

459
src/libv27/psk.c Normal file
View File

@ -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
}
}

48
src/libv27/psk.h Normal file
View File

@ -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);

163
src/libv27/scrambler.c Normal file
View File

@ -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;
}
}

13
src/libv27/scrambler.h Normal file
View File

@ -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);

View File

@ -9,7 +9,8 @@ noinst_PROGRAMS = \
test_dms \ test_dms \
test_sms \ test_sms \
test_performance \ test_performance \
test_hagelbarger test_hagelbarger \
test_v27scrambler
test_filter_SOURCES = test_filter.c dummy.c test_filter_SOURCES = test_filter.c dummy.c
@ -136,3 +137,10 @@ test_hagelbarger_LDADD = \
$(top_builddir)/src/libhagelbarger/libhagelbarger.a \ $(top_builddir)/src/libhagelbarger/libhagelbarger.a \
-lm -lm
test_v27scrambler_SOURCES = dummy.c test_v27scrambler.c
test_v27scrambler_LDADD = \
$(COMMON_LA) \
$(top_builddir)/src/libv27/libv27.a \
-lm

View File

@ -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;
}