1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-12 19:36:10 -05:00

ATV Modulator: use assymetrical filter for vestigial sideband modulation

This commit is contained in:
f4exb 2017-03-16 10:11:03 +01:00
parent 94d72d5ba5
commit b079fae843
5 changed files with 102 additions and 47 deletions

View File

@ -98,6 +98,7 @@ ATVMod::~ATVMod()
void ATVMod::configure(MessageQueue* messageQueue, void ATVMod::configure(MessageQueue* messageQueue,
Real rfBandwidth, Real rfBandwidth,
Real rfOppBandwidth,
ATVStd atvStd, ATVStd atvStd,
ATVModInput atvModInput, ATVModInput atvModInput,
Real uniformLevel, Real uniformLevel,
@ -105,11 +106,11 @@ void ATVMod::configure(MessageQueue* messageQueue,
bool videoPlayLoop, bool videoPlayLoop,
bool videoPlay, bool videoPlay,
bool cameraPlay, bool cameraPlay,
bool channelMute, bool channelMute)
Real vestigialRatio)
{ {
Message* cmd = MsgConfigureATVMod::create( Message* cmd = MsgConfigureATVMod::create(
rfBandwidth, rfBandwidth,
rfOppBandwidth,
atvStd, atvStd,
atvModInput, atvModInput,
uniformLevel, uniformLevel,
@ -117,8 +118,7 @@ void ATVMod::configure(MessageQueue* messageQueue,
videoPlayLoop, videoPlayLoop,
videoPlay, videoPlay,
cameraPlay, cameraPlay,
channelMute, channelMute);
vestigialRatio);
messageQueue->push(cmd); messageQueue->push(cmd);
} }
@ -240,7 +240,7 @@ Complex& ATVMod::modulateVestigialSSB(Real& sample)
Complex ci(sample, 0.0f); Complex ci(sample, 0.0f);
fftfilt::cmplx *filtered; fftfilt::cmplx *filtered;
n_out = m_DSBFilter->runVestigial(ci, &filtered, m_running.m_atvModulation == ATVModulationVestigialUSB, m_running.m_vestigialRatio); n_out = m_DSBFilter->runAsym(ci, &filtered, m_running.m_atvModulation == ATVModulationVestigialUSB);
if (n_out > 0) if (n_out > 0)
{ {
@ -512,6 +512,7 @@ bool ATVMod::handleMessage(const Message& cmd)
MsgConfigureATVMod& cfg = (MsgConfigureATVMod&) cmd; MsgConfigureATVMod& cfg = (MsgConfigureATVMod&) cmd;
m_config.m_rfBandwidth = cfg.getRFBandwidth(); m_config.m_rfBandwidth = cfg.getRFBandwidth();
m_config.m_rfOppBandwidth = cfg.getRFOppBandwidth();
m_config.m_atvModInput = cfg.getATVModInput(); m_config.m_atvModInput = cfg.getATVModInput();
m_config.m_atvStd = cfg.getATVStd(); m_config.m_atvStd = cfg.getATVStd();
m_config.m_uniformLevel = cfg.getUniformLevel(); m_config.m_uniformLevel = cfg.getUniformLevel();
@ -520,12 +521,12 @@ bool ATVMod::handleMessage(const Message& cmd)
m_config.m_videoPlay = cfg.getVideoPlay(); m_config.m_videoPlay = cfg.getVideoPlay();
m_config.m_cameraPlay = cfg.getCameraPlay(); m_config.m_cameraPlay = cfg.getCameraPlay();
m_config.m_channelMute = cfg.getChannelMute(); m_config.m_channelMute = cfg.getChannelMute();
m_config.m_vestigialRatio = cfg.getVestigialRatio();
apply(); apply();
qDebug() << "ATVMod::handleMessage: MsgConfigureATVMod:" qDebug() << "ATVMod::handleMessage: MsgConfigureATVMod:"
<< " m_rfBandwidth: " << m_config.m_rfBandwidth << " m_rfBandwidth: " << m_config.m_rfBandwidth
<< " m_rfOppBandwidth: " << m_config.m_rfOppBandwidth
<< " m_atvStd: " << (int) m_config.m_atvStd << " m_atvStd: " << (int) m_config.m_atvStd
<< " m_atvModInput: " << (int) m_config.m_atvModInput << " m_atvModInput: " << (int) m_config.m_atvModInput
<< " m_uniformLevel: " << m_config.m_uniformLevel << " m_uniformLevel: " << m_config.m_uniformLevel
@ -533,8 +534,7 @@ bool ATVMod::handleMessage(const Message& cmd)
<< " m_videoPlayLoop: " << m_config.m_videoPlayLoop << " m_videoPlayLoop: " << m_config.m_videoPlayLoop
<< " m_videoPlay: " << m_config.m_videoPlay << " m_videoPlay: " << m_config.m_videoPlay
<< " m_cameraPlay: " << m_config.m_cameraPlay << " m_cameraPlay: " << m_config.m_cameraPlay
<< " m_channelMute: " << m_config.m_channelMute << " m_channelMute: " << m_config.m_channelMute;
<< " m_vestigialRatio: " << m_config.m_vestigialRatio;
return true; return true;
} }
@ -654,11 +654,20 @@ void ATVMod::apply(bool force)
memset(m_SSBFilterBuffer, 0, sizeof(Complex)*(m_ssbFftLen>>1)); memset(m_SSBFilterBuffer, 0, sizeof(Complex)*(m_ssbFftLen>>1));
m_SSBFilterBufferIndex = 0; m_SSBFilterBufferIndex = 0;
m_DSBFilter->create_dsb_filter(m_config.m_rfBandwidth / m_config.m_outputSampleRate); applyStandard(); // set all timings
m_settingsMutex.unlock();
}
if ((m_config.m_outputSampleRate != m_running.m_outputSampleRate) ||
(m_config.m_rfOppBandwidth != m_running.m_rfOppBandwidth) ||
(m_config.m_rfBandwidth != m_running.m_rfBandwidth) || force)
{
m_settingsMutex.lock();
m_DSBFilter->create_asym_filter(m_config.m_rfOppBandwidth / m_config.m_outputSampleRate, m_config.m_rfBandwidth / m_config.m_outputSampleRate);
memset(m_DSBFilterBuffer, 0, sizeof(Complex)*(m_ssbFftLen)); memset(m_DSBFilterBuffer, 0, sizeof(Complex)*(m_ssbFftLen));
m_DSBFilterBufferIndex = 0; m_DSBFilterBufferIndex = 0;
applyStandard(); // set all timings
m_settingsMutex.unlock(); m_settingsMutex.unlock();
} }
@ -673,6 +682,7 @@ void ATVMod::apply(bool force)
m_running.m_outputSampleRate = m_config.m_outputSampleRate; m_running.m_outputSampleRate = m_config.m_outputSampleRate;
m_running.m_inputFrequencyOffset = m_config.m_inputFrequencyOffset; m_running.m_inputFrequencyOffset = m_config.m_inputFrequencyOffset;
m_running.m_rfBandwidth = m_config.m_rfBandwidth; m_running.m_rfBandwidth = m_config.m_rfBandwidth;
m_running.m_rfOppBandwidth = m_config.m_rfOppBandwidth;
m_running.m_atvModInput = m_config.m_atvModInput; m_running.m_atvModInput = m_config.m_atvModInput;
m_running.m_atvStd = m_config.m_atvStd; m_running.m_atvStd = m_config.m_atvStd;
m_running.m_uniformLevel = m_config.m_uniformLevel; m_running.m_uniformLevel = m_config.m_uniformLevel;
@ -681,7 +691,6 @@ void ATVMod::apply(bool force)
m_running.m_videoPlay = m_config.m_videoPlay; m_running.m_videoPlay = m_config.m_videoPlay;
m_running.m_cameraPlay = m_config.m_cameraPlay; m_running.m_cameraPlay = m_config.m_cameraPlay;
m_running.m_channelMute = m_config.m_channelMute; m_running.m_channelMute = m_config.m_channelMute;
m_running.m_vestigialRatio = m_config.m_vestigialRatio;
} }
int ATVMod::getSampleRateUnits(ATVStd std) int ATVMod::getSampleRateUnits(ATVStd std)

