Hi All, Here are the new psk.h and psk.cxx files to correct the issue with the standard psk modes missing the first character. It also improves the display when idling. Rein, Regarding the bandwidth, I don't believe it is worse than for the stock BPSK500. What we see in the preamble is the same distribution of energy between the centre lines and the outer lines. If you transmit a long data sequence in either version (with/without FEC) you will see that the distribution of energy does not stop at the bandwidth bars (the red lines), and are the same regardless of FEC or not. The preamble colours with FEC mirror that distribution with the centre lines being white while the outer lines of the same blue as the data section of the waterfall. I know it is not an accurate spectrum analysis but it is fairly representative. I re-checked the code and I absolutely use the same symbol transmission as the standard BPSK, same envelope, same symbol inversion from one symbol to the next, so I can't see why the bandwidth would be different. On the W1hkj site, the PSK250 mode is marked as 500Hz according to the ITU definition, so I don't think PSK500 will fit in that bandwidth. Sorry, you may have to move to Australia, we can have 2Khz for digital modes or even voice on 30M ;). Best regards, John On Mon, Nov 23, 2009 at 10:39 PM, Rein Couperus <rein@xxxxxxxxxxxx> wrote: > John, > > I have been testing the FEC modes on-air, on PI4TUE. I have found the > following issues: > > * The FEC modes are definitely a lot more robust than the raw PSK modes, so > you are > on the right track! > * Using FEC modes only, the auto speed switch works very well. > > * The PSK500 FEC mode is too wide. It grabs 700 Hz bandwidth, which means we > can not use it as is > on 30 meters (only 500 Hz bandwidth allowed in Region 1). > Part of the problem is the preamble, which looks very hard-switched... The > 'soft' bandwidth limiting nature > of PSK is completely gone, and we are bound to get trouble from neighbouring > channels when we leave it like this. > On the other hand, this preamble forces sync immediately. > > * I have been testing an auto-switching combination of PSK250 FEC and PSK500 > raw... > The raw PSK500 signal decoded only 20% of the time because the <SOH> > character at the > start of a block was not decoded, an extra preamble of 'XX' did not help. > Looks like the raw signal decoding changed somewhat.. did > you test both raw and FEC sigs? I think PSK500 raw as the highest speed is > important, being 4x faster than PSK250FEC... > > Needs some tuning, but the results are already impressive :) > > 73, > > Rein PA0R > > > > >> -----Ursprüngliche Nachricht----- >> Von: "John Douyere" <vk2eta@xxxxxxxxx> >> Gesendet: 23.11.09 07:34:03 >> An: pskmail@xxxxxxxxxxxxx >> Betreff: [pskmail] Improved FEC for PSK modes > > >> Hi all, >> >> I have done more work on the FEC option for BPSK modes: >> >> I added a second Viterbi decoder shifted by one bit and compare the >> output of both, then I select the output of the one with the less >> errors. >> >> Cpu load has not increased significantly, but re-synchronisation is >> now fast (typically one to 3 characters). >> >> I have also changed the way we send the preamble and postamble for >> PSK: I pass both through the encoder/decoder as this gives the chance >> for the encoder and decoder to be fully synchronized before the frame >> data and to flush out the remaining characters at the end of the frame >> too. This gives a different sound to the preamble and postamble of >> this mode compared to the traditional BPSK modes. >> >> I am still doing hard decoding. >> >> The result seems good based on my tests today. Still not the >> robustness of the THOR or MFSK for lighting strikes, but much better >> at handling white noise and regular qrm it seems. >> >> In my tests, most of the time I could get better results than the BPSK >> mode one speed below, which is a fair comparison in terms of >> throughput. And certainly much better for the same BPSK mode without >> FEC. >> >> PSK500 with FEC gives good results. I even did some tests mobile and >> got good copies when generally the quick fades are not very kind to >> the PSK modes. >> >> I feel that version is really worth a try. It could give us a nice >> speed downgrade/reliability increase for the automatic mode changes. >> >> Attached are two files: psk.h in the src/include directory and psk.cxx >> in the src/psk directory. These should work with any source of Fldigi >> from at least 3.13AY onwards. >> >> To access these modes, use the QPSK modes on the Fldigi gui, with the >> exception of QPSK31 which is PSK500+FEC. This is of course a temporary >> and quick solution since we do not use the QPSK modes in Pskmail. >> >> I will also do some comparisons with the Multipsk implementation of >> these FEC modes to see if I am not missing something on the coding >> gain. >> >> Please let me know of your results if you can test. >> >> Best regards, >> >> John (VK2ETA) >> >> > > -- > http://pa0r.blogspirit.com > >
// ---------------------------------------------------------------------------- // 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" //===================================================================== #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; // 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 for comparison viterbi *dec2; int fecmet; int fecmet2; unsigned char symcur[2]; unsigned char symP1[2]; int c1; int c2; int savedc2; 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 for debugging 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; int lastdoublezero; // 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); 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 FEC symcur[0] = symcur[1] = 0; symP1[0] = symP1[1] = 0; } void psk::rx_init() { phaseacc = 0; prevsymbol = complex (1.0, 0.0); quality = complex (0.0, 0.0); shreg = 0; shreg2 = 0; dcdshreg = 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 FEC symcur[0] = symcur[1] = 0; symP1[0] = symP1[1] = 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; 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; } 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; //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; //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; //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; //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 for comparison dec2 = (viterbi *)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); firstbit = 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; if (firstbit) { symP1[1] = symcur[0] = (bits & 1) ? 255 : 0; c2 = dec2->decode(symP1, &met); if (c2 != -1) fecmet2 = decayavg(fecmet2, met, 10); if (c2 != -1) { // rx_bit2(c2 & 0x80); // rx_bit2(c2 & 0x40); // rx_bit2(c2 & 0x20); // rx_bit2(c2 & 0x10); rx_bit2(c2 & 0x08); rx_bit2(c2 & 0x04); rx_bit2(c2 & 0x02); rx_bit2(c2 & 0x01); } } else { symP1[0] = symcur[1] = (bits & 1) ? 255 : 0; c1 = dec->decode(symcur, &met); if (c1 != -1) fecmet = decayavg(fecmet, met, 10); if (c1 != -1) { // rx_bit(c1 & 0x80); // rx_bit(c1 & 0x40); // rx_bit(c1 & 0x20); // rx_bit(c1 & 0x10); rx_bit(c1 & 0x08); rx_bit(c1 & 0x04); rx_bit(c1 & 0x02); rx_bit(c1 & 0x01); } } firstbit = ! firstbit; } 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(); if (_qpsk) { metric = metric * 2; 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) unsigned int savedsym; if (_qpsk) { savedsym = enc->encode(bit); sym = (savedsym & 1) << 1; tx_symbol(sym); sym = savedsym & 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) { for (int i = 0; i < dcdbits; i++) tx_bit(0); //VK2ETA FEC : replace unmodulated carrier by an encoded sequence of zeros } else { // DCD off sequence (unmodulated carrier) for (int i = 0; i < dcdbits; i++) tx_symbol(2); } } int psk::tx_process() { int c; if (preamble > 0) { if (_qpsk) { //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(); } }