summaryrefslogtreecommitdiffstats
path: root/plugins/channel/tetra
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/channel/tetra')
-rw-r--r--plugins/channel/tetra/CMakeLists.txt47
-rw-r--r--plugins/channel/tetra/tetrademod.cpp106
-rw-r--r--plugins/channel/tetra/tetrademod.h67
-rw-r--r--plugins/channel/tetra/tetrademodgui.cpp110
-rw-r--r--plugins/channel/tetra/tetrademodgui.h56
-rw-r--r--plugins/channel/tetra/tetrademodgui.ui56
-rw-r--r--plugins/channel/tetra/tetraplugin.cpp50
-rw-r--r--plugins/channel/tetra/tetraplugin.h29
8 files changed, 521 insertions, 0 deletions
diff --git a/plugins/channel/tetra/CMakeLists.txt b/plugins/channel/tetra/CMakeLists.txt
new file mode 100644
index 0000000..f46c2bb
--- /dev/null
+++ b/plugins/channel/tetra/CMakeLists.txt
@@ -0,0 +1,47 @@
+project(tetra)
+
+set(tetra_SOURCES
+ tetrademod.cpp
+ tetrademodgui.cpp
+ tetraplugin.cpp
+)
+
+set(tetra_HEADERS
+ tetrademod.h
+ tetrademodgui.h
+ tetraplugin.h
+)
+
+set(tetra_FORMS
+ tetrademodgui.ui
+)
+
+include_directories(
+ .
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/include
+ ${CMAKE_SOURCE_DIR}/include-gpl
+ ${OPENGL_INCLUDE_DIR}
+)
+
+#include(${QT_USE_FILE})
+add_definitions(${QT_DEFINITIONS})
+add_definitions(-DQT_PLUGIN)
+add_definitions(-DQT_SHARED)
+
+#qt5_wrap_cpp(tetra_HEADERS_MOC ${tetra_HEADERS})
+qt5_wrap_ui(tetra_FORMS_HEADERS ${tetra_FORMS})
+
+add_library(demodtetra SHARED
+ ${tetra_SOURCES}
+ ${tetra_HEADERS_MOC}
+ ${tetra_FORMS_HEADERS}
+)
+
+target_link_libraries(demodtetra
+ ${QT_LIBRARIES}
+ ${OPENGL_LIBRARIES}
+ sdrbase
+)
+
+qt5_use_modules(demodtetra Core Widgets OpenGL Multimedia)
diff --git a/plugins/channel/tetra/tetrademod.cpp b/plugins/channel/tetra/tetrademod.cpp
new file mode 100644
index 0000000..f26810a
--- /dev/null
+++ b/plugins/channel/tetra/tetrademod.cpp
@@ -0,0 +1,106 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
+// written by Christian Daniel //
+// //
+// 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 <http://www.gnu.org/licenses/>. //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include <stdio.h>
+#include "tetrademod.h"
+#include "dsp/dspcommands.h"
+
+MessageRegistrator TetraDemod::MsgConfigureTetraDemod::ID("MsgConfigureTetraDemod");
+
+static FILE* f = NULL;
+
+TetraDemod::TetraDemod(SampleSink* sampleSink) :
+ m_sampleSink(sampleSink)
+{
+ m_sampleRate = 500000;
+ m_frequency = 0;
+
+ m_nco.setFreq(m_frequency, m_sampleRate);
+ m_interpolator.create(32, 32 * m_sampleRate, 36000);
+ m_sampleDistanceRemain = (Real)m_sampleRate / 36000.0;
+}
+
+TetraDemod::~TetraDemod()
+{
+}
+
+void TetraDemod::configure(MessageQueue* messageQueue)
+{
+ Message* cmd = MsgConfigureTetraDemod::create();
+ cmd->submit(messageQueue, this);
+}
+
+void TetraDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst)
+{
+ size_t count = end - begin;
+
+ Complex ci;
+ bool consumed;
+
+ for(SampleVector::const_iterator it = begin; it < end; ++it) {
+ Complex c(it->real() / 32768.0, it->imag() / 32768.0);
+ c *= m_nco.nextIQ();
+
+ consumed = false;
+ if(m_interpolator.interpolate(&m_sampleDistanceRemain, c, &consumed, &ci)) {
+ m_sampleBuffer.push_back(Sample(ci.real() * 32768.0, ci.imag() * 32768.0));
+
+ m_sampleDistanceRemain += (Real)m_sampleRate / 36000.0;
+ }
+ }
+
+ if(f != NULL) {
+ fwrite(&m_sampleBuffer[0], m_sampleBuffer.size(), sizeof(m_sampleBuffer[0]), f);
+ }
+
+ if(m_sampleSink != NULL)
+ m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), firstOfBurst);
+ m_sampleBuffer.clear();
+}
+
+void TetraDemod::start()
+{
+}
+
+void TetraDemod::stop()
+{
+}
+
+bool TetraDemod::handleMessage(Message* cmd)
+{
+ if(cmd->id() == DSPSignalNotification::ID()) {
+ DSPSignalNotification* signal = (DSPSignalNotification*)cmd;
+ qDebug("%d samples/sec, %lld Hz offset", signal->getSampleRate(), signal->getFrequencyOffset());
+ m_sampleRate = signal->getSampleRate();
+ m_nco.setFreq(-signal->getFrequencyOffset(), m_sampleRate);
+ m_interpolator.create(32, m_sampleRate, 25000 / 2);
+ m_sampleDistanceRemain = m_sampleRate / 36000.0;
+ cmd->completed();
+ return true;
+ } else if(cmd->id() == MsgConfigureTetraDemod::ID()) {
+ if(f == NULL) {
+ f = fopen("/tmp/tetra.iq", "wb");
+ qDebug("started writing samples");
+ } else {
+ fclose(f);
+ f = NULL;
+ qDebug("stopped writing samples");
+ }
+ } else {
+ return false;
+ }
+}
diff --git a/plugins/channel/tetra/tetrademod.h b/plugins/channel/tetra/tetrademod.h
new file mode 100644
index 0000000..a2a474b
--- /dev/null
+++ b/plugins/channel/tetra/tetrademod.h
@@ -0,0 +1,67 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
+// written by Christian Daniel //
+// //
+// 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 <http://www.gnu.org/licenses/>. //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef INCLUDE_TETRADEMOD_H
+#define INCLUDE_TETRADEMOD_H
+
+#include "dsp/samplesink.h"
+#include "dsp/nco.h"
+#include "dsp/interpolator.h"
+#include "util/message.h"
+
+class MessageQueue;
+
+class TetraDemod : public SampleSink {
+public:
+ TetraDemod(SampleSink* sampleSink);
+ ~TetraDemod();
+
+ void configure(MessageQueue* messageQueue);
+
+ void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst);
+ void start();
+ void stop();
+ bool handleMessage(Message* cmd);
+
+private:
+ class MsgConfigureTetraDemod : public Message {
+ public:
+ static MessageRegistrator ID;
+
+ static MsgConfigureTetraDemod* create()
+ {
+ return new MsgConfigureTetraDemod();
+ }
+
+ private:
+ MsgConfigureTetraDemod() :
+ Message(ID())
+ { }
+ };
+
+ int m_sampleRate;
+ int m_frequency;
+
+ NCO m_nco;
+ Interpolator m_interpolator;
+ Real m_sampleDistanceRemain;
+
+ SampleSink* m_sampleSink;
+ SampleVector m_sampleBuffer;
+};
+
+#endif // INCLUDE_TETRADEMOD_H
diff --git a/plugins/channel/tetra/tetrademodgui.cpp b/plugins/channel/tetra/tetrademodgui.cpp
new file mode 100644
index 0000000..978f4f7
--- /dev/null
+++ b/plugins/channel/tetra/tetrademodgui.cpp
@@ -0,0 +1,110 @@
+#include <QDockWidget>
+#include <QMainWindow>
+#include "tetrademodgui.h"
+#include "ui_tetrademodgui.h"
+#include "dsp/threadedsamplesink.h"
+#include "dsp/channelizer.h"
+#include "tetrademod.h"
+#include "dsp/spectrumvis.h"
+#include "gui/glspectrum.h"
+#include "plugin/pluginapi.h"
+
+TetraDemodGUI* TetraDemodGUI::create(PluginAPI* pluginAPI)
+{
+ QDockWidget* dock = pluginAPI->createMainWindowDock(Qt::RightDockWidgetArea, tr("Tetra Demodulator"));
+ dock->setObjectName(QString::fromUtf8("Tetra Demodulator"));
+ TetraDemodGUI* gui = new TetraDemodGUI(pluginAPI, dock);
+ dock->setWidget(gui);
+ return gui;
+}
+
+void TetraDemodGUI::destroy()
+{
+ delete m_dockWidget;
+}
+
+void TetraDemodGUI::setWidgetName(const QString& name)
+{
+ m_dockWidget->setObjectName(name);
+}
+
+void TetraDemodGUI::resetToDefaults()
+{
+}
+
+QByteArray TetraDemodGUI::serializeGeneral() const
+{
+ return QByteArray();
+}
+
+bool TetraDemodGUI::deserializeGeneral(const QByteArray& data)
+{
+ return false;
+}
+
+QByteArray TetraDemodGUI::serialize() const
+{
+ return QByteArray();
+}
+
+bool TetraDemodGUI::deserialize(const QByteArray& data)
+{
+ return false;
+}
+
+bool TetraDemodGUI::handleMessage(Message* message)
+{
+ return false;
+}
+
+void TetraDemodGUI::viewChanged()
+{
+ m_channelizer->configure(m_threadedSampleSink->getMessageQueue(), 36000, m_channelMarker->getCenterFrequency());
+}
+
+TetraDemodGUI::TetraDemodGUI(PluginAPI* pluginAPI, QDockWidget* dockWidget, QWidget* parent) :
+ PluginGUI(parent),
+ ui(new Ui::TetraDemodGUI),
+ m_pluginAPI(pluginAPI),
+ m_dockWidget(dockWidget)
+{
+ ui->setupUi(this);
+
+ m_spectrumVis = new SpectrumVis(ui->glSpectrum);
+ m_tetraDemod = new TetraDemod(m_spectrumVis);
+ m_channelizer = new Channelizer(m_tetraDemod);
+ m_threadedSampleSink = new ThreadedSampleSink(m_channelizer);
+ m_pluginAPI->addSampleSink(m_threadedSampleSink);
+
+ ui->glSpectrum->setCenterFrequency(0);
+ ui->glSpectrum->setSampleRate(36000);
+ ui->glSpectrum->setDisplayWaterfall(true);
+ ui->glSpectrum->setDisplayMaxHold(true);
+ m_spectrumVis->configure(m_threadedSampleSink->getMessageQueue(), 64, 10, FFTWindow::BlackmanHarris);
+
+ m_channelMarker = new ChannelMarker(this);
+ m_channelMarker->setColor(Qt::darkGreen);
+ m_channelMarker->setBandwidth(25000);
+ m_channelMarker->setCenterFrequency(0);
+ m_channelMarker->setVisible(true);
+ connect(m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged()));
+ m_pluginAPI->addChannelMarker(m_channelMarker);
+
+ viewChanged();
+}
+
+TetraDemodGUI::~TetraDemodGUI()
+{
+ m_pluginAPI->removeSampleSink(m_threadedSampleSink);
+ delete m_threadedSampleSink;
+ delete m_channelizer;
+ delete m_tetraDemod;
+ delete m_spectrumVis;
+ delete m_channelMarker;
+ delete ui;
+}
+
+void TetraDemodGUI::on_test_clicked()
+{
+ m_tetraDemod->configure(m_threadedSampleSink->getMessageQueue());
+}
diff --git a/plugins/channel/tetra/tetrademodgui.h b/plugins/channel/tetra/tetrademodgui.h
new file mode 100644
index 0000000..9eacae6
--- /dev/null
+++ b/plugins/channel/tetra/tetrademodgui.h
@@ -0,0 +1,56 @@
+#ifndef INCLUDE_TETRADEMODGUI_H
+#define INCLUDE_TETRADEMODGUI_H
+
+#include "plugin/plugingui.h"
+
+class QDockWidget;
+
+class PluginAPI;
+class ChannelMarker;
+
+class ThreadedSampleSink;
+class Channelizer;
+class TetraDemod;
+class SpectrumVis;
+
+namespace Ui {
+ class TetraDemodGUI;
+}
+
+class TetraDemodGUI : public PluginGUI {
+ Q_OBJECT
+
+public:
+ static TetraDemodGUI* create(PluginAPI* pluginAPI);
+ void destroy();
+
+ void setWidgetName(const QString& name);
+
+ void resetToDefaults();
+ QByteArray serializeGeneral() const;
+ bool deserializeGeneral(const QByteArray& data);
+ QByteArray serialize() const;
+ bool deserialize(const QByteArray& data);
+
+ bool handleMessage(Message* message);
+
+private slots:
+ void on_test_clicked();
+ void viewChanged();
+
+private:
+ explicit TetraDemodGUI(PluginAPI* pluginAPI, QDockWidget* dockWidget, QWidget* parent = NULL);
+ ~TetraDemodGUI();
+
+ Ui::TetraDemodGUI* ui;
+ PluginAPI* m_pluginAPI;
+ QDockWidget* m_dockWidget;
+ ChannelMarker* m_channelMarker;
+
+ ThreadedSampleSink* m_threadedSampleSink;
+ Channelizer* m_channelizer;
+ TetraDemod* m_tetraDemod;
+ SpectrumVis* m_spectrumVis;
+};
+
+#endif // INCLUDE_TETRADEMODGUI_H
diff --git a/plugins/channel/tetra/tetrademodgui.ui b/plugins/channel/tetra/tetrademodgui.ui
new file mode 100644
index 0000000..feaef10
--- /dev/null
+++ b/plugins/channel/tetra/tetrademodgui.ui
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>TetraDemodGUI</class>
+ <widget class="QWidget" name="TetraDemodGUI">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>200</width>
+ <height>179</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="GLSpectrum" name="glSpectrum" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>200</width>
+ <height>150</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="test">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>GLSpectrum</class>
+ <extends>QWidget</extends>
+ <header>gui/glspectrum.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/plugins/channel/tetra/tetraplugin.cpp b/plugins/channel/tetra/tetraplugin.cpp
new file mode 100644
index 0000000..9dc48cc
--- /dev/null
+++ b/plugins/channel/tetra/tetraplugin.cpp
@@ -0,0 +1,50 @@
+#include <QtPlugin>
+#include <QAction>
+#include "plugin/pluginapi.h"
+#include "tetraplugin.h"
+#include "tetrademodgui.h"
+
+const PluginDescriptor TetraPlugin::m_pluginDescriptor = {
+ QString("Tetra Demodulator"),
+ QString("---"),
+ QString("(c) maintech GmbH (written by Christian Daniel)"),
+ QString("http://www.maintech.de"),
+ true,
+ QString("http://www.maintech.de")
+};
+
+TetraPlugin::TetraPlugin(QObject* parent) :
+ QObject(parent)
+{
+}
+
+const PluginDescriptor& TetraPlugin::getPluginDescriptor() const
+{
+ return m_pluginDescriptor;
+}
+
+void TetraPlugin::initPlugin(PluginAPI* pluginAPI)
+{
+ m_pluginAPI = pluginAPI;
+
+ // register Tetra demodulator
+ QAction* action = new QAction(tr("&Tetra"), this);
+ connect(action, SIGNAL(triggered()), this, SLOT(createInstanceTetra()));
+ m_pluginAPI->registerDemodulator("de.maintech.sdrangelove.demod.tetra", this, action);
+}
+
+PluginGUI* TetraPlugin::createDemod(const QString& demodName)
+{
+ if(demodName == "de.maintech.sdrangelove.demod.tetra") {
+ PluginGUI* gui = TetraDemodGUI::create(m_pluginAPI);
+ m_pluginAPI->registerDemodulatorInstance("de.maintech.sdrangelove.demod.tetra", gui);
+ return gui;
+ } else {
+ return NULL;
+ }
+}
+
+void TetraPlugin::createInstanceTetra()
+{
+ m_pluginAPI->registerDemodulatorInstance("de.maintech.sdrangelove.demod.tetra", TetraDemodGUI::create(m_pluginAPI));
+}
diff --git a/plugins/channel/tetra/tetraplugin.h b/plugins/channel/tetra/tetraplugin.h
new file mode 100644
index 0000000..ec94a18
--- /dev/null
+++ b/plugins/channel/tetra/tetraplugin.h
@@ -0,0 +1,29 @@
+#ifndef INCLUDE_TETRAPLUGIN_H
+#define INCLUDE_TETRAPLUGIN_H
+
+#include <QObject>
+#include "plugin/plugininterface.h"
+
+class TetraPlugin : public QObject, PluginInterface {
+ Q_OBJECT
+ Q_INTERFACES(PluginInterface)
+ Q_PLUGIN_METADATA(IID "de.maintech.sdrangelove.demod.tetra")
+
+public:
+ explicit TetraPlugin(QObject* parent = NULL);
+
+ const PluginDescriptor& getPluginDescriptor() const;
+ void initPlugin(PluginAPI* pluginAPI);
+
+ PluginGUI* createDemod(const QString& demodName);
+
+private:
+ static const PluginDescriptor m_pluginDescriptor;
+
+ PluginAPI* m_pluginAPI;
+
+private slots:
+ void createInstanceTetra();
+};
+
+#endif // INCLUDE_TETRAPLUGIN_H