예제 #1
0
void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly)
{
	Complex ci;
	qint16 sample;
	Real a, b, s, demod;
	double meansqr = 1.0;

	for(SampleVector::const_iterator it = begin; it < end; ++it) {
		Complex c(it->real() / 32768.0, it->imag() / 32768.0);
		c *= m_nco.nextIQ();

		if(m_interpolator.interpolate(&m_sampleDistanceRemain, c, &ci)) {
			s = ci.real() * ci.real() + ci.imag() * ci.imag();
			meansqr += s;
			m_movingAverage.feed(s);
			if(m_movingAverage.average() >= m_squelchLevel)
				m_squelchState = m_sampleRate / 50;

			a = m_scale * m_this.real() * (m_last.imag() - ci.imag());
			b = m_scale * m_this.imag() * (m_last.real() - ci.real());
			m_last = m_this;
			m_this = Complex(ci.real(), ci.imag());

			demod = m_volume * m_lowpass.filter(b - a);
			sample = demod * 30000;

			// Display audio spectrum to 12kHz
			if (++m_framedrop & 1)
				m_sampleBuffer.push_back(Sample(sample, sample));

			if(m_squelchState > 0)
				m_squelchState--;
			else
				sample = 0;
			{
				m_audioBuffer[m_audioBufferFill].l = sample;
				m_audioBuffer[m_audioBufferFill].r = sample;
				++m_audioBufferFill;
				if(m_audioBufferFill >= m_audioBuffer.size()) {
					uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1);
					if(res != m_audioBufferFill)
						qDebug("lost %u samples", m_audioBufferFill - res);
					m_audioBufferFill = 0;
				}
			}

			m_sampleDistanceRemain += (Real)m_sampleRate / 48000.0;
		}
	}
	if(m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 0) != m_audioBufferFill)
		;//qDebug("lost samples");
	m_audioBufferFill = 0;

	if(m_sampleSink != NULL)
		m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), true);
	m_sampleBuffer.clear();

	// TODO: correct levels
	m_scale = ( end - begin) * m_sampleRate / 48000 / meansqr;
}
예제 #2
0
파일: scopevis.cpp 프로젝트: f4exb/sdrangel
bool ScopeVis::triggerCondition(SampleVector::const_iterator& it)
{
    Complex c(it->real()/32768.0f, it->imag()/32768.0f);
    m_traceback.push_back(c); // store into trace memory FIFO

    if (m_tracebackCount < m_traceback.size())
    {   // increment count up to trace memory size
        m_tracebackCount++;
    }

    if (m_triggerChannel[m_triggerIndex] == TriggerChannelI)
    {
        return c.real() > m_triggerLevel[m_triggerIndex];
    }
    else if (m_triggerChannel[m_triggerIndex] == TriggerChannelQ)
    {
        return c.imag() > m_triggerLevel[m_triggerIndex];
    }
    else if (m_triggerChannel[m_triggerIndex] == TriggerMagLin)
    {
        return abs(c) > m_triggerLevel[m_triggerIndex];
    }
    else if (m_triggerChannel[m_triggerIndex] == TriggerMagDb)
    {
        Real mult = (10.0f / log2f(10.0f));
        Real v = c.real() * c.real() + c.imag() * c.imag();
        return mult * log2f(v) > m_triggerLevel[m_triggerIndex];
    }
    else if (m_triggerChannel[m_triggerIndex] == TriggerPhase)
    {
        return arg(c) / M_PI > m_triggerLevel[m_triggerIndex];
    }
    else if (m_triggerChannel[m_triggerIndex] == TriggerDPhase)
    {
        Real curArg = arg(c) - m_prevArg;
        m_prevArg = arg(c);

        if (curArg < -M_PI) {
            curArg += 2.0 * M_PI;
        } else if (curArg > M_PI) {
            curArg -= 2.0 * M_PI;
        }

        if (m_firstArg)
        {
            m_firstArg = false;
            return false;
        }
        else
        {
            return curArg / M_PI > m_triggerLevel[m_triggerIndex];
        }
    }
    else
    {
        return false;
    }
}
예제 #3
0
void WFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly)
{
	qint16 sample;
	Real x, y, demod;

	for(SampleVector::const_iterator it = begin; it < end; ++it) {
		x = it->real() * m_last.real() + it->imag() * m_last.imag();
		y = it->real() * m_last.imag() - it->imag() * m_last.real();
		m_last = Complex(it->real(), it->imag());
		demod = atan2(y, x);

		Complex e(demod, 0);
		Complex c;
		if(m_interpolator.interpolate(&m_sampleDistanceRemain, e, &c)) {
			sample = (qint16)(c.real() * 100 * m_volume * m_volume);
			m_sampleBuffer.push_back(Sample(sample, sample));
			m_audioBuffer[m_audioBufferFill].l = sample;
			m_audioBuffer[m_audioBufferFill].r = sample;
			++m_audioBufferFill;
			if(m_audioBufferFill >= m_audioBuffer.size()) {
				uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1);
				if(res != m_audioBufferFill)
					qDebug("lost %u samples", m_audioBufferFill - res);
				m_audioBufferFill = 0;
			}
			m_sampleDistanceRemain += (Real)m_sampleRate / 48000.0;
		}
	}
	if(m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 0) != m_audioBufferFill)
		qDebug("lost samples");
	m_audioBufferFill = 0;

	if(m_sampleSink != NULL)
		m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), true);
	m_sampleBuffer.clear();
}
예제 #4
0
void TCPSrc::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly)
{
	Complex ci;
	cmplx* sideband;
	Real l, r;

	m_sampleBuffer.clear();

	// Rtl-Sdr uses full 16-bit scale; FCDPP does not
	int rescale = 30000 * (1 << m_boost);

	for(SampleVector::const_iterator it = begin; it < end; ++it) {
		Complex c(it->real() / 32768.0, it->imag() / 32768.0);
		c *= m_nco.nextIQ();

		if(m_interpolator.interpolate(&m_sampleDistanceRemain, c, &ci)) {
			m_sampleBuffer.push_back(Sample(ci.real() * rescale, ci.imag() * rescale));
			m_sampleDistanceRemain += m_inputSampleRate / m_outputSampleRate;
		}
	}

	if((m_spectrum != NULL) && (m_spectrumEnabled))
		m_spectrum->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), positiveOnly);

	for(int i = 0; i < m_s16leSockets.count(); i++)
		m_s16leSockets[i].socket->write((const char*)&m_sampleBuffer[0], m_sampleBuffer.size() * 4);

	if((m_sampleFormat == FormatSSB) && (m_ssbSockets.count() > 0)) {
		for(SampleVector::const_iterator it = m_sampleBuffer.begin(); it != m_sampleBuffer.end(); ++it) {
			Complex cj(it->real() / 30000.0, it->imag() / 30000.0);
			int n_out = TCPFilter->runSSB(cj, &sideband, true);
			if (n_out) {
				for (int i = 0; i < n_out; i+=2) {
					l = (sideband[i].real() + sideband[i].imag()) * 0.7 * 32000.0;
					r = (sideband[i+1].real() + sideband[i+1].imag()) * 0.7 * 32000.0;
					m_sampleBufferSSB.push_back(Sample(l, r));
				}
				for(int i = 0; i < m_ssbSockets.count(); i++)
					m_ssbSockets[i].socket->write((const char*)&m_sampleBufferSSB[0], n_out * 2);
				m_sampleBufferSSB.clear();
			}
		}
	}

	if((m_sampleFormat == FormatNFM) && (m_ssbSockets.count() > 0)) {
		for(SampleVector::const_iterator it = m_sampleBuffer.begin(); it != m_sampleBuffer.end(); ++it) {
			Complex cj(it->real() / 30000.0, it->imag() / 30000.0);
			// An FFT filter here is overkill, but was already set up for SSB
			int n_out = TCPFilter->runFilt(cj, &sideband);
			if (n_out) {
				Real sum = 1.0;
				for (int i = 0; i < n_out; i+=2) {
					l = m_this.real() * (m_last.imag() - sideband[i].imag())
					  - m_this.imag() * (m_last.real() - sideband[i].real());
					m_last = sideband[i];
					r = m_last.real() * (m_this.imag() - sideband[i+1].imag())
					  - m_last.imag() * (m_this.real() - sideband[i+1].real());
					m_this = sideband[i+1];
					m_sampleBufferSSB.push_back(Sample(l * m_scale, r * m_scale));
					sum += m_this.real() * m_this.real() + m_this.imag() * m_this.imag(); 
				}
				// TODO: correct levels
				m_scale = 24000 * tcpFftLen / sum;
				for(int i = 0; i < m_ssbSockets.count(); i++)
					m_ssbSockets[i].socket->write((const char*)&m_sampleBufferSSB[0], n_out * 2);
				m_sampleBufferSSB.clear();
			}
		}
	}
}
예제 #5
0
void ScopeVis::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst)
{
	while(begin < end) {
		if(m_triggerChannel == TriggerChannelI) {
			if(m_triggerState == Untriggered) {
				while(begin < end) {
					if(begin->real() >= m_triggerLevelHigh) {
						m_triggerState = Triggered;
						break;
					}
					++begin;
				}
			}
			if(m_triggerState == Triggered) {
				int count = end - begin;
				if(count > (int)(m_trace.size() - m_fill))
					count = m_trace.size() - m_fill;
				std::vector<Complex>::iterator it = m_trace.begin() + m_fill;
				for(int i = 0; i < count; ++i) {
					*it++ = Complex(begin->real() / 32768.0, begin->imag() / 32768.0);
					++begin;
				}
				m_fill += count;
				if(m_fill >= m_trace.size()) {
					m_glScope->newTrace(m_trace, m_sampleRate);
					m_fill = 0;
					m_triggerState = WaitForReset;
				}
			}
			if(m_triggerState == WaitForReset) {
				while(begin < end) {
					if(begin->real() < m_triggerLevelLow) {
						m_triggerState = Untriggered;
						break;
					}
					++begin;
				}
			}
		} else if(m_triggerChannel == TriggerChannelQ) {
			if(m_triggerState == Untriggered) {
				while(begin < end) {
					if(begin->imag() >= m_triggerLevelHigh) {
						m_triggerState = Triggered;
						break;
					}
					++begin;
				}
			}
			if(m_triggerState == Triggered) {
				int count = end - begin;
				if(count > (int)(m_trace.size() - m_fill))
					count = m_trace.size() - m_fill;
				std::vector<Complex>::iterator it = m_trace.begin() + m_fill;
				for(int i = 0; i < count; ++i) {
					*it++ = Complex(begin->real() / 32768.0, begin->imag() / 32768.0);
					++begin;
				}
				m_fill += count;
				if(m_fill >= m_trace.size()) {
					m_glScope->newTrace(m_trace, m_sampleRate);
					m_fill = 0;
					m_triggerState = WaitForReset;
				}
			}
			if(m_triggerState == WaitForReset) {
				while(begin < end) {
					if(begin->imag() < m_triggerLevelLow) {
						m_triggerState = Untriggered;
						break;
					}
					++begin;
				}
			}
		} else {
			int count = end - begin;
			if(count > (int)(m_trace.size() - m_fill))
				count = m_trace.size() - m_fill;
			std::vector<Complex>::iterator it = m_trace.begin() + m_fill;
			for(int i = 0; i < count; ++i) {
				*it++ = Complex(begin->real() / 32768.0, begin->imag() / 32768.0);
				++begin;
			}
			m_fill += count;
			if(m_fill >= m_trace.size()) {
				m_glScope->newTrace(m_trace, m_sampleRate);
				m_fill = 0;
			}
		}
	}
}
예제 #6
0
파일: dsddemod.cpp 프로젝트: f4exb/sdrangel
void DSDDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst)
{
	Complex ci;
	int samplesPerSymbol = m_dsdDecoder.getSamplesPerSymbol();

	m_settingsMutex.lock();
	m_scopeSampleBuffer.clear();

	m_dsdDecoder.enableMbelib(!DSPEngine::instance()->hasDVSerialSupport()); // disable mbelib if DV serial support is present and activated else enable it

	for (SampleVector::const_iterator it = begin; it != end; ++it)
	{
		Complex c(it->real(), it->imag());
		c *= m_nco.nextIQ();

        if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci))
        {
            qint16 sample, delayedSample;

            Real magsq = ((ci.real()*ci.real() +  ci.imag()*ci.imag()))  / (1<<30);
            m_movingAverage.feed(magsq);

            m_magsqSum += magsq;

            if (magsq > m_magsqPeak)
            {
                m_magsqPeak = magsq;
            }

            m_magsqCount++;

            Real demod = 32768.0f * m_phaseDiscri.phaseDiscriminator(ci) * ((float) m_running.m_demodGain / 100.0f);
            m_sampleCount++;

            // AF processing

            if (m_movingAverage.average() > m_squelchLevel)
            {
                if (m_squelchGate > 0)
                {
                    if (m_squelchCount < m_squelchGate) {
                        m_squelchCount++;
                    }

                    m_squelchOpen = m_squelchCount == m_squelchGate;
                }
                else
                {
                    m_squelchOpen = true;
                }
            }
            else
            {
                m_squelchCount = 0;
                m_squelchOpen = false;
            }

            if (m_squelchOpen)
            {
                sample = demod;
            }
            else
            {
                sample = 0;
            }

            m_dsdDecoder.pushSample(sample);

            if (m_running.m_enableCosineFiltering) { // show actual input to FSK demod
            	sample = m_dsdDecoder.getFilteredSample();
            }

            if (m_sampleBufferIndex < (1<<17)) {
                m_sampleBufferIndex++;
            } else {
                m_sampleBufferIndex = 0;
            }

            m_sampleBuffer[m_sampleBufferIndex] = sample;

            if (m_sampleBufferIndex < samplesPerSymbol) {
                delayedSample = m_sampleBuffer[(1<<17) - samplesPerSymbol + m_sampleBufferIndex]; // wrap
            } else {
                delayedSample = m_sampleBuffer[m_sampleBufferIndex - samplesPerSymbol];
            }

            if (m_running.m_syncOrConstellation)
            {
                Sample s(sample, m_dsdDecoder.getSymbolSyncSample());
                m_scopeSampleBuffer.push_back(s);
            }
            else
            {
                Sample s(sample, delayedSample); // I=signal, Q=signal delayed by 20 samples (2400 baud: lowest rate)
                m_scopeSampleBuffer.push_back(s);
            }

            if (DSPEngine::instance()->hasDVSerialSupport())
            {
                if ((m_running.m_slot1On) && m_dsdDecoder.mbeDVReady1())
                {
                    if (!m_running.m_audioMute)
                    {
                        DSPEngine::instance()->pushMbeFrame(
                                m_dsdDecoder.getMbeDVFrame1(),
                                m_dsdDecoder.getMbeRateIndex(),
                                m_running.m_volume,
                                m_running.m_tdmaStereo ? 1 : 3, // left or both channels
                                &m_audioFifo1);
                    }

                    m_dsdDecoder.resetMbeDV1();
                }

                if ((m_running.m_slot2On) && m_dsdDecoder.mbeDVReady2())
                {
                    if (!m_running.m_audioMute)
                    {
                        DSPEngine::instance()->pushMbeFrame(
                                m_dsdDecoder.getMbeDVFrame2(),
                                m_dsdDecoder.getMbeRateIndex(),
                                m_running.m_volume,
                                m_running.m_tdmaStereo ? 2 : 3, // right or both channels
                                &m_audioFifo2);
                    }

                    m_dsdDecoder.resetMbeDV2();
                }
            }

//            if (DSPEngine::instance()->hasDVSerialSupport() && m_dsdDecoder.mbeDVReady1())
//            {
//                if (!m_running.m_audioMute)
//                {
//                    DSPEngine::instance()->pushMbeFrame(m_dsdDecoder.getMbeDVFrame1(), m_dsdDecoder.getMbeRateIndex(), m_running.m_volume, &m_audioFifo1);
//                }
//
//                m_dsdDecoder.resetMbeDV1();
//            }

            m_interpolatorDistanceRemain += m_interpolatorDistance;
        }
	}

	if (!DSPEngine::instance()->hasDVSerialSupport())
	{
	    if (m_running.m_slot1On)
	    {
	        int nbAudioSamples;
	        short *dsdAudio = m_dsdDecoder.getAudio1(nbAudioSamples);

	        if (nbAudioSamples > 0)
	        {
	            if (!m_running.m_audioMute) {
	                uint res = m_audioFifo1.write((const quint8*) dsdAudio, nbAudioSamples, 10);
	            }

	            m_dsdDecoder.resetAudio1();
	        }
	    }

        if (m_running.m_slot2On)
        {
            int nbAudioSamples;
            short *dsdAudio = m_dsdDecoder.getAudio2(nbAudioSamples);

            if (nbAudioSamples > 0)
            {
                if (!m_running.m_audioMute) {
                    uint res = m_audioFifo2.write((const quint8*) dsdAudio, nbAudioSamples, 10);
                }

                m_dsdDecoder.resetAudio2();
            }
        }

//	    int nbAudioSamples;
//	    short *dsdAudio = m_dsdDecoder.getAudio1(nbAudioSamples);
//
//	    if (nbAudioSamples > 0)
//	    {
//	        if (!m_running.m_audioMute) {
//	            uint res = m_audioFifo1.write((const quint8*) dsdAudio, nbAudioSamples, 10);
//	        }
//
//	        m_dsdDecoder.resetAudio1();
//	    }
	}


    if ((m_scope != 0) && (m_scopeEnabled))
    {
        m_scope->feed(m_scopeSampleBuffer.begin(), m_scopeSampleBuffer.end(), true); // true = real samples for what it's worth
    }

	m_settingsMutex.unlock();
}
예제 #7
0
파일: nfmdemod.cpp 프로젝트: f4exb/sdrangel
void NFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst)
{
	Complex ci;

	m_settingsMutex.lock();

	for (SampleVector::const_iterator it = begin; it != end; ++it)
	{
		//Complex c(it->real() / 32768.0f, it->imag() / 32768.0f);
		Complex c(it->real(), it->imag());
		c *= m_nco.nextIQ();

		{
			if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci))
			{

				qint16 sample;

				m_AGC.feed(ci);

                double magsqRaw = m_AGC.getMagSq();
                Real magsq = magsqRaw / (1<<30);
                m_movingAverage.feed(magsq);
                m_magsqSum += magsq;

                if (magsq > m_magsqPeak)
                {
                    m_magsqPeak = magsq;
                }

                m_magsqCount++;

				Real demod = m_phaseDiscri.phaseDiscriminator2(ci);

				//m_m2Sample = m_m1Sample;
				//m_m1Sample = ci;
				m_sampleCount++;

				// AF processing

                if (m_movingAverage.average() > m_squelchLevel)
				{
					if (m_squelchCount < m_squelchGate)
					{
						m_squelchCount++;
					}
				}
				else
				{
					m_squelchCount = 0;
				}

				//squelchOpen = (getMag() > m_squelchLevel);
				m_squelchOpen = m_squelchCount == m_squelchGate; // wait for AGC to stabilize

				/*
				if (m_afSquelch.analyze(demod))
				{
					squelchOpen = m_afSquelch.evaluate();
				}*/

				if ((m_squelchOpen) && !m_running.m_audioMute)
				//if (m_AGC.getAverage() > m_squelchLevel)
				{
					if (m_running.m_ctcssOn)
					{
						Real ctcss_sample = m_lowpass.filter(demod);

						if ((m_sampleCount & 7) == 7) // decimate 48k -> 6k
						{
							if (m_ctcssDetector.analyze(&ctcss_sample))
							{
								int maxToneIndex;

								if (m_ctcssDetector.getDetectedTone(maxToneIndex))
								{
									if (maxToneIndex+1 != m_ctcssIndex)
									{
										m_nfmDemodGUI->setCtcssFreq(m_ctcssDetector.getToneSet()[maxToneIndex]);
										m_ctcssIndex = maxToneIndex+1;
									}
								}
								else
								{
									if (m_ctcssIndex != 0)
									{
										m_nfmDemodGUI->setCtcssFreq(0);
										m_ctcssIndex = 0;
									}
								}
							}
						}
					}

					if (m_running.m_ctcssOn && m_ctcssIndexSelected && (m_ctcssIndexSelected != m_ctcssIndex))
					{
						sample = 0;
					}
					else
					{
						demod = m_bandpass.filter(demod);
						sample = demod * m_running.m_volume;
					}
				}
				else
				{
					if (m_ctcssIndex != 0)
					{
						m_nfmDemodGUI->setCtcssFreq(0);
						m_ctcssIndex = 0;
					}

					sample = 0;
				}

				m_audioBuffer[m_audioBufferFill].l = sample;
				m_audioBuffer[m_audioBufferFill].r = sample;
				++m_audioBufferFill;

				if (m_audioBufferFill >= m_audioBuffer.size())
				{
					uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 10);

					if (res != m_audioBufferFill)
					{
						qDebug("NFMDemod::feed: %u/%u audio samples written", res, m_audioBufferFill);
					}

					m_audioBufferFill = 0;
				}

				m_interpolatorDistanceRemain += m_interpolatorDistance;
			}
		}
	}

	if (m_audioBufferFill > 0)
	{
		uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 10);

		if (res != m_audioBufferFill)
		{
			qDebug("NFMDemod::feed: %u/%u tail samples written", res, m_audioBufferFill);
		}

		m_audioBufferFill = 0;
	}

	m_settingsMutex.unlock();
}
예제 #8
0
void DSDDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst)
{
    (void) firstOfBurst;
	Complex ci;
	int samplesPerSymbol = m_dsdDecoder.getSamplesPerSymbol();

	m_settingsMutex.lock();
	m_scopeSampleBuffer.clear();

	m_dsdDecoder.enableMbelib(!DSPEngine::instance()->hasDVSerialSupport()); // disable mbelib if DV serial support is present and activated else enable it

	for (SampleVector::const_iterator it = begin; it != end; ++it)
	{
		Complex c(it->real(), it->imag());
		c *= m_nco.nextIQ();

        if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci))
        {
            FixReal sample, delayedSample;
            qint16 sampleDSD;

            Real re = ci.real() / SDR_RX_SCALED;
            Real im = ci.imag() / SDR_RX_SCALED;
            Real magsq = re*re + im*im;
            m_movingAverage(magsq);

            m_magsqSum += magsq;

            if (magsq > m_magsqPeak)
            {
                m_magsqPeak = magsq;
            }

            m_magsqCount++;

            Real demod = m_phaseDiscri.phaseDiscriminator(ci) * m_settings.m_demodGain; // [-1.0:1.0]
            m_sampleCount++;

            // AF processing

            if (m_movingAverage.asDouble() > m_squelchLevel)
            {
                if (m_squelchGate > 0)
                {

                    if (m_squelchCount < m_squelchGate*2) {
                        m_squelchCount++;
                    }

                    m_squelchDelayLine.write(demod);
                    m_squelchOpen = m_squelchCount > m_squelchGate;
                }
                else
                {
                    m_squelchOpen = true;
                }
            }
            else
            {
                if (m_squelchGate > 0)
                {
                    if (m_squelchCount > 0) {
                        m_squelchCount--;
                    }

                    m_squelchDelayLine.write(0);
                    m_squelchOpen = m_squelchCount > m_squelchGate;
                }
                else
                {
                    m_squelchOpen = false;
                }
            }

            if (m_squelchOpen)
            {
                if (m_squelchGate > 0)
                {
                    sampleDSD = m_squelchDelayLine.readBack(m_squelchGate) * 32768.0f;   // DSD decoder takes int16 samples
                    sample = m_squelchDelayLine.readBack(m_squelchGate) * SDR_RX_SCALEF; // scale to sample size
                }
                else
                {
                    sampleDSD = demod * 32768.0f;   // DSD decoder takes int16 samples
                    sample = demod * SDR_RX_SCALEF; // scale to sample size
                }
            }
            else
            {
                sampleDSD = 0;
                sample = 0;
            }

            m_dsdDecoder.pushSample(sampleDSD);

            if (m_settings.m_enableCosineFiltering) { // show actual input to FSK demod
            	sample = m_dsdDecoder.getFilteredSample() * m_scaleFromShort;
            }

            if (m_sampleBufferIndex < (1<<17)-1) {
                m_sampleBufferIndex++;
            } else {
                m_sampleBufferIndex = 0;
            }

            m_sampleBuffer[m_sampleBufferIndex] = sample;

            if (m_sampleBufferIndex < samplesPerSymbol) {
                delayedSample = m_sampleBuffer[(1<<17) - samplesPerSymbol + m_sampleBufferIndex]; // wrap
            } else {
                delayedSample = m_sampleBuffer[m_sampleBufferIndex - samplesPerSymbol];
            }

            if (m_settings.m_syncOrConstellation)
            {
                Sample s(sample, m_dsdDecoder.getSymbolSyncSample() * m_scaleFromShort * 0.84);
                m_scopeSampleBuffer.push_back(s);
            }
            else
            {
                Sample s(sample, delayedSample); // I=signal, Q=signal delayed by 20 samples (2400 baud: lowest rate)
                m_scopeSampleBuffer.push_back(s);
            }

            if (DSPEngine::instance()->hasDVSerialSupport())
            {
                if ((m_settings.m_slot1On) && m_dsdDecoder.mbeDVReady1())
                {
                    if (!m_settings.m_audioMute)
                    {
                        DSPEngine::instance()->pushMbeFrame(
                                m_dsdDecoder.getMbeDVFrame1(),
                                m_dsdDecoder.getMbeRateIndex(),
                                m_settings.m_volume * 10.0,
                                m_settings.m_tdmaStereo ? 1 : 3, // left or both channels
                                m_settings.m_highPassFilter,
                                m_audioSampleRate/8000, // upsample from native 8k
                                &m_audioFifo1);
                    }

                    m_dsdDecoder.resetMbeDV1();
                }

                if ((m_settings.m_slot2On) && m_dsdDecoder.mbeDVReady2())
                {
                    if (!m_settings.m_audioMute)
                    {
                        DSPEngine::instance()->pushMbeFrame(
                                m_dsdDecoder.getMbeDVFrame2(),
                                m_dsdDecoder.getMbeRateIndex(),
                                m_settings.m_volume * 10.0,
                                m_settings.m_tdmaStereo ? 2 : 3, // right or both channels
                                m_settings.m_highPassFilter,
                                m_audioSampleRate/8000, // upsample from native 8k
                                &m_audioFifo2);
                    }

                    m_dsdDecoder.resetMbeDV2();
                }
            }

//            if (DSPEngine::instance()->hasDVSerialSupport() && m_dsdDecoder.mbeDVReady1())
//            {
//                if (!m_settings.m_audioMute)
//                {
//                    DSPEngine::instance()->pushMbeFrame(m_dsdDecoder.getMbeDVFrame1(), m_dsdDecoder.getMbeRateIndex(), m_settings.m_volume, &m_audioFifo1);
//                }
//
//                m_dsdDecoder.resetMbeDV1();
//            }

            m_interpolatorDistanceRemain += m_interpolatorDistance;
        }
	}

	if (!DSPEngine::instance()->hasDVSerialSupport())
	{
	    if (m_settings.m_slot1On)
	    {
	        int nbAudioSamples;
	        short *dsdAudio = m_dsdDecoder.getAudio1(nbAudioSamples);

	        if (nbAudioSamples > 0)
	        {
	            if (!m_settings.m_audioMute) {
	                m_audioFifo1.write((const quint8*) dsdAudio, nbAudioSamples);
	            }

	            m_dsdDecoder.resetAudio1();
	        }
	    }

        if (m_settings.m_slot2On)
        {
            int nbAudioSamples;
            short *dsdAudio = m_dsdDecoder.getAudio2(nbAudioSamples);

            if (nbAudioSamples > 0)
            {
                if (!m_settings.m_audioMute) {
                    m_audioFifo2.write((const quint8*) dsdAudio, nbAudioSamples);
                }

                m_dsdDecoder.resetAudio2();
            }
        }

//	    int nbAudioSamples;
//	    short *dsdAudio = m_dsdDecoder.getAudio1(nbAudioSamples);
//
//	    if (nbAudioSamples > 0)
//	    {
//	        if (!m_settings.m_audioMute) {
//	            uint res = m_audioFifo1.write((const quint8*) dsdAudio, nbAudioSamples, 10);
//	        }
//
//	        m_dsdDecoder.resetAudio1();
//	    }
	}

    if ((m_scopeXY != 0) && (m_scopeEnabled))
    {
        m_scopeXY->feed(m_scopeSampleBuffer.begin(), m_scopeSampleBuffer.end(), true); // true = real samples for what it's worth
    }

	m_settingsMutex.unlock();
}