/*****************************************************************************/ /* * p3dmodemsimple.c -- AO-40 P3D 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 # else # ifdef _AIX #pragma alloca # else # ifndef alloca /* predefined by HP cc +Olibcalls */ char *alloca (); # endif # endif # endif #endif #include "modem.h" #include "p3d.h" #include "p3dtbl.h" #include "simd.h" #include #include #include #include #include /* --------------------------------------------------------------------- */ static inline double sinc(double x) { double arg = x * M_PI; if (fabs(arg) < 1e-10) return 1; return sin(arg) / arg; } static inline double hamming(double x) { return 0.54-0.46*cos(2*M_PI*x); } /* ---------------------------------------------------------------------- */ struct txstate { struct modemchannel *chan; }; 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 = 8000; return s; } static void modinit(void *state, unsigned int samplerate) { struct txstate *s = (struct txstate *)state; } static void modmodulate(void *state, unsigned int txdelay) { struct txstate *tx = (struct txstate *)state; } struct modulator p3dmodulator = { NULL, "p3d", modparams, /*modconfig*/NULL, /*modinit*/NULL, /*modmodulate*/NULL, free }; /* ---------------------------------------------------------------------- */ struct rxfilter { int16_t re[RXFILTOVER][RXFILTLEN]; int16_t im[RXFILTOVER][RXFILTLEN]; }; struct rxstate { struct modemchannel *chan; unsigned int srate; unsigned int rxfiltlen; int16_t basebandfilter[RXFILTOVER][RXFILTLEN]; }; /* ---------------------------------------------------------------------- */ /* * Compute the filter coefficients */ #define RCOSALPHA 0.4 #define FILTERRELAX 1.4 static void compute_rxfilter_from_bb(struct rxstate *rx, struct rxfilter *filter, unsigned int phinc) { unsigned int phase = 0, phase2, phinc2, i, j; memset(filter, 0, sizeof(struct rxfilter)); phinc2 = phinc >> RXFILTOVERBITS; //phase += (RXFILTOVER-1) * phinc2; for (i = 0; i < rx->rxfiltlen; i++) { phase2 = phase; for (j = 0; j < RXFILTOVER; j++) { filter->re[j][i] = (COS(phase2) * rx->basebandfilter[j][i]) >> 15; filter->im[j][i] = -(SIN(phase2) * rx->basebandfilter[j][i]) >> 15; phase2 -= phinc2; } phase += phinc; } } static inline void compute_rxfilter(struct rxstate *rx) { float coeff[RXFILTOVER][RXFILTLEN] = { { 0, }, }; float pulseen[RXFILTOVER] = { 0, }; double tmul; float max1, max2, t, tm, at, f1, f2, f3; unsigned int i, j; memset(coeff, 0, sizeof(coeff)); rx->rxfiltlen = (rx->srate * RXFILTSPAN + SYMRATE - 1) / SYMRATE; /* round to next 4 because SIMD is more efficient with this */ rx->rxfiltlen = (rx->rxfiltlen + 3) & ~3; if (rx->rxfiltlen > RXFILTLEN) { logprintf(MLOG_WARNING, "demodp3d: input filter length too long\n"); rx->rxfiltlen = RXFILTLEN; } logprintf(257, "p3d: rxfilter length %u, sampling rate %u\n", rx->rxfiltlen, rx->srate); tm = rx->rxfiltlen*RXFILTOVER*0.5; #if 0 tmul = FILTERRELAX * (2.0 * SYMRATE) / RXFILTOVER / ((double)rx->srate); for (i = 0; i < RXFILTOVER*rx->rxfiltlen; i++) coeff[i % RXFILTOVER][i / RXFILTOVER] = sinc((i - tm) * tmul) * hamming((double)i / (double)(RXFILTOVER*rx->rxfiltlen-1)); #else tmul = (2.0*SYMRATE) / RXFILTOVER / ((double)rx->srate); for (i = 0; i < RXFILTOVER*rx->rxfiltlen; i++) { t = (i - tm) * tmul; at = t * RCOSALPHA; f1 = 1 - 4 * at * at; if (fabs(f1) < 1e-10) f2 = M_PI * (1.0 / 8.0) * sin(M_PI * at) / at; else f2 = cos(M_PI * at) / f1; f3 = f2 * sinc(t); #if 0 logprintf(258, "p3d: i %4u t %10g at %10g f1 %10g f3 %10g\n", i, t, at, f1, f3); #endif coeff[i % RXFILTOVER][i / RXFILTOVER] = f3; } #endif if (logcheck(257)) { char buf[32768]; char *cp = buf; for (i = 0; i < RXFILTOVER*rx->rxfiltlen; i++) cp += sprintf(cp, " %g", coeff[i % RXFILTOVER][i / RXFILTOVER]); logprintf(257, "p3d: pulse = [ %s ];\n", buf+1); } max1 = 0; for (i = 0; i < RXFILTOVER; i++) { max2 = 0; for (j = 0; j < rx->rxfiltlen; j++) max2 += fabs(coeff[i][j]); if (max2 > max1) max1 = max2; } max2 = ((float)0x7fffffff / (float)0x7fff) / max1; for (i = 0; i < RXFILTOVER; i++) { f1 = 0; for (j = 0; j < rx->rxfiltlen; j++) { rx->basebandfilter[i][j] = max2 * coeff[RXFILTOVER-1-i][j]; f1 += rx->basebandfilter[i][j] * rx->basebandfilter[i][j]; } pulseen[i] = f1; } if (logcheck(257)) { char buf[512]; char *cp = buf; for (i = 0; i < RXFILTOVER; i++) cp += sprintf(cp, ", %6.2gdB", 10*M_LOG10E*log(pulseen[i]) - 10*M_LOG10E*log(32768.0 * (1<<16))); logprintf(257, "p3d: pulse energies: %s\n", buf+2); } } static cplxint_t calc_rxfilter(const int16_t *val, const struct rxfilter *filter, unsigned int filtlen, unsigned int phase) { const int16_t *re, *im; cplxint_t r; re = filter->re[RXFILTFIDX(phase)]; im = filter->im[RXFILTFIDX(phase)]; val += RXFILTFSAMP(phase); r.re = simdfir16(val, re, filtlen) >> 15; r.im = simdfir16(val, im, filtlen) >> 15; return r; } static cplxint_t calc_baseband_rxfilter(const struct rxstate *rx, const int16_t *re, const int16_t *im, unsigned int phase) { unsigned int ph1, ph2; cplxint_t r; ph1 = RXFILTFIDX(phase); ph2 = RXFILTFSAMP(phase); r.re = simdfir16(re + ph2, rx->basebandfilter[ph1], rx->rxfiltlen) >> 15; r.im = simdfir16(im + ph2, rx->basebandfilter[ph1], rx->rxfiltlen) >> 15; return r; } /* ---------------------------------------------------------------------- */ #define CRXBLOCKSIZE 1024 #define SYNCTIMEOUT (5*SYMRATE) static unsigned int contrx(struct rxstate *rx, unsigned int phase, unsigned int phinc, unsigned int freqinc) { unsigned int bufphase = (phase - (10 << 16)) & ~0xffff, freq = 0, phearlylate = phinc >> 3, phcomp = phinc >> 7; unsigned int sbufptr = 0, i, me, ml, shreg = 0, rxbufptr = (512+2)*8; u_int16_t crc = 0xffff; int16_t samp_re[CRXBLOCKSIZE+RXFILTLEN], samp_im[CRXBLOCKSIZE+RXFILTLEN]; cplxint_t sbuf[32], lasts, news, earlys, lates, r1; unsigned char rxbuf[512+2]; audioread(rx->chan, &samp_im[0], RXFILTLEN, bufphase >> 16); for (i = 0; i < RXFILTLEN; i++) { r1.re = samp_im[i] * COS(freq); r1.im = - samp_im[i] * SIN(freq); freq += freqinc; samp_re[i] = r1.re >> 15; samp_im[i] = r1.im >> 15; } lasts.re = lasts.im = 0; for (;;) { audioread(rx->chan, &samp_im[RXFILTLEN], CRXBLOCKSIZE, (bufphase >> 16) + RXFILTLEN); for (i = RXFILTLEN; i < CRXBLOCKSIZE+RXFILTLEN; i++) { r1.re = samp_im[i] * COS(freq); r1.im = - samp_im[i] * SIN(freq); freq += freqinc; samp_re[i] = r1.re >> 15; samp_im[i] = r1.im >> 15; } while ((phase - bufphase) < ((CRXBLOCKSIZE+10) << 16)) { news = calc_baseband_rxfilter(rx, samp_re, samp_im, phase-bufphase); earlys = calc_baseband_rxfilter(rx, samp_re, samp_im, phase-phearlylate-bufphase); lates = calc_baseband_rxfilter(rx, samp_re, samp_im, phase+phearlylate-bufphase); phase += phinc; r1 = calc_baseband_rxfilter(rx, samp_re, samp_im, phase-bufphase); news.re -= r1.re; news.im -= r1.im; r1 = calc_baseband_rxfilter(rx, samp_re, samp_im, phase-phearlylate-bufphase); earlys.re -= r1.re; earlys.im -= r1.im; r1 = calc_baseband_rxfilter(rx, samp_re, samp_im, phase+phearlylate-bufphase); lates.re -= r1.re; lates.im -= r1.im; phase += phinc; me = earlys.re * earlys.re + earlys.im * earlys.im; ml = lates.re * lates.re + lates.im * lates.im; if (me > ml) phase -= phcomp; else phase += phcomp; r1.re = (news.re * lasts.re + news.im * lasts.im) >> 5; r1.im = (news.im * lasts.re - news.re * lasts.im) >> 5; sbuf[sbufptr] = r1; sbufptr = (sbufptr + 1) & 31; lasts = news; shreg <<= 1; shreg |= (r1.re < 0); if (rxbufptr >= (512+2)*8) { if ((shreg & 0xffffffff) == SYNCWORD) { crc = 0xffff; rxbufptr = 0; r1.re = r1.im = 0; for (i = 0; i < 32; i++) { if (sbuf[i].re < 0) { r1.re -= sbuf[i].re; r1.im -= sbuf[i].im; } else { r1.re += sbuf[i].re; r1.im += sbuf[i].im; } } simdpreparefpu(); freqinc += (0x8000 / M_PI * SYMRATE) * atan2(r1.im, r1.re) / rx->srate; p3drxstate(rx->chan, 1, (rx->srate * freqinc + 0x8000) >> 16); logprintf(256, "p3ddemod: SYNC word: phase 0x%08x finc: 0x%06x (%d%+di)\n", phase, freqinc, r1.re, r1.im); continue; } rxbufptr++; if (rxbufptr < (512+2)*8+SYNCTIMEOUT) continue; return phase; } if ((rxbufptr & 7) == 7) rxbuf[rxbufptr >> 3] = shreg; rxbufptr++; if (rxbufptr < (512+2)*8) continue; crc = 0xffff; for (i = 0; i < (512+2); i++) crc = (crc << 8) ^ amsat_crc[((crc >> 8) ^ rxbuf[i]) & 0xff]; p3dreceive(rx->chan, rxbuf, crc & 0xffff); p3drxstate(rx->chan, 2, (rx->srate * freqinc + 0x8000) >> 16); } memcpy(&samp_re[0], &samp_re[CRXBLOCKSIZE], RXFILTLEN * sizeof(samp_re[0])); memcpy(&samp_im[0], &samp_im[CRXBLOCKSIZE], RXFILTLEN * sizeof(samp_im[0])); bufphase += CRXBLOCKSIZE << 16; } } /* ---------------------------------------------------------------------- */ static void rxblock(struct rxstate *rx, unsigned int phase, unsigned int phinc, unsigned int freqinc) { unsigned int samplen, i, j, crc; int16_t *samp_re, *samp_im; cplxint_t sbuf[(512+2)*8*2+1], r1, r2, r3; unsigned char rxbuf[512+2], *bp; samplen = ((((512+2)*8*2+1)*phinc) >> 16) + 1 + rx->rxfiltlen; samp_re = alloca(samplen * sizeof(samp_re[0])); samp_im = alloca(samplen * sizeof(samp_im[0])); audioread(rx->chan, &samp_im[0], samplen, phase >> 16); phase &= 0xffff; /* downconvert */ j = 0; for (i = 0; i < samplen; i++) { r1.re = samp_im[i] * COS(j); r1.im = - samp_im[i] * SIN(j); j += freqinc; samp_re[i] = r1.re >> 15; samp_im[i] = r1.im >> 15; } /* baseband filtering */ for (i = 0; i < (512+2)*8*2+1; i++) { sbuf[i] = calc_baseband_rxfilter(rx, samp_re, samp_im, phase); phase += phinc; } /* "Manchester" PSK decode */ memset(rxbuf, 0, sizeof(rxbuf)); bp = rxbuf; crc = 0xffff; for (i = 0; i < (512+2)*8; i++) { r1.re = sbuf[2*i].re - sbuf[2*i+1].re; r1.im = sbuf[2*i].im - sbuf[2*i+1].im; r2.re = sbuf[2*i+2].re - sbuf[2*i+3].re; r2.im = sbuf[2*i+2].im - sbuf[2*i+3].im; r3.re = (r2.re * r1.re + r2.im * r1.im) >> 15; r3.im = (r2.im * r1.re - r2.re * r1.im) >> 15; *bp <<= 1; *bp |= (r3.re < 0); if ((i & 7) == 7) bp++; crc <<= 1; crc |= ((crc >> 16) ^ (r3.re < 0)) & 1; if (crc & 1) crc ^= (1 << 5) | (1 << 12); } p3dreceive(rx->chan, rxbuf, crc & 0xffff); } /* ---------------------------------------------------------------------- */ static unsigned int finesync_corr(struct rxstate *rx, unsigned int phase, unsigned int phsamp, unsigned int phinc, int16_t *samp_re, int16_t *samp_im, cplxint_t *freqr) { unsigned int ph = phase - phsamp, i, j; cplxint_t sbuf[33*2], r1, r2, r3, r4; for (i = 0; i < 33*2; i++) { sbuf[i] = calc_baseband_rxfilter(rx, samp_re, samp_im, ph); ph += phinc; } r4.re = r4.im = 0; j = 0; for (i = 0; i < 32; i++) { r1.re = sbuf[2*i].re - sbuf[2*i+1].re; r1.im = sbuf[2*i].im - sbuf[2*i+1].im; r2.re = sbuf[2*i+2].re - sbuf[2*i+3].re; r2.im = sbuf[2*i+2].im - sbuf[2*i+3].im; r3.re = (r2.re * r1.re + r2.im * r1.im) >> 5; r3.im = (r2.im * r1.re - r2.re * r1.im) >> 5; j <<= 1; j |= (r3.re < 0); if (j & 1) { r4.re -= r3.re; r4.im -= r3.im; } else { r4.re += r3.re; r4.im += r3.im; } } *freqr = r4; if ((j & 0xffffffff) != SYNCWORD) { logprintf(256, "p3ddemod: finesync: phase 0x%08x word 0x%08x\n", phase, j); return 0; } r3.re = r3.im = 0; j = 0; for (i = 0; i < 33; i++) { r1.re = sbuf[2*i].re - sbuf[2*i+1].re; r1.im = sbuf[2*i].im - sbuf[2*i+1].im; if (j) { r3.re -= r1.re; r3.im -= r1.im; } else { r3.re += r1.re; r3.im += r1.im; } if (SYNCWORD & (0x80000000 >> i)) j = !j; } r3.re >>= 5; r3.im >>= 5; i = r3.re * r3.re + r3.im * r3.im; logprintf(256, "p3ddemod: finesync: phase 0x%08x val %6u freqr %6d%+7di\n", phase, i, r4.re, r4.im); return i; } struct finesyncparams { unsigned int phest; unsigned int phinc; unsigned int finc; unsigned int maxen; }; static struct finesyncparams fine_sync(struct rxstate *rx, const struct rxfilter *filter, unsigned int phest, unsigned int freqest) { unsigned int phase = phest, phsamp, phinc, finc, i, j, samplen, maxen; int tmg, tmgmax; cplxint_t r1, maxv, freqr; int16_t *samp, *samp_re, *samp_im; struct finesyncparams fsp; //printf("Sync Det: phest 0x%08x freqest 0x%08x\n", phest, freqest); finc = ((freqest << 16) + rx->srate/2) / rx->srate; phinc = ((rx->srate << 16) + SYMRATE) / (2 * SYMRATE); samplen = (((36+2)*2*phinc) >> 16) + rx->rxfiltlen; phsamp = (phest - 4*phinc) & ~0xffff; samp = alloca(samplen * sizeof(samp[0])); samp_re = alloca(samplen * sizeof(samp_re[0])); samp_im = alloca(samplen * sizeof(samp_im[0])); audioread(rx->chan, &samp[0], samplen, phsamp >> 16); j = 0; for (i = 0; i < samplen; i++) { r1.re = samp[i] * COS(j); r1.im = - samp[i] * SIN(j); j += finc; samp_re[i] = r1.re >> 15; samp_im[i] = r1.im >> 15; } #if 0 i = (0x7b8a-(phsamp >> 16)) & 0xffff; if (i < samplen) { printf("Samples:"); for (j = 0; j < 16; j++) printf(" %d%+di", samp_re[i+j], samp_im[i+j]); printf("\n"); } #endif maxen = 0; maxv.re = maxv.im = 0; for (tmgmax = tmg = -16; tmg <= 16; tmg++) { phase = phest + tmg * (phinc >> 3); if (((phase + 33*2*phinc - phsamp) >> 16)+rx->rxfiltlen > samplen) abort(); i = finesync_corr(rx, phase, phsamp, phinc, samp_re, samp_im, &r1); if (i > maxen) { maxen = i; tmgmax = tmg; freqr = r1; } } phest += tmgmax * (phinc >> 3); simdpreparefpu(); finc += (0x8000 / M_PI * SYMRATE) * atan2(freqr.im, freqr.re) / rx->srate; j = 0; for (i = 0; i < samplen; i++) { r1.re = samp[i] * COS(j); r1.im = - samp[i] * SIN(j); j += finc; samp_re[i] = r1.re >> 15; samp_im[i] = r1.im >> 15; } maxen = 0; maxv.re = maxv.im = 0; freqr.re = freqr.im = 0; for (tmgmax = tmg = -8; tmg <= 8; tmg++) { phase = phest + tmg * (phinc >> 6); if (((phase + 33*2*phinc - phsamp) >> 16)+rx->rxfiltlen > samplen) abort(); i = finesync_corr(rx, phase, phsamp, phinc, samp_re, samp_im, &r1); if (i > maxen) { maxen = i; tmgmax = tmg; freqr = r1; } } phest += tmgmax * (phinc >> 6); simdpreparefpu(); finc += (0x8000 / M_PI * SYMRATE) * atan2(freqr.im, freqr.re) / rx->srate; fsp.phest = phest; fsp.phinc = phinc; fsp.finc = finc; fsp.maxen = maxen; return fsp; } /* ---------------------------------------------------------------------- */ #define BLOCKSIZE 1024 #define RBUFSIZE 512 #define SAMPLESPERBIT 8 static void synchunt(struct rxstate *rx) { int16_t samp[RXFILTLEN+BLOCKSIZE]; struct { unsigned int phase, phinc, freq; struct rxfilter filter; unsigned int bitstr[SAMPLESPERBIT]; cplxint_t rbuf[RBUFSIZE]; } carrier[FNUMBER]; cplxint_t r1, r2, r3; unsigned int phase = 0, phase2, phinc, syncph = 0, rptr, i, j; struct finesyncparams fsp; memset(carrier, 0, sizeof(carrier)); for (i = 0; i < FNUMBER; i++) { carrier[i].freq = j = (FCENTER - (FNUMBER-1) * FSPACING / 2) + i * FSPACING; carrier[i].phinc = ((j << 16) + (SAMPLESPERBIT * SYMRATE / 2)) / (SAMPLESPERBIT * SYMRATE); compute_rxfilter_from_bb(rx, &carrier[i].filter, ((j << 16) + rx->srate/2) / rx->srate); } phinc = ((rx->srate << 16) + (SAMPLESPERBIT * SYMRATE / 2)) / (SAMPLESPERBIT * SYMRATE); restart: p3drxstate(rx->chan, 0, 0); phase2 = phase & 0xffff; audioread(rx->chan, &samp[0], RXFILTLEN, phase >> 16); for (;;) { audioread(rx->chan, &samp[RXFILTLEN], BLOCKSIZE, (phase >> 16) + RXFILTLEN); for (rptr = 0; phase2 < (BLOCKSIZE << 16); phase2 += phinc, phase += phinc) { for (i = 0; i < FNUMBER; i++) { r1 = calc_rxfilter(samp, &carrier[i].filter, rx->rxfiltlen, phase2); r2.re = COS(carrier[i].phase); r2.im = -SIN(carrier[i].phase); carrier[i].phase += carrier[i].phinc; r3.re = (r2.re * r1.re - r2.im * r1.im) >> 15; r3.im = (r2.im * r1.re + r2.re * r1.im) >> 15; carrier[i].rbuf[rptr + 2*SAMPLESPERBIT] = r3; } rptr++; } /* do differential decode */ for (i = 0; i < rptr; i++) { for (j = 0; j < FNUMBER; j++) { r1.re = carrier[j].rbuf[i].re - carrier[j].rbuf[i+SAMPLESPERBIT/2].re; r1.im = carrier[j].rbuf[i].im - carrier[j].rbuf[i+SAMPLESPERBIT/2].im; r2.re = carrier[j].rbuf[i+SAMPLESPERBIT].re - carrier[j].rbuf[i+3*SAMPLESPERBIT/2].re; r2.im = carrier[j].rbuf[i+SAMPLESPERBIT].im - carrier[j].rbuf[i+3*SAMPLESPERBIT/2].im; r3.re = (r2.re * r1.re + r2.im * r1.im) >> 15; r3.im = (r2.im * r1.re - r2.re * r1.im) >> 15; carrier[j].bitstr[syncph] <<= 1; carrier[j].bitstr[syncph] |= (r3.re < 0); if (SYNCWORD == (carrier[j].bitstr[syncph] & 0xffffffff)) { //printf("\n\n====== SYNC DETECTED: phase %08x rbuf %u freq %u syncph %u\n\n\n", phase, i, j, syncph); fsp = fine_sync(rx, &carrier[j].filter, phase - (rptr-i+33*SAMPLESPERBIT) * phinc, carrier[j].freq); if (fsp.maxen > 10) { p3drxstate(rx->chan, 1, (rx->srate * fsp.finc + 0x8000) >> 16); #if 0 rxblock(rx, fsp.phest+32*2*fsp.phinc, fsp.phinc, fsp.finc); phase = fsp.phest + (4+512+2)*8*2*phinc; #else phase = contrx(rx, fsp.phest, fsp.phinc, fsp.finc); #endif goto restart; } } #if 0 if (!syncph) printf("\n"); printf("| %c ", '0'+(r3.re < 0)); #endif } syncph++; if (syncph >= SAMPLESPERBIT) syncph = 0; } fflush(stdout); /* prepare for next iteration */ phase2 -= (BLOCKSIZE << 16); memcpy(&samp[0], &samp[BLOCKSIZE], RXFILTLEN * sizeof(samp[0])); for (i = 0; i < FNUMBER; i++) { memcpy(&carrier[i].rbuf[0], &carrier[i].rbuf[rptr], 2*SAMPLESPERBIT*sizeof(carrier[i].rbuf[0])); } } } static void receiver(void *__rx) { struct rxstate *rx = (struct rxstate *)__rx; synchunt(rx); } 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 = 8000; return s; } static void demodinit(void *state, unsigned int samplerate, unsigned int *bitrate) { struct rxstate *s = (struct rxstate *)state; s->srate = samplerate; *bitrate = 400; compute_rxfilter(s); } struct demodulator p3ddemodulator = { NULL, "p3d", demodparams, demodconfig, demodinit, receiver, free }; /* ---------------------------------------------------------------------- */