mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-05-24 03:02:29 -04:00
Working unoptimzied WFM just mono (AF baseband)
This commit is contained in:
parent
b7c0e85329
commit
3f106ade9e
@ -5,7 +5,7 @@
|
|||||||
#ifndef _FFTFILT_H
|
#ifndef _FFTFILT_H
|
||||||
#define _FFTFILT_H
|
#define _FFTFILT_H
|
||||||
|
|
||||||
#include "complex.h"
|
#include <complex>
|
||||||
#include "gfft.h"
|
#include "gfft.h"
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
@ -13,6 +13,20 @@
|
|||||||
class fftfilt {
|
class fftfilt {
|
||||||
enum {NONE, BLACKMAN, HAMMING, HANNING};
|
enum {NONE, BLACKMAN, HAMMING, HANNING};
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef std::complex<float> cmplx;
|
||||||
|
|
||||||
|
fftfilt(float f1, float f2, int len);
|
||||||
|
fftfilt(float f, int len);
|
||||||
|
~fftfilt();
|
||||||
|
// f1 < f2 ==> bandpass
|
||||||
|
// f1 > f2 ==> band reject
|
||||||
|
void create_filter(float f1, float f2);
|
||||||
|
void rtty_filter(float);
|
||||||
|
|
||||||
|
int runFilt(const cmplx& in, cmplx **out);
|
||||||
|
int runSSB(const cmplx& in, cmplx **out, bool usb);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int flen;
|
int flen;
|
||||||
int flen2;
|
int flen2;
|
||||||
@ -36,18 +50,6 @@ protected:
|
|||||||
0.08 * cos(4.0 * M_PI * i / len));
|
0.08 * cos(4.0 * M_PI * i / len));
|
||||||
}
|
}
|
||||||
void init_filter();
|
void init_filter();
|
||||||
|
|
||||||
public:
|
|
||||||
fftfilt(float f1, float f2, int len);
|
|
||||||
fftfilt(float f, int len);
|
|
||||||
~fftfilt();
|
|
||||||
// f1 < f2 ==> bandpass
|
|
||||||
// f1 > f2 ==> band reject
|
|
||||||
void create_filter(float f1, float f2);
|
|
||||||
void rtty_filter(float);
|
|
||||||
|
|
||||||
int runFilt(const cmplx& in, cmplx **out);
|
|
||||||
int runSSB(const cmplx& in, cmplx **out, bool usb);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -55,6 +57,12 @@ public:
|
|||||||
/* Sliding FFT filter from Fldigi */
|
/* Sliding FFT filter from Fldigi */
|
||||||
class sfft {
|
class sfft {
|
||||||
#define K1 0.99999
|
#define K1 0.99999
|
||||||
|
public:
|
||||||
|
typedef std::complex<float> cmplx;
|
||||||
|
sfft(int len);
|
||||||
|
~sfft();
|
||||||
|
void run(const cmplx& input);
|
||||||
|
void fetch(float *result);
|
||||||
private:
|
private:
|
||||||
int fftlen;
|
int fftlen;
|
||||||
int first;
|
int first;
|
||||||
@ -64,11 +72,6 @@ private:
|
|||||||
vrot_bins_pair *vrot_bins;
|
vrot_bins_pair *vrot_bins;
|
||||||
cmplx *delay;
|
cmplx *delay;
|
||||||
float k2;
|
float k2;
|
||||||
public:
|
|
||||||
sfft(int len);
|
|
||||||
~sfft();
|
|
||||||
void run(const cmplx& input);
|
|
||||||
void fetch(float *result);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -5,5 +5,6 @@ add_subdirectory(am)
|
|||||||
add_subdirectory(nfm)
|
add_subdirectory(nfm)
|
||||||
add_subdirectory(ssb)
|
add_subdirectory(ssb)
|
||||||
add_subdirectory(tcpsrc)
|
add_subdirectory(tcpsrc)
|
||||||
|
add_subdirectory(wfm)
|
||||||
|
|
||||||
#add_subdirectory(tetra)
|
#add_subdirectory(tetra)
|
||||||
|
@ -62,7 +62,7 @@ void SSBDemod::configure(MessageQueue* messageQueue, Real Bandwidth, Real LowCut
|
|||||||
void SSBDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly)
|
void SSBDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly)
|
||||||
{
|
{
|
||||||
Complex ci;
|
Complex ci;
|
||||||
cmplx *sideband;
|
fftfilt::cmplx *sideband;
|
||||||
int n_out;
|
int n_out;
|
||||||
|
|
||||||
for(SampleVector::const_iterator it = begin; it < end; ++it) {
|
for(SampleVector::const_iterator it = begin; it < end; ++it) {
|
||||||
|
@ -70,7 +70,7 @@ void TCPSrc::setSpectrum(MessageQueue* messageQueue, bool enabled)
|
|||||||
void TCPSrc::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly)
|
void TCPSrc::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly)
|
||||||
{
|
{
|
||||||
Complex ci;
|
Complex ci;
|
||||||
cmplx* sideband;
|
fftfilt::cmplx* sideband;
|
||||||
Real l, r;
|
Real l, r;
|
||||||
|
|
||||||
m_sampleBuffer.clear();
|
m_sampleBuffer.clear();
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
#include "dsp/pidcontroller.h"
|
#include "dsp/pidcontroller.h"
|
||||||
#include "wfmdemod.h"
|
#include "wfmdemod.h"
|
||||||
|
|
||||||
#include <iostream>
|
//#include <iostream>
|
||||||
|
|
||||||
MESSAGE_CLASS_DEFINITION(WFMDemod::MsgConfigureWFMDemod, Message)
|
MESSAGE_CLASS_DEFINITION(WFMDemod::MsgConfigureWFMDemod, Message)
|
||||||
|
|
||||||
@ -31,13 +31,14 @@ WFMDemod::WFMDemod(AudioFifo* audioFifo, SampleSink* sampleSink) :
|
|||||||
m_sampleSink(sampleSink),
|
m_sampleSink(sampleSink),
|
||||||
m_audioFifo(audioFifo)
|
m_audioFifo(audioFifo)
|
||||||
{
|
{
|
||||||
m_config.m_inputSampleRate = 96000;
|
m_config.m_inputSampleRate = 384000;
|
||||||
m_config.m_inputFrequencyOffset = 0;
|
m_config.m_inputFrequencyOffset = 0;
|
||||||
m_config.m_rfBandwidth = 180000;
|
m_config.m_rfBandwidth = 180000;
|
||||||
m_config.m_afBandwidth = 15000;
|
m_config.m_afBandwidth = 15000;
|
||||||
m_config.m_squelch = -60.0;
|
m_config.m_squelch = -60.0;
|
||||||
m_config.m_volume = 2.0;
|
m_config.m_volume = 2.0;
|
||||||
m_config.m_audioSampleRate = 48000;
|
m_config.m_audioSampleRate = 48000;
|
||||||
|
m_rfFilter = new fftfilt(-50000.0 / 384000.0, 50000.0 / 384000.0, rfFilterFftLength);
|
||||||
|
|
||||||
apply();
|
apply();
|
||||||
|
|
||||||
@ -49,6 +50,8 @@ WFMDemod::WFMDemod(AudioFifo* audioFifo, SampleSink* sampleSink) :
|
|||||||
|
|
||||||
WFMDemod::~WFMDemod()
|
WFMDemod::~WFMDemod()
|
||||||
{
|
{
|
||||||
|
if (m_rfFilter)
|
||||||
|
delete m_rfFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WFMDemod::configure(MessageQueue* messageQueue, Real rfBandwidth, Real afBandwidth, Real volume, Real squelch)
|
void WFMDemod::configure(MessageQueue* messageQueue, Real rfBandwidth, Real afBandwidth, Real volume, Real squelch)
|
||||||
@ -92,16 +95,51 @@ Real angleDist(Real a, Real b)
|
|||||||
void WFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst)
|
void WFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst)
|
||||||
{
|
{
|
||||||
Complex ci;
|
Complex ci;
|
||||||
|
fftfilt::cmplx *rf;
|
||||||
|
int rf_out;
|
||||||
|
|
||||||
if (m_audioFifo->size() <= 0)
|
if (m_audioFifo->size() <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for(SampleVector::const_iterator it = begin; it != end; ++it) {
|
for (SampleVector::const_iterator it = begin; it != end; ++it)
|
||||||
|
{
|
||||||
Complex c(it->real() / 32768.0, it->imag() / 32768.0);
|
Complex c(it->real() / 32768.0, it->imag() / 32768.0);
|
||||||
c *= m_nco.nextIQ();
|
c *= m_nco.nextIQ();
|
||||||
|
|
||||||
|
rf_out = m_rfFilter->runFilt(c, &rf); // filter RF before demod
|
||||||
|
|
||||||
|
for (int i =0 ; i <rf_out; i++)
|
||||||
{
|
{
|
||||||
if(m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci))
|
Real x = rf[i].real() * m_lastSample.real() + rf[i].imag() * m_lastSample.imag();
|
||||||
|
Real y = rf[i].real() * m_lastSample.imag() - rf[i].imag() * m_lastSample.real();
|
||||||
|
Real demod = atan2(x,y) / M_PI;
|
||||||
|
m_lastSample = rf[i];
|
||||||
|
|
||||||
|
Complex e(demod, 0);
|
||||||
|
|
||||||
|
if(m_interpolator.interpolate(&m_interpolatorDistanceRemain, e, &ci))
|
||||||
|
{
|
||||||
|
quint16 sample = (qint16)(ci.real() * 3000 * m_running.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_interpolatorDistanceRemain += m_interpolatorDistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
if(m_interpolator.interpolate(&m_interpolatorDistanceRemain, e, &ci))
|
||||||
{
|
{
|
||||||
m_sampleBuffer.push_back(Sample(ci.real() * 32767.0, ci.imag() * 32767.0));
|
m_sampleBuffer.push_back(Sample(ci.real() * 32767.0, ci.imag() * 32767.0));
|
||||||
|
|
||||||
@ -113,10 +151,12 @@ void WFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter
|
|||||||
if(m_squelchState > 0) {
|
if(m_squelchState > 0) {
|
||||||
m_squelchState--;
|
m_squelchState--;
|
||||||
|
|
||||||
|
/*
|
||||||
Real argument = arg(ci);
|
Real argument = arg(ci);
|
||||||
argument /= M_PI;
|
argument /= M_PI;
|
||||||
Real demod = argument - m_lastArgument;
|
Real demod = argument - m_lastArgument;
|
||||||
m_lastArgument = argument;
|
m_lastArgument = argument;
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
//ci *= 32768.0;
|
//ci *= 32768.0;
|
||||||
@ -178,7 +218,10 @@ void WFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter
|
|||||||
m_interpolatorDistanceRemain += m_interpolatorDistance;
|
m_interpolatorDistanceRemain += m_interpolatorDistance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_audioBufferFill > 0) {
|
if(m_audioBufferFill > 0) {
|
||||||
uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1);
|
uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1);
|
||||||
if(res != m_audioBufferFill)
|
if(res != m_audioBufferFill)
|
||||||
@ -234,20 +277,31 @@ void WFMDemod::apply()
|
|||||||
{
|
{
|
||||||
|
|
||||||
if((m_config.m_inputFrequencyOffset != m_running.m_inputFrequencyOffset) ||
|
if((m_config.m_inputFrequencyOffset != m_running.m_inputFrequencyOffset) ||
|
||||||
(m_config.m_inputSampleRate != m_running.m_inputSampleRate)) {
|
(m_config.m_inputSampleRate != m_running.m_inputSampleRate))
|
||||||
|
{
|
||||||
m_nco.setFreq(-m_config.m_inputFrequencyOffset, m_config.m_inputSampleRate);
|
m_nco.setFreq(-m_config.m_inputFrequencyOffset, m_config.m_inputSampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
if((m_config.m_inputSampleRate != m_running.m_inputSampleRate) ||
|
if((m_config.m_inputSampleRate != m_running.m_inputSampleRate) ||
|
||||||
(m_config.m_rfBandwidth != m_running.m_rfBandwidth)) {
|
(m_config.m_afBandwidth != m_running.m_afBandwidth))
|
||||||
std::cerr << "WFMDemod::apply: in=" << m_config.m_inputSampleRate << ", rf=" << m_config.m_rfBandwidth << std::endl;
|
{
|
||||||
m_interpolator.create(16, m_config.m_inputSampleRate, m_config.m_rfBandwidth / 2.2);
|
m_interpolator.create(16, m_config.m_inputSampleRate, m_config.m_afBandwidth);
|
||||||
m_interpolatorDistanceRemain = 0;
|
m_interpolatorDistanceRemain = (Real) m_config.m_inputSampleRate / m_config.m_audioSampleRate;
|
||||||
m_interpolatorDistance = m_config.m_inputSampleRate / m_config.m_audioSampleRate;
|
m_interpolatorDistance = m_config.m_inputSampleRate / m_config.m_audioSampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if((m_config.m_inputSampleRate != m_running.m_inputSampleRate) ||
|
||||||
|
(m_config.m_rfBandwidth != m_running.m_rfBandwidth) ||
|
||||||
|
(m_config.m_inputFrequencyOffset != m_running.m_inputFrequencyOffset))
|
||||||
|
{
|
||||||
|
Real lowCut = (m_config.m_inputFrequencyOffset - (m_config.m_rfBandwidth / 2.0)) / m_config.m_inputSampleRate;
|
||||||
|
Real hiCut = (m_config.m_inputFrequencyOffset + (m_config.m_rfBandwidth / 2.0)) / m_config.m_inputSampleRate;
|
||||||
|
m_rfFilter->create_filter(lowCut, hiCut);
|
||||||
|
}
|
||||||
|
|
||||||
if((m_config.m_afBandwidth != m_running.m_afBandwidth) ||
|
if((m_config.m_afBandwidth != m_running.m_afBandwidth) ||
|
||||||
(m_config.m_audioSampleRate != m_running.m_audioSampleRate)) {
|
(m_config.m_audioSampleRate != m_running.m_audioSampleRate))
|
||||||
|
{
|
||||||
m_lowpass.create(21, m_config.m_audioSampleRate, m_config.m_afBandwidth);
|
m_lowpass.create(21, m_config.m_audioSampleRate, m_config.m_afBandwidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,4 +316,13 @@ void WFMDemod::apply()
|
|||||||
m_running.m_squelch = m_config.m_squelch;
|
m_running.m_squelch = m_config.m_squelch;
|
||||||
m_running.m_volume = m_config.m_volume;
|
m_running.m_volume = m_config.m_volume;
|
||||||
m_running.m_audioSampleRate = m_config.m_audioSampleRate;
|
m_running.m_audioSampleRate = m_config.m_audioSampleRate;
|
||||||
|
|
||||||
|
/*
|
||||||
|
std::cerr << "WFMDemod::apply: in=" << m_config.m_inputSampleRate
|
||||||
|
<< ", df=" << m_config.m_inputFrequencyOffset
|
||||||
|
<< ", rfbw=" << m_config.m_rfBandwidth
|
||||||
|
<< ", afbw=" << m_config.m_afBandwidth
|
||||||
|
<< std::endl;
|
||||||
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,12 @@
|
|||||||
#include "dsp/interpolator.h"
|
#include "dsp/interpolator.h"
|
||||||
#include "dsp/lowpass.h"
|
#include "dsp/lowpass.h"
|
||||||
#include "dsp/movingaverage.h"
|
#include "dsp/movingaverage.h"
|
||||||
|
#include "dsp/fftfilt.h"
|
||||||
#include "audio/audiofifo.h"
|
#include "audio/audiofifo.h"
|
||||||
#include "util/message.h"
|
#include "util/message.h"
|
||||||
|
|
||||||
|
#define rfFilterFftLength 1024
|
||||||
|
|
||||||
class AudioFifo;
|
class AudioFifo;
|
||||||
|
|
||||||
class WFMDemod : public SampleSink {
|
class WFMDemod : public SampleSink {
|
||||||
@ -111,6 +114,7 @@ private:
|
|||||||
Real m_interpolatorDistance;
|
Real m_interpolatorDistance;
|
||||||
Real m_interpolatorDistanceRemain;
|
Real m_interpolatorDistanceRemain;
|
||||||
Lowpass<Real> m_lowpass;
|
Lowpass<Real> m_lowpass;
|
||||||
|
fftfilt* m_rfFilter;
|
||||||
|
|
||||||
Real m_squelchLevel;
|
Real m_squelchLevel;
|
||||||
int m_squelchState;
|
int m_squelchState;
|
||||||
|
@ -17,6 +17,16 @@ const int WFMDemodGUI::m_rfBW[] = {
|
|||||||
48000, 80000, 120000, 140000, 160000, 180000, 200000, 220000, 250000
|
48000, 80000, 120000, 140000, 160000, 180000, 200000, 220000, 250000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int requiredBW(int rfBW)
|
||||||
|
{
|
||||||
|
if (rfBW <= 48000)
|
||||||
|
return 48000;
|
||||||
|
else if (rfBW < 100000)
|
||||||
|
return 96000;
|
||||||
|
else
|
||||||
|
return 384000;
|
||||||
|
}
|
||||||
|
|
||||||
WFMDemodGUI* WFMDemodGUI::create(PluginAPI* pluginAPI)
|
WFMDemodGUI* WFMDemodGUI::create(PluginAPI* pluginAPI)
|
||||||
{
|
{
|
||||||
WFMDemodGUI* gui = new WFMDemodGUI(pluginAPI);
|
WFMDemodGUI* gui = new WFMDemodGUI(pluginAPI);
|
||||||
@ -208,7 +218,7 @@ void WFMDemodGUI::applySettings()
|
|||||||
{
|
{
|
||||||
setTitleColor(m_channelMarker->getColor());
|
setTitleColor(m_channelMarker->getColor());
|
||||||
m_channelizer->configure(m_threadedSampleSink->getMessageQueue(),
|
m_channelizer->configure(m_threadedSampleSink->getMessageQueue(),
|
||||||
256000, // TODO: this is where requested sample rate is specified
|
requiredBW(m_rfBW[ui->rfBW->value()]), // TODO: this is where requested sample rate is specified
|
||||||
m_channelMarker->getCenterFrequency());
|
m_channelMarker->getCenterFrequency());
|
||||||
ui->deltaFrequency->setValue(abs(m_channelMarker->getCenterFrequency()));
|
ui->deltaFrequency->setValue(abs(m_channelMarker->getCenterFrequency()));
|
||||||
ui->deltaMinus->setChecked(m_channelMarker->getCenterFrequency() < 0);
|
ui->deltaMinus->setChecked(m_channelMarker->getCenterFrequency() < 0);
|
||||||
|
@ -96,11 +96,17 @@ bool Channelizer::handleMessage(Message* cmd)
|
|||||||
void Channelizer::applyConfiguration()
|
void Channelizer::applyConfiguration()
|
||||||
{
|
{
|
||||||
freeFilterChain();
|
freeFilterChain();
|
||||||
//std::cerr << "Channelizer::applyConfiguration in=" << m_inputSampleRate << ", req=" << m_requestedOutputSampleRate << std::endl;
|
|
||||||
m_currentCenterFrequency = createFilterChain(
|
m_currentCenterFrequency = createFilterChain(
|
||||||
m_inputSampleRate / -2, m_inputSampleRate / 2,
|
m_inputSampleRate / -2, m_inputSampleRate / 2,
|
||||||
m_requestedCenterFrequency - m_requestedOutputSampleRate / 2, m_requestedCenterFrequency + m_requestedOutputSampleRate / 2);
|
m_requestedCenterFrequency - m_requestedOutputSampleRate / 2, m_requestedCenterFrequency + m_requestedOutputSampleRate / 2);
|
||||||
m_currentOutputSampleRate = m_inputSampleRate / (1 << m_filterStages.size());
|
m_currentOutputSampleRate = m_inputSampleRate / (1 << m_filterStages.size());
|
||||||
|
/*
|
||||||
|
std::cerr << "Channelizer::applyConfiguration in=" << m_inputSampleRate
|
||||||
|
<< ", req=" << m_requestedOutputSampleRate
|
||||||
|
<< ", out=" << m_currentOutputSampleRate
|
||||||
|
<< ", fc=" << m_currentCenterFrequency
|
||||||
|
<< std::endl;
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
Channelizer::FilterStage::FilterStage(Mode mode) :
|
Channelizer::FilterStage::FilterStage(Mode mode) :
|
||||||
|
Loading…
x
Reference in New Issue
Block a user