ATV Modulator: implemented FM

This commit is contained in:
f4exb 2017-03-07 19:19:54 +01:00
parent b832ea2173
commit 71d4776a93
5 changed files with 102 additions and 25 deletions

View File

@ -26,6 +26,7 @@ const float ATVMod::m_spanLevel = 0.7f;
const int ATVMod::m_levelNbSamples = 10000; // every 10ms const int ATVMod::m_levelNbSamples = 10000; // every 10ms
ATVMod::ATVMod() : ATVMod::ATVMod() :
m_modPhasor(0.0f),
m_evenImage(true), m_evenImage(true),
m_tvSampleRate(1000000), m_tvSampleRate(1000000),
m_settingsMutex(QMutex::Recursive), m_settingsMutex(QMutex::Recursive),
@ -59,9 +60,10 @@ void ATVMod::configure(MessageQueue* messageQueue,
ATVStd atvStd, ATVStd atvStd,
ATVModInput atvModInput, ATVModInput atvModInput,
Real uniformLevel, Real uniformLevel,
ATVModulation atvModulation,
bool channelMute) bool channelMute)
{ {
Message* cmd = MsgConfigureATVMod::create(rfBandwidth, atvStd, atvModInput, uniformLevel); Message* cmd = MsgConfigureATVMod::create(rfBandwidth, atvStd, atvModInput, uniformLevel, atvModulation);
messageQueue->push(cmd); messageQueue->push(cmd);
} }
@ -125,9 +127,20 @@ void ATVMod::modulateSample()
pullVideo(t); pullVideo(t);
calculateLevel(t); calculateLevel(t);
// TODO For now do AM 90% switch (m_running.m_atvModulation)
m_modSample.real((t*1.8f + 0.1f) * 16384.0f); // modulate and scale zero frequency carrier {
m_modSample.imag(0.0f); case ATVModulationFM: // FM half bandwidth deviation
m_modPhasor += (t - 0.5f) * M_PI;
if (m_modPhasor > 2.0f * M_PI) m_modPhasor -= 2.0f * M_PI; // limit growth
if (m_modPhasor < 2.0f * M_PI) m_modPhasor += 2.0f * M_PI; // limit growth
m_modSample.real(cos(m_modPhasor) * 29204.0f); // -1 dB
m_modSample.imag(sin(m_modPhasor) * 29204.0f);
break;
case ATVModulationAM: // AM 90%
default:
m_modSample.real((t*1.8f + 0.1f) * 16384.0f); // modulate and scale zero frequency carrier
m_modSample.imag(0.0f);
}
} }
void ATVMod::pullVideo(Real& sample) void ATVMod::pullVideo(Real& sample)
@ -216,6 +229,7 @@ bool ATVMod::handleMessage(const Message& cmd)
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();
m_config.m_atvModulation = cfg.getModulation();
apply(); apply();
@ -223,7 +237,8 @@ bool ATVMod::handleMessage(const Message& cmd)
<< " m_rfBandwidth: " << m_config.m_rfBandwidth << " m_rfBandwidth: " << m_config.m_rfBandwidth
<< " 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
<< " m_atvModulation: " << (int) m_config.m_atvModulation;
return true; return true;
} }
@ -273,6 +288,7 @@ void ATVMod::apply(bool force)
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;
m_running.m_atvModulation = m_config.m_atvModulation;
} }
int ATVMod::getSampleRateUnits(ATVStd std) int ATVMod::getSampleRateUnits(ATVStd std)

View File

@ -47,6 +47,12 @@ public:
ATVModInputVGradient ATVModInputVGradient
} ATVModInput; } ATVModInput;
typedef enum
{
ATVModulationAM,
ATVModulationFM
} ATVModulation;
ATVMod(); ATVMod();
~ATVMod(); ~ATVMod();
@ -55,6 +61,7 @@ public:
ATVStd atvStd, ATVStd atvStd,
ATVModInput atvModInput, ATVModInput atvModInput,
Real uniformLevel, Real uniformLevel,
ATVModulation atvModulation,
bool channelMute); bool channelMute);
virtual void pull(Sample& sample); virtual void pull(Sample& sample);
@ -86,43 +93,49 @@ private:
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; }
ATVModulation getModulation() const { return m_atvModulation; }
static MsgConfigureATVMod* create( static MsgConfigureATVMod* create(
Real rfBandwidth, Real rfBandwidth,
ATVStd atvStd, ATVStd atvStd,
ATVModInput atvModInput, ATVModInput atvModInput,
Real uniformLevel) Real uniformLevel,
ATVModulation atvModulation)
{ {
return new MsgConfigureATVMod(rfBandwidth, atvStd, atvModInput, uniformLevel); return new MsgConfigureATVMod(rfBandwidth, atvStd, atvModInput, uniformLevel, atvModulation);
} }
private: private:
Real m_rfBandwidth; Real m_rfBandwidth;
ATVStd m_atvStd; ATVStd m_atvStd;
ATVModInput m_atvModInput; ATVModInput m_atvModInput;
Real m_uniformLevel; Real m_uniformLevel;
ATVModulation m_atvModulation;
MsgConfigureATVMod( MsgConfigureATVMod(
Real rfBandwidth, Real rfBandwidth,
ATVStd atvStd, ATVStd atvStd,
ATVModInput atvModInput, ATVModInput atvModInput,
Real uniformLevel) : Real uniformLevel,
ATVModulation atvModulation) :
Message(), Message(),
m_rfBandwidth(rfBandwidth), m_rfBandwidth(rfBandwidth),
m_atvStd(atvStd), m_atvStd(atvStd),
m_atvModInput(atvModInput), m_atvModInput(atvModInput),
m_uniformLevel(uniformLevel) m_uniformLevel(uniformLevel),
m_atvModulation(atvModulation)
{ } { }
}; };
struct Config struct Config
{ {
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
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
ATVModulation m_atvModulation; //!< RF modulation type
Config() : Config() :
m_outputSampleRate(-1), m_outputSampleRate(-1),
@ -130,7 +143,8 @@ private:
m_rfBandwidth(0), m_rfBandwidth(0),
m_atvStd(ATVStdPAL625), m_atvStd(ATVStdPAL625),
m_atvModInput(ATVModInputHBars), m_atvModInput(ATVModInputHBars),
m_uniformLevel(0.5f) m_uniformLevel(0.5f),
m_atvModulation(ATVModulationAM)
{ } { }
}; };
@ -139,6 +153,7 @@ private:
NCO m_carrierNco; NCO m_carrierNco;
Complex m_modSample; Complex m_modSample;
float m_modPhasor; //!< For FM modulation
Interpolator m_interpolator; Interpolator m_interpolator;
Real m_interpolatorDistance; Real m_interpolatorDistance;
Real m_interpolatorDistanceRemain; Real m_interpolatorDistanceRemain;

View File

@ -75,6 +75,7 @@ void ATVModGUI::resetToDefaults()
ui->standard->setCurrentIndex(0); ui->standard->setCurrentIndex(0);
ui->inputSelect->setCurrentIndex(0); ui->inputSelect->setCurrentIndex(0);
ui->deltaFrequency->setValue(0); ui->deltaFrequency->setValue(0);
ui->modulation->setCurrentIndex(0);
blockApplySettings(false); blockApplySettings(false);
applySettings(); applySettings();
@ -91,6 +92,7 @@ QByteArray ATVModGUI::serialize() const
s.writeS32(5, ui->inputSelect->currentIndex()); s.writeS32(5, ui->inputSelect->currentIndex());
s.writeU32(6, m_channelMarker.getColor().rgb()); s.writeU32(6, m_channelMarker.getColor().rgb());
s.writeS32(7, ui->volume->value()); s.writeS32(7, ui->volume->value());
s.writeS32(8, ui->modulation->currentIndex());
return s.final(); return s.final();
} }
@ -132,6 +134,8 @@ bool ATVModGUI::deserialize(const QByteArray& data)
d.readS32(7, &tmp, 10); d.readS32(7, &tmp, 10);
ui->volume->setValue(tmp); ui->volume->setValue(tmp);
d.readS32(8, &tmp, 0);
ui->modulation->setCurrentIndex(tmp);
blockApplySettings(false); blockApplySettings(false);
m_channelMarker.blockSignals(false); m_channelMarker.blockSignals(false);
@ -189,6 +193,11 @@ void ATVModGUI::on_deltaFrequency_changed(quint64 value)
} }
} }
void ATVModGUI::on_modulation_currentIndexChanged(int index)
{
applySettings();
}
void ATVModGUI::on_rfBW_valueChanged(int value) 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));
@ -313,6 +322,7 @@ void ATVModGUI::applySettings()
(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,
(ATVMod::ATVModulation) ui->modulation->currentIndex(),
ui->channelMute->isChecked()); ui->channelMute->isChecked());
} }
} }

View File

@ -60,6 +60,7 @@ private slots:
void on_deltaFrequency_changed(quint64 value); void on_deltaFrequency_changed(quint64 value);
void on_deltaMinus_toggled(bool minus); void on_deltaMinus_toggled(bool minus);
void on_modulation_currentIndexChanged(int index);
void on_rfBW_valueChanged(int value); void on_rfBW_valueChanged(int value);
void on_uniformLevel_valueChanged(int value); void on_uniformLevel_valueChanged(int value);
void on_inputSelect_currentIndexChanged(int index); void on_inputSelect_currentIndexChanged(int index);

View File

@ -191,16 +191,30 @@
<item> <item>
<layout class="QHBoxLayout" name="rfBandwidthLayout"> <layout class="QHBoxLayout" name="rfBandwidthLayout">
<item> <item>
<widget class="QComboBox" name="standard"> <widget class="QComboBox" name="modulation">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Modulation type</string>
</property>
<item> <item>
<property name="text"> <property name="text">
<string>PAL625L</string> <string>AM</string>
</property>
</item>
<item>
<property name="text">
<string>FM</string>
</property> </property>
</item> </item>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="label"> <widget class="QLabel" name="rfBWLabel">
<property name="text"> <property name="text">
<string>RFBW</string> <string>RFBW</string>
</property> </property>
@ -321,8 +335,24 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item> <item>
<layout class="QHBoxLayout" name="recordFileSelectLayout"> <layout class="QHBoxLayout" name="recordFileSelectLayout">
<item>
<widget class="QComboBox" name="standard">
<item>
<property name="text">
<string>PAL625L</string>
</property>
</item>
</widget>
</item>
<item> <item>
<widget class="QComboBox" name="inputSelect"> <widget class="QComboBox" name="inputSelect">
<item> <item>
@ -361,7 +391,7 @@
</size> </size>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Tone frequency</string> <string>Uniform level luminance (%)</string>
</property> </property>
<property name="minimum"> <property name="minimum">
<number>0</number> <number>0</number>
@ -419,7 +449,7 @@
</layout> </layout>
</item> </item>
<item> <item>
<widget class="Line" name="line_3"> <widget class="Line" name="line">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
@ -640,6 +670,11 @@
<include location="../../../sdrbase/resources/res.qrc"/> <include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/> <include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/> <include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
</resources> </resources>
<connections/> <connections/>
</ui> </ui>