[pskmail] FEC + longer and variable Interleaver for PSK modes

  • From: John Douyere <vk2eta@xxxxxxxxx>
  • To: pskmail@xxxxxxxxxxxxx
  • Date: Fri, 27 Nov 2009 17:30:37 +1100

Team,

This is the new version with a variable interleaver size so that the
time delay between bits is constant and independant of the speed used.

It is also longer: double the size of the previous version for BPSK63
+ FEC, then doubling with the speed every time.

It should provide better immunity to noise bursts like static crashes,
but may spread the errors too much so that a (long) noise burst may
affect several blocks.

Only practical tests will tell.

On the bench, improvements over the shorter interleaver are marginal.
Visible, but not significant. I think we are now chasing diminishing
returns on these modes.

Still thinking about soft decoding though.

Attached files: psk.h in src/include, psk.cxx in src/psk,
interleave.cxx in src/mfsk.

Regards,

John
// ----------------------------------------------------------------------------
// psk.h  --  psk modem
//
// Copyright (C) 2006
//              Dave Freese, W1HKJ
//
// This file is part of fldigi.  Adapted from code contained in gmfsk source 
code 
// distribution.
//
// fldigi 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.
//
// fldigi 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 fldigi; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
// ----------------------------------------------------------------------------

#ifndef _PSK_H
#define _PSK_H

#include "complex.h"
#include "modem.h"
#include "globals.h"
#include "viterbi.h"
#include "filters.h"
#include "pskcoeff.h"
#include "pskvaricode.h"
#include "viewpsk.h"
#include "pskeval.h"
//VK2ETA Interleaver
#include "interleave.h"

//=====================================================================
#define PskSampleRate   (8000)
#define PipeLen                 (64)

#define SNTHRESHOLD 6.0
#define AFCDECAYSLOW 8

#define NUM_FILTERS 3
#define GOERTZEL 288            //96 x 2 must be an integer value

//=====================================================================

class psk : public modem {
private:
// tx & rx
        int                             symbollen;
        bool                    _qpsk;
        double                  phaseacc;
        complex                 prevsymbol;
        unsigned int            shreg;
//VK2ETA FEC
        unsigned int            shreg2;
        int                     numinterleavers; //interleaver size (speed 
dependant)

// rx variables & functions

        C_FIR_filter            *fir1;
        C_FIR_filter            *fir2;
//      C_FIR_filter            *fir3;
        double                  *fir1c;
        double                  *fir2c;
        Cmovavg                 *snfilt;
        Cmovavg                 *imdfilt;

        double                  I1[NUM_FILTERS];
        double                  I2[NUM_FILTERS];
        double                  Q1[NUM_FILTERS];
        double                  Q2[NUM_FILTERS];
        double                  COEF[NUM_FILTERS];
        double                  m_Energy[NUM_FILTERS];
        int                             m_NCount;
        bool                    imdValid;

        encoder                 *enc;
        viterbi                 *dec;
//VK2ETA BPSK+FEC with 2nd Viterbi decoder and 2 receive de-interleavers for 
comparison
        viterbi                 *dec2;
        interleave              *Rxinlv;
        interleave              *Rxinlv2;
        interleave              *Txinlv;
        int                     bitstate;
        unsigned int            bitshreg;
        int                     rxbitstate;
        unsigned int            rxbitshreg;
        unsigned int            rxbitshreg2;


        int                     fecmet;
        int                     fecmet2;
        unsigned char           symcur[2];
        unsigned char           symP1[2];

        double                  phase;
        double                  freqerr;
        int                             bits;
        double                  bitclk;
        double                  syncbuf[16];
        double                  scope_pipe[2*PipeLen];//[PipeLen];
        unsigned int            pipeptr;
        unsigned int            dcdshreg;
//VK2ETA FEC
        unsigned int            dcdshreg2;

        int                     dcd;
        int                     dcdbits;
        complex                 quality;
        int                     acquire;

        viewpsk*                pskviewer;
        pskeval*                evalpsk;

        void                    rx_symbol(complex symbol);
        void                    rx_bit(int bit);
//VK2ETA FEC
        void                    rx_bit2(int bit);

        void                    rx_qpsk(int bits);
        double                  scopedata[16];
// IMD & s/n variables
        double                  k0, k1, k2;
        double                  I11, I12, I21, I22, I31, I32;
        double                  snratio, s2n, imdratio, imd;
        double                  E1, E2, E3;
        double                  afcmetric;
        
//VK2ETA signal quality
        double                  s2n_ncount;
        double                  s2n_sum;
        double                  s2n_sum2;
//VK2ETA FEC for BPSK modes
        bool                    firstbit;
        bool                    startpreamble;


        
