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:
parent
94d72d5ba5
commit
b079fae843
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user