mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-13 11:51:47 -05:00
ATV Modulator: implemented vestigial sideband
This commit is contained in:
parent
1ae2135c3b
commit
df88215798
@ -57,7 +57,11 @@ ATVMod::ATVMod() :
|
||||
m_cameraIndex(-1),
|
||||
m_showOverlayText(false),
|
||||
m_SSBFilter(0),
|
||||
m_SSBFilterBuffer(0)
|
||||
m_DSBFilter(0),
|
||||
m_SSBFilterBuffer(0),
|
||||
m_DSBFilterBuffer(0),
|
||||
m_SSBFilterBufferIndex(0),
|
||||
m_DSBFilterBufferIndex(0)
|
||||
{
|
||||
setObjectName("ATVMod");
|
||||
scanCameras();
|
||||
@ -72,6 +76,10 @@ ATVMod::ATVMod() :
|
||||
m_SSBFilterBuffer = new Complex[m_ssbFftLen>>1]; // filter returns data exactly half of its size
|
||||
memset(m_SSBFilterBuffer, 0, sizeof(Complex)*(m_ssbFftLen>>1));
|
||||
|
||||
m_DSBFilter = new fftfilt((2.0f * m_config.m_rfBandwidth) / m_config.m_outputSampleRate, 2 * m_ssbFftLen);
|
||||
m_DSBFilterBuffer = new Complex[m_ssbFftLen];
|
||||
memset(m_DSBFilterBuffer, 0, sizeof(Complex)*(m_ssbFftLen));
|
||||
|
||||
applyStandard();
|
||||
|
||||
m_interpolatorDistanceRemain = 0.0f;
|
||||
@ -193,6 +201,11 @@ void ATVMod::modulateSample()
|
||||
m_modSample = modulateSSB(t);
|
||||
m_modSample *= 29204.0f;
|
||||
break;
|
||||
case ATVModulationVestigialLSB:
|
||||
case ATVModulationVestigialUSB:
|
||||
m_modSample = modulateVestigialSSB(t);
|
||||
m_modSample *= 29204.0f;
|
||||
break;
|
||||
case ATVModulationAM: // AM 90%
|
||||
default:
|
||||
m_modSample.real((t*1.8f + 0.1f) * 16384.0f); // modulate and scale zero frequency carrier
|
||||
@ -219,6 +232,25 @@ Complex& ATVMod::modulateSSB(Real& sample)
|
||||
return m_SSBFilterBuffer[m_SSBFilterBufferIndex-1];
|
||||
}
|
||||
|
||||
Complex& ATVMod::modulateVestigialSSB(Real& sample)
|
||||
{
|
||||
int n_out;
|
||||
Complex ci(sample, 0.0f);
|
||||
fftfilt::cmplx *filtered;
|
||||
|
||||
n_out = m_DSBFilter->runVestigial(ci, &filtered, m_running.m_atvModulation == ATVModulationVestigialUSB, 0.15f);
|
||||
|
||||
if (n_out > 0)
|
||||
{
|
||||
memcpy((void *) m_DSBFilterBuffer, (const void *) filtered, n_out*sizeof(Complex));
|
||||
m_DSBFilterBufferIndex = 0;
|
||||
}
|
||||
|
||||
m_DSBFilterBufferIndex++;
|
||||
|
||||
return m_DSBFilterBuffer[m_DSBFilterBufferIndex-1];
|
||||
}
|
||||
|
||||
void ATVMod::pullVideo(Real& sample)
|
||||
{
|
||||
int iLine = m_lineCount % m_nbLines2;
|
||||
@ -618,6 +650,10 @@ 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);
|
||||
memset(m_DSBFilterBuffer, 0, sizeof(Complex)*(m_ssbFftLen));
|
||||
m_DSBFilterBufferIndex = 0;
|
||||
|
||||
applyStandard(); // set all timings
|
||||
m_settingsMutex.unlock();
|
||||
}
|
||||
|
@ -62,7 +62,9 @@ public:
|
||||
ATVModulationAM,
|
||||
ATVModulationFM,
|
||||
ATVModulationUSB,
|
||||
ATVModulationLSB
|
||||
ATVModulationLSB,
|
||||
ATVModulationVestigialUSB,
|
||||
ATVModulationVestigialLSB
|
||||
} ATVModulation;
|
||||
|
||||
class MsgConfigureImageFileName : public Message
|
||||
@ -532,8 +534,11 @@ private:
|
||||
bool m_showOverlayText;
|
||||
|
||||
fftfilt* m_SSBFilter;
|
||||
fftfilt* m_DSBFilter;
|
||||
Complex* m_SSBFilterBuffer;
|
||||
Complex* m_DSBFilterBuffer;
|
||||
int m_SSBFilterBufferIndex;
|
||||
int m_DSBFilterBufferIndex;
|
||||
static const int m_ssbFftLen;
|
||||
|
||||
static const float m_blackLevel;
|
||||
@ -548,6 +553,7 @@ private:
|
||||
void calculateLevel(Real& sample);
|
||||
void modulateSample();
|
||||
Complex& modulateSSB(Real& sample);
|
||||
Complex& modulateVestigialSSB(Real& sample);
|
||||
void applyStandard();
|
||||
void openImage(const QString& fileName);
|
||||
void openVideo(const QString& fileName);
|
||||
|
@ -202,7 +202,9 @@ void ATVModGUI::viewChanged()
|
||||
void ATVModGUI::channelizerOutputSampleRateChanged()
|
||||
{
|
||||
if ((ui->modulation->currentIndex() == (int) ATVMod::ATVModulationLSB) ||
|
||||
(ui->modulation->currentIndex() == (int) ATVMod::ATVModulationUSB))
|
||||
(ui->modulation->currentIndex() == (int) ATVMod::ATVModulationUSB) ||
|
||||
(ui->modulation->currentIndex() == (int) ATVMod::ATVModulationVestigialLSB) ||
|
||||
(ui->modulation->currentIndex() == (int) ATVMod::ATVModulationVestigialUSB))
|
||||
{
|
||||
ui->rfBW->setMaximum(m_channelizer->getOutputSampleRate() / 200000);
|
||||
}
|
||||
@ -247,13 +249,15 @@ void ATVModGUI::on_deltaFrequency_changed(quint64 value)
|
||||
|
||||
void ATVModGUI::on_modulation_currentIndexChanged(int index)
|
||||
{
|
||||
if (index == (int) ATVMod::ATVModulationLSB)
|
||||
if ((index == (int) ATVMod::ATVModulationLSB) ||
|
||||
(index == (int) ATVMod::ATVModulationVestigialLSB))
|
||||
{
|
||||
ui->rfBW->setMaximum(m_channelizer->getOutputSampleRate() / 200000);
|
||||
m_channelMarker.setBandwidth(-ui->rfBW->value()*200000);
|
||||
m_channelMarker.setSidebands(ChannelMarker::lsb);
|
||||
}
|
||||
else if (index == (int) ATVMod::ATVModulationUSB)
|
||||
else if ((index == (int) ATVMod::ATVModulationUSB) ||
|
||||
(index == (int) ATVMod::ATVModulationVestigialUSB))
|
||||
{
|
||||
ui->rfBW->setMaximum(m_channelizer->getOutputSampleRate() / 200000);
|
||||
m_channelMarker.setBandwidth(ui->rfBW->value()*200000);
|
||||
@ -273,11 +277,13 @@ void ATVModGUI::on_rfBW_valueChanged(int value)
|
||||
{
|
||||
ui->rfBWText->setText(QString("%1 MHz").arg(value / 10.0, 0, 'f', 1));
|
||||
|
||||
if (ui->modulation->currentIndex() == (int) ATVMod::ATVModulationLSB)
|
||||
if ((ui->modulation->currentIndex() == (int) ATVMod::ATVModulationLSB) ||
|
||||
(ui->modulation->currentIndex() == (int) ATVMod::ATVModulationVestigialLSB))
|
||||
{
|
||||
m_channelMarker.setBandwidth(-ui->rfBW->value()*200000);
|
||||
}
|
||||
else if (ui->modulation->currentIndex() == (int) ATVMod::ATVModulationUSB)
|
||||
else if ((ui->modulation->currentIndex() == (int) ATVMod::ATVModulationUSB) ||
|
||||
(ui->modulation->currentIndex() == (int) ATVMod::ATVModulationVestigialUSB))
|
||||
{
|
||||
m_channelMarker.setBandwidth(ui->rfBW->value()*200000);
|
||||
}
|
||||
|
@ -194,7 +194,7 @@
|
||||
<widget class="QComboBox" name="modulation">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<width>60</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
@ -221,6 +221,16 @@
|
||||
<string>LSB</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>VUSB</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>VLSB</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -271,6 +271,64 @@ 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)
|
||||
{
|
||||
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;
|
||||
inptr = 0;
|
||||
|
||||
fft->ComplexFFT(data);
|
||||
|
||||
data[0] *= filter[0]; // always keep DC
|
||||
|
||||
if (usb)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 1; i < flen2; i++)
|
||||
{
|
||||
if (i < (int) (vf * flen2)) { // vestigial usb
|
||||
data[i] *= filter[i];
|
||||
} else {
|
||||
data[i] = 0;
|
||||
}
|
||||
|
||||
data[flen2 + i] *= filter[flen2 + i]; // lsb
|
||||
}
|
||||
}
|
||||
|
||||
// in-place FFT: freqdata overwritten with filtered timedata
|
||||
fft->InverseComplexFFT(data);
|
||||
|
||||
// overlap and add
|
||||
for (int i = 0; i < flen2; i++) {
|
||||
output[i] = ovlbuf[i] + data[i];
|
||||
ovlbuf[i] = data[i+flen2];
|
||||
}
|
||||
|
||||
memset (data, 0, flen * sizeof(cmplx));
|
||||
|
||||
*out = output;
|
||||
return flen2;
|
||||
}
|
||||
|
||||
/* Sliding FFT from Fldigi */
|
||||
|
||||
struct sfft::vrot_bins_pair {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Filters from Fldigi.
|
||||
* Filters from Fldigi.
|
||||
*/
|
||||
|
||||
#ifndef _FFTFILT_H
|
||||
@ -28,6 +28,7 @@ public:
|
||||
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);
|
||||
|
||||
protected:
|
||||
int flen;
|
||||
@ -43,13 +44,13 @@ protected:
|
||||
int window;
|
||||
|
||||
inline float fsinc(float fc, int i, int len) {
|
||||
return (i == len/2) ? 2.0 * fc:
|
||||
return (i == len/2) ? 2.0 * fc:
|
||||
sin(2 * M_PI * fc * (i - len/2)) / (M_PI * (i - len/2));
|
||||
}
|
||||
|
||||
inline float _blackman(int i, int len) {
|
||||
return (0.42 -
|
||||
0.50 * cos(2.0 * M_PI * i / len) +
|
||||
return (0.42 -
|
||||
0.50 * cos(2.0 * M_PI * i / len) +
|
||||
0.08 * cos(4.0 * M_PI * i / len));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user