//      complex thirdorder;
// tx variables & functions
        double                  *tx_shape;
        int                     preamble;
        void                    tx_symbol(int sym);
        void                    tx_bit(int bit);
        void                    tx_char(unsigned char c);
        void                    tx_flush();
        void                    update_syncscope();
        void                    signalquality();
        void                    findsignal();
        void                    phaseafc();
        void                    afc();
        void                    coreafc();

        void                    initSN_IMD();
        void                    resetSN_IMD();
        void                    calcSN_IMD(complex z);
//VK2ETA interleaver
        void                    clearbits();
        
public:
        psk(trx_mode mode);
        ~psk();
        void init();
        void rx_init();
        void tx_init(SoundBase *sc);
        void restart();
        int rx_process(const double *buf, int len);
        int tx_process();
        void searchDown();
        void searchUp();
};

#endif
// ----------------------------------------------------------------------------
// psk.cxx  --  psk modem
//
// Copyright (C) 2006
//              Dave Freese, W1HKJ
//
// This file is part of fldigi.  Adapted from code contained in gmfsk 
// source code distribution.
// gmfsk Copyright (C) 2001, 2002, 2003
// Tomi Manninen (oh2bns@xxxxxxx)
//
// fldigi 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.
//
// fldigi 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 fldigi; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, 
// Boston, MA  02111-1307  USA
// --------------------------------------------------------------------


#include <config.h>

#include <stdlib.h>
#include <stdio.h>
#include <iomanip>

#include "psk.h"
#include "main.h"
#include "fl_digi.h"
#include "trx.h"
#include "misc.h"
#include "waterfall.h"
#include "configuration.h"
#include "status.h"
#include "viewpsk.h"
#include "pskeval.h"

extern waterfall *wf;

// Change the following for DCD low pass filter adjustment
#define SQLCOEFF 0.01
#define SQLDECAY 50

//=====================================================================

//#define       K               5
//#define       POLY1   0x17
//#define       POLY2   0x19

//VK2ETA QPSK+FEC
#define K       7
#define POLY1   0x6d
#define POLY2   0x4f

#define         EOT     0x04            //EOT ascii character

char pskmsg[80];
viewpsk *pskviewer = (viewpsk *)0;


void psk::tx_init(SoundBase *sc)
{
        scard = sc;
        phaseacc = 0;
        prevsymbol = complex (1.0, 0.0);
        preamble = dcdbits;
        shreg = 0;
        shreg2 = 0;
        videoText();

//VK2ETA interleaver
        bitshreg = 0;
        bitstate = 0;
        startpreamble = true;

}

void psk::rx_init()
{
        phaseacc = 0;
        prevsymbol      = complex (1.0, 0.0);
        quality         = complex (0.0, 0.0);
        shreg = 0;
        shreg2 = 0;
        dcdshreg = 0;
        dcdshreg2 = 0;
        dcd = 0;
        bitclk = 0;
        freqerr = 0.0;
        if (mailserver && progdefaults.PSKmailSweetSpot) sigsearch = SIGSEARCH;
        put_MODEstatus(mode);
        resetSN_IMD();
        imdValid = false;
        afcmetric = 0.0;
//VK2ETA interleaver
        rxbitshreg = 0;
        rxbitshreg2 = 0;
        rxbitstate = 0;

}


void psk::restart()
{
        pskviewer->restart(mode);
        evalpsk->setbw(bandwidth);
}

void psk::init()
{
        modem::init();
        restart();
        set_scope_mode(Digiscope::PHASE);
        initSN_IMD();
        snratio = 1.0;
        imdratio = 0.001;
        rx_init();
}

psk::~psk()
{
        if (tx_shape) delete [] tx_shape;
        if (enc) delete enc;
        if (dec) delete dec;
//VK2ETA 2nd Viterbi decoder
        if (dec2) delete dec2;
//VK2ETA FEC + INTERLEAVER

        if (fir1) delete fir1;
        if (fir2) delete fir2;
        if (snfilt) delete snfilt;
        if (imdfilt) delete imdfilt;
        if (::pskviewer == pskviewer)
                ::pskviewer = 0;
        delete pskviewer;
        delete evalpsk;

//VK2ETA Interleaver
        if (Rxinlv) delete Rxinlv;
        if (Rxinlv2) delete Rxinlv2;
        if (Txinlv) delete Txinlv;

}

