diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt
index f7bd86fd4..0a91952d9 100644
--- a/plugins/CMakeLists.txt
+++ b/plugins/CMakeLists.txt
@@ -1,5 +1,6 @@
project(plugins)
add_subdirectory(channelrx)
+add_subdirectory(channeltx)
add_subdirectory(samplesource)
add_subdirectory(samplesink)
diff --git a/plugins/channelrx/demodam/amdemod.cpp b/plugins/channelrx/demodam/amdemod.cpp
index 2f4020b80..a52f059fd 100644
--- a/plugins/channelrx/demodam/amdemod.cpp
+++ b/plugins/channelrx/demodam/amdemod.cpp
@@ -14,7 +14,7 @@
// along with this program. If not, see . //
///////////////////////////////////////////////////////////////////////////////////
-#include "../../channelrx/demodam/amdemod.h"
+#include "amdemod.h"
#include
#include
diff --git a/plugins/channelrx/demodam/amdemodgui.cpp b/plugins/channelrx/demodam/amdemodgui.cpp
index 27ea14bff..4275d9134 100644
--- a/plugins/channelrx/demodam/amdemodgui.cpp
+++ b/plugins/channelrx/demodam/amdemodgui.cpp
@@ -1,11 +1,28 @@
-#include "../../channelrx/demodam/amdemodgui.h"
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2016 Edouard Griffiths, F4EXB //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
-#include
-#include
#include
#include
-#include "../../../sdrbase/dsp/threadedbasebandsamplesink.h"
+#include "amdemodgui.h"
+
+#include "device/devicesourceapi.h"
+#include "dsp/downchannelizer.h"
+
+#include "dsp/threadedbasebandsamplesink.h"
#include "ui_amdemodgui.h"
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
@@ -14,7 +31,7 @@
#include "dsp/dspengine.h"
#include "mainwindow.h"
-#include "../../channelrx/demodam/amdemod.h"
+#include "amdemod.h"
const QString AMDemodGUI::m_channelID = "de.maintech.sdrangelove.channel.am";
@@ -30,7 +47,7 @@ AMDemodGUI* AMDemodGUI::create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI)
void AMDemodGUI::destroy()
{
- delete this;
+ delete this; // TODO: is this really useful?
}
void AMDemodGUI::setName(const QString& name)
diff --git a/plugins/channeltx/CMakeLists.txt b/plugins/channeltx/CMakeLists.txt
new file mode 100644
index 000000000..90e1a4068
--- /dev/null
+++ b/plugins/channeltx/CMakeLists.txt
@@ -0,0 +1,3 @@
+project(mod)
+
+add_subdirectory(modam)
diff --git a/plugins/channeltx/modam/CMakeLists.txt b/plugins/channeltx/modam/CMakeLists.txt
new file mode 100644
index 000000000..23ede6948
--- /dev/null
+++ b/plugins/channeltx/modam/CMakeLists.txt
@@ -0,0 +1,43 @@
+project(modam)
+
+set(modam_SOURCES
+ ammod.cpp
+ ammodgui.cpp
+ ammodplugin.cpp
+)
+
+set(modam_HEADERS
+ ammod.h
+ ammodgui.h
+ ammodplugin.h
+)
+
+set(modam_FORMS
+ ammodgui.ui
+)
+
+include_directories(
+ .
+ ${CMAKE_CURRENT_BINARY_DIR}
+)
+
+add_definitions(${QT_DEFINITIONS})
+add_definitions(-DQT_PLUGIN)
+add_definitions(-DQT_SHARED)
+
+qt5_wrap_ui(modam_FORMS_HEADERS ${modam_FORMS})
+
+add_library(modam SHARED
+ ${modam_SOURCES}
+ ${modam_HEADERS_MOC}
+ ${modam_FORMS_HEADERS}
+)
+
+target_link_libraries(modam
+ ${QT_LIBRARIES}
+ sdrbase
+)
+
+qt5_use_modules(modam Core Widgets OpenGL Multimedia)
+
+install(TARGETS modam DESTINATION lib/plugins/channeltx)
\ No newline at end of file
diff --git a/plugins/channeltx/modam/ammod.cpp b/plugins/channeltx/modam/ammod.cpp
new file mode 100644
index 000000000..141e229a0
--- /dev/null
+++ b/plugins/channeltx/modam/ammod.cpp
@@ -0,0 +1,157 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2016 Edouard Griffiths, F4EXB //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include "ammod.h"
+
+#include
+#include
+#include
+#include
+#include
+#include "dsp/dspengine.h"
+#include "dsp/pidcontroller.h"
+
+MESSAGE_CLASS_DEFINITION(AMMod::MsgConfigureAMMod, Message)
+
+AMMod::AMMod() :
+ m_settingsMutex(QMutex::Recursive)
+{
+ setObjectName("AMMod");
+
+ m_config.m_outputSampleRate = 48000;
+ m_config.m_inputFrequencyOffset = 0;
+ m_config.m_rfBandwidth = 12500;
+ m_config.m_afBandwidth = 3000;
+ m_config.m_modPercent = 20;
+ m_config.m_audioSampleRate = DSPEngine::instance()->getAudioSampleRate();
+
+ apply();
+
+ m_audioBuffer.resize(1<<14);
+ m_audioBufferFill = 0;
+
+ m_movingAverage.resize(16, 0);
+ m_volumeAGC.resize(4096, 0.003, 0);
+ m_magsq = 0.0;
+}
+
+AMMod::~AMMod()
+{
+}
+
+void AMMod::configure(MessageQueue* messageQueue, Real rfBandwidth, Real afBandwidth, int modPercent, bool audioMute)
+{
+ Message* cmd = MsgConfigureAMMod::create(rfBandwidth, afBandwidth, modPercent, audioMute);
+ messageQueue->push(cmd);
+}
+
+void AMMod::pull(Sample& sample)
+{
+ // TODO
+}
+
+void AMMod::start()
+{
+ qDebug() << "AMMod::start: m_outputSampleRate: " << m_config.m_outputSampleRate
+ << " m_inputFrequencyOffset: " << m_config.m_inputFrequencyOffset;
+
+ m_audioFifo.clear();
+}
+
+void AMMod::stop()
+{
+}
+
+bool AMMod::handleMessage(const Message& cmd)
+{
+ qDebug() << "AMMod::handleMessage";
+
+ if (UpChannelizer::MsgChannelizerNotification::match(cmd))
+ {
+ UpChannelizer::MsgChannelizerNotification& notif = (UpChannelizer::MsgChannelizerNotification&) cmd;
+
+ m_config.m_outputSampleRate = notif.getSampleRate();
+ m_config.m_inputFrequencyOffset = notif.getFrequencyOffset();
+
+ apply();
+
+ qDebug() << "AMMod::handleMessage: MsgChannelizerNotification:"
+ << " m_outputSampleRate: " << m_config.m_outputSampleRate
+ << " m_inputFrequencyOffset: " << m_config.m_inputFrequencyOffset;
+
+ return true;
+ }
+ else if (MsgConfigureAMMod::match(cmd))
+ {
+ MsgConfigureAMMod& cfg = (MsgConfigureAMMod&) cmd;
+
+ m_config.m_rfBandwidth = cfg.getRFBandwidth();
+ m_config.m_afBandwidth = cfg.getAFBandwidth();
+ m_config.m_modPercent = cfg.getModPercent();
+ m_config.m_audioMute = cfg.getAudioMute();
+
+ apply();
+
+ qDebug() << "AMMod::handleMessage: MsgConfigureAMMod:"
+ << " m_rfBandwidth: " << m_config.m_rfBandwidth
+ << " m_afBandwidth: " << m_config.m_afBandwidth
+ << " m_modPercent: " << m_config.m_modPercent
+ << " m_audioMute: " << m_config.m_audioMute;
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void AMMod::apply()
+{
+
+ if((m_config.m_inputFrequencyOffset != m_running.m_inputFrequencyOffset) ||
+ (m_config.m_outputSampleRate != m_running.m_outputSampleRate))
+ {
+ m_carrierNco.setFreq(-m_config.m_inputFrequencyOffset, m_config.m_outputSampleRate);
+ }
+
+ if((m_config.m_outputSampleRate != m_running.m_outputSampleRate) ||
+ (m_config.m_rfBandwidth != m_running.m_rfBandwidth))
+ {
+ m_settingsMutex.lock();
+ m_interpolator.create(16, m_config.m_outputSampleRate, m_config.m_rfBandwidth / 2.2);
+ m_interpolatorDistanceRemain = 0;
+ m_interpolatorDistance = (Real) m_config.m_outputSampleRate / (Real) m_config.m_audioSampleRate;
+ m_settingsMutex.unlock();
+ }
+
+ if((m_config.m_afBandwidth != m_running.m_afBandwidth) ||
+ (m_config.m_audioSampleRate != m_running.m_audioSampleRate))
+ {
+ m_settingsMutex.lock();
+ m_lowpass.create(21, m_config.m_audioSampleRate, m_config.m_afBandwidth);
+ m_settingsMutex.unlock();
+ }
+
+ 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_afBandwidth = m_config.m_afBandwidth;
+ m_running.m_modPercent = m_config.m_modPercent;
+ m_running.m_audioSampleRate = m_config.m_audioSampleRate;
+ m_running.m_audioMute = m_config.m_audioMute;
+}
+
diff --git a/plugins/channeltx/modam/ammod.h b/plugins/channeltx/modam/ammod.h
index 66beb8d26..4af515e35 100644
--- a/plugins/channeltx/modam/ammod.h
+++ b/plugins/channeltx/modam/ammod.h
@@ -45,7 +45,7 @@ public:
Real getMagSq() const { return m_magsq; }
private:
- class MsgConfigureAMDemod : public Message {
+ class MsgConfigureAMMod : public Message {
MESSAGE_CLASS_DECLARATION
public:
@@ -54,9 +54,9 @@ private:
Real getModPercent() const { return m_modPercent; }
bool getAudioMute() const { return m_audioMute; }
- static MsgConfigureAMDemod* create(Real rfBandwidth, Real afBandwidth, int modPercent, bool audioMute)
+ static MsgConfigureAMMod* create(Real rfBandwidth, Real afBandwidth, int modPercent, bool audioMute)
{
- return new MsgConfigureAMDemod(rfBandwidth, afBandwidth, modPercent, audioMute);
+ return new MsgConfigureAMMod(rfBandwidth, afBandwidth, modPercent, audioMute);
}
private:
@@ -65,7 +65,7 @@ private:
Real m_modPercent;
bool m_audioMute;
- MsgConfigureAMDemod(Real rfBandwidth, Real afBandwidth, int modPercent, bool audioMute) :
+ MsgConfigureAMMod(Real rfBandwidth, Real afBandwidth, int modPercent, bool audioMute) :
Message(),
m_rfBandwidth(rfBandwidth),
m_afBandwidth(afBandwidth),
@@ -86,7 +86,7 @@ private:
};
struct Config {
- int m_inputSampleRate;
+ int m_outputSampleRate;
qint64 m_inputFrequencyOffset;
Real m_rfBandwidth;
Real m_afBandwidth;
@@ -95,7 +95,7 @@ private:
bool m_audioMute;
Config() :
- m_inputSampleRate(-1),
+ m_outputSampleRate(-1),
m_inputFrequencyOffset(0),
m_rfBandwidth(-1),
m_afBandwidth(-1),
@@ -108,7 +108,8 @@ private:
Config m_config;
Config m_running;
- NCO m_nco;
+ NCO m_carrierNco;
+ NCO m_toneNco;
Interpolator m_interpolator;
Real m_interpolatorDistance;
Real m_interpolatorDistanceRemain;
diff --git a/plugins/channeltx/modam/ammodgui.cpp b/plugins/channeltx/modam/ammodgui.cpp
new file mode 100644
index 000000000..a5c223d68
--- /dev/null
+++ b/plugins/channeltx/modam/ammodgui.cpp
@@ -0,0 +1,306 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2016 Edouard Griffiths, F4EXB //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include
+#include
+
+#include "ammodgui.h"
+
+#include "device/devicesinkapi.h"
+#include "dsp/upchannelizer.h"
+
+#include "dsp/threadedbasebandsamplesource.h"
+#include "ui_ammodgui.h"
+#include "plugin/pluginapi.h"
+#include "util/simpleserializer.h"
+#include "util/db.h"
+#include "gui/basicchannelsettingswidget.h"
+#include "dsp/dspengine.h"
+#include "mainwindow.h"
+
+#include "ammod.h"
+
+const QString AMModGUI::m_channelID = "sdrangel.channel.modam";
+
+const int AMModGUI::m_rfBW[] = {
+ 5000, 6250, 8330, 10000, 12500, 15000, 20000, 25000, 40000
+};
+
+AMModGUI* AMModGUI::create(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI)
+{
+ AMModGUI* gui = new AMModGUI(pluginAPI, deviceAPI);
+ return gui;
+}
+
+void AMModGUI::destroy()
+{
+}
+
+void AMModGUI::setName(const QString& name)
+{
+ setObjectName(name);
+}
+
+QString AMModGUI::getName() const
+{
+ return objectName();
+}
+
+qint64 AMModGUI::getCenterFrequency() const {
+ return m_channelMarker.getCenterFrequency();
+}
+
+void AMModGUI::setCenterFrequency(qint64 centerFrequency)
+{
+ m_channelMarker.setCenterFrequency(centerFrequency);
+ applySettings();
+}
+
+void AMModGUI::resetToDefaults()
+{
+ blockApplySettings(true);
+
+ ui->rfBW->setValue(4);
+ ui->afBW->setValue(3);
+ ui->modPercent->setValue(20);
+ ui->deltaFrequency->setValue(0);
+
+ blockApplySettings(false);
+ applySettings();
+}
+
+QByteArray AMModGUI::serialize() const
+{
+ SimpleSerializer s(1);
+ s.writeS32(1, m_channelMarker.getCenterFrequency());
+ s.writeS32(2, ui->rfBW->value());
+ s.writeS32(3, ui->afBW->value());
+ s.writeS32(4, ui->modPercent->value());
+ s.writeU32(5, m_channelMarker.getColor().rgb());
+ return s.final();
+}
+
+bool AMModGUI::deserialize(const QByteArray& data)
+{
+ SimpleDeserializer d(data);
+
+ if(!d.isValid())
+ {
+ resetToDefaults();
+ return false;
+ }
+
+ if(d.getVersion() == 1)
+ {
+ QByteArray bytetmp;
+ quint32 u32tmp;
+ qint32 tmp;
+
+ blockApplySettings(true);
+ m_channelMarker.blockSignals(true);
+
+ d.readS32(1, &tmp, 0);
+ m_channelMarker.setCenterFrequency(tmp);
+ d.readS32(2, &tmp, 4);
+ ui->rfBW->setValue(tmp);
+ d.readS32(3, &tmp, 3);
+ ui->afBW->setValue(tmp);
+ d.readS32(4, &tmp, 20);
+ ui->modPercent->setValue(tmp);
+
+ if(d.readU32(5, &u32tmp))
+ {
+ m_channelMarker.setColor(u32tmp);
+ }
+
+ blockApplySettings(false);
+ m_channelMarker.blockSignals(false);
+
+ applySettings();
+ return true;
+ }
+ else
+ {
+ resetToDefaults();
+ return false;
+ }
+}
+
+bool AMModGUI::handleMessage(const Message& message)
+{
+ return false;
+}
+
+void AMModGUI::viewChanged()
+{
+ applySettings();
+}
+
+void AMModGUI::on_deltaMinus_toggled(bool minus)
+{
+ int deltaFrequency = m_channelMarker.getCenterFrequency();
+ bool minusDelta = (deltaFrequency < 0);
+
+ if (minus ^ minusDelta) // sign change
+ {
+ m_channelMarker.setCenterFrequency(-deltaFrequency);
+ }
+}
+
+void AMModGUI::on_deltaFrequency_changed(quint64 value)
+{
+ if (ui->deltaMinus->isChecked()) {
+ m_channelMarker.setCenterFrequency(-value);
+ } else {
+ m_channelMarker.setCenterFrequency(value);
+ }
+}
+
+void AMModGUI::on_rfBW_valueChanged(int value)
+{
+ ui->rfBWText->setText(QString("%1 kHz").arg(m_rfBW[value] / 1000.0));
+ m_channelMarker.setBandwidth(m_rfBW[value]);
+ applySettings();
+}
+
+void AMModGUI::on_afBW_valueChanged(int value)
+{
+ ui->afBWText->setText(QString("%1 kHz").arg(value));
+ applySettings();
+}
+
+void AMModGUI::on_modPercent_valueChanged(int value)
+{
+ ui->modPercentText->setText(QString("%1").arg(value));
+ applySettings();
+}
+
+void AMModGUI::on_audioMute_toggled(bool checked)
+{
+ applySettings();
+}
+
+void AMModGUI::onWidgetRolled(QWidget* widget, bool rollDown)
+{
+}
+
+void AMModGUI::onMenuDoubleClicked()
+{
+ if(!m_basicSettingsShown) {
+ m_basicSettingsShown = true;
+ BasicChannelSettingsWidget* bcsw = new BasicChannelSettingsWidget(&m_channelMarker, this);
+ bcsw->show();
+ }
+}
+
+AMModGUI::AMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* parent) :
+ RollupWidget(parent),
+ ui(new Ui::AMModGUI),
+ m_pluginAPI(pluginAPI),
+ m_deviceAPI(deviceAPI),
+ m_channelMarker(this),
+ m_basicSettingsShown(false),
+ m_doApplySettings(true),
+ m_channelPowerDbAvg(20,0)
+{
+ ui->setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose, true);
+ connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
+ connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked()));
+
+ m_amMod = new AMMod();
+ m_channelizer = new UpChannelizer(m_amMod);
+ m_threadedChannelizer = new ThreadedBasebandSampleSource(m_channelizer, this);
+ //m_pluginAPI->addThreadedSink(m_threadedChannelizer);
+ m_deviceAPI->addThreadedSource(m_threadedChannelizer);
+
+ connect(&m_pluginAPI->getMainWindow()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick()));
+
+ ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::ReverseGold));
+
+ //m_channelMarker = new ChannelMarker(this);
+ m_channelMarker.setColor(Qt::yellow);
+ m_channelMarker.setBandwidth(12500);
+ m_channelMarker.setCenterFrequency(0);
+ m_channelMarker.setVisible(true);
+
+ connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged()));
+
+ m_deviceAPI->registerChannelInstance(m_channelID, this);
+ m_deviceAPI->addChannelMarker(&m_channelMarker);
+ m_deviceAPI->addRollupWidget(this);
+
+ applySettings();
+}
+
+AMModGUI::~AMModGUI()
+{
+ m_deviceAPI->removeChannelInstance(this);
+ m_deviceAPI->removeThreadedSource(m_threadedChannelizer);
+ delete m_threadedChannelizer;
+ delete m_channelizer;
+ delete m_amMod;
+ //delete m_channelMarker;
+ delete ui;
+}
+
+void AMModGUI::blockApplySettings(bool block)
+{
+ m_doApplySettings = !block;
+}
+
+void AMModGUI::applySettings()
+{
+ if (m_doApplySettings)
+ {
+ setTitleColor(m_channelMarker.getColor());
+
+ m_channelizer->configure(m_channelizer->getInputMessageQueue(),
+ 48000,
+ m_channelMarker.getCenterFrequency());
+
+ ui->deltaFrequency->setValue(abs(m_channelMarker.getCenterFrequency()));
+ ui->deltaMinus->setChecked(m_channelMarker.getCenterFrequency() < 0);
+
+ m_amMod->configure(m_amMod->getInputMessageQueue(),
+ m_rfBW[ui->rfBW->value()],
+ ui->afBW->value() * 1000.0,
+ ui->modPercent->value(),
+ ui->audioMute->isChecked());
+ }
+}
+
+void AMModGUI::leaveEvent(QEvent*)
+{
+ blockApplySettings(true);
+ m_channelMarker.setHighlighted(false);
+ blockApplySettings(false);
+}
+
+void AMModGUI::enterEvent(QEvent*)
+{
+ blockApplySettings(true);
+ m_channelMarker.setHighlighted(true);
+ blockApplySettings(false);
+}
+
+void AMModGUI::tick()
+{
+ Real powDb = CalcDb::dbPower(m_amMod->getMagSq());
+ m_channelPowerDbAvg.feed(powDb);
+ ui->channelPower->setText(QString::number(m_channelPowerDbAvg.average(), 'f', 1));
+}
+
diff --git a/plugins/channeltx/modam/ammodgui.h b/plugins/channeltx/modam/ammodgui.h
index eebc4b735..699f1cc25 100644
--- a/plugins/channeltx/modam/ammodgui.h
+++ b/plugins/channeltx/modam/ammodgui.h
@@ -75,7 +75,7 @@ private:
ThreadedBasebandSampleSource* m_threadedChannelizer;
UpChannelizer* m_channelizer;
- AMMod* m_amDemod;
+ AMMod* m_amMod;
MovingAverage m_channelPowerDbAvg;
static const int m_rfBW[];
diff --git a/plugins/channeltx/modam/ammodplugin.cpp b/plugins/channeltx/modam/ammodplugin.cpp
index 532e7651e..806fc894a 100644
--- a/plugins/channeltx/modam/ammodplugin.cpp
+++ b/plugins/channeltx/modam/ammodplugin.cpp
@@ -18,6 +18,7 @@
#include
#include "plugin/pluginapi.h"
+#include "ammodgui.h"
#include "ammodplugin.h"
const PluginDescriptor AMModPlugin::m_pluginDescriptor = {
@@ -38,3 +39,27 @@ const PluginDescriptor& AMModPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
+
+void AMModPlugin::initPlugin(PluginAPI* pluginAPI)
+{
+ m_pluginAPI = pluginAPI;
+
+ // register AM modulator
+ m_pluginAPI->registerTxChannel(AMModGUI::m_channelID, this);
+}
+
+PluginGUI* AMModPlugin::createTxChannel(const QString& channelName, DeviceSinkAPI *deviceAPI)
+{
+ if(channelName == AMModGUI::m_channelID)
+ {
+ AMModGUI* gui = AMModGUI::create(m_pluginAPI, deviceAPI);
+ return gui;
+ } else {
+ return 0;
+ }
+}
+
+void AMModPlugin::createInstanceModAM(DeviceSinkAPI *deviceAPI)
+{
+ AMModGUI* gui = AMModGUI::create(m_pluginAPI, deviceAPI);
+}
diff --git a/plugins/channeltx/modam/modam.pro b/plugins/channeltx/modam/modam.pro
new file mode 100644
index 000000000..b2fe8b5b8
--- /dev/null
+++ b/plugins/channeltx/modam/modam.pro
@@ -0,0 +1,31 @@
+#--------------------------------------------------------
+#
+# Pro file for Android and Windows builds with Qt Creator
+#
+#--------------------------------------------------------
+
+TEMPLATE = lib
+CONFIG += plugin
+
+QT += core gui widgets multimedia
+
+TARGET = modam
+INCLUDEPATH += $$PWD
+INCLUDEPATH += ../../../sdrbase
+
+CONFIG(Release):build_subdir = release
+CONFIG(Debug):build_subdir = debug
+
+SOURCES += ammod.cpp\
+ ammodgui.cpp\
+ ammodplugin.cpp
+
+HEADERS += ammod.h\
+ ammodgui.h\
+ ammodplugin.h
+
+FORMS += ammodgui.ui
+
+LIBS += -L../../../sdrbase/$${build_subdir} -lsdrbase
+
+RESOURCES = ../../../sdrbase/resources/res.qrc
diff --git a/sdrangel.android.pro b/sdrangel.android.pro
index 2569fe25c..fd217b125 100644
--- a/sdrangel.android.pro
+++ b/sdrangel.android.pro
@@ -16,15 +16,16 @@ SUBDIRS += plugins/samplesource/sdrdaemon
#SUBDIRS += plugins/samplesource/hackrf
#SUBDIRS += plugins/samplesource/airspy
SUBDIRS += plugins/samplesink/filesink
-SUBDIRS += plugins/channel/chanalyzer
-SUBDIRS += plugins/channel/demodam
-SUBDIRS += plugins/channel/demodbfm
-SUBDIRS += plugins/channel/demodlora
-SUBDIRS += plugins/channel/demodnfm
-SUBDIRS += plugins/channel/demodssb
-SUBDIRS += plugins/channel/demodwfm
-SUBDIRS += plugins/channel/tcpsrc
-SUBDIRS += plugins/channel/udpsrc
+SUBDIRS += plugins/channelrx/chanalyzer
+SUBDIRS += plugins/channelrx/demodam
+SUBDIRS += plugins/channelrx/demodbfm
+SUBDIRS += plugins/channelrx/demodlora
+SUBDIRS += plugins/channelrx/demodnfm
+SUBDIRS += plugins/channelrx/demodssb
+SUBDIRS += plugins/channelrx/demodwfm
+SUBDIRS += plugins/channelrx/tcpsrc
+SUBDIRS += plugins/channelrx/udpsrc
+SUBDIRS += plugins/channeltx/modam
# Main app must be last
CONFIG += ordered
diff --git a/sdrangel.macos.pro b/sdrangel.macos.pro
index c0baac5a7..04248bab6 100644
--- a/sdrangel.macos.pro
+++ b/sdrangel.macos.pro
@@ -25,6 +25,7 @@ SUBDIRS += plugins/channelrx/demodssb
SUBDIRS += plugins/channelrx/demodwfm
SUBDIRS += plugins/channelrx/tcpsrc
SUBDIRS += plugins/channelrx/udpsrc
+SUBDIRS += plugins/channeltx/modam
# Main app must be last
CONFIG += ordered
diff --git a/sdrangel.windows.pro b/sdrangel.windows.pro
index 7eb68737f..e392cef90 100644
--- a/sdrangel.windows.pro
+++ b/sdrangel.windows.pro
@@ -36,6 +36,7 @@ SUBDIRS += plugins/channelrx/demodssb
SUBDIRS += plugins/channelrx/demodwfm
SUBDIRS += plugins/channelrx/tcpsrc
SUBDIRS += plugins/channelrx/udpsrc
+SUBDIRS += plugins/channeltx/modam
# Main app must be last
CONFIG += ordered
diff --git a/windows.install.bat b/windows.install.bat
index 69995190a..72546a9f7 100644
--- a/windows.install.bat
+++ b/windows.install.bat
@@ -13,6 +13,7 @@ copy libbladerf\%1\libbladerf.dll %2
copy %libusbdir%\dll\libusb-1.0.dll %2
mkdir %2\plugins
mkdir %2\plugins\channelrx
+mkdir %2\plugins\channeltx
mkdir %2\plugins\samplesource
mkdir %2\plugins\samplesink
copy plugins\channelrx\chanalyzer\%1\chanalyzer.dll %2\plugins\channelrx
@@ -25,6 +26,7 @@ copy plugins\channelrx\demodssb\%1\demodssb.dll %2\plugins\channelrx
copy plugins\channelrx\demodwfm\%1\demodwfm.dll %2\plugins\channelrx
copy plugins\channelrx\tcpsrc\%1\tcpsrc.dll %2\plugins\channelrx
copy plugins\channelrx\udpsrc\%1\udpsrc.dll %2\plugins\channelrx
+copy plugins\channeltx\modam\%1\modam.dll %2\plugins\channeltx
copy plugins\samplesource\filesource\%1\inputfilesource.dll %2\plugins\samplesource
copy plugins\samplesource\rtlsdr\%1\inputrtlsdr.dll %2\plugins\samplesource
copy plugins\samplesource\hackrf\%1\inputhackrf.dll %2\plugins\samplesource
diff --git a/windows64.install.bat b/windows64.install.bat
index dc5b12cf2..f4382cfd0 100644
--- a/windows64.install.bat
+++ b/windows64.install.bat
@@ -26,6 +26,7 @@ copy libbladerf\%1\libbladerf.dll %2
copy %libusbdir%\dll\libusb-1.0.dll %2
mkdir %2\plugins
mkdir %2\plugins\channelrx
+mkdir %2\plugins\channeltx
mkdir %2\plugins\samplesource
mkdir %2\plugins\samplesink
copy plugins\channelrx\chanalyzer\%1\chanalyzer.dll %2\plugins\channelrx
@@ -38,6 +39,7 @@ copy plugins\channelrx\demodssb\%1\demodssb.dll %2\plugins\channelrxd
copy plugins\channelrx\demodwfm\%1\demodwfm.dll %2\plugins\channelrx
copy plugins\channelrx\tcpsrc\%1\tcpsrc.dll %2\plugins\channelrx
copy plugins\channelrx\udpsrc\%1\udpsrc.dll %2\plugins\channelrx
+copy plugins\channeltx\modam\%1\modam.dll %2\plugins\channeltx
copy plugins\samplesource\filesource\%1\inputfilesource.dll %2\plugins\samplesource
copy plugins\samplesource\sdrdaemon\%1\inputsdrdaemon.dll %2\plugins\samplesource
copy plugins\samplesource\sdrdaemonfec\%1\inputsdrdaemonfec.dll %2\plugins\samplesource