View File

@ -308,6 +308,7 @@ public:
void configure(MessageQueue* messageQueue, void configure(MessageQueue* messageQueue,
Real rfBandwidth, Real rfBandwidth,
Real rfOppBandwidth,
ATVStd atvStd, ATVStd atvStd,
ATVModInput atvModInput, ATVModInput atvModInput,
Real uniformLevel, Real uniformLevel,
@ -315,8 +316,7 @@ public:
bool videoPlayLoop, bool videoPlayLoop,
bool videoPlay, bool videoPlay,
bool cameraPLay, bool cameraPLay,
bool channelMute, bool channelMute);
Real vestigialRatio);
virtual void pull(Sample& sample); virtual void pull(Sample& sample);
virtual void pullAudio(int nbSamples); // this is used for video signal actually virtual void pullAudio(int nbSamples); // this is used for video signal actually
@ -346,6 +346,7 @@ private:
public: public:
Real getRFBandwidth() const { return m_rfBandwidth; } Real getRFBandwidth() const { return m_rfBandwidth; }
Real getRFOppBandwidth() const { return m_rfOppBandwidth; }
ATVStd getATVStd() const { return m_atvStd; } ATVStd getATVStd() const { return m_atvStd; }
ATVModInput getATVModInput() const { return m_atvModInput; } ATVModInput getATVModInput() const { return m_atvModInput; }
Real getUniformLevel() const { return m_uniformLevel; } Real getUniformLevel() const { return m_uniformLevel; }
@ -354,10 +355,10 @@ private:
bool getVideoPlay() const { return m_videoPlay; } bool getVideoPlay() const { return m_videoPlay; }
bool getCameraPlay() const { return m_cameraPlay; } bool getCameraPlay() const { return m_cameraPlay; }
bool getChannelMute() const { return m_channelMute; } bool getChannelMute() const { return m_channelMute; }
Real getVestigialRatio() const { return m_vestigialRatio; }
static MsgConfigureATVMod* create( static MsgConfigureATVMod* create(
Real rfBandwidth, Real rfBandwidth,
Real rfOppBandwidth,
ATVStd atvStd, ATVStd atvStd,
ATVModInput atvModInput, ATVModInput atvModInput,
Real uniformLevel, Real uniformLevel,
@ -365,11 +366,11 @@ private:
bool videoPlayLoop, bool videoPlayLoop,
bool videoPlay, bool videoPlay,
bool cameraPlay, bool cameraPlay,
bool channelMute, bool channelMute)
Real vestigialRatio)
{ {
return new MsgConfigureATVMod( return new MsgConfigureATVMod(
rfBandwidth, rfBandwidth,
rfOppBandwidth,
atvStd, atvStd,
atvModInput, atvModInput,
uniformLevel, uniformLevel,
@ -377,12 +378,12 @@ private:
videoPlayLoop, videoPlayLoop,
videoPlay, videoPlay,
cameraPlay, cameraPlay,
channelMute, channelMute);
vestigialRatio);
} }
private: private:
Real m_rfBandwidth; Real m_rfBandwidth;
Real m_rfOppBandwidth;
ATVStd m_atvStd; ATVStd m_atvStd;
ATVModInput m_atvModInput; ATVModInput m_atvModInput;
Real m_uniformLevel; Real m_uniformLevel;
@ -391,10 +392,10 @@ private:
bool m_videoPlay; bool m_videoPlay;
bool m_cameraPlay; bool m_cameraPlay;
bool m_channelMute; bool m_channelMute;
Real m_vestigialRatio;
MsgConfigureATVMod( MsgConfigureATVMod(
Real rfBandwidth, Real rfBandwidth,
Real rfOppBandwidth,
ATVStd atvStd, ATVStd atvStd,
ATVModInput atvModInput, ATVModInput atvModInput,
Real uniformLevel, Real uniformLevel,
@ -402,10 +403,10 @@ private:
bool videoPlayLoop, bool videoPlayLoop,
bool videoPlay, bool videoPlay,
bool cameraPlay, bool cameraPlay,
bool channelMute, bool channelMute) :
Real vestigialRatio) :
Message(), Message(),
m_rfBandwidth(rfBandwidth), m_rfBandwidth(rfBandwidth),
m_rfOppBandwidth(rfOppBandwidth),
m_atvStd(atvStd), m_atvStd(atvStd),
m_atvModInput(atvModInput), m_atvModInput(atvModInput),
m_uniformLevel(uniformLevel), m_uniformLevel(uniformLevel),
@ -413,8 +414,7 @@ private:
m_videoPlayLoop(videoPlayLoop), m_videoPlayLoop(videoPlayLoop),
m_videoPlay(videoPlay), m_videoPlay(videoPlay),
m_cameraPlay(cameraPlay), m_cameraPlay(cameraPlay),
m_channelMute(channelMute), m_channelMute(channelMute)
m_vestigialRatio(vestigialRatio)
{ } { }
}; };
@ -450,7 +450,8 @@ private:
{ {
int m_outputSampleRate; //!< sample rate from channelizer int m_outputSampleRate; //!< sample rate from channelizer
qint64 m_inputFrequencyOffset; //!< offset from baseband center frequency qint64 m_inputFrequencyOffset; //!< offset from baseband center frequency
Real m_rfBandwidth; //!< Bandwidth of modulated signal Real m_rfBandwidth; //!< Bandwidth of modulated signal or direct sideband for SSB / vestigial SSB
Real m_rfOppBandwidth; //!< Bandwidth of opposite sideband for vestigial SSB
ATVStd m_atvStd; //!< Standard ATVStd m_atvStd; //!< Standard
ATVModInput m_atvModInput; //!< Input source type ATVModInput m_atvModInput; //!< Input source type
Real m_uniformLevel; //!< Percentage between black and white for uniform screen display Real m_uniformLevel; //!< Percentage between black and white for uniform screen display
@ -465,6 +466,7 @@ private:
m_outputSampleRate(-1), m_outputSampleRate(-1),
m_inputFrequencyOffset(0), m_inputFrequencyOffset(0),
m_rfBandwidth(0), m_rfBandwidth(0),
m_rfOppBandwidth(0),
m_atvStd(ATVStdPAL625), m_atvStd(ATVStdPAL625),
m_atvModInput(ATVModInputHBars), m_atvModInput(ATVModInputHBars),
m_uniformLevel(0.5f), m_uniformLevel(0.5f),
@ -542,12 +544,16 @@ private:
std::string m_overlayText; std::string m_overlayText;
bool m_showOverlayText; bool m_showOverlayText;
// Used for standard SSB
fftfilt* m_SSBFilter; fftfilt* m_SSBFilter;
fftfilt* m_DSBFilter;
Complex* m_SSBFilterBuffer; Complex* m_SSBFilterBuffer;
Complex* m_DSBFilterBuffer;
int m_SSBFilterBufferIndex; int m_SSBFilterBufferIndex;
// Used for vestigial SSB with asymmetrical filtering (needs double sideband scheme)
fftfilt* m_DSBFilter;
Complex* m_DSBFilterBuffer;
int m_DSBFilterBufferIndex; int m_DSBFilterBufferIndex;
static const int m_ssbFftLen; static const int m_ssbFftLen;
static const float m_blackLevel; static const float m_blackLevel;

View File

@ -512,6 +512,7 @@ void ATVModGUI::applySettings()
m_atvMod->configure(m_atvMod->getInputMessageQueue(), m_atvMod->configure(m_atvMod->getInputMessageQueue(),
ui->rfBW->value() * 100000.0f, ui->rfBW->value() * 100000.0f,
(ui->vestigial->value() / 100.0f) * ui->rfBW->value() * 100000.0f,
(ATVMod::ATVStd) ui->standard->currentIndex(), (ATVMod::ATVStd) ui->standard->currentIndex(),
(ATVMod::ATVModInput) ui->inputSelect->currentIndex(), (ATVMod::ATVModInput) ui->inputSelect->currentIndex(),
ui->uniformLevel->value() / 100.0f, ui->uniformLevel->value() / 100.0f,
@ -519,8 +520,7 @@ void ATVModGUI::applySettings()
ui->playLoop->isChecked(), ui->playLoop->isChecked(),
ui->playVideo->isChecked(), ui->playVideo->isChecked(),
ui->playCamera->isChecked(), ui->playCamera->isChecked(),
ui->channelMute->isChecked(), ui->channelMute->isChecked());
ui->vestigial->value() / 100.0f);
} }
} }