psk::psk(trx_mode pskmode) : modem()
{
        cap = CAP_AFC | CAP_AFC_SR;

        mode = pskmode;

        switch (mode) {
        case MODE_BPSK31:
                symbollen = 256;
                _qpsk = false;
                dcdbits = 32;
                break;
        case MODE_QPSK31:
//VK2ETA FEC test replace qpsk31 by BPSK500 + MFEC
//              symbollen = 256;
//              _qpsk = true;
//              dcdbits = 32;
                symbollen = 16;
                _qpsk = true;
                dcdbits = 512;
                numinterleavers = -2160;        //2x2x160 interleaver
//VK2ETA BPSK+FEC               cap |= CAP_REV;
                break;
        case MODE_PSK63:
                symbollen = 128;
                _qpsk = false;
                dcdbits = 64;
                break;
        case MODE_QPSK63:
                symbollen = 128;
                _qpsk = true;
                dcdbits = 64;
                numinterleavers = -220; //2x2x20 interleaver
//VK2ETA BPSK+FEC               cap |= CAP_REV;
                break;
        case MODE_PSK125:
                symbollen = 64;
                _qpsk = false;
                dcdbits = 128;
                break;
        case MODE_QPSK125:
                symbollen = 64;
                _qpsk = true;
                dcdbits = 128;
                numinterleavers = -240; //2x2x40 interleaver
//VK2ETA BPSK+FEC               cap |= CAP_REV;
                break;
        case MODE_PSK250:
                symbollen = 32;
                _qpsk = false;
                dcdbits = 256;
                break;
        case MODE_QPSK250:
                symbollen = 32;
                _qpsk = true;
                dcdbits = 256;
                numinterleavers = -280; //2x2x80 interleaver
//VK2ETA BPSK+FEC               cap |= CAP_REV;
                break;
        case MODE_PSK500:
                symbollen = 16;
                _qpsk = false;
                dcdbits = 512;
                break;
        default:
                mode = MODE_BPSK31;
                symbollen = 256;
                _qpsk = false;
                dcdbits = 32;
        }

        enc = (encoder *)0;
        dec = (viterbi *)0;
//VK2ETA BPSK+FEC - 2nd Viterbi decoder and de-interleaver
        dec2 = (viterbi *)0;
        Txinlv = (interleave *)0;
        Rxinlv = (interleave *)0;
        Rxinlv2 = (interleave *)0;

        
// create impulse response for experimental FIR filters
        double fir1c[64];
        double fir2c[64];

        fir1 = new C_FIR_filter();
        fir2 = new C_FIR_filter();

    switch (progdefaults.PSK_filter) {
        case 1:
// use the original gmfsk matched filters
                for (int i = 0; i < 64; i++) {
                        fir1c[i] = gmfir1c[i];
                        fir2c[i] = gmfir2c[i];
                }
                fir1->init(FIRLEN, symbollen / 16, fir1c, fir1c);
                fir2->init(FIRLEN, 1, fir2c, fir2c);
            break;
        case 2:
// creates fir1c matched sin(x)/x filter w hamming
                wsincfilt(fir1c, 1.0 / symbollen, false);
                fir1->init(FIRLEN, symbollen / 16, fir1c, fir1c);
// creates fir2c matched sin(x)/x filter w hamming
                wsincfilt(fir2c, 1.0 / 16.0, false);
                fir2->init(FIRLEN, 1, fir2c, fir2c);
            break;
        case 3:
// creates fir1c matched sin(x)/x filter w hamming
                wsincfilt(fir1c, 1.0 / symbollen, false);
                fir1->init(FIRLEN, symbollen / 16, fir1c, fir1c);
// 1/22 with Hamming window nearly identical to gmfir2c
                wsincfilt(fir2c, 1.0 / 22.0, false);
                fir2->init(FIRLEN, 1, fir2c, fir2c);
            break;
        case 4:
            fir1->init_lowpass (FIRLEN, 16, 1.5 / symbollen);
                wsincfilt(fir2c, 1.5 / 16.0, true);
            fir2->init(FIRLEN, 1, fir2c, fir2c);
        case 0:
        default :
// creates fir1c matched sin(x)/x filter w blackman
                wsincfilt(fir1c, 1.0 / symbollen, true);
                fir1->init(FIRLEN, symbollen / 16, fir1c, fir1c);
// creates fir2c matched sin(x)/x filter w blackman
                wsincfilt(fir2c, 1.0 / 16.0, true);
                fir2->init(FIRLEN, 1, fir2c, fir2c);
    }
    
        snfilt = new Cmovavg(16);
        imdfilt = new Cmovavg(16);

        if (_qpsk) {
                enc = new encoder(K, POLY1, POLY2);
                dec = new viterbi(K, POLY1, POLY2);
//VK2ETA FEC for BPSK. Use a 2nd Viterbi decoder for comparison
                dec->setchunksize(4); 
                dec2 = new viterbi(K, POLY1, POLY2);
                dec2->setchunksize(4); 
//VK2ETA interleaver
                Txinlv = new interleave (numinterleavers, INTERLEAVE_FWD); // 
2x2x10
                Rxinlv = new interleave (numinterleavers, INTERLEAVE_REV); // 
2x2x10
                Rxinlv2 = new interleave (numinterleavers, INTERLEAVE_REV); // 
2x2x10
                bitshreg = 0;
                bitstate = 0;
                rxbitshreg = 0;
                rxbitshreg2 = 0;
                rxbitstate = 0;
                startpreamble = true;

        }

        tx_shape = new double[symbollen];

        /* raised cosine shape for the transmitter */
        for ( int i = 0; i < symbollen; i++)
                tx_shape[i] = 0.5 * cos(i * M_PI / symbollen) + 0.5;

        samplerate = PskSampleRate;
        fragmentsize = symbollen;
        bandwidth = samplerate / symbollen;
        snratio = s2n = imdratio = imd = 0;

        if (mailserver && progdefaults.PSKmailSweetSpot)
                sigsearch = SIGSEARCH;
        else
                sigsearch = 0;
        for (int i = 0; i < 16; i++)
                syncbuf[i] = 0.0;
        E1 = E2 = E3 = 0.0;
        acquire = 0;

        evalpsk = new pskeval;
        ::pskviewer = pskviewer = new viewpsk(evalpsk, mode);

        init();
}

