mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-11-03 21:20:31 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			746 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			746 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
///////////////////////////////////////////////////////////////////////////////////
 | 
						|
// Copyright (C) 2019-2020 Edouard Griffiths, F4EXB <f4exb06@gmail.com>          //
 | 
						|
//                                                                               //
 | 
						|
// 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 as version 3 of the License, or                  //
 | 
						|
// (at your option) any later version.                                           //
 | 
						|
//                                                                               //
 | 
						|
// This program is distributed in the hope that it will be useful,               //
 | 
						|
// but WITHOUT ANY WARRANTY; without even the implied warranty of                //
 | 
						|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                  //
 | 
						|
// GNU General Public License V3 for more details.                               //
 | 
						|
//                                                                               //
 | 
						|
// You should have received a copy of the GNU General Public License             //
 | 
						|
// along with this program. If not, see <http://www.gnu.org/licenses/>.          //
 | 
						|
///////////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
#include <QTime>
 | 
						|
#include <QDebug>
 | 
						|
#include <stdio.h>
 | 
						|
 | 
						|
#include "dsp/dsptypes.h"
 | 
						|
#include "dsp/basebandsamplesink.h"
 | 
						|
#include "dsp/dspengine.h"
 | 
						|
#include "dsp/fftfactory.h"
 | 
						|
#include "dsp/fftengine.h"
 | 
						|
#include "util/db.h"
 | 
						|
 | 
						|
#include "chirpchatdemodmsg.h"
 | 
						|
#include "chirpchatdemodsink.h"
 | 
						|
 | 
						|
ChirpChatDemodSink::ChirpChatDemodSink() :
 | 
						|
    m_decodeMsg(nullptr),
 | 
						|
    m_decoderMsgQueue(nullptr),
 | 
						|
    m_fftSequence(-1),
 | 
						|
    m_fftSFDSequence(-1),
 | 
						|
    m_downChirps(nullptr),
 | 
						|
    m_upChirps(nullptr),
 | 
						|
    m_spectrumLine(nullptr),
 | 
						|
    m_spectrumSink(nullptr),
 | 
						|
    m_spectrumBuffer(nullptr)
 | 
						|
{
 | 
						|
    m_demodActive = false;
 | 
						|
	m_bandwidth = ChirpChatDemodSettings::bandwidths[0];
 | 
						|
	m_channelSampleRate = 96000;
 | 
						|
	m_channelFrequencyOffset = 0;
 | 
						|
	m_nco.setFreq(m_channelFrequencyOffset, m_channelSampleRate);
 | 
						|
	m_interpolator.create(16, m_channelSampleRate, m_bandwidth / 1.9f);
 | 
						|
	m_interpolatorDistance = (Real) m_channelSampleRate / (Real) m_bandwidth;
 | 
						|
    m_sampleDistanceRemain = 0;
 | 
						|
 | 
						|
    m_state = ChirpChatStateReset;
 | 
						|
	m_chirp = 0;
 | 
						|
	m_chirp0 = 0;
 | 
						|
 | 
						|
    initSF(m_settings.m_spreadFactor, m_settings.m_deBits, m_settings.m_fftWindow);
 | 
						|
}
 | 
						|
 | 
						|
ChirpChatDemodSink::~ChirpChatDemodSink()
 | 
						|
{
 | 
						|
    FFTFactory *fftFactory = DSPEngine::instance()->getFFTFactory();
 | 
						|
 | 
						|
    if (m_fftSequence >= 0)
 | 
						|
    {
 | 
						|
        fftFactory->releaseEngine(m_interpolatedFFTLength, false, m_fftSequence);
 | 
						|
        fftFactory->releaseEngine(m_interpolatedFFTLength, false, m_fftSFDSequence);
 | 
						|
    }
 | 
						|
 | 
						|
    delete[] m_downChirps;
 | 
						|
    delete[] m_upChirps;
 | 
						|
    delete[] m_spectrumBuffer;
 | 
						|
    delete[] m_spectrumLine;
 | 
						|
}
 | 
						|
 | 
						|
