kopia lustrzana https://github.com/f4exb/sdrangel
				
				
				
			AM demod: basic synchronous AM detection option
							rodzic
							
								
									1549ecaa0f
								
							
						
					
					
						commit
						e9f64a05f2
					
				| 
						 | 
				
			
			@ -66,6 +66,7 @@ AMDemod::AMDemod(DeviceSourceAPI *deviceAPI) :
 | 
			
		|||
 | 
			
		||||
	DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(&m_audioFifo, getInputMessageQueue());
 | 
			
		||||
	m_audioSampleRate = DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate();
 | 
			
		||||
    DSBFilter = new fftfilt((2.0f * m_settings.m_rfBandwidth) / m_audioSampleRate, 2 * 1024);
 | 
			
		||||
 | 
			
		||||
    applyChannelSettings(m_inputSampleRate, m_inputFrequencyOffset, true);
 | 
			
		||||
    applySettings(m_settings, true);
 | 
			
		||||
| 
						 | 
				
			
			@ -74,6 +75,10 @@ AMDemod::AMDemod(DeviceSourceAPI *deviceAPI) :
 | 
			
		|||
    m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this);
 | 
			
		||||
    m_deviceAPI->addThreadedSink(m_threadedChannelizer);
 | 
			
		||||
    m_deviceAPI->addChannelAPI(this);
 | 
			
		||||
 | 
			
		||||
    m_pllFilt.create(101, m_audioSampleRate, 500.0);
 | 
			
		||||
    m_pll.computeCoefficients(0.05, 0.707, 1000);
 | 
			
		||||
    m_syncAMBuffIndex = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AMDemod::~AMDemod()
 | 
			
		||||
| 
						 | 
				
			
			@ -83,6 +88,7 @@ AMDemod::~AMDemod()
 | 
			
		|||
    m_deviceAPI->removeThreadedSink(m_threadedChannelizer);
 | 
			
		||||
    delete m_threadedChannelizer;
 | 
			
		||||
    delete m_channelizer;
 | 
			
		||||
    delete DSBFilter;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AMDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst __attribute__((unused)))
 | 
			
		||||
| 
						 | 
				
			
			@ -233,6 +239,8 @@ void AMDemod::applyAudioSampleRate(int sampleRate)
 | 
			
		|||
    m_bandpass.create(301, sampleRate, 300.0, m_settings.m_rfBandwidth / 2.0f);
 | 
			
		||||
    m_audioFifo.setSize(sampleRate);
 | 
			
		||||
    m_squelchDelayLine.resize(sampleRate/5);
 | 
			
		||||
    DSBFilter->create_dsb_filter((2.0f * m_settings.m_rfBandwidth) / (float) sampleRate);
 | 
			
		||||
    m_pllFilt.create(101, sampleRate, 500.0);
 | 
			
		||||
    m_settingsMutex.unlock();
 | 
			
		||||
 | 
			
		||||
    m_audioSampleRate = sampleRate;
 | 
			
		||||