//=============================================================================
//=========================== psk31 receive routines ==========================
//=============================================================================

void psk::rx_bit(int bit)
{
        int c;

        char qualityfb[100];
        unsigned i,limit;
        static  double s2n_avg;
        static  double s2n_stddev;

        dcdshreg = (dcdshreg << 2) | bit;

        switch (dcdshreg) {
        case 0xAAAAAAAA:        /* DCD on by preamble */
                dcd = true;
                acquire = 0;
                quality = complex (1.0, 0.0);
                imdValid = true;
//VK2ETA signal report
                s2n_sum = s2n_sum2 = s2n_ncount = 0;
                break;

        case 0:                 /* DCD off by postamble */
//VK2ETA don't cut too soon, we need to get the rest of the decoding
                dcd = false;
//              dcd = true;

                acquire = 0;
//              quality = complex (0.0, 0.0);
//              imdValid = false;
                break;
        }
        
        shreg = (shreg << 1) | !!bit;
        if ((shreg & 3) == 0) {
                c = psk_varicode_decode(shreg >> 2);
                if ((fecmet >= fecmet2) || (_qpsk == false)) {
                        if ((c != -1) && (dcd == true)) {
//                              put_rx_char('<');
                                put_rx_char(c);
                        }
                } 
                shreg = 0;
        }
}




void psk::rx_bit2(int bit)
{
        int c;

        dcdshreg2 = (dcdshreg2 << 2) | bit;

        switch (dcdshreg2) {
        case 0xAAAAAAAA:        /* DCD on by preamble */
                dcd = true;
                acquire = 0;
                quality = complex (1.0, 0.0);
                imdValid = true;
//VK2ETA signal report
                s2n_sum = s2n_sum2 = s2n_ncount = 0;
                break;

        case 0:                 /* DCD off by postamble */
//VK2ETA don't cut too soon, we need to get the rest of the decoding
                dcd = false;
//              dcd = true;

                acquire = 0;
//              quality = complex (0.0, 0.0);
//              imdValid = false;
                break;
        }

        shreg2 = (shreg2 << 1) | !!bit;
        if ((shreg2 & 3) == 0) {
                c = psk_varicode_decode(shreg2 >> 2);
                if (fecmet < fecmet2) {
                        if ((c != -1) && (dcd == true)) {
//                              put_rx_char('>');
                                put_rx_char(c);
                        }
                } 
                shreg2 = 0;
        }
}





void psk::rx_qpsk(int bits)
{
        int met;
        unsigned int rbits;
        int c;

//Accumulate the bits for the interleaver THEN submit to Viterbi decoder
                rxbitshreg2 = (rxbitshreg2 << 1) | (bits & 1);
                rxbitshreg = (rxbitshreg << 1) | (bits & 1);
                if (rxbitstate == 0) {
                        rxbitstate++;
                        //De-interleave
                        Rxinlv2->bits(&rxbitshreg2);
                        firstbit = false;
                        for (int i = 1; i >-1; i--) {
                                rbits = (rxbitshreg2 & (1 << i)) >> i;
                                if (firstbit) {
                                        symP1[0] = (rbits & 1) ? 255 : 0;
                                } else {
                                        symP1[1] = (rbits & 1) ? 255 : 0;
                                        //Then viterbi decoder
                                        c = dec2->decode(symP1, &met);
                                        if (c != -1) {
                                                //VK2ETA only take metric 
measurement after backtrace
                                                fecmet2 = decayavg(fecmet2, 
met, 10);
                                                rx_bit2(c & 0x08);
                                                rx_bit2(c & 0x04);
                                                rx_bit2(c & 0x02);
                                                rx_bit2(c & 0x01);
                                        }
                                }       

                                firstbit = ! firstbit;
                        }
                        rxbitshreg2 = 0;
                } else {
                        rxbitstate = 0;
                        //De-interleave
                        Rxinlv->bits(&rxbitshreg);
                        firstbit = false;
                        for (int i = 1; i >-1; i--) {
                                rbits = (rxbitshreg & (1 << i)) >> i;           
        
                                if (firstbit) {
                                        symcur[0] = (rbits & 1) ? 255 : 0;
                                } else {
                                        symcur[1] = (rbits & 1) ? 255 : 0;
                                        //Then viterbi decoder
                                        c = dec->decode(symcur, &met);
                                        if (c != -1) {
                                                //VK2ETA only take metric 
measurement after backtrace
                                                fecmet = decayavg(fecmet, met, 
10);
                                                rx_bit(c & 0x08);
                                                rx_bit(c & 0x04);
                                                rx_bit(c & 0x02);
                                                rx_bit(c & 0x01);
                                        }
                                }       

                                firstbit = ! firstbit;
                        }
                        rxbitshreg = 0;
                }
}