void ChirpChatDemodSink::initSF(unsigned int sf, unsigned int deBits, FFTWindow::Function fftWindow)
 | 
						|
{
 | 
						|
    if (m_downChirps) {
 | 
						|
        delete[] m_downChirps;
 | 
						|
    }
 | 
						|
    if (m_upChirps) {
 | 
						|
        delete[] m_upChirps;
 | 
						|
    }
 | 
						|
    if (m_spectrumBuffer) {
 | 
						|
        delete[] m_spectrumBuffer;
 | 
						|
    }
 | 
						|
    if (m_spectrumLine) {
 | 
						|
        delete[] m_spectrumLine;
 | 
						|
    }
 | 
						|
 | 
						|
    FFTFactory *fftFactory = DSPEngine::instance()->getFFTFactory();
 | 
						|
 | 
						|
    if (m_fftSequence >= 0)
 | 
						|
    {
 | 
						|
        fftFactory->releaseEngine(m_interpolatedFFTLength, false, m_fftSequence);
 | 
						|
        fftFactory->releaseEngine(m_interpolatedFFTLength, false, m_fftSFDSequence);
 | 
						|
    }
 | 
						|
 | 
						|
    m_nbSymbols = 1 << sf;
 | 
						|
    m_nbSymbolsEff = 1 << (sf - deBits);
 | 
						|
    m_deLength = 1 << deBits;
 | 
						|
    m_fftLength = m_nbSymbols;
 | 
						|
    m_fftWindow.create(fftWindow, m_fftLength);
 | 
						|
    m_fftWindow.setKaiserAlpha(M_PI);
 | 
						|
    m_interpolatedFFTLength = m_fftInterpolation*m_fftLength;
 | 
						|
    m_preambleTolerance = (m_deLength*m_fftInterpolation)/2;
 | 
						|
    m_fftSequence = fftFactory->getEngine(m_interpolatedFFTLength, false, &m_fft);
 | 
						|
    m_fftSFDSequence = fftFactory->getEngine(m_interpolatedFFTLength, false, &m_fftSFD);
 | 
						|
    m_state = ChirpChatStateReset;
 | 
						|
    m_sfdSkip = m_fftLength / 4;
 | 
						|
    m_downChirps = new Complex[2*m_nbSymbols]; // Each table is 2 chirps long to allow processing from arbitrary offsets.
 | 
						|
    m_upChirps = new Complex[2*m_nbSymbols];
 | 
						|
    m_spectrumBuffer = new Complex[m_nbSymbols];
 | 
						|
    m_spectrumLine = new Complex[m_nbSymbols];
 | 
						|
    std::fill(m_spectrumLine, m_spectrumLine+m_nbSymbols, Complex(std::polar(1e-6*SDR_RX_SCALED, 0.0)));
 | 
						|
 | 
						|
    float halfAngle = M_PI;
 | 
						|
    float phase = -halfAngle;
 | 
						|
    double accumulator = 0;
 | 
						|
 | 
						|
    for (unsigned int i = 0; i < m_fftLength; i++)
 | 
						|
    {
 | 
						|
        accumulator = fmod(accumulator + phase, 2*M_PI);
 | 
						|
        m_downChirps[i] = Complex(std::conj(std::polar(1.0, accumulator)));
 | 
						|
        m_upChirps[i] = Complex(std::polar(1.0, accumulator));
 | 
						|
        phase += (2*halfAngle) / m_nbSymbols;
 | 
						|
    }
 | 
						|
 | 
						|
    // Duplicate table to allow processing from arbitrary offsets
 | 
						|
    std::copy(m_downChirps, m_downChirps+m_fftLength, m_downChirps+m_fftLength);
 | 
						|
    std::copy(m_upChirps, m_upChirps+m_fftLength, m_upChirps+m_fftLength);
 | 
						|
}
 | 
						|
 | 
						|
void ChirpChatDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
 | 
						|
{
 | 
						|
	Complex ci;
 | 
						|
 | 
						|
	for (SampleVector::const_iterator it = begin; it < end; ++it)
 | 
						|
	{
 | 
						|
		Complex c(it->real() / SDR_RX_SCALEF, it->imag() / SDR_RX_SCALEF);
 | 
						|
		c *= m_nco.nextIQ();
 | 
						|
 | 
						|
		if (m_interpolator.decimate(&m_sampleDistanceRemain, c, &ci))
 | 
						|
		{
 | 
						|
            processSample(ci);
 | 
						|
			m_sampleDistanceRemain += m_interpolatorDistance;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void ChirpChatDemodSink::processSample(const Complex& ci)
 | 
						|
{
 | 
						|
    if (m_state == ChirpChatStateReset) // start over
 | 
						|
    {
 | 
						|
        m_demodActive = false;
 | 
						|
        reset();
 | 
						|
        std::queue<double>().swap(m_magsqQueue); // this clears the queue
 | 
						|
        m_state = ChirpChatStateDetectPreamble;
 | 
						|
    }
 | 
						|
    else if (m_state == ChirpChatStateDetectPreamble) // look for preamble
 | 
						|
    {
 | 
						|
        m_fft->in()[m_fftCounter++] = ci * m_downChirps[m_chirp]; // de-chirp the up ramp
 | 
						|
 | 
						|
        if (m_fftCounter == m_fftLength)
 | 
						|
        {
 | 
						|
            m_fftWindow.apply(m_fft->in());
 | 
						|
            std::fill(m_fft->in()+m_fftLength, m_fft->in()+m_interpolatedFFTLength, Complex{0.0, 0.0});
 | 
						|
            m_fft->transform();
 | 
						|
            m_fftCounter = 0;
 | 
						|
            double magsq, magsqTotal;
 | 
						|
 | 
						|
            unsigned int imax = argmax(
 | 
						|
                m_fft->out(),
 | 
						|
                m_fftInterpolation,
 | 
						|
                m_fftLength,
 | 
						|
                magsq,
 | 
						|
                magsqTotal,
 | 
						|
                m_spectrumBuffer,
 | 
						|
                m_fftInterpolation
 | 
						|
            ) / m_fftInterpolation;
 | 
						|
 | 
						|
            if (m_magsqQueue.size() > m_settings.m_preambleChirps) {
 | 
						|
                m_magsqQueue.pop();
 | 
						|
            }
 | 
						|
 | 
						|
            m_magsqTotalAvg(magsqTotal);
 | 
						|
            m_magsqQueue.push(magsq);
 | 
						|
            m_argMaxHistory[m_argMaxHistoryCounter++] = imax;
 | 
						|
 | 
						|
            if (m_argMaxHistoryCounter == m_requiredPreambleChirps)
 | 
						|
            {
 | 
						|
                m_argMaxHistoryCounter = 0;
 | 
						|
                bool preambleFound = true;
 | 
						|
 | 
						|
                for (unsigned int i = 1; i < m_requiredPreambleChirps; i++)
 | 
						|
                {
 | 
						|
                    int delta = m_argMaxHistory[i] - m_argMaxHistory[i-1];
 | 
						|
                    // qDebug("ChirpChatDemodSink::processSample: search: delta: %d / %d", delta, m_deLength);
 | 
						|
 | 
						|
                    if ((delta < -m_preambleTolerance) || (delta > m_preambleTolerance))
 | 
						|
                    {
 | 
						|
                        preambleFound = false;
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
 | 
						|
                    // if (m_argMaxHistory[0] != m_argMaxHistory[i])
 | 
						|
                    // {
 | 
						|
                    //     preambleFound = false;
 | 
						|
                    //     break;
 | 
						|
                    // }
 | 
						|
                }
 | 
						|
 | 
						|
                if ((preambleFound) && (magsq > 1e-9))
 | 
						|
                {
 | 
						|
                    if (m_spectrumSink) {
 | 
						|
                        m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols);
 | 
						|
                    }
 | 
						|
 | 
						|
                    qDebug("ChirpChatDemodSink::processSample: preamble found: %u|%f", m_argMaxHistory[0], magsq);
 | 
						|
                    m_chirp = m_argMaxHistory[0];
 | 
						|
                    m_fftCounter = m_chirp;
 | 
						|
                    m_chirp0 = 0;
 | 
						|
                    m_chirpCount = 0;
 | 
						|
                    m_state = ChirpChatStatePreambleResyc;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    m_magsqOffAvg(m_magsqQueue.front());
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else if (m_state == ChirpChatStatePreambleResyc)
 | 
						|
    {
 | 
						|
        m_fftCounter++;
 | 
						|
 | 
						|
        if (m_fftCounter == m_fftLength)
 | 
						|
        {
 | 
						|
            if (m_spectrumSink) {
 | 
						|
                m_spectrumSink->feed(m_spectrumLine, m_nbSymbols);
 | 
						|
            }
 | 
						|
 | 
						|
            m_fftCounter = 0;
 | 
						|
            m_demodActive = true;
 | 
						|
            m_state = ChirpChatStatePreamble;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else if (m_state == ChirpChatStatePreamble) // preamble found look for SFD start
 | 
						|
    {
 | 
						|
        m_fft->in()[m_fftCounter] = ci * m_downChirps[m_chirp];  // de-chirp the up ramp
 | 
						|
        m_fftSFD->in()[m_fftCounter] = ci * m_upChirps[m_chirp]; // de-chirp the down ramp
 | 
						|
        m_fftCounter++;
 | 
						|
 | 
						|
        if (m_fftCounter == m_fftLength)
 | 
						|
        {
 | 
						|
            m_fftWindow.apply(m_fft->in());
 | 
						|
            std::fill(m_fft->in()+m_fftLength, m_fft->in()+m_interpolatedFFTLength, Complex{0.0, 0.0});
 | 
						|
            m_fft->transform();
 | 
						|
 | 
						|
            m_fftWindow.apply(m_fftSFD->in());
 | 
						|
            std::fill(m_fftSFD->in()+m_fftLength, m_fftSFD->in()+m_interpolatedFFTLength, Complex{0.0, 0.0});
 | 
						|
            m_fftSFD->transform();
 | 
						|
 | 
						|
            m_fftCounter = 0;
 | 
						|
            double magsqPre, magsqSFD;
 | 
						|
            double magsqTotal, magsqSFDTotal;
 | 
						|
 | 
						|
            unsigned int imaxSFD = argmax(
 | 
						|
                m_fftSFD->out(),
 | 
						|
                m_fftInterpolation,
 | 
						|
                m_fftLength,
 | 
						|
                magsqSFD,
 | 
						|
                magsqTotal,
 | 
						|
                nullptr,
 | 
						|
                m_fftInterpolation
 | 
						|
            ) / m_fftInterpolation;
 | 
						|
 | 
						|
            unsigned int imax = argmax(
 | 
						|
                m_fft->out(),
 | 
						|
                m_fftInterpolation,
 | 
						|
                m_fftLength,
 | 
						|
                magsqPre,
 | 
						|
                magsqSFDTotal,
 | 
						|
                m_spectrumBuffer,
 | 
						|
                m_fftInterpolation
 | 
						|
            ) / m_fftInterpolation;
 | 
						|
 | 
						|
            m_preambleHistory[m_chirpCount] = imax;
 | 
						|
            m_chirpCount++;
 | 
						|
            double preDrop = magsqPre - magsqSFD;
 | 
						|
            double dropRatio = -preDrop / magsqSFD;
 | 
						|
 | 
						|
            if ((preDrop <  0.0) && (dropRatio > 0.5)) // preamble drop
 | 
						|
            {
 | 
						|
                m_magsqTotalAvg(magsqSFDTotal);
 | 
						|
 | 
						|
                if (m_chirpCount < 1 + (m_settings.hasSyncWord() ? 2 : 0)) // too early
 | 
						|
                {
 | 
						|
                    m_state = ChirpChatStateReset;
 | 
						|
                    qDebug("ChirpChatDemodSink::processSample: SFD search: signal drop is too early");
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    if (m_settings.hasSyncWord())
 | 
						|
                    {
 | 
						|
                        m_syncWord = round(m_preambleHistory[m_chirpCount-2] / 8.0);
 | 
						|
                        m_syncWord += 16 * round(m_preambleHistory[m_chirpCount-3] / 8.0);
 | 
						|
                        qDebug("ChirpChatDemodSink::processSample: SFD found:  pre: %4u|%11.6f - sfd: %4u|%11.6f sync: %x", imax, magsqPre, imaxSFD, magsqSFD, m_syncWord);
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        qDebug("ChirpChatDemodSink::processSample: SFD found:  pre: %4u|%11.6f - sfd: %4u|%11.6f", imax, magsqPre, imaxSFD, magsqSFD);
 | 
						|
                    }
 | 
						|
 | 
						|
                    int sadj = 0;
 | 
						|
                    int nadj = 0;
 | 
						|
                    int zadj;
 | 
						|
                    int sfdSkip = m_sfdSkip;
 | 
						|
 | 
						|
                    for (unsigned int i = 0; i < m_chirpCount - 1 - (m_settings.hasSyncWord() ? 2 : 0); i++)
 | 
						|
                    {
 | 
						|
                        sadj += m_preambleHistory[i] > m_nbSymbols/2 ? m_preambleHistory[i] - m_nbSymbols : m_preambleHistory[i];
 | 
						|
                        nadj++;
 | 
						|
                    }
 | 
						|
 | 
						|
                    zadj = nadj == 0 ? 0 : sadj / nadj;
 | 
						|
                    zadj = zadj < -(sfdSkip/2) ? -(sfdSkip/2) : zadj > sfdSkip/2 ? sfdSkip/2 : zadj;
 | 
						|
                    qDebug("ChirpChatDemodSink::processSample: zero adjust: %d (%d)", zadj, nadj);
 | 
						|
 | 
						|
                    m_sfdSkipCounter = 0;
 | 
						|
                    m_fftCounter = m_fftLength - m_sfdSkip + zadj;
 | 
						|
                    m_chirp += zadj;
 | 
						|
                    m_state = ChirpChatStateSkipSFD; //ChirpChatStateSlideSFD;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else if (m_chirpCount > (m_settings.m_preambleChirps - m_requiredPreambleChirps + 2)) // SFD missed start over
 | 
						|
            {
 | 
						|
                qDebug("ChirpChatDemodSink::processSample: SFD search: number of possible chirps exceeded");
 | 
						|
                m_magsqTotalAvg(magsqTotal);
 | 
						|
                m_state = ChirpChatStateReset;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                if (m_spectrumSink) {
 | 
						|
                    m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols);
 | 
						|
                }
 | 
						|
 | 
						|
                qDebug("ChirpChatDemodSink::processSample: SFD search: pre: %4u|%11.6f - sfd: %4u|%11.6f", imax, magsqPre, imaxSFD, magsqSFD);
 | 
						|
                m_magsqTotalAvg(magsqTotal);
 | 
						|
                m_magsqOnAvg(magsqPre);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else if (m_state == ChirpChatStateSkipSFD) // Just skip the rest of SFD
 | 
						|
    {
 | 
						|
        m_fftCounter++;
 | 
						|
 | 
						|
        if (m_fftCounter == m_fftLength)
 | 
						|
        {
 | 
						|
            m_fftCounter = m_fftLength - m_sfdSkip;
 | 
						|
            m_sfdSkipCounter++;
 | 
						|
 | 
						|
            if (m_sfdSkipCounter == m_settings.getNbSFDFourths() - 4U) // SFD chips fourths less one full period
 | 
						|
            {
 | 
						|
                qDebug("ChirpChatDemodSink::processSample: SFD skipped");
 | 
						|
                m_chirp = m_chirp0;
 | 
						|
                m_fftCounter = 0;
 | 
						|
                m_chirpCount = 0;
 | 
						|
                m_magsqMax = 0.0;
 | 
						|
                m_decodeMsg = ChirpChatDemodMsg::MsgDecodeSymbols::create();
 | 
						|
                m_decodeMsg->setSyncWord(m_syncWord);
 | 
						|
                m_state = ChirpChatStateReadPayload;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else if (m_state == ChirpChatStateReadPayload)
 | 
						|
    {
 | 
						|
        m_fft->in()[m_fftCounter] = ci * m_downChirps[m_chirp]; // de-chirp the up ramp
 | 
						|
        m_fftCounter++;
 | 
						|
 | 
						|
        if (m_fftCounter == m_fftLength)
 | 
						|
        {
 | 
						|
            m_fftWindow.apply(m_fft->in());
 | 
						|
            std::fill(m_fft->in()+m_fftLength, m_fft->in()+m_interpolatedFFTLength, Complex{0.0, 0.0});
 | 
						|
            m_fft->transform();
 | 
						|
            m_fftCounter = 0;
 | 
						|
            double magsq, magsqTotal;
 | 
						|
            unsigned short symbol;
 | 
						|
 | 
						|
            if (m_settings.m_codingScheme == ChirpChatDemodSettings::CodingFT)
 | 
						|
            {
 | 
						|
                std::vector<float> magnitudes;
 | 
						|
                symbol = evalSymbol(
 | 
						|
                    extractMagnitudes(
 | 
						|
                        magnitudes,
 | 
						|
                        m_fft->out(),
 | 
						|
                        m_fftInterpolation,
 | 
						|
                        m_fftLength,
 | 
						|
                        magsq,
 | 
						|
                        magsqTotal,
 | 
						|
                        m_spectrumBuffer,
 | 
						|
                        m_fftInterpolation
 | 
						|
                    )
 | 
						|
                ) % m_nbSymbolsEff;
 | 
						|
                m_decodeMsg->pushBackSymbol(symbol);
 | 
						|
                m_decodeMsg->pushBackMagnitudes(magnitudes);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                int imax;
 | 
						|
 | 
						|
                if (m_settings.m_deBits > 0)
 | 
						|
                {
 | 
						|
                    double magSqNoise;
 | 
						|
                    imax = argmaxSpreaded(
 | 
						|
                        m_fft->out(),
 | 
						|
                        m_fftInterpolation,
 | 
						|
                        m_fftLength,
 | 
						|
                        magsq,
 | 
						|
                        magSqNoise,
 | 
						|
                        magsqTotal,
 | 
						|
                        m_spectrumBuffer,
 | 
						|
                        m_fftInterpolation
 | 
						|
                    );
 | 
						|
                    // double dbS = CalcDb::dbPower(magsq);
 | 
						|
                    // double dbN = CalcDb::dbPower(magSqNoise);
 | 
						|
                    // qDebug("ChirpChatDemodSink::processSample: S: %5.2f N: %5.2f S/N: %5.2f", dbS, dbN, dbS - dbN);
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    imax = argmax(
 | 
						|
                        m_fft->out(),
 | 
						|
                        m_fftInterpolation,
 | 
						|
                        m_fftLength,
 | 
						|
                        magsq,
 | 
						|
                        magsqTotal,
 | 
						|
                        m_spectrumBuffer,
 | 
						|
                        m_fftInterpolation
 | 
						|
                    );
 | 
						|
                }
 | 
						|
 | 
						|
                symbol = evalSymbol(imax) % m_nbSymbolsEff;
 | 
						|
                m_decodeMsg->pushBackSymbol(symbol);
 | 
						|
            }
 | 
						|
 | 
						|
            if (m_spectrumSink) {
 | 
						|
                m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols);
 | 
						|
            }
 | 
						|
 | 
						|
            if (magsq > m_magsqMax) {
 | 
						|
                m_magsqMax = magsq;
 | 
						|
            }
 | 
						|
 | 
						|
            m_magsqTotalAvg(magsq);
 | 
						|
 | 
						|
            if ((m_chirpCount == 0)
 | 
						|
            ||  (m_settings.m_eomSquelchTenths == 121) // max - disable squelch
 | 
						|
            || ((m_settings.m_eomSquelchTenths*magsq)/10.0 > m_magsqMax))
 | 
						|
            {
 | 
						|
                qDebug("ChirpChatDemodSink::processSample: symbol %02u: %4u|%11.6f", m_chirpCount, symbol, magsq);
 | 
						|
                m_magsqOnAvg(magsq);
 | 
						|
                m_chirpCount++;
 | 
						|
 | 
						|
                if (m_chirpCount > m_settings.m_nbSymbolsMax)
 | 
						|
                {
 | 
						|
                    qDebug("ChirpChatDemodSink::processSample: message length reached");
 | 
						|
                    m_state = ChirpChatStateReset;
 | 
						|
                    m_decodeMsg->setSignalDb(CalcDb::dbPower(m_magsqOnAvg.asDouble() / (1<<m_settings.m_spreadFactor)));
 | 
						|
                    m_decodeMsg->setNoiseDb(CalcDb::dbPower(m_magsqOffAvg.asDouble() / (1<<m_settings.m_spreadFactor)));
 | 
						|
 | 
						|
                    if (m_decoderMsgQueue && m_settings.m_decodeActive) {
 | 
						|
                        m_decoderMsgQueue->push(m_decodeMsg);
 | 
						|
                    } else {
 | 
						|
                        delete m_decodeMsg;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                qDebug("ChirpChatDemodSink::processSample: end of message");
 | 
						|
                m_state = ChirpChatStateReset;
 | 
						|
                m_decodeMsg->popSymbol(); // last symbol is garbage
 | 
						|
                m_decodeMsg->setSignalDb(CalcDb::dbPower(m_magsqOnAvg.asDouble() / (1<<m_settings.m_spreadFactor)));
 | 
						|
                m_decodeMsg->setNoiseDb(CalcDb::dbPower(m_magsqOffAvg.asDouble() / (1<<m_settings.m_spreadFactor)));
 | 
						|
 | 
						|
                if (m_decoderMsgQueue && m_settings.m_decodeActive) {
 | 
						|
                    m_decoderMsgQueue->push(m_decodeMsg);
 | 
						|
                } else {
 | 
						|
                    delete m_decodeMsg;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        m_state = ChirpChatStateReset;
 | 
						|
    }
 | 
						|
 | 
						|
    m_chirp++;
 | 
						|
 | 
						|
    if (m_chirp >= m_chirp0 + m_nbSymbols) {
 | 
						|
        m_chirp = m_chirp0;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void ChirpChatDemodSink::reset()
 | 
						|
{
 | 
						|
    m_chirp = 0;
 | 
						|
    m_chirp0 = 0;
 | 
						|
    m_fftCounter = 0;
 | 
						|
    m_argMaxHistoryCounter = 0;
 | 
						|
    m_sfdSkipCounter = 0;
 | 
						|
}
 | 
						|
 | 
						|
unsigned int ChirpChatDemodSink::argmax(
 | 
						|
    const Complex *fftBins,
 | 
						|
    unsigned int fftMult,
 | 
						|
    unsigned int fftLength,
 | 
						|
    double& magsqMax,
 | 
						|
    double& magsqTotal,
 | 
						|
    Complex *specBuffer,
 | 
						|
    unsigned int specDecim)
 | 
						|
{
 | 
						|
    magsqMax = 0.0;
 | 
						|
    magsqTotal = 0.0;
 | 
						|
    unsigned int imax = 0;
 | 
						|
    double magSum = 0.0;
 | 
						|
 | 
						|
    for (unsigned int i = 0; i < fftMult*fftLength; i++)
 | 
						|
    {
 | 
						|
        double magsq = std::norm(fftBins[i]);
 | 
						|
        magsqTotal += magsq;
 | 
						|
 | 
						|
        if (magsq > magsqMax)
 | 
						|
        {
 | 
						|
            imax = i;
 | 
						|
            magsqMax = magsq;
 | 
						|
        }
 | 
						|
 | 
						|
        if (specBuffer)
 | 
						|
        {
 | 
						|
            magSum += magsq;
 | 
						|
 | 
						|
            if (i % specDecim == specDecim - 1)
 | 
						|
            {
 | 
						|
                specBuffer[i/specDecim] = Complex(std::polar(magSum, 0.0));
 | 
						|
                magSum = 0.0;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    magsqTotal /= fftMult*fftLength;
 | 
						|
 | 
						|
    return imax;
 | 
						|
}
 | 
						|
 | 
						|
unsigned int ChirpChatDemodSink::extractMagnitudes(
 | 
						|
    std::vector<float>& magnitudes,
 | 
						|
    const Complex *fftBins,
 | 
						|
    unsigned int fftMult,
 | 
						|
    unsigned int fftLength,
 | 
						|
    double& magsqMax,
 | 
						|
    double& magsqTotal,
 | 
						|
    Complex *specBuffer,
 | 
						|
    unsigned int specDecim)
 | 
						|
{
 | 
						|
    magsqMax = 0.0;
 | 
						|
    magsqTotal = 0.0;
 | 
						|
    unsigned int imax = 0;
 | 
						|
    double magSum = 0.0;
 | 
						|
    unsigned int spread = fftMult * (1<<m_settings.m_deBits);
 | 
						|
    unsigned int istart = fftMult*fftLength - spread/2 + 1;
 | 
						|
    float magnitude = 0.0;
 | 
						|
 | 
						|
    for (unsigned int i2 = istart; i2 < istart + fftMult*fftLength; i2++)
 | 
						|
    {
 | 
						|
        int i = i2 % (fftMult*fftLength);
 | 
						|
        double magsq = std::norm(fftBins[i]);
 | 
						|
        magsqTotal += magsq;
 | 
						|
        magnitude += magsq;
 | 
						|
 | 
						|
        if (i % spread == (spread/2)-1) // boundary (inclusive)
 | 
						|
        {
 | 
						|
            if (magnitude > magsqMax)
 | 
						|
            {
 | 
						|
                imax = (i/spread)*spread;
 | 
						|
                magsqMax = magnitude;
 | 
						|
            }
 | 
						|
 | 
						|
            magnitudes.push_back(magnitude);
 | 
						|
            magnitude = 0.0;
 | 
						|
        }
 | 
						|
 | 
						|
        if (specBuffer)
 | 
						|
        {
 | 
						|
            magSum += magsq;
 | 
						|
 | 
						|
            if (i % specDecim == specDecim - 1)
 | 
						|
            {
 | 
						|
                specBuffer[i/specDecim] = Complex(std::polar(magSum, 0.0));
 | 
						|
                magSum = 0.0;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    magsqTotal /= fftMult*fftLength;
 | 
						|
 | 
						|
    return imax;
 | 
						|
}
 | 
						|
 | 
						|
unsigned int ChirpChatDemodSink::argmaxSpreaded(
 | 
						|
    const Complex *fftBins,
 | 
						|
    unsigned int fftMult,
 | 
						|
    unsigned int fftLength,
 | 
						|
    double& magsqMax,
 | 
						|
    double& magsqNoise,
 | 
						|
    double& magsqTotal,
 | 
						|
    Complex *specBuffer,
 | 
						|
    unsigned int specDecim)
 | 
						|
{
 | 
						|
    magsqMax = 0.0;
 | 
						|
    magsqNoise = 0.0;
 | 
						|
    magsqTotal = 0.0;
 | 
						|
    unsigned int imax = 0;
 | 
						|
    double magSum = 0.0;
 | 
						|
    unsigned int nbsymbols = 1<<(m_settings.m_spreadFactor - m_settings.m_deBits);
 | 
						|
    unsigned int spread = fftMult * (1<<m_settings.m_deBits);
 | 
						|
    unsigned int istart = fftMult*fftLength - spread/2 + 1;
 | 
						|
    double magSymbol = 0.0;
 | 
						|
 | 
						|
    for (unsigned int i2 = istart; i2 < istart + fftMult*fftLength; i2++)
 | 
						|
    {
 | 
						|
        int i = i2 % (fftMult*fftLength);
 | 
						|
        double magsq = std::norm(fftBins[i]);
 | 
						|
        magsqTotal += magsq;
 | 
						|
        magSymbol += magsq;
 | 
						|
 | 
						|
        if (i % spread == (spread/2)-1) // boundary (inclusive)
 | 
						|
        {
 | 
						|
            if (magSymbol > magsqMax)
 | 
						|
            {
 | 
						|
                imax = (i/spread)*spread;
 | 
						|
                magsqMax = magSymbol;
 | 
						|
            }
 | 
						|
 | 
						|
            magsqNoise += magSymbol;
 | 
						|
            magSymbol = 0.0;
 | 
						|
        }
 | 
						|
 | 
						|
        if (specBuffer)
 | 
						|
        {
 | 
						|
            magSum += magsq;
 | 
						|
 | 
						|
            if (i % specDecim == specDecim - 1)
 | 
						|
            {
 | 
						|
                specBuffer[i/specDecim] = Complex(std::polar(magSum, 0.0));
 | 
						|
                magSum = 0.0;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    magsqNoise -= magsqMax;
 | 
						|
    magsqNoise /= (nbsymbols - 1);
 | 
						|
    magsqTotal /= nbsymbols;
 | 
						|
        // magsqNoise /= fftLength;
 | 
						|
        // magsqTotal /= fftMult*fftLength;
 | 
						|
 | 
						|
    return imax;
 | 
						|
}
 | 
						|
 | 
						|
void ChirpChatDemodSink::decimateSpectrum(Complex *in, Complex *out, unsigned int size, unsigned int decimation)
 | 
						|
{
 | 
						|
    for (unsigned int i = 0; i < size; i++)
 | 
						|
    {
 | 
						|
        if (i % decimation == 0) {
 | 
						|
            out[i/decimation] = in[i];
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
int ChirpChatDemodSink::toSigned(int u, int intSize)
 | 
						|
{
 | 
						|
    if (u > intSize/2) {
 | 
						|
        return u - intSize;
 | 
						|
    } else {
 | 
						|
        return u;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
unsigned int ChirpChatDemodSink::evalSymbol(unsigned int rawSymbol)
 | 
						|
{
 | 
						|
    unsigned int spread = m_fftInterpolation * (1<<m_settings.m_deBits);
 | 
						|
 | 
						|
    if (spread < 2 ) {
 | 
						|
        return rawSymbol;
 | 
						|
    } else {
 | 
						|
        return (rawSymbol + spread/2 - 1) / spread; // middle point goes to symbol below (smear to the right)
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void ChirpChatDemodSink::applyChannelSettings(int channelSampleRate, int bandwidth, int channelFrequencyOffset, bool force)
 | 
						|
{
 | 
						|
    qDebug() << "ChirpChatDemodSink::applyChannelSettings:"
 | 
						|
            << " channelSampleRate: " << channelSampleRate
 | 
						|
            << " channelFrequencyOffset: " << channelFrequencyOffset
 | 
						|
            << " bandwidth: " << bandwidth;
 | 
						|
 | 
						|
    if ((channelFrequencyOffset != m_channelFrequencyOffset) ||
 | 
						|
        (channelSampleRate != m_channelSampleRate) || force)
 | 
						|
    {
 | 
						|
        m_nco.setFreq(-channelFrequencyOffset, channelSampleRate);
 | 
						|
    }
 | 
						|
 | 
						|
    if ((channelSampleRate != m_channelSampleRate) ||
 | 
						|
        (bandwidth != m_bandwidth) || force)
 | 
						|
    {
 | 
						|
        m_interpolator.create(16, channelSampleRate, bandwidth / 1.25f);
 | 
						|
        m_interpolatorDistance = (Real) channelSampleRate / (Real) bandwidth;
 | 
						|
        m_sampleDistanceRemain = 0;
 | 
						|
        qDebug() << "ChirpChatDemodSink::applyChannelSettings: m_interpolator.create:"
 | 
						|
            << " m_interpolatorDistance: " << m_interpolatorDistance;
 | 
						|
    }
 | 
						|
 | 
						|
    m_channelSampleRate = channelSampleRate;
 | 
						|
    m_bandwidth = bandwidth;
 | 
						|
    m_channelFrequencyOffset = channelFrequencyOffset;
 | 
						|
}
 | 
						|
 | 
						|
void ChirpChatDemodSink::applySettings(const ChirpChatDemodSettings& settings, bool force)
 | 
						|
{
 | 
						|
    qDebug() << "ChirpChatDemodSink::applySettings:"
 | 
						|
            << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
 | 
						|
            << " m_bandwidthIndex: " << settings.m_bandwidthIndex
 | 
						|
            << " m_spreadFactor: " << settings.m_spreadFactor
 | 
						|
            << " m_rgbColor: " << settings.m_rgbColor
 | 
						|
            << " m_title: " << settings.m_title
 | 
						|
            << " force: " << force;
 | 
						|
 | 
						|
    if ((settings.m_spreadFactor != m_settings.m_spreadFactor)
 | 
						|
     || (settings.m_deBits != m_settings.m_deBits)
 | 
						|
     || (settings.m_fftWindow != m_settings.m_fftWindow) || force) {
 | 
						|
        initSF(settings.m_spreadFactor, settings.m_deBits, settings.m_fftWindow);
 | 
						|
    }
 | 
						|
 | 
						|
    m_settings = settings;
 | 
						|
}
 |