1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-10 10:33:29 -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,
Real rfBandwidth,
Real rfOppBandwidth,
ATVStd atvStd,
ATVModInput atvModInput,
Real uniformLevel,
@ -105,11 +106,11 @@ void ATVMod::configure(MessageQueue* messageQueue,
bool videoPlayLoop,
bool videoPlay,
bool cameraPlay,
bool channelMute,
Real vestigialRatio)
bool channelMute)
{
Message* cmd = MsgConfigureATVMod::create(
rfBandwidth,
rfOppBandwidth,
atvStd,
atvModInput,
uniformLevel,
@ -117,8 +118,7 @@ void ATVMod::configure(MessageQueue* messageQueue,
videoPlayLoop,
videoPlay,
cameraPlay,
channelMute,
vestigialRatio);
channelMute);
messageQueue->push(cmd);
}
@ -240,7 +240,7 @@ Complex& ATVMod::modulateVestigialSSB(Real& sample)
Complex ci(sample, 0.0f);
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)
{
@ -512,6 +512,7 @@ bool ATVMod::handleMessage(const Message& cmd)
MsgConfigureATVMod& cfg = (MsgConfigureATVMod&) cmd;
m_config.m_rfBandwidth = cfg.getRFBandwidth();
m_config.m_rfOppBandwidth = cfg.getRFOppBandwidth();
m_config.m_atvModInput = cfg.getATVModInput();
m_config.m_atvStd = cfg.getATVStd();
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_cameraPlay = cfg.getCameraPlay();
m_config.m_channelMute = cfg.getChannelMute();
m_config.m_vestigialRatio = cfg.getVestigialRatio();
apply();
qDebug() << "ATVMod::handleMessage: MsgConfigureATVMod:"
<< " m_rfBandwidth: " << m_config.m_rfBandwidth
<< " m_rfOppBandwidth: " << m_config.m_rfOppBandwidth
<< " m_atvStd: " << (int) m_config.m_atvStd
<< " m_atvModInput: " << (int) m_config.m_atvModInput
<< " m_uniformLevel: " << m_config.m_uniformLevel
@ -533,8 +534,7 @@ bool ATVMod::handleMessage(const Message& cmd)
<< " m_videoPlayLoop: " << m_config.m_videoPlayLoop
<< " m_videoPlay: " << m_config.m_videoPlay
<< " m_cameraPlay: " << m_config.m_cameraPlay
<< " m_channelMute: " << m_config.m_channelMute
<< " m_vestigialRatio: " << m_config.m_vestigialRatio;
<< " m_channelMute: " << m_config.m_channelMute;
return true;
}
@ -654,11 +654,20 @@ void ATVMod::apply(bool force)
memset(m_SSBFilterBuffer, 0, sizeof(Complex)*(m_ssbFftLen>>1));
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));
m_DSBFilterBufferIndex = 0;
applyStandard(); // set all timings
m_settingsMutex.unlock();
}
@ -673,6 +682,7 @@ void ATVMod::apply(bool force)
m_running.m_outputSampleRate = m_config.m_outputSampleRate;
m_running.m_inputFrequencyOffset = m_config.m_inputFrequencyOffset;
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_atvStd = m_config.m_atvStd;
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_cameraPlay = m_config.m_cameraPlay;
m_running.m_channelMute = m_config.m_channelMute;
m_running.m_vestigialRatio = m_config.m_vestigialRatio;
}
int ATVMod::getSampleRateUnits(ATVStd std)

View File

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

View File

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

View File

@ -54,11 +54,13 @@ void fftfilt::init_filter()
fft = new g_fft<float>(flen);
filter = new cmplx[flen];
filterOpp = new cmplx[flen];
data = new cmplx[flen];
output = new cmplx[flen2];
ovlbuf = new cmplx[flen2];
memset(filter, 0, flen * sizeof(cmplx));
memset(filterOpp, 0, flen * sizeof(cmplx));
memset(data, 0, flen * sizeof(cmplx));
memset(output, 0, flen2 * sizeof(cmplx));
memset(ovlbuf, 0, flen2 * sizeof(cmplx));
@ -92,6 +94,7 @@ fftfilt::~fftfilt()
if (fft) delete fft;
if (filter) delete [] filter;
if (filterOpp) delete [] filterOpp;
if (data) delete [] data;
if (output) delete [] output;
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
int fftfilt::noFilt(const cmplx & in, cmplx **out)
{
@ -271,13 +323,9 @@ int fftfilt::runDSB(const cmplx & in, cmplx **out)
return flen2;
}
// Version for vestigial sideband. You have to double the FFT size used for SSB.
int fftfilt::runVestigial(const cmplx & in, cmplx **out, bool usb, float vf)
// Version for asymmetrical sidebands. You have to double the FFT size used for SSB.
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;
if (inptr < flen2)
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++)
{
data[i] *= filter[i]; // usb
if (i > (int) (vf * flen2)) { // vestigial lsb
data[flen2 + i] *= filter[flen2 + i];
} else {
data[flen2 + i] = 0;
}
data[flen2 + i] *= filterOpp[flen2 + i]; // lsb is the opposite
}
}
else
{
for (int i = 1; i < flen2; i++)
{
if (i < (int) (vf * flen2)) { // vestigial usb
data[i] *= filter[i];
} else {
data[i] = 0;
}
data[i] *= filterOpp[i]; // usb is the opposite
data[flen2 + i] *= filter[flen2 + i]; // lsb
}
}

View File

@ -23,12 +23,13 @@ public:
// f1 > f2 ==> band reject
void create_filter(float f1, 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 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 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:
int flen;
@ -36,6 +37,7 @@ protected:
g_fft<float> *fft;
g_fft<float> *ift;
cmplx *filter;
cmplx *filterOpp;
cmplx *data;
cmplx *ovlbuf;
cmplx *output;