2018-06-17 14:19:22 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* pskmodem.c -- PSK modem.
|
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 1999-2000
|
|
|
|
|
* Thomas Sailer (sailer@ife.ee.ethz.ch)
|
|
|
|
|
*
|
|
|
|
|
* 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 2 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, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
*
|
|
|
|
|
* Please note that the GPL allows you to use the driver, NOT the radio.
|
|
|
|
|
* In order to use the radio, you need a license from the communications
|
|
|
|
|
* authority of your country.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
|
#define _REENTRANT
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
#include "config.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* AIX requires this to be the first thing in the file. */
|
|
|
|
|
#ifndef __GNUC__
|
|
|
|
|
# if HAVE_ALLOCA_H
|
|
|
|
|
# include <alloca.h>
|
|
|
|
|
# else
|
|
|
|
|
# ifdef _AIX
|
|
|
|
|
#pragma alloca
|
|
|
|
|
# else
|
|
|
|
|
# ifndef alloca /* predefined by HP cc +Olibcalls */
|
|
|
|
|
char *alloca ();
|
|
|
|
|
# endif
|
|
|
|
|
# endif
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "modem.h"
|
|
|
|
|
#include "psk.h"
|
|
|
|
|
#include "psktbl.h"
|
|
|
|
|
#include "simd.h"
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
struct txstate {
|
|
|
|
|
struct modemchannel *chan;
|
|
|
|
|
unsigned int txphinc;
|
|
|
|
|
unsigned int txphase;
|
|
|
|
|
unsigned int carphase;
|
|
|
|
|
unsigned int carphinc;
|
|
|
|
|
cplxshort_t filter[TXFILTLEN];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct modemparams modparams[] = {
|
|
|
|
|
{ NULL }
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void *modconfig(struct modemchannel *chan, unsigned int *samplerate, const char *params[])
|
|
|
|
|
{
|
|
|
|
|
struct txstate *s;
|
|
|
|
|
|
|
|
|
|
if (!(s = calloc(1, sizeof(struct txstate))))
|
|
|
|
|
logprintf(MLOG_FATAL, "out of memory\n");
|
|
|
|
|
s->chan = chan;
|
|
|
|
|
*samplerate = SAMPLERATE;
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void modinit(void *state, unsigned int samplerate)
|
|
|
|
|
{
|
|
|
|
|
struct txstate *s = (struct txstate *)state;
|
|
|
|
|
|
|
|
|
|
s->txphinc = ((SYMRATE << 16) + samplerate / 2) / samplerate;
|
|
|
|
|
s->carphinc = ((FCARRIER << 16) + samplerate / 2) / samplerate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void txsendsymbols(struct txstate *tx, const unsigned char *sym, unsigned int nrsyms)
|
|
|
|
|
{
|
|
|
|
|
int16_t sbuf[512];
|
|
|
|
|
int16_t *sptr = sbuf, *eptr = sbuf + sizeof(sbuf)/sizeof(sbuf[0]);
|
|
|
|
|
unsigned int i;
|
|
|
|
|
int sumr, sumi;
|
|
|
|
|
const int *coeff;
|
|
|
|
|
|
|
|
|
|
while (nrsyms > 0) {
|
|
|
|
|
if (tx->txphase >= 0x10000) {
|
|
|
|
|
tx->txphase &= 0xffff;
|
|
|
|
|
memmove(&tx->filter[1], tx->filter, sizeof(tx->filter) - sizeof(tx->filter[0]));
|
|
|
|
|
tx->filter[0] = psk_symmapping[sym[0] & SYMBITMASK];
|
|
|
|
|
sym++;
|
|
|
|
|
nrsyms--;
|
|
|
|
|
}
|
|
|
|
|
coeff = txfilter[TXFILTFIDX(tx->txphase)];
|
|
|
|
|
for (sumr = sumi = i = 0; i < TXFILTLEN; i++, coeff++) {
|
|
|
|
|
sumr += (*coeff) * tx->filter[i].re;
|
|
|
|
|
sumi += (*coeff) * tx->filter[i].im;
|
|
|
|
|
}
|
|
|
|
|
sumr >>= 15;
|
|
|
|
|
sumi >>= 15;
|
|
|
|
|
*sptr++ = (sumr * COS(tx->carphase) - sumi * SIN(tx->carphase)) >> 15;
|
|
|
|
|
tx->carphase += tx->carphinc;
|
|
|
|
|
tx->txphase += tx->txphinc;
|
|
|
|
|
if (sptr >= eptr) {
|
|
|
|
|
audiowrite(tx->chan, sbuf, sptr - sbuf);
|
|
|
|
|
sptr = sbuf;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
audiowrite(tx->chan, sbuf, sptr - sbuf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void txsendtrain(struct txstate *tx)
|
|
|
|
|
{
|
|
|
|
|
txsendsymbols(tx, trainsyms, TRAINSYMS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int txsenddata(struct txstate *tx)
|
|
|
|
|
{
|
|
|
|
|
unsigned char buf[DATABYTES+1];
|
|
|
|
|
unsigned char sym[DATASYMS];
|
|
|
|
|
unsigned int i, j, k;
|
|
|
|
|
unsigned char *bp = buf;
|
|
|
|
|
|
|
|
|
|
if (!pktget(tx->chan, buf, DATABYTES))
|
|
|
|
|
return -1;
|
|
|
|
|
buf[DATABYTES] = 0;
|
|
|
|
|
for (k = j = i = 0; i < DATASYMS; i++) {
|
|
|
|
|
if (j < SYMBITS) {
|
|
|
|
|
k |= (*bp++) << j;
|
|
|
|
|
j += 8;
|
|
|
|
|
}
|
|
|
|
|
sym[i] = k & SYMBITMASK;
|
|
|
|
|
logprintf(260, "txsymbol[%2u] = %u %d%+di\n", i, sym[i], psk_symmapping[sym[i]].re, psk_symmapping[sym[i]].im);
|
|
|
|
|
k >>= SYMBITS;
|
|
|
|
|
j -= SYMBITS;
|
|
|
|
|
}
|
|
|
|
|
txsendsymbols(tx, sym, DATASYMS);
|
|
|
|
|
txsendtrain(tx);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void modmodulate(void *state, unsigned int txdelay)
|
|
|
|
|
{
|
|
|
|
|
struct txstate *tx = (struct txstate *)state;
|
|
|
|
|
unsigned char txddata[DATASYMS];
|
|
|
|
|
unsigned int i, j, k;
|
|
|
|
|
|
|
|
|
|
if (txdelay > 0) {
|
|
|
|
|
for (i = j = k = 0; i < DATASYMS; i++) {
|
|
|
|
|
if (j < SYMBITS) {
|
|
|
|
|
k |= 0x7e << j;
|
|
|
|
|
j += 8;
|
|
|
|
|
}
|
|
|
|
|
txddata[i] = k & SYMBITMASK;
|
|
|
|
|
k >>= SYMBITS;
|
|
|
|
|
j -= SYMBITS;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
while (txdelay > 0) {
|
|
|
|
|
txsendtrain(tx);
|
|
|
|
|
txsendsymbols(tx, txddata, DATASYMS);
|
|
|
|
|
if (txdelay > ((TRAINSYMS+DATASYMS)*1000/SYMRATE))
|
|
|
|
|
txdelay -= ((TRAINSYMS+DATASYMS)*1000/SYMRATE);
|
|
|
|
|
else
|
|
|
|
|
txdelay = 0;
|
|
|
|
|
}
|
|
|
|
|
txsendtrain(tx);
|
|
|
|
|
while (!txsenddata(tx));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct modulator pskmodulator = {
|
|
|
|
|
NULL,
|
|
|
|
|
"psk",
|
|
|
|
|
modparams,
|
|
|
|
|
modconfig,
|
|
|
|
|
modinit,
|
|
|
|
|
modmodulate,
|
|
|
|
|
free
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
static inline int rxfilter_real(const int16_t *val, unsigned int phase)
|
|
|
|
|
{
|
|
|
|
|
const int *coeff = rxfilter_re[RXFILTFIDX(phase)];
|
|
|
|
|
unsigned int i;
|
|
|
|
|
int s = 0;
|
|
|
|
|
|
|
|
|
|
val += RXFILTFSAMP(phase);
|
|
|
|
|
for (i = 0; i < RXFILTLEN; i++, val++, coeff++)
|
|
|
|
|
s += (*val) * (*coeff);
|
|
|
|
|
return s >> 16;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline int rxfilter_imag(const int16_t *val, unsigned int phase)
|
|
|
|
|
{
|
|
|
|
|
const int *coeff = rxfilter_im[RXFILTFIDX(phase)];
|
|
|
|
|
unsigned int i;
|
|
|
|
|
int s = 0;
|
|
|
|
|
|
|
|
|
|
val += RXFILTFSAMP(phase);
|
|
|
|
|
for (i = 0; i < RXFILTLEN; i++, val++, coeff++)
|
|
|
|
|
s += (*val) * (*coeff);
|
|
|
|
|
return s >> 16;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
#define RXCARPHASEINC (((FCARRIER << 16) + SYMRATE / 2) / SYMRATE)
|
|
|
|
|
|
|
|
|
|
struct rxstate {
|
|
|
|
|
struct modemchannel *chan;
|
|
|
|
|
unsigned int srate;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void rxread(struct rxstate *rx, cplxshort_t *ptr, unsigned int nr, unsigned int phase, unsigned int phaseinc)
|
|
|
|
|
{
|
|
|
|
|
int16_t *samples;
|
|
|
|
|
unsigned int totsamp = ((nr * phaseinc) >> 16) + RXFILTLEN;
|
|
|
|
|
|
|
|
|
|
samples = alloca(totsamp * sizeof(samples[0]));
|
|
|
|
|
audioread(rx->chan, samples, totsamp, phase >> 16);
|
|
|
|
|
phase &= 0xffff;
|
|
|
|
|
for (; nr > 0; nr--, ptr++, phase += phaseinc) {
|
|
|
|
|
ptr->re = rxfilter_real(samples, phase);
|
|
|
|
|
ptr->im = rxfilter_imag(samples, phase);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void rxrotate(cplxshort_t *ptr, unsigned int nr, unsigned int carphase, unsigned int carphaseinc)
|
|
|
|
|
{
|
|
|
|
|
int vr, vi, cr, ci;
|
|
|
|
|
|
|
|
|
|
for (; nr > 0; nr--, ptr++, carphase += carphaseinc) {
|
|
|
|
|
vr = ptr->re;
|
|
|
|
|
vi = ptr->im;
|
|
|
|
|
cr = COS(carphase);
|
|
|
|
|
ci = SIN(carphase);
|
|
|
|
|
ptr->re = (vr * cr + vi * ci) >> 15;
|
|
|
|
|
ptr->im = (vi * cr - vr * ci) >> 15;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-18 10:46:45 +02:00
|
|
|
static inline int calcsync(int *toten, int *corren, cplxshort_t *samples)
|
2018-06-17 14:19:22 +02:00
|
|
|
{
|
|
|
|
|
const cplxshort_t *tr = traincorrrotated;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
int acc1r, acc1i, acc2;
|
|
|
|
|
|
|
|
|
|
for (acc1r = acc1i = acc2 = 0, i = 0; i < TRAINSYMS; i++, samples += 2, tr++) {
|
|
|
|
|
acc1r += ((tr->re) * (samples->re) - (tr->im) * (samples->im)) >> 5;
|
|
|
|
|
acc1i += ((tr->re) * (samples->im) + (tr->im) * (samples->re)) >> 5;
|
|
|
|
|
acc2 += ((samples->re) * (samples->re) + (samples->im) * (samples->im)) >> 10;
|
|
|
|
|
}
|
|
|
|
|
acc1r >>= 15;
|
|
|
|
|
acc1i >>= 15;
|
|
|
|
|
acc1r = acc1r * acc1r + acc1i * acc1i;
|
|
|
|
|
acc1r /= TRAINSYMS;
|
|
|
|
|
if (toten)
|
|
|
|
|
*toten = acc2;
|
|
|
|
|
if (corren)
|
|
|
|
|
*corren = acc1r;
|
|
|
|
|
logprintf(258, "Sync energy %d correlation %d\n", acc2, acc1r);
|
|
|
|
|
if (acc2 < 16*TRAINSYMS || acc1r*2 < acc2)
|
|
|
|
|
return 0;
|
|
|
|
|
logprintf(257, "Sync found, energy %d correlation %d\n", acc2, acc1r);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* searches for a training sequence; returns the phase (time) of the training sequence start.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static unsigned int synchunt(struct rxstate *rx, unsigned int phase, unsigned int phaseinc, unsigned int dcdcnt)
|
|
|
|
|
{
|
|
|
|
|
cplxshort_t syncbuf[4*TRAINSYMS+1];
|
|
|
|
|
cplxshort_t syncbuf2[2*TRAINSYMS];
|
|
|
|
|
unsigned int phaseblkinc;
|
|
|
|
|
int toten1, toten2, toten3, syncen1, syncen2, syncen3;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
phaseinc >>= 1;
|
|
|
|
|
phaseblkinc = phaseinc * (2 * TRAINSYMS);
|
|
|
|
|
rxread(rx, syncbuf, 4*TRAINSYMS+1, phase, phaseinc);
|
|
|
|
|
for (;;) {
|
|
|
|
|
for (i = 0; i < 2*TRAINSYMS; i++) {
|
|
|
|
|
if (calcsync(&toten1, &syncen1, syncbuf+i)) {
|
|
|
|
|
if (!calcsync(&toten2, &syncen2, syncbuf+i+1) || syncen2 < syncen1) {
|
|
|
|
|
phase += i * phaseinc;
|
|
|
|
|
rxread(rx, syncbuf2, 2*TRAINSYMS, phase - (phaseinc >> 1), phaseinc);
|
|
|
|
|
calcsync(&toten2, &syncen2, syncbuf2);
|
|
|
|
|
calcsync(&toten3, &syncen3, syncbuf2+1);
|
|
|
|
|
if (syncen2 > syncen1 && syncen2 > syncen3)
|
|
|
|
|
phase -= (phaseinc >> 1);
|
|
|
|
|
else if (syncen3 > syncen1 && syncen3 > syncen2)
|
|
|
|
|
phase += (phaseinc >> 1);
|
|
|
|
|
logprintf(256, "Sync found, dcd %u\n", dcdcnt);
|
|
|
|
|
return phase;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (dcdcnt > 0) {
|
|
|
|
|
dcdcnt--;
|
|
|
|
|
if (!dcdcnt)
|
|
|
|
|
pktsetdcd(rx->chan, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
phase += phaseblkinc;
|
|
|
|
|
memmove(syncbuf, syncbuf+2*TRAINSYMS, (2*TRAINSYMS+1)*sizeof(syncbuf[0]));
|
|
|
|
|
rxread(rx, syncbuf+(2*TRAINSYMS+1), 2*TRAINSYMS, phase + phaseblkinc, phaseinc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void calcchannel(struct rxstate *rx, unsigned int phase, unsigned int phaseinc, cplxshort_t *channel, int *chenergy)
|
|
|
|
|
{
|
|
|
|
|
cplxshort_t trseq[OBSTRAINSYMS];
|
|
|
|
|
const cplxshort_t *p1, *p2;
|
|
|
|
|
unsigned int i, j;
|
|
|
|
|
int sumr, sumi, en;
|
|
|
|
|
|
|
|
|
|
rxread(rx, trseq, OBSTRAINSYMS, phase + (CHANNELLEN/2) * phaseinc, phaseinc);
|
|
|
|
|
for (p1 = trainmatrotated, en = 0, i = 0; i < CHANNELLEN; i++) {
|
|
|
|
|
for (sumr = sumi = 0, p2 = trseq, j = 0; j < OBSTRAINSYMS; j++, p1++, p2++) {
|
|
|
|
|
sumr += (p1->re) * (p2->re) - (p1->im) * (p2->im);
|
|
|
|
|
sumi += (p1->re) * (p2->im) + (p1->im) * (p2->re);
|
|
|
|
|
}
|
|
|
|
|
sumr >>= 16;
|
|
|
|
|
sumi >>= 16;
|
|
|
|
|
channel[i].re = sumr;
|
|
|
|
|
channel[i].im = sumi;
|
|
|
|
|
en += sumr * sumr + sumi * sumi;
|
|
|
|
|
}
|
|
|
|
|
*chenergy = en;
|
|
|
|
|
if (logcheck(256)) {
|
|
|
|
|
char buf[512], *bp = buf;
|
|
|
|
|
int r;
|
|
|
|
|
r = snprintf(bp, buf+sizeof(buf)-bp, "Sync found, chenergy %d, channel", en);
|
|
|
|
|
if (r > 0)
|
|
|
|
|
bp += r;
|
|
|
|
|
for (i = 0; i < CHANNELLEN; i++) {
|
|
|
|
|
r = snprintf(bp, buf+sizeof(buf)-bp, " %d%+di", channel[i].re, channel[i].im);
|
|
|
|
|
if (r > 0)
|
|
|
|
|
bp += r;
|
|
|
|
|
}
|
|
|
|
|
logprintf(256, "%s\n", buf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
static unsigned int mlsebacktrack(unsigned char *syms, unsigned int nrsyms, unsigned int startnode, unsigned short *backtrack)
|
|
|
|
|
{
|
|
|
|
|
for (; nrsyms > 0; nrsyms--, syms--, backtrack -= MLSENODES) {
|
|
|
|
|
if (syms)
|
|
|
|
|
*syms = startnode & SYMBITMASK;
|
|
|
|
|
startnode = backtrack[startnode];
|
|
|
|
|
}
|
|
|
|
|
return startnode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void symtochar(unsigned char *sym, unsigned char *msg)
|
|
|
|
|
{
|
|
|
|
|
unsigned int i, j, k;
|
|
|
|
|
|
|
|
|
|
for (i = j = k = 0; i < DATABYTES;) {
|
|
|
|
|
k |= sym[0] << j;
|
|
|
|
|
sym++;
|
|
|
|
|
j += SYMBITS;
|
|
|
|
|
if (j >= 8) {
|
|
|
|
|
msg[0] = k;
|
|
|
|
|
msg++;
|
|
|
|
|
k >>= 8;
|
|
|
|
|
j -= 8;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
static void mlseblock(struct rxstate *rx, unsigned int phase, unsigned int phaseinc, cplxshort_t *channel)
|
|
|
|
|
{
|
|
|
|
|
unsigned int *nptr1, *nptr2, *nptr3;
|
|
|
|
|
unsigned short *btptr;
|
|
|
|
|
unsigned int i, j, k, carphase = 0, energy = 0;
|
|
|
|
|
cplxshort_t samp[8];
|
|
|
|
|
unsigned int nodes[2*MLSENODES];
|
|
|
|
|
metrictab_t metrictab;
|
|
|
|
|
unsigned short backtrack[(DATASYMS+CHANNELLEN-1)*MLSENODES];
|
|
|
|
|
unsigned char msg[DATABYTES];
|
|
|
|
|
unsigned char sym[DATASYMS+CHANNELLEN-1];
|
|
|
|
|
|
|
|
|
|
/* initialize MLSE state */
|
|
|
|
|
pskmlse_initmetric(channel, &metrictab);
|
|
|
|
|
btptr = backtrack;
|
|
|
|
|
nptr1 = nodes;
|
|
|
|
|
nptr2 = nodes + MLSENODES;
|
|
|
|
|
for (i = 0; i < MLSENODES; i++)
|
|
|
|
|
nptr1[i] = UINT_MAX >> 2;
|
|
|
|
|
nptr1[MLSEROOTNODE] = 0;
|
|
|
|
|
/* MLSE decoder loop */
|
|
|
|
|
for (i = DATASYMS+CHANNELLEN-1; i > 0;) {
|
|
|
|
|
j = i;
|
|
|
|
|
if (j > 8)
|
|
|
|
|
j = 8;
|
|
|
|
|
i -= j;
|
|
|
|
|
rxread(rx, samp, j, phase, phaseinc);
|
|
|
|
|
rxrotate(samp, j, carphase, RXCARPHASEINC);
|
|
|
|
|
phase += j * phaseinc;
|
|
|
|
|
carphase += j * RXCARPHASEINC;
|
|
|
|
|
for (k = 0; k < j; k++) {
|
|
|
|
|
logprintf(260, "RxSymbol: %d%+di\n", samp[k].re, samp[k].im);
|
|
|
|
|
energy += (samp[k].re * samp[k].re + samp[k].im * samp[k].im) >> MLSEENERGYSH;
|
|
|
|
|
pskmlse_trellis(nptr1, nptr2, &metrictab, btptr, samp[k].re, samp[k].im);
|
|
|
|
|
nptr3 = nptr1;
|
|
|
|
|
nptr1 = nptr2;
|
|
|
|
|
nptr2 = nptr3;
|
|
|
|
|
btptr += MLSENODES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* backtracking */
|
|
|
|
|
btptr -= MLSENODES;
|
|
|
|
|
k = mlsebacktrack(&sym[DATASYMS+CHANNELLEN-2], DATASYMS+CHANNELLEN-1, MLSETOORNODE, btptr);
|
|
|
|
|
symtochar(sym, msg);
|
|
|
|
|
if (k != MLSEROOTNODE)
|
|
|
|
|
logprintf(1, "MLSE: uhoh surviving path does not end in root node 0x%x, 0x%x\n", MLSEROOTNODE, k);
|
|
|
|
|
#if 0
|
|
|
|
|
j = 0;
|
|
|
|
|
for (i = 1; i < MLSENODES; i++)
|
|
|
|
|
if (nptr1[i] < nptr1[j])
|
|
|
|
|
j = i;
|
|
|
|
|
if (j != MLSETOORNODE)
|
|
|
|
|
logprintf(1, "MLSE: uhoh best metric not at toor node 0x%x, 0x%x (en %u, %u)\n",
|
|
|
|
|
MLSETOORNODE, j, nptr1[MLSETOORNODE], nptr1[j]);
|
|
|
|
|
#endif
|
|
|
|
|
pktput(rx->chan, msg, DATABYTES);
|
|
|
|
|
k = nptr1[MLSETOORNODE];
|
|
|
|
|
#if defined(USEVIS)
|
|
|
|
|
if (checksimd())
|
|
|
|
|
k <<= 2;
|
|
|
|
|
#endif /* USEVIS */
|
|
|
|
|
logprintf(1, "MLSE: signal energy %u, error energy %u S/(N+D) %5.1fdB\n",
|
|
|
|
|
energy, k, -10*log10(k / (double)(energy ? energy : 1)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void receiver(void *__rx)
|
|
|
|
|
{
|
|
|
|
|
struct rxstate *rx = (struct rxstate *)__rx;
|
|
|
|
|
cplxshort_t chan[CHANNELLEN];
|
|
|
|
|
unsigned int phase, oldphase = (~0U) >> 1, phaseinc, estphaseinc;
|
|
|
|
|
unsigned int changle, oldchangle = 0;
|
|
|
|
|
int en, splphasediff, anglediff, totanglediff, totphasediff;
|
|
|
|
|
|
|
|
|
|
phase = audiocurtime(rx->chan) << 16;
|
|
|
|
|
phaseinc = ((rx->srate << 16) + SYMRATE / 2) / SYMRATE;
|
|
|
|
|
logprintf(256, "rxphaseinc: 0x%x\n", phaseinc);
|
|
|
|
|
for (;;) {
|
|
|
|
|
phase = synchunt(rx, phase, phaseinc, 20);
|
|
|
|
|
pktsetdcd(rx->chan, 1);
|
|
|
|
|
splphasediff = phase - oldphase - (TRAINSYMS+DATASYMS) * phaseinc;
|
|
|
|
|
calcchannel(rx, phase, phaseinc, chan, &en);
|
|
|
|
|
changle = ((1 << 15) / M_PI) * atan2(chan[CHANNELLEN/2].im, chan[CHANNELLEN/2].re);
|
|
|
|
|
changle &= 0xffff;
|
|
|
|
|
anglediff = (int)(int16_t)((changle-oldchangle - RXCARPHASEINC * (DATASYMS + TRAINSYMS)) & 0xffff);
|
|
|
|
|
totphasediff = splphasediff - anglediff * (int)rx->srate / FCARRIER;
|
|
|
|
|
totanglediff = (anglediff - splphasediff * FCARRIER / (int)rx->srate);
|
|
|
|
|
estphaseinc = phaseinc;
|
|
|
|
|
if (abs(splphasediff) < 0x40000) {
|
|
|
|
|
estphaseinc = phaseinc - (totphasediff / (int)(DATASYMS + TRAINSYMS));
|
|
|
|
|
logprintf(257, "last train phase: 0x%08x current train phase: 0x%08x diff: %d\n",
|
|
|
|
|
oldphase, phase, splphasediff);
|
|
|
|
|
logprintf(258, "channel angle: 0x%04x prev channel angle: 0x%04x diff: %d\n", changle, oldchangle, anglediff);
|
|
|
|
|
logprintf(257, "total angle difference: %6d total phase difference: %8d\n", totanglediff, totphasediff);
|
|
|
|
|
logprintf(257, "nominal phase inc: 0x%05x estimated phase inc: 0x%05x delta: %3d o/oo\n",
|
|
|
|
|
phaseinc, estphaseinc, 1000 * (int)(estphaseinc - phaseinc) / (int)phaseinc);
|
|
|
|
|
}
|
|
|
|
|
oldchangle = changle;
|
|
|
|
|
oldphase = phase;
|
|
|
|
|
phase += ((CHANNELLEN/2)+OBSTRAINSYMS) * phaseinc;
|
|
|
|
|
mlseblock(rx, phase, /*est*/phaseinc, chan);
|
|
|
|
|
phase += (DATASYMS-1) * phaseinc;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct modemparams demodparams[] = {
|
|
|
|
|
{ NULL }
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void *demodconfig(struct modemchannel *chan, unsigned int *samplerate, const char *params[])
|
|
|
|
|
{
|
|
|
|
|
struct rxstate *s;
|
|
|
|
|
|
|
|
|
|
if (!(s = calloc(1, sizeof(struct rxstate))))
|
|
|
|
|
logprintf(MLOG_FATAL, "out of memory\n");
|
|
|
|
|
s->chan = chan;
|
|
|
|
|
*samplerate = SAMPLERATE;
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void demodinit(void *state, unsigned int samplerate, unsigned int *bitrate)
|
|
|
|
|
{
|
|
|
|
|
struct rxstate *s = (struct rxstate *)state;
|
|
|
|
|
|
|
|
|
|
s->srate = samplerate;
|
|
|
|
|
*bitrate = 7600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct demodulator pskdemodulator = {
|
|
|
|
|
NULL,
|
|
|
|
|
"psk",
|
|
|
|
|
demodparams,
|
|
|
|
|
demodconfig,
|
|
|
|
|
demodinit,
|
|
|
|
|
receiver,
|
|
|
|
|
free
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|