| 
						 | 
				
			
			@ -273,6 +281,7 @@ void AMDemod::applySettings(const AMDemodSettings& settings, bool force)
 | 
			
		|||
            << " m_audioMute: " << settings.m_audioMute
 | 
			
		||||
            << " m_bandpassEnable: " << settings.m_bandpassEnable
 | 
			
		||||
            << " m_audioDeviceName: " << settings.m_audioDeviceName
 | 
			
		||||
            << " m_pll: " << settings.m_pll
 | 
			
		||||
            << " force: " << force;
 | 
			
		||||
 | 
			
		||||
    if((m_settings.m_rfBandwidth != settings.m_rfBandwidth) ||
 | 
			
		||||
| 
						 | 
				
			
			@ -283,6 +292,7 @@ void AMDemod::applySettings(const AMDemodSettings& settings, bool force)
 | 
			
		|||
        m_interpolatorDistanceRemain = 0;
 | 
			
		||||
        m_interpolatorDistance = (Real) m_inputSampleRate / (Real) m_audioSampleRate;
 | 
			
		||||
        m_bandpass.create(301, m_audioSampleRate, 300.0, settings.m_rfBandwidth / 2.0f);
 | 
			
		||||
        DSBFilter->create_dsb_filter((2.0f * settings.m_rfBandwidth) / (float) m_audioSampleRate);
 | 
			
		||||
        m_settingsMutex.unlock();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,9 @@
 | 
			
		|||
#include "util/movingaverage.h"
 | 
			
		||||
#include "dsp/agc.h"
 | 
			
		||||
#include "dsp/bandpass.h"
 | 
			
		||||
#include "dsp/lowpass.h"
 | 
			
		||||
#include "dsp/phaselockcomplex.h"
 | 
			
		||||
#include "dsp/fftfilt.h"
 | 
			
		||||
#include "audio/audiofifo.h"
 | 
			
		||||
#include "util/message.h"
 | 
			
		||||
#include "util/doublebufferfifo.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -118,6 +121,7 @@ public:
 | 
			
		|||
    uint32_t getAudioSampleRate() const { return m_audioSampleRate; }
 | 
			
		||||
	double getMagSq() const { return m_magsq; }
 | 
			
		||||
	bool getSquelchOpen() const { return m_squelchOpen; }
 | 
			
		||||
	bool getPllLocked() const { return m_settings.m_pll && m_pll.locked(); }
 | 
			
		||||
 | 
			
		||||
	void getMagSqLevels(double& avg, double& peak, int& nbSamples)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -165,6 +169,11 @@ private:
 | 
			
		|||
	MovingAverageUtil<Real, double, 16> m_movingAverage;
 | 
			
		||||
	SimpleAGC<4096> m_volumeAGC;
 | 
			
		||||
    Bandpass<Real> m_bandpass;
 | 
			
		||||
    Lowpass<std::complex<float> > m_pllFilt;
 | 
			
		||||
    PhaseLockComplex m_pll;
 | 
			
		||||
    fftfilt* DSBFilter;
 | 
			
		||||
    Real m_syncAMBuff[2*1024];
 | 
			
		||||
    uint32_t m_syncAMBuffIndex;
 | 
			
		||||
 | 
			
		||||
	AudioVector m_audioBuffer;
 | 
			
		||||
	uint32_t m_audioBufferFill;
 | 
			
		||||
| 
						 | 
				
			
			@ -217,9 +226,37 @@ private:
 | 
			
		|||
 | 
			
		||||
        if (m_squelchOpen && !m_settings.m_audioMute)
 | 
			
		||||
        {
 | 
			
		||||
            Real demod = sqrt(m_squelchDelayLine.readBack(m_audioSampleRate/20));
 | 
			
		||||
            m_volumeAGC.feed(demod);
 | 
			
		||||
            demod = (demod - m_volumeAGC.getValue()) / m_volumeAGC.getValue();
 | 
			
		||||
            Real demod;
 | 
			
		||||
 | 
			
		||||
            if (m_settings.m_pll)
 | 
			
		||||
            {
 | 
			
		||||
                std::complex<float> s(re, im);
 | 
			
		||||
                s = m_pllFilt.filter(s);
 | 
			
		||||
                m_pll.feed(s.real(), s.imag());
 | 
			
		||||
                float yr = re * m_pll.getImag() - im * m_pll.getReal();
 | 
			
		||||
                float yi = re * m_pll.getReal() + im * m_pll.getImag();
 | 
			
		||||
 | 
			
		||||
                fftfilt::cmplx *sideband;
 | 
			
		||||
                std::complex<float> cs(yr, yi);
 | 
			
		||||
                int n_out = DSBFilter->runDSB(cs, &sideband, false);
 | 
			
		||||
 | 
			
		||||
                for (int i = 0; i < n_out; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    m_syncAMBuff[i] = (sideband[i].real() + sideband[i].imag());
 | 
			
		||||
                    m_syncAMBuffIndex = 0;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                m_syncAMBuffIndex = m_syncAMBuffIndex < 2*1024 ? m_syncAMBuffIndex : 0;
 | 
			
		||||
                demod = m_syncAMBuff[m_syncAMBuffIndex++]*0.7*(SDR_RX_SCALEF/602.0f);
 | 
			
		||||
                m_volumeAGC.feed(demod);
 | 
			
		||||
                demod /= (10.0*m_volumeAGC.getValue());
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                demod = sqrt(m_squelchDelayLine.readBack(m_audioSampleRate/20));
 | 
			
		||||
                m_volumeAGC.feed(demod);
 | 
			
		||||
                demod = (demod - m_volumeAGC.getValue()) / m_volumeAGC.getValue();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (m_settings.m_bandpassEnable)
 | 
			
		||||
            {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -139,6 +139,16 @@ void AMDemodGUI::on_deltaFrequency_changed(qint64 value)
 | 
			
		|||
    applySettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AMDemodGUI::on_pll_toggled(bool checked)
 | 
			
		||||
{
 | 
			
		||||
    if (!checked) {
 | 
			
		||||
        ui->pll->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    m_settings.m_pll = checked;
 | 
			
		||||
    applySettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AMDemodGUI::on_bandpassEnable_toggled(bool checked)
 | 
			
		||||
{
 | 
			
		||||
    m_settings.m_bandpassEnable = checked;
 | 
			
		||||
| 
						 | 
				
			
			@ -302,6 +312,7 @@ void AMDemodGUI::displaySettings()
 | 
			
		|||
 | 
			
		||||
    ui->audioMute->setChecked(m_settings.m_audioMute);
 | 
			
		||||
    ui->bandpassEnable->setChecked(m_settings.m_bandpassEnable);
 | 
			
		||||
    ui->pll->setChecked(m_settings.m_pll);
 | 
			
		||||
 | 
			
		||||
    blockApplySettings(false);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -359,6 +370,15 @@ void AMDemodGUI::tick()
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (m_settings.m_pll)
 | 
			
		||||
	{
 | 
			
		||||
	    if (m_amDemod->getPllLocked()) {
 | 
			
		||||
	        ui->pll->setStyleSheet("QToolButton { background-color : green; }");
 | 
			
		||||
	    } else {
 | 
			
		||||
	        ui->pll->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
 | 
			
		||||
	    }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_tickCount++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,6 +65,7 @@ private:
 | 
			
		|||
 | 
			
		||||
private slots:
 | 
			
		||||
	void on_deltaFrequency_changed(qint64 value);
 | 
			
		||||
	void on_pll_toggled(bool checked);
 | 
			
		||||
	void on_bandpassEnable_toggled(bool checked);
 | 
			
		||||
	void on_rfBW_valueChanged(int value);
 | 
			
		||||
	void on_volume_valueChanged(int value);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -129,6 +129,31 @@
 | 
			
		|||
          </property>
 | 
			
		||||
         </widget>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item>
 | 
			
		||||
         <widget class="Line" name="line">
 | 
			
		||||
          <property name="orientation">
 | 
			
		||||
           <enum>Qt::Vertical</enum>
 | 
			
		||||
          </property>
 | 
			
		||||
         </widget>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item>
 | 
			
		||||
         <widget class="QToolButton" name="pll">
 | 
			
		||||
          <property name="toolTip">
 | 
			
		||||
           <string>PLL for synchronous AM</string>
 | 
			
		||||
          </property>
 | 
			
		||||
          <property name="text">
 | 
			
		||||
           <string/>
 | 
			
		||||
          </property>
 | 
			
		||||
          <property name="icon">
 | 
			
		||||
           <iconset resource="../../../sdrgui/resources/res.qrc">
 | 
			
		||||
            <normaloff>:/unlocked.png</normaloff>
 | 
			
		||||
            <normalon>:/locked.png</normalon>:/unlocked.png</iconset>
 | 
			
		||||
          </property>
 | 
			
		||||
          <property name="checkable">
 | 
			
		||||
           <bool>true</bool>
 | 
			
		||||
          </property>
 | 
			
		||||
         </widget>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item>
 | 
			
		||||
         <spacer name="horizontalSpacer">
 | 
			
		||||
          <property name="orientation">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,7 @@
 | 
			
		|||
 | 
			
		||||
const PluginDescriptor AMDemodPlugin::m_pluginDescriptor = {
 | 
			
		||||
	QString("AM Demodulator"),
 | 
			
		||||
	QString("3.14.5"),
 | 
			
		||||
	QString("3.14.7"),
 | 
			
		||||
	QString("(c) Edouard Griffiths, F4EXB"),
 | 
			
		||||
	QString("https://github.com/f4exb/sdrangel"),
 | 
			
		||||
	true,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,6 +38,7 @@ void AMDemodSettings::resetToDefaults()
 | 
			
		|||
    m_rgbColor = QColor(255, 255, 0).rgb();
 | 
			
		||||
    m_title = "AM Demodulator";
 | 
			
		||||
    m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName;
 | 
			
		||||
    m_pll = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QByteArray AMDemodSettings::serialize() const
 | 
			
		||||
| 
						 | 
				
			
			@ -56,6 +57,7 @@ QByteArray AMDemodSettings::serialize() const
 | 
			
		|||
    s.writeBool(8, m_bandpassEnable);
 | 
			
		||||
    s.writeString(9, m_title);
 | 
			
		||||
    s.writeString(11, m_audioDeviceName);
 | 
			
		||||
    s.writeBool(12, m_pll);
 | 
			
		||||
 | 
			
		||||
    return s.final();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -93,6 +95,7 @@ bool AMDemodSettings::deserialize(const QByteArray& data)
 | 
			
		|||
        d.readBool(8, &m_bandpassEnable, false);
 | 
			
		||||
        d.readString(9, &m_title, "AM Demodulator");
 | 
			
		||||
        d.readString(11, &m_audioDeviceName, AudioDeviceManager::m_defaultDeviceName);
 | 
			
		||||
        d.readBool(12, &m_pll, false);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,6 +33,7 @@ struct AMDemodSettings
 | 
			
		|||
    QString m_title;
 | 
			
		||||
    Serializable *m_channelMarker;
 | 
			
		||||
    QString m_audioDeviceName;
 | 
			
		||||
    bool m_pll;
 | 
			
		||||
 | 
			
		||||
    AMDemodSettings();
 | 
			
		||||
    void resetToDefaults();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -298,7 +298,7 @@ int fftfilt::runSSB(const cmplx & in, cmplx **out, bool usb, bool getDC)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Version for double sideband. You have to double the FFT size used for SSB.
 | 
			
		||||
int fftfilt::runDSB(const cmplx & in, cmplx **out)
 | 
			
		||||
int fftfilt::runDSB(const cmplx & in, cmplx **out, bool getDC)
 | 
			
		||||
{
 | 
			
		||||
	data[inptr++] = in;
 | 
			
		||||
	if (inptr < flen2)
 | 
			
		||||
| 
						 | 
				
			
			@ -312,6 +312,9 @@ int fftfilt::runDSB(const cmplx & in, cmplx **out)
 | 
			
		|||
		data[flen2 + i] *= filter[flen2 + i];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    // get or reject DC component
 | 
			
		||||
    data[0] = getDC ? data[0] : 0;
 | 
			
		||||
 | 
			
		||||
	// in-place FFT: freqdata overwritten with filtered timedata
 | 
			
		||||
	fft->InverseComplexFFT(data);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ public:
 | 
			
		|||
	int noFilt(const cmplx& in, cmplx **out);
 | 
			
		||||
	int runFilt(const cmplx& in, cmplx **out);
 | 
			
		||||
	int runSSB(const cmplx& in, cmplx **out, bool usb, bool getDC = true);
 | 
			
		||||
	int runDSB(const cmplx& in, cmplx **out);
 | 
			
		||||
	int runDSB(const cmplx& in, cmplx **out, bool getDC = true);
 | 
			
		||||
	int runAsym(const cmplx & in, cmplx **out, bool usb); //!< Asymmetrical fitering can be used for vestigial sideband
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Ładowanie…
	
		Reference in New Issue