mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-10 10:33:29 -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_cameraIndex(-1),
|
||||||
m_showOverlayText(false),
|
m_showOverlayText(false),
|
||||||
m_SSBFilter(0),
|
m_SSBFilter(0),
|
||||||
m_SSBFilterBuffer(0)
|
m_DSBFilter(0),
|
||||||
|
m_SSBFilterBuffer(0),
|
||||||
|
m_DSBFilterBuffer(0),
|
||||||
|
m_SSBFilterBufferIndex(0),
|
||||||
|
m_DSBFilterBufferIndex(0)
|
||||||
{
|
{
|
||||||
setObjectName("ATVMod");
|
setObjectName("ATVMod");
|
||||||
scanCameras();
|
scanCameras();
|
||||||
@ -72,6 +76,10 @@ ATVMod::ATVMod() :
|
|||||||
m_SSBFilterBuffer = new Complex[m_ssbFftLen>>1]; // filter returns data exactly half of its size
|
m_SSBFilterBuffer = new Complex[m_ssbFftLen>>1]; // filter returns data exactly half of its size
|
||||||
memset(m_SSBFilterBuffer, 0, sizeof(Complex)*(m_ssbFftLen>>1));
|
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();
|
applyStandard();
|
||||||
|
|
||||||
m_interpolatorDistanceRemain = 0.0f;
|
m_interpolatorDistanceRemain = 0.0f;
|
||||||
@ -193,6 +201,11 @@ void ATVMod::modulateSample()
|
|||||||
m_modSample = modulateSSB(t);
|
m_modSample = modulateSSB(t);
|
||||||
m_modSample *= 29204.0f;
|
m_modSample *= 29204.0f;
|
||||||
break;
|
break;
|
||||||
|
case ATVModulationVestigialLSB:
|
||||||
|
case ATVModulationVestigialUSB:
|
||||||
|
m_modSample = modulateVestigialSSB(t);
|
||||||
|
m_modSample *= 29204.0f;
|
||||||
|
break;
|
||||||
case ATVModulationAM: // AM 90%
|
case ATVModulationAM: // AM 90%
|
||||||
default:
|
default:
|
||||||
m_modSample.real((t*1.8f + 0.1f) * 16384.0f); // modulate and scale zero frequency carrier
|
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];
|
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)
|
void ATVMod::pullVideo(Real& sample)
|
||||||
{
|
{
|
||||||
int iLine = m_lineCount % m_nbLines2;
|
int iLine = m_lineCount % m_nbLines2;
|
||||||
@ -618,6 +650,10 @@ 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);
|
||||||
|
memset(m_DSBFilterBuffer, 0, sizeof(Complex)*(m_ssbFftLen));
|
||||||
|
m_DSBFilterBufferIndex = 0;
|
||||||
|
|
||||||
applyStandard(); // set all timings
|
applyStandard(); // set all timings
|
||||||
m_settingsMutex.unlock();
|
m_settingsMutex.unlock();
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,9 @@ public:
|
|||||||
ATVModulationAM,
|
ATVModulationAM,
|
||||||
ATVModulationFM,
|
ATVModulationFM,
|
||||||
ATVModulationUSB,
|
ATVModulationUSB,
|
||||||
ATVModulationLSB
|
ATVModulationLSB,
|
||||||
|
ATVModulationVestigialUSB,
|
||||||
|
ATVModulationVestigialLSB
|
||||||
} ATVModulation;
|
} ATVModulation;
|
||||||
|
|
||||||
class MsgConfigureImageFileName : public Message
|
class MsgConfigureImageFileName : public Message
|
||||||
@ -532,8 +534,11 @@ private:
|
|||||||
bool m_showOverlayText;
|
bool m_showOverlayText;
|
||||||
|
|
||||||
fftfilt* m_SSBFilter;
|
fftfilt* m_SSBFilter;
|
||||||
|
fftfilt* m_DSBFilter;
|
||||||
Complex* m_SSBFilterBuffer;
|
Complex* m_SSBFilterBuffer;
|
||||||
|
Complex* m_DSBFilterBuffer;
|
||||||
int m_SSBFilterBufferIndex;
|
int m_SSBFilterBufferIndex;
|
||||||
|
int m_DSBFilterBufferIndex;
|
||||||
static const int m_ssbFftLen;
|
static const int m_ssbFftLen;
|
||||||
|
|
||||||
static const float m_blackLevel;
|
static const float m_blackLevel;
|
||||||
@ -548,6 +553,7 @@ private:
|
|||||||
void calculateLevel(Real& sample);
|
void calculateLevel(Real& sample);
|
||||||
void modulateSample();
|
void modulateSample();
|
||||||
Complex& modulateSSB(Real& sample);
|
Complex& modulateSSB(Real& sample);
|
||||||
|
Complex& modulateVestigialSSB(Real& sample);
|
||||||
void applyStandard();
|
void applyStandard();
|
||||||
void openImage(const QString& fileName);
|
void openImage(const QString& fileName);
|
||||||
void openVideo(const QString& fileName);
|
void openVideo(const QString& fileName);
|
||||||
|
@ -202,7 +202,9 @@ void ATVModGUI::viewChanged()
|
|||||||
void ATVModGUI::channelizerOutputSampleRateChanged()
|
void ATVModGUI::channelizerOutputSampleRateChanged()
|
||||||
{
|
{
|
||||||
if ((ui->modulation->currentIndex() == (int) ATVMod::ATVModulationLSB) ||
|
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);
|
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)
|
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);
|
ui->rfBW->setMaximum(m_channelizer->getOutputSampleRate() / 200000);
|
||||||
m_channelMarker.setBandwidth(-ui->rfBW->value()*200000);
|
m_channelMarker.setBandwidth(-ui->rfBW->value()*200000);
|
||||||
m_channelMarker.setSidebands(ChannelMarker::lsb);
|
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);
|
ui->rfBW->setMaximum(m_channelizer->getOutputSampleRate() / 200000);
|
||||||
m_channelMarker.setBandwidth(ui->rfBW->value()*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));
|
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);
|
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);
|
m_channelMarker.setBandwidth(ui->rfBW->value()*200000);
|
||||||
}
|
}
|
||||||
|
@ -194,7 +194,7 @@
|
|||||||
<widget class="QComboBox" name="modulation">
|
<widget class="QComboBox" name="modulation">
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>50</width>
|
<width>60</width>
|
||||||
<height>16777215</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
@ -221,6 +221,16 @@
|
|||||||
<string>LSB</string>
|
<string>LSB</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>VUSB</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>VLSB</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -271,6 +271,64 @@ 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.
|
||||||
|
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 */
|
/* Sliding FFT from Fldigi */
|
||||||
|
|
||||||
struct sfft::vrot_bins_pair {
|
struct sfft::vrot_bins_pair {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Filters from Fldigi.
|
* Filters from Fldigi.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _FFTFILT_H
|
#ifndef _FFTFILT_H
|
||||||
@ -28,6 +28,7 @@ public:
|
|||||||
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);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int flen;
|
int flen;
|
||||||
@ -43,13 +44,13 @@ protected:
|
|||||||
int window;
|
int window;
|
||||||
|
|
||||||
inline float fsinc(float fc, int i, int len) {
|
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));
|
sin(2 * M_PI * fc * (i - len/2)) / (M_PI * (i - len/2));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline float _blackman(int i, int len) {
|
inline float _blackman(int i, int len) {
|
||||||
return (0.42 -
|
return (0.42 -
|
||||||
0.50 * cos(2.0 * M_PI * i / len) +
|
0.50 * cos(2.0 * M_PI * i / len) +
|
||||||
0.08 * cos(4.0 * M_PI * i / len));
|
0.08 * cos(4.0 * M_PI * i / len));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user