View File

@ -54,11 +54,13 @@ void fftfilt::init_filter()
fft = new g_fft<float>(flen); fft = new g_fft<float>(flen);
filter = new cmplx[flen]; filter = new cmplx[flen];
filterOpp = new cmplx[flen];
data = new cmplx[flen]; data = new cmplx[flen];
output = new cmplx[flen2]; output = new cmplx[flen2];
ovlbuf = new cmplx[flen2]; ovlbuf = new cmplx[flen2];
memset(filter, 0, flen * sizeof(cmplx)); memset(filter, 0, flen * sizeof(cmplx));
memset(filterOpp, 0, flen * sizeof(cmplx));
memset(data, 0, flen * sizeof(cmplx)); memset(data, 0, flen * sizeof(cmplx));
memset(output, 0, flen2 * sizeof(cmplx)); memset(output, 0, flen2 * sizeof(cmplx));
memset(ovlbuf, 0, flen2 * sizeof(cmplx)); memset(ovlbuf, 0, flen2 * sizeof(cmplx));
@ -92,6 +94,7 @@ fftfilt::~fftfilt()
if (fft) delete fft; if (fft) delete fft;
if (filter) delete [] filter; if (filter) delete [] filter;
if (filterOpp) delete [] filterOpp;
if (data) delete [] data; if (data) delete [] data;
if (output) delete [] output; if (output) delete [] output;
if (ovlbuf) delete [] ovlbuf; if (ovlbuf) delete [] ovlbuf;
@ -162,6 +165,55 @@ void fftfilt::create_dsb_filter(float f2)
} }
} }
// Double the size of FFT used for equivalent SSB filter or assume FFT is half the size of the one used for SSB
// used with runAsym for in band / opposite band asymmetrical filtering. Can be used for vestigial sideband modulation.
void fftfilt::create_asym_filter(float fopp, float fin)
{
// in band
// initialize the filter to zero
memset(filter, 0, flen * sizeof(cmplx));
for (int i = 0; i < flen2; i++) {
filter[i] = fsinc(fin, i, flen2);
filter[i] *= _blackman(i, flen2);
}
fft->ComplexFFT(filter);
// normalize the output filter for unity gain
float scale = 0, mag;
for (int i = 0; i < flen2; i++) {
mag = abs(filter[i]);
if (mag > scale) scale = mag;
}
if (scale != 0) {
for (int i = 0; i < flen; i++)
filter[i] /= scale;
}
// opposite band
// initialize the filter to zero
memset(filterOpp, 0, flen * sizeof(cmplx));
for (int i = 0; i < flen2; i++) {
filterOpp[i] = fsinc(fopp, i, flen2);
filterOpp[i] *= _blackman(i, flen2);
}
fft->ComplexFFT(filterOpp);
// normalize the output filter for unity gain
scale = 0;
for (int i = 0; i < flen2; i++) {
mag = abs(filterOpp[i]);
if (mag > scale) scale = mag;
}
if (scale != 0) {
for (int i = 0; i < flen; i++)
filterOpp[i] /= scale;
}
}
// test bypass // test bypass
int fftfilt::noFilt(const cmplx & in, cmplx **out) int fftfilt::noFilt(const cmplx & in, cmplx **out)
{ {
@ -271,13 +323,9 @@ int fftfilt::runDSB(const cmplx & in, cmplx **out)
return flen2; return flen2;
} }
// Version for vestigial sideband. You have to double the FFT size used for SSB. // Version for asymmetrical sidebands. You have to double the FFT size used for SSB.
int fftfilt::runVestigial(const cmplx & in, cmplx **out, bool usb, float vf) int fftfilt::runAsym(const cmplx & in, cmplx **out, bool usb)
{ {
if (vf < 0.0f) vf = 0.0f;
if (vf > 1.0f) vf = 1.0f;
if (usb) vf = 1.0f - vf;
data[inptr++] = in; data[inptr++] = in;
if (inptr < flen2) if (inptr < flen2)
return 0; return 0;
@ -292,24 +340,14 @@ int fftfilt::runVestigial(const cmplx & in, cmplx **out, bool usb, float vf)
for (int i = 1; i < flen2; i++) for (int i = 1; i < flen2; i++)
{ {
data[i] *= filter[i]; // usb data[i] *= filter[i]; // usb
data[flen2 + i] *= filterOpp[flen2 + i]; // lsb is the opposite
if (i > (int) (vf * flen2)) { // vestigial lsb
data[flen2 + i] *= filter[flen2 + i];
} else {
data[flen2 + i] = 0;
}
} }
} }
else else
{ {
for (int i = 1; i < flen2; i++) for (int i = 1; i < flen2; i++)
{ {
if (i < (int) (vf * flen2)) { // vestigial usb data[i] *= filterOpp[i]; // usb is the opposite
data[i] *= filter[i];
} else {
data[i] = 0;
}
data[flen2 + i] *= filter[flen2 + i]; // lsb data[flen2 + i] *= filter[flen2 + i]; // lsb
} }
} }

View File

@ -23,12 +23,13 @@ public:
// f1 > f2 ==> band reject // f1 > f2 ==> band reject
void create_filter(float f1, float f2); void create_filter(float f1, float f2);
void create_dsb_filter(float f2); void create_dsb_filter(float f2);
void create_asym_filter(float fopp, float fin); //!< two different filters for in band and opposite band
int noFilt(const cmplx& in, cmplx **out); int noFilt(const cmplx& in, cmplx **out);
int runFilt(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 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);
int runVestigial(const cmplx & in, cmplx **out, bool usb, float vf); int runAsym(const cmplx & in, cmplx **out, bool usb); //!< Asymmetrical fitering can be used for vestigial sideband
protected: protected:
int flen; int flen;
@ -36,6 +37,7 @@ protected:
g_fft<float> *fft; g_fft<float> *fft;
g_fft<float> *ift; g_fft<float> *ift;
cmplx *filter; cmplx *filter;
cmplx *filterOpp;
cmplx *data; cmplx *data;
cmplx *ovlbuf; cmplx *ovlbuf;
cmplx *output; cmplx *output;