void psk::searchDown()
{
        double srchfreq = frequency - bandwidth * 2;
        double minfreq = bandwidth * 2;
        double spwr, npwr;
        while (srchfreq > minfreq) {
                spwr = wf->powerDensity(srchfreq, bandwidth);
                npwr = wf->powerDensity(srchfreq + bandwidth, bandwidth/2) + 
1e-10;
                if (spwr / npwr > pow(10, progdefaults.ServerACQsn / 10)) {
                        frequency = srchfreq;
                        set_freq(frequency);
                        sigsearch = SIGSEARCH;
                        break;
                }
                srchfreq -= bandwidth;
        }
}

void psk::searchUp()
{
        double srchfreq = frequency + bandwidth * 2;
        double maxfreq = IMAGE_WIDTH - bandwidth * 2;
        double spwr, npwr;
        while (srchfreq < maxfreq) {
                spwr = wf->powerDensity(srchfreq, bandwidth/2);
                npwr = wf->powerDensity(srchfreq - bandwidth, bandwidth/2) + 
1e-10;
                if (spwr / npwr > pow(10, progdefaults.ServerACQsn / 10)) {
                        frequency = srchfreq;
                        set_freq(frequency);
                        sigsearch = SIGSEARCH;
                        break;
                }
                srchfreq += bandwidth;
        }
}

int waitcount = 0;

void psk::findsignal()
{
        int ftest, f1, f2;
        
        if (sigsearch > 0) {
                sigsearch--;
                if (mailserver) { // mail server search algorithm
                        if (progdefaults.PSKmailSweetSpot) {
                                f1 = (int)(progdefaults.ServerCarrier - 
progdefaults.ServerOffset);
                                f2 = (int)(progdefaults.ServerCarrier + 
progdefaults.ServerOffset);
                        } else {
                                f1 = (int)(frequency - 
progdefaults.ServerOffset);
                                f2 = (int)(frequency + 
progdefaults.ServerOffset);
                        }
                        if (evalpsk->sigpeak(ftest, f1, f2) > pow(10, 
progdefaults.ServerACQsn / 10) ) {
                                if (progdefaults.PSKmailSweetSpot) {
                                        if (fabs(ftest - 
progdefaults.ServerCarrier) < progdefaults.ServerOffset) {
                                                frequency = ftest;
                                                set_freq(frequency);
                                                freqerr = 0.0;
                                        } else {
                                                frequency = 
progdefaults.ServerCarrier;
                                                set_freq(frequency);
                                                freqerr = 0.0;
                                        }
                                } else {
                                        frequency = ftest;
                                        set_freq(frequency);
                                        freqerr = 0.0;
                                }
                        } else { // less than the detection threshold
                                if (progdefaults.PSKmailSweetSpot) {
                                        frequency = progdefaults.ServerCarrier;
                                        set_freq(frequency);
                                        sigsearch = SIGSEARCH;
                                }
                        }
                } else { // normal signal search algorithm
                        f1 = (int)(frequency - progdefaults.SearchRange/2);
                        f2 = (int)(frequency + progdefaults.SearchRange/2);
                        if (evalpsk->sigpeak(ftest, f1, f2) > pow(10, 
progdefaults.ACQsn / 10.0) ) {
                                frequency = ftest;
                                set_freq(frequency);
                                freqerr = 0.0;
                                sigsearch = 0;
                                acquire = dcdbits;
                        }
                }
        }
}

void psk::phaseafc()
{
        double error;
        if (afcmetric < 0.05) return;
        
        error = (phase - bits * M_PI / 2.0);
        if (error < -M_PI / 2.0 || error > M_PI / 2.0) return;
        error *= samplerate / (TWOPI * symbollen);
        if (fabs(error) < bandwidth ) {
                freqerr = error / dcdbits;
                frequency -= freqerr;
                if (mailserver) {
                        if (frequency < progdefaults.ServerCarrier - 
progdefaults.ServerAFCrange)
                                frequency = progdefaults.ServerCarrier - 
progdefaults.ServerAFCrange;
                        if (frequency > progdefaults.ServerCarrier + 
progdefaults.ServerAFCrange)
                                frequency = progdefaults.ServerCarrier + 
progdefaults.ServerAFCrange;
                }
                set_freq (frequency);
        }
        if (acquire) acquire--;
}

void psk::afc()
{
        if (!progStatus.afconoff)
                return;
        if (dcd == true || acquire)
                phaseafc();
}


void psk::rx_symbol(complex symbol)
{
        int n;
        phase = (prevsymbol % symbol).arg();
        prevsymbol = symbol;

        if (phase < 0) 
                phase += TWOPI;
//VK2ETA BPSK+FEC
//      if (_qpsk) {
//              bits = ((int) (phase / M_PI_2 + 0.5)) & 3;
//              n = 4;
//      } else {
                bits = (((int) (phase / M_PI + 0.5)) & 1) << 1;
                n = 2;
//      }
// simple low pass filter for quality of signal
        quality.re = decayavg(quality.re, cos(n*phase), SQLDECAY);
        quality.im = decayavg(quality.im, sin(n*phase), SQLDECAY);
        
        metric = 100.0 * quality.norm();

//VK2ETA FEC: adjust squelch for extra sensitivity
        if (_qpsk) {
                metric = metric * 2.5;
                if (metric > 100)    metric = 100;
        }

        afcmetric = decayavg(afcmetric, quality.norm(), 50);

        if (metric > progStatus.sldrSquelchValue || progStatus.sqlonoff == 
false) {
                dcd = true;
        } else {
                dcd = false;
//VK2ETA   was it     supposed to be here       instead:        imdValid = 
false;
        }
                imdValid = false;

//VK2ETA FEC: moved below the rx_bit to used proper value for dcd
//      set_phase(phase, quality.norm(), dcd);

//VK2ETA FEC   always process, then decide to print characters later
//      if (dcd == true) {

                if (_qpsk)
//VK2ETA (reverse the bit for FEC too                   rx_qpsk(bits);
                        rx_qpsk(!bits);
                else
                        rx_bit(!bits);
//      }

        set_phase(phase, quality.norm(), dcd);

        
}

void psk::signalquality()
{ 

        if (m_Energy[1])
                snratio = snfilt->run(m_Energy[0]/m_Energy[1]);
        else
                snratio = snfilt->run(1.0);

        if (m_Energy[0] && imdValid)
                imdratio = imdfilt->run(m_Energy[2]/m_Energy[0]);
        else
                imdratio = imdfilt->run(0.001);

}

void psk::update_syncscope()
{
        static char msg1[15];
        static char msg2[15];

        display_metric(metric);

        s2n = 10.0*log10( snratio );
        snprintf(msg1, sizeof(msg1), "s/n %2d dB", (int)(floor(s2n))); 
        
        imd = 10.0*log10( imdratio );
        snprintf(msg2, sizeof(msg2), "imd %3d dB", (int)(floor(imd))); 

        if (imdValid) {
                put_Status1(msg1, progdefaults.StatusTimeout, 
progdefaults.StatusDim ? STATUS_DIM : STATUS_CLEAR);
                put_Status2(msg2, progdefaults.StatusTimeout, 
progdefaults.StatusDim ? STATUS_DIM : STATUS_CLEAR);
        }
}

char bitstatus[100];

int psk::rx_process(const double *buf, int len)
{
        double delta;
        complex z, z2;

        if (pskviewer && !bHistory) pskviewer->rx_process(buf, len);
        if (evalpsk) evalpsk->sigdensity();
                
        delta = TWOPI * frequency / samplerate;
        
        while (len-- > 0) {
// Mix with the internal NCO
                z = complex ( *buf * cos(phaseacc), *buf * sin(phaseacc) );

                buf++;
                phaseacc += delta;
                if (phaseacc > M_PI)
                        phaseacc -= TWOPI;

// Filter and downsample 
// by 16 (psk31, qpsk31) 
// by  8 (psk63, qpsk63)
// by  4 (psk125, qpsk125)
// by  2 (psk250, qpsk250)
// first filter
                if (fir1->run( z, z )) { // fir1 returns true every Nth sample
// final filter
                        fir2->run( z, z2 ); // fir2 returns value on every 
sample
                        calcSN_IMD(z);
                        
//                      fir3->run( z, z3);
//                      coreafc(z3);
                                                
                        int idx = (int) bitclk;
                        double sum = 0.0;
                        double ampsum = 0.0;
                        syncbuf[idx] = 0.8 * syncbuf[idx] + 0.2 * z2.mag();
                        
                        for (int i = 0; i < 8; i++) {
                                sum += (syncbuf[i] - syncbuf[i+8]);
                                ampsum += (syncbuf[i] + syncbuf[i+8]);
                        }
// added correction as per PocketDigi
                        sum = (ampsum == 0 ? 0 : sum / ampsum);
                        
                        bitclk -= sum / 5.0;
                        bitclk += 1;
                        
                        if (bitclk < 0) bitclk += 16.0;
                        if (bitclk >= 16.0) {
                                bitclk -= 16.0;
                                rx_symbol(z2);
                                update_syncscope();
                                afc();
                        }
                }
        }
        
        if (sigsearch)
                findsignal();
        else if (mailserver) {
                if (waitcount > 0) {
                        --waitcount;
                        if (waitcount == 0) {
                                if (progdefaults.PSKmailSweetSpot) {
                                        frequency = progdefaults.PSKsweetspot;
                                        set_freq(frequency);
                                }                               
                                sigsearch = SIGSEARCH;
                        }
                }
                else if ( E1/ E2 <= 1.0) { //(snratio <= 1.0) {
                        waitcount = 8;
                        sigsearch = 0;
                }
        }
        return 0;
}

//=====================================================================
// transmit processes
//=====================================================================

void psk::tx_symbol(int sym)
{
        double delta;
        double  ival, qval, shapeA, shapeB;
        complex symbol;

//VK2ETA BPSK+FEC
//      if (_qpsk && !reverse)
//              sym = (4 - sym) & 3;

        /* differential QPSK modulation - top bit flipped */
        switch (sym) {
        case 0:
                symbol = complex (-1.0, 0.0);   // 180 degrees
                break;
        case 1:
                symbol = complex (0.0, -1.0);   // 270 degrees
                break;
        case 2:
                symbol = complex (1.0, 0.0);            //   0 degrees
                break;
        case 3:
                symbol = complex (0.0, 1.0);            //  90 degrees
                break;
        }
        symbol = prevsymbol * symbol;   // complex multiplication

        delta = 2.0 * M_PI * get_txfreq_woffset() / samplerate;

        for (int i = 0; i < symbollen; i++) {
                
                shapeA = tx_shape[i];
                shapeB = (1.0 - shapeA);
                
                ival = shapeA * prevsymbol.real() + shapeB * symbol.real();
                qval = shapeA * prevsymbol.imag() + shapeB * symbol.imag();
                
                outbuf[i] = ival * cos(phaseacc) + qval * sin(phaseacc);
                
                phaseacc += delta;
                if (phaseacc > M_PI)
                        phaseacc -= 2.0 * M_PI;
        }

        ModulateXmtr(outbuf, symbollen);

        prevsymbol = symbol;
}

void psk::tx_bit(int bit)
{
        unsigned int sym;

//VK2ETA PSK+FEC Send encoded bit(s) (result is two bits to send sequencially 
since R = 1/2)

        if (_qpsk) {

//Encode into two bits
                bitshreg = enc->encode(bit);

//pass through interleaver
                Txinlv->bits(&bitshreg);

//Send low bit first
                sym = (bitshreg & 1) << 1;
                tx_symbol(sym);
                sym = bitshreg & 2;
                tx_symbol(sym);


        } else {
                sym = bit << 1;
                tx_symbol(sym);
        }
}



void psk::tx_char(unsigned char c)
{
        const char *code;

        code = psk_varicode_encode(c);

        while (*code) {
                tx_bit((*code - '0'));
                code++;
        }
        tx_bit(0);
        tx_bit(0);
}

void psk::tx_flush()
{
// flush the encoder (QPSK only)
        if (_qpsk) {
                //VK2ETA FEC : replace unmodulated carrier by an encoded 
sequence of zeros
                for (int i = 0; i < dcdbits; i++)
                tx_bit(0);
        } else {

// DCD off sequence (unmodulated carrier)
        for (int i = 0; i < dcdbits; i++)
                tx_symbol(2);
        }
}


void psk::clearbits()
{
        bitshreg = enc->encode(0);
        for (int k = 0; k < 100; k++) {
                Txinlv->bits(&bitshreg);
        }
}



int psk::tx_process()
{
        int c;

        if (preamble > 0) {
                if (_qpsk) {
                        if (startpreamble == true) {
                                clearbits();
                                startpreamble = false;
                        }       
//VK2ETA FEC prep the encoder with one/zero sequences of bits
                        preamble--;
                        preamble--;
                        tx_bit(1);
                        tx_bit(0);
//VK2ETA FEC Mark start of first character with a double zero
                        if (preamble == 0)  tx_bit(0);
                        return 0;
                } else{
                        preamble--;
                        tx_symbol(0);   /* send phase reversals */
                        return 0;
                }
        }

        c = get_tx_char();

        if (c == 0x03 || stopflag) {
                tx_flush();
                stopflag = false;
                cwid();
                return -1;      /* we're done */
        }

        if (c == -1)
                tx_bit(0);
        else {
                tx_char(c);
                put_echo_char(c);
        }
        return 0;
}

//============================================================================
// psk signal evaluation
// using Goertzel IIR filter
// derived from pskcore by Moe Wheatley, AE4JY
//============================================================================

void psk::initSN_IMD()
{
        for(int i = 0; i < NUM_FILTERS; i++)
        {
                I1[i] = I2[i] = Q1[i] = Q2[i] = 0.0;
                m_Energy[i] = 0.0;
        }
        m_NCount = 0;
        
        COEF[0] = 2.0 * cos(TWOPI * 9 / GOERTZEL);
        COEF[1] = 2.0 * cos(TWOPI * 18 / GOERTZEL);
        COEF[2] = 2.0 * cos(TWOPI  * 27 / GOERTZEL);
}

void psk::resetSN_IMD()
{
        for(int i = 0; i < NUM_FILTERS; i++) {
                I1[i] = I2[i] = Q1[i] = Q2[i] = 0.0;
        }
        m_NCount = 0;
}

//============================================================================
//  This routine calculates the energy in the frequency bands of
//   carrier=F0(15.625), noise=F1(31.25), and 
//   3rd order product=F2(46.875)
//  It is called with complex data samples at 500 Hz.
//============================================================================

void psk::calcSN_IMD(complex z)
{
        int i;
        complex temp;

        for(i = 0; i < NUM_FILTERS; i++) {
                temp.re = I1[i]; temp.im = Q1[i];
                I1[i] = I1[i] * COEF[i]- I2[i] + z.re;
                Q1[i] = Q1[i] * COEF[i]- Q2[i] + z.im;
                I2[i] = temp.re; Q2[i] = temp.im;
        }

        if( ++m_NCount >= GOERTZEL ) {
                m_NCount = 0;
                for(i = 0; i < NUM_FILTERS; i++) {
                        m_Energy[i] =   I1[i]*I1[i] + Q1[i]*Q1[i] 
                                      + I2[i]*I2[i] + Q2[i]*Q2[i] 
                                                  - I1[i]*I2[i]*COEF[i]
                                                  - Q1[i]*Q2[i]*COEF[i];
                        I1[i] = I2[i] = Q1[i] = Q2[i] = 0.0;
                }
                signalquality();
        }
}
// ----------------------------------------------------------------------------
// interleave.cxx  --  MFSK (de)interleaver
//
// Copyright (C) 2006
//              Dave Freese, W1HKJ
//
// This file is part of fldigi.  Adapted from code contained in gmfsk source 
code 
// distribution.
//  gmfsk Copyright (C) 2001, 2002, 2003
//  Tomi Manninen (oh2bns@xxxxxxx)
//
// fldigi 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.
//
// fldigi 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 fldigi; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
// ----------------------------------------------------------------------------

#include <config.h>

#include <cstring>

#include "interleave.h"

// ---------------------------------------------------------------------- 

interleave::interleave (int _size, int dir)
{
        size = _size;
        if (size == -1) { // dominoEX interleaver
                size = 4;
                depth = 4;
        } else if (size == -220) { // BPSK FEC + Interleaver 2x2x20
                size = 2;
                depth = 20;
        } else if (size == -240) { // BPSK FEC + Interleaver 2x2x40
                size = 2;
                depth = 40;
        } else if (size == -280) { // BPSK FEC + Interleaver 2x2x80
                size = 2;
                depth = 80;
        } else if (size == -2160) { // BPSK FEC + Interleaver 2x2x160
                size = 2;
                depth = 160;
        } else if (size == 5)
                depth = 5;
        else
                depth = 10;
        direction = dir;
        table = new unsigned char [depth * size * size];
        memset(table, 0, depth * size * size);
}

interleave::~interleave ()
{
        delete [] table;
}


void interleave::symbols(unsigned char *psyms)
{
        int i, j, k;

        for (k = 0; k < depth; k++) {
                for (i = 0; i < size; i++)
                        for (j = 0; j < size - 1; j++)
                                *tab(k, i, j) = *tab(k, i, j + 1);

                for (i = 0; i < size; i++)
                        *tab(k, i, size - 1) = psyms[i];

                for (i = 0; i < size; i++) {
                        if (direction == INTERLEAVE_FWD)
                                psyms[i] = *tab(k, i, size - i - 1);
                        else
                                psyms[i] = *tab(k, i, i);
                }
        }
}

void interleave::bits(unsigned int *pbits)
{
        unsigned char syms[size];
        int i;

        for (i = 0; i < size; i++)
                syms[i] = (*pbits >> (size - i - 1)) & 1;

        symbols(syms);

        for (*pbits = i = 0; i < size; i++)
                *pbits = (*pbits << 1) | syms[i];
}

// ---------------------------------------------------------------------- 

Other related posts:

  • » [pskmail] FEC + longer and variable Interleaver for PSK modes - John Douyere