diff --git a/plugins/channeltx/modatv/atvmod.cpp b/plugins/channeltx/modatv/atvmod.cpp
index c8325b560..b54cfae99 100644
--- a/plugins/channeltx/modatv/atvmod.cpp
+++ b/plugins/channeltx/modatv/atvmod.cpp
@@ -26,6 +26,7 @@ const float ATVMod::m_spanLevel = 0.7f;
const int ATVMod::m_levelNbSamples = 10000; // every 10ms
ATVMod::ATVMod() :
+ m_modPhasor(0.0f),
m_evenImage(true),
m_tvSampleRate(1000000),
m_settingsMutex(QMutex::Recursive),
@@ -59,9 +60,10 @@ void ATVMod::configure(MessageQueue* messageQueue,
ATVStd atvStd,
ATVModInput atvModInput,
Real uniformLevel,
+ ATVModulation atvModulation,
bool channelMute)
{
- Message* cmd = MsgConfigureATVMod::create(rfBandwidth, atvStd, atvModInput, uniformLevel);
+ Message* cmd = MsgConfigureATVMod::create(rfBandwidth, atvStd, atvModInput, uniformLevel, atvModulation);
messageQueue->push(cmd);
}
@@ -125,9 +127,20 @@ void ATVMod::modulateSample()
pullVideo(t);
calculateLevel(t);
- // TODO For now do AM 90%
- m_modSample.real((t*1.8f + 0.1f) * 16384.0f); // modulate and scale zero frequency carrier
- m_modSample.imag(0.0f);
+ switch (m_running.m_atvModulation)
+ {
+ 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)
@@ -216,6 +229,7 @@ bool ATVMod::handleMessage(const Message& cmd)
m_config.m_atvModInput = cfg.getATVModInput();
m_config.m_atvStd = cfg.getATVStd();
m_config.m_uniformLevel = cfg.getUniformLevel();
+ m_config.m_atvModulation = cfg.getModulation();
apply();
@@ -223,7 +237,8 @@ bool ATVMod::handleMessage(const Message& cmd)
<< " m_rfBandwidth: " << m_config.m_rfBandwidth
<< " m_atvStd: " << (int) m_config.m_atvStd
<< " 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;
}
@@ -273,6 +288,7 @@ void ATVMod::apply(bool force)
m_running.m_atvModInput = m_config.m_atvModInput;
m_running.m_atvStd = m_config.m_atvStd;
m_running.m_uniformLevel = m_config.m_uniformLevel;
+ m_running.m_atvModulation = m_config.m_atvModulation;
}
int ATVMod::getSampleRateUnits(ATVStd std)
diff --git a/plugins/channeltx/modatv/atvmod.h b/plugins/channeltx/modatv/atvmod.h
index 09bf3c6fa..4ad46865a 100644
--- a/plugins/channeltx/modatv/atvmod.h
+++ b/plugins/channeltx/modatv/atvmod.h
@@ -47,6 +47,12 @@ public:
ATVModInputVGradient
} ATVModInput;
+ typedef enum
+ {
+ ATVModulationAM,
+ ATVModulationFM
+ } ATVModulation;
+
ATVMod();
~ATVMod();
@@ -55,6 +61,7 @@ public:
ATVStd atvStd,
ATVModInput atvModInput,
Real uniformLevel,
+ ATVModulation atvModulation,
bool channelMute);
virtual void pull(Sample& sample);
@@ -86,43 +93,49 @@ private:
ATVStd getATVStd() const { return m_atvStd; }
ATVModInput getATVModInput() const { return m_atvModInput; }
Real getUniformLevel() const { return m_uniformLevel; }
+ ATVModulation getModulation() const { return m_atvModulation; }
static MsgConfigureATVMod* create(
Real rfBandwidth,
ATVStd atvStd,
ATVModInput atvModInput,
- Real uniformLevel)
+ Real uniformLevel,
+ ATVModulation atvModulation)
{
- return new MsgConfigureATVMod(rfBandwidth, atvStd, atvModInput, uniformLevel);
+ return new MsgConfigureATVMod(rfBandwidth, atvStd, atvModInput, uniformLevel, atvModulation);
}
private:
- Real m_rfBandwidth;
- ATVStd m_atvStd;
- ATVModInput m_atvModInput;
- Real m_uniformLevel;
+ Real m_rfBandwidth;
+ ATVStd m_atvStd;
+ ATVModInput m_atvModInput;
+ Real m_uniformLevel;
+ ATVModulation m_atvModulation;
MsgConfigureATVMod(
Real rfBandwidth,
ATVStd atvStd,
ATVModInput atvModInput,
- Real uniformLevel) :
+ Real uniformLevel,
+ ATVModulation atvModulation) :
Message(),
m_rfBandwidth(rfBandwidth),
m_atvStd(atvStd),
m_atvModInput(atvModInput),
- m_uniformLevel(uniformLevel)
+ m_uniformLevel(uniformLevel),
+ m_atvModulation(atvModulation)
{ }
};
struct Config
{
- int m_outputSampleRate; //!< sample rate from channelizer
- qint64 m_inputFrequencyOffset; //!< offset from baseband center frequency
- Real m_rfBandwidth; //!< Bandwidth of modulated signal
- ATVStd m_atvStd; //!< Standard
- ATVModInput m_atvModInput; //!< Input source type
- Real m_uniformLevel; //!< Percentage between black and white for uniform screen display
+ int m_outputSampleRate; //!< sample rate from channelizer
+ qint64 m_inputFrequencyOffset; //!< offset from baseband center frequency
+ Real m_rfBandwidth; //!< Bandwidth of modulated signal
+ ATVStd m_atvStd; //!< Standard
+ ATVModInput m_atvModInput; //!< Input source type
+ Real m_uniformLevel; //!< Percentage between black and white for uniform screen display
+ ATVModulation m_atvModulation; //!< RF modulation type
Config() :
m_outputSampleRate(-1),
@@ -130,7 +143,8 @@ private:
m_rfBandwidth(0),
m_atvStd(ATVStdPAL625),
m_atvModInput(ATVModInputHBars),
- m_uniformLevel(0.5f)
+ m_uniformLevel(0.5f),
+ m_atvModulation(ATVModulationAM)
{ }
};
@@ -139,6 +153,7 @@ private:
NCO m_carrierNco;
Complex m_modSample;
+ float m_modPhasor; //!< For FM modulation
Interpolator m_interpolator;
Real m_interpolatorDistance;
Real m_interpolatorDistanceRemain;
diff --git a/plugins/channeltx/modatv/atvmodgui.cpp b/plugins/channeltx/modatv/atvmodgui.cpp
index 33d1e601c..db304b742 100644
--- a/plugins/channeltx/modatv/atvmodgui.cpp
+++ b/plugins/channeltx/modatv/atvmodgui.cpp
@@ -75,6 +75,7 @@ void ATVModGUI::resetToDefaults()
ui->standard->setCurrentIndex(0);
ui->inputSelect->setCurrentIndex(0);
ui->deltaFrequency->setValue(0);
+ ui->modulation->setCurrentIndex(0);
blockApplySettings(false);
applySettings();
@@ -91,6 +92,7 @@ QByteArray ATVModGUI::serialize() const
s.writeS32(5, ui->inputSelect->currentIndex());
s.writeU32(6, m_channelMarker.getColor().rgb());
s.writeS32(7, ui->volume->value());
+ s.writeS32(8, ui->modulation->currentIndex());
return s.final();
}
@@ -132,6 +134,8 @@ bool ATVModGUI::deserialize(const QByteArray& data)
d.readS32(7, &tmp, 10);
ui->volume->setValue(tmp);
+ d.readS32(8, &tmp, 0);
+ ui->modulation->setCurrentIndex(tmp);
blockApplySettings(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)
{
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::ATVModInput) ui->inputSelect->currentIndex(),
ui->uniformLevel->value() / 100.0f,
+ (ATVMod::ATVModulation) ui->modulation->currentIndex(),
ui->channelMute->isChecked());
}
}
diff --git a/plugins/channeltx/modatv/atvmodgui.h b/plugins/channeltx/modatv/atvmodgui.h
index decac1d90..57d3beae0 100644
--- a/plugins/channeltx/modatv/atvmodgui.h
+++ b/plugins/channeltx/modatv/atvmodgui.h
@@ -60,6 +60,7 @@ private slots:
void on_deltaFrequency_changed(quint64 value);
void on_deltaMinus_toggled(bool minus);
+ void on_modulation_currentIndexChanged(int index);
void on_rfBW_valueChanged(int value);
void on_uniformLevel_valueChanged(int value);
void on_inputSelect_currentIndexChanged(int index);
diff --git a/plugins/channeltx/modatv/atvmodgui.ui b/plugins/channeltx/modatv/atvmodgui.ui
index ae53a4b23..296ea7389 100644
--- a/plugins/channeltx/modatv/atvmodgui.ui
+++ b/plugins/channeltx/modatv/atvmodgui.ui
@@ -191,16 +191,30 @@
-
-
-
+
+
+
+ 50
+ 16777215
+
+
+
+ Modulation type
+
-
- PAL625L
+ AM
+
+
+ -
+
+ FM
-
-
+
RFBW
@@ -321,8 +335,24 @@
+ -
+
+
+ Qt::Horizontal
+
+
+
-
+
-
+
+
-
+
+ PAL625L
+
+
+
+
-
-
@@ -361,7 +391,7 @@
- Tone frequency
+ Uniform level luminance (%)
0
@@ -419,7 +449,7 @@
-
-
+
Qt::Horizontal
@@ -640,6 +670,11 @@
+
+
+
+
+