1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-25 01:18:38 -05:00

git clone git://git.osmocom.org/sdrangelove.git

This commit is contained in:
Hexameron 2014-05-18 16:52:39 +01:00
commit 7d3bfb26fc
203 changed files with 27958 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
CMakeLists.txt.user*
build/*
LOCAL/*

294
CMakeLists.txt Normal file
View File

@ -0,0 +1,294 @@
cmake_minimum_required(VERSION 2.8.9)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules)
project(sdrangelove)
set(CMAKE_BUILD_TYPE "Release")
#set(CMAKE_BUILD_TYPE "ReleaseWithDebugInfo")
#set(CMAKE_BUILD_TYPE "Debug")
set(QT_USE_QTOPENGL TRUE)
set(CMAKE_AUTOMOC ON)
#find_package(Qt4 REQUIRED)
find_package(Qt5Core 5.0 REQUIRED)
find_package(Qt5Widgets 5.0 REQUIRED)
find_package(Qt5Multimedia 5.0 REQUIRED)
#find_package(QT5OpenGL 5.0 REQUIRED)
find_package(OpenGL REQUIRED)
find_package(PkgConfig)
find_package(FFTW3F)
##############################################################################
#include(${QT_USE_FILE})
add_definitions(${QT_DEFINITIONS})
if(MSVC)
foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES})
string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/bin_${OUTPUTCONFIG})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/bin_${OUTPUTCONFIG})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/bin_${OUTPUTCONFIG})
endforeach(OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES)
endif()
##############################################################################
set(sdrbase_SOURCES
sdrbase/mainwindow.cpp
sdrbase/audio/audiodeviceinfo.cpp
sdrbase/audio/audiofifo.cpp
sdrbase/audio/audiooutput.cpp
sdrbase/dsp/channelizer.cpp
sdrbase/dsp/channelmarker.cpp
sdrbase/dsp/dspcommands.cpp
sdrbase/dsp/dspengine.cpp
sdrbase/dsp/fftengine.cpp
sdrbase/dsp/fftwindow.cpp
sdrbase/dsp/interpolator.cpp
sdrbase/dsp/inthalfbandfilter.cpp
sdrbase/dsp/lowpass.cpp
sdrbase/dsp/movingaverage.cpp
sdrbase/dsp/nco.cpp
sdrbase/dsp/pidcontroller.cpp
sdrbase/dsp/samplefifo.cpp
sdrbase/dsp/samplesink.cpp
sdrbase/dsp/scopevis.cpp
sdrbase/dsp/spectrumvis.cpp
sdrbase/dsp/threadedsamplesink.cpp
sdrbase/gui/aboutdialog.cpp
sdrbase/gui/addpresetdialog.cpp
sdrbase/gui/basicchannelsettingswidget.cpp
sdrbase/gui/buttonswitch.cpp
sdrbase/gui/channelwindow.cpp
sdrbase/gui/glscope.cpp
sdrbase/gui/glspectrum.cpp
sdrbase/gui/glspectrumgui.cpp
sdrbase/gui/indicator.cpp
sdrbase/gui/pluginsdialog.cpp
sdrbase/gui/preferencesdialog.cpp
sdrbase/gui/presetitem.cpp
sdrbase/gui/rollupwidget.cpp
sdrbase/gui/scale.cpp
sdrbase/gui/scaleengine.cpp
sdrbase/gui/scopewindow.cpp
sdrbase/gui/valuedial.cpp
sdrbase/dsp/samplesource/samplesource.cpp
sdrbase/plugin/pluginapi.cpp
sdrbase/plugin/plugingui.cpp
sdrbase/plugin/plugininterface.cpp
sdrbase/plugin/pluginmanager.cpp
sdrbase/settings/preferences.cpp
sdrbase/settings/preset.cpp
sdrbase/settings/settings.cpp
sdrbase/util/message.cpp
sdrbase/util/messagequeue.cpp
sdrbase/util/miniz.cpp
sdrbase/util/simpleserializer.cpp
sdrbase/util/spinlock.cpp
)
set(sdrbase_HEADERS
include-gpl/mainwindow.h
include-gpl/audio/audiodeviceinfo.h
include-gpl/audio/audiofifo.h
include-gpl/audio/audiooutput.h
include-gpl/dsp/channelizer.h
include/dsp/channelmarker.h
include-gpl/dsp/dspcommands.h
include-gpl/dsp/dspengine.h
include/dsp/dsptypes.h
include-gpl/dsp/fftengine.h
include-gpl/dsp/fftwengine.h
include-gpl/dsp/fftwindow.h
include-gpl/dsp/interpolator.h
include-gpl/dsp/inthalfbandfilter.h
include/dsp/kissfft.h
include-gpl/dsp/kissengine.h
include-gpl/dsp/lowpass.h
include-gpl/dsp/movingaverage.h
include-gpl/dsp/nco.h
sdrbase/dsp/pidcontroller.h
include/dsp/samplefifo.h
include/dsp/samplesink.h
include-gpl/dsp/scopevis.h
include-gpl/dsp/spectrumvis.h
include/dsp/threadedsamplesink.h
include-gpl/gui/aboutdialog.h
include-gpl/gui/addpresetdialog.h
include/gui/basicchannelsettingswidget.h
include-gpl/gui/buttonswitch.h
include-gpl/gui/channelwindow.h
include-gpl/gui/glscope.h
include-gpl/gui/glspectrum.h
include-gpl/gui/glspectrumgui.h
include-gpl/gui/indicator.h
include-gpl/gui/physicalunit.h
include-gpl/gui/pluginsdialog.h
include-gpl/gui/preferencesdialog.h
include-gpl/gui/presetitem.h
include/gui/rollupwidget.h
include-gpl/gui/scale.h
include-gpl/gui/scaleengine.h
include-gpl/gui/scopewindow.h
include-gpl/gui/valuedial.h
include/dsp/samplesource/samplesource.h
include/plugin/pluginapi.h
include/plugin/plugingui.h
include/plugin/plugininterface.h
include-gpl/plugin/pluginmanager.h
include-gpl/settings/preferences.h
include-gpl/settings/preset.h
include-gpl/settings/settings.h
include/util/export.h
include/util/message.h
include/util/messagequeue.h
include/util/miniz.h
include/util/simpleserializer.h
include/util/spinlock.h
)
set(sdrbase_SOURCES
${sdrbase_SOURCES}
${sdrbase_HEADERS}
)
set(sdrbase_FORMS
sdrbase/mainwindow.ui
sdrbase/gui/aboutdialog.ui
sdrbase/gui/addpresetdialog.ui
sdrbase/gui/basicchannelsettingswidget.ui
sdrbase/gui/glspectrumgui.ui
sdrbase/gui/pluginsdialog.ui
sdrbase/gui/preferencesdialog.ui
sdrbase/gui/scopewindow.ui
)
set(sdrbase_RESOURCES
sdrbase/resources/res.qrc
)
if(FFTW3F_FOUND)
set(sdrbase_SOURCES
${sdrbase_SOURCES}
sdrbase/dsp/fftwengine.cpp
)
set(sdrbase_HEADERS
${sdrbase_HEADERS}
include-gpl/dsp/fftwengine.h
)
add_definitions(-DUSE_FFTW)
include_directories(${FFTW3F_INCLUDE_DIRS})
else(FFTW3F_FOUND)
set(sdrbase_SOURCES
${sdrbase_SOURCES}
sdrbase/dsp/kissengine.cpp
include/dsp/kissfft.h
)
set(sdrbase_HEADERS
${sdrbase_HEADERS}
include-gpl/dsp/kissengine.h
)
add_definitions(-DUSE_KISSFFT)
endif(FFTW3F_FOUND)
#include(${QT_USE_FILE})
add_definitions(${QT_DEFINITIONS})
#qt5_wrap_cpp(sdrbase_HEADERS_MOC ${sdrbase_HEADERS})
qt5_wrap_ui(sdrbase_FORMS_HEADERS ${sdrbase_FORMS})
qt5_add_resources(sdrbase_RESOURCES_RCC ${sdrbase_RESOURCES})
if(WIN32)
SET(sdrbase_SOURCES ${sdrbase_SOURCES} sdrbase/resources/sdrangelove.rc)
endif(WIN32)
add_library(sdrbase SHARED
${sdrbase_SOURCES}
${sdrbase_HEADERS_MOC}
${sdrbase_FORMS_HEADERS}
${sdrbase_RESOURCES_RCC}
)
target_link_libraries(sdrbase
${QT_LIBRARIES}
${OPENGL_LIBRARIES}
${LIBUSB_LIBRARIES}
)
if(FFTW3F_FOUND)
target_link_libraries(sdrbase ${FFTW3F_LIBRARIES})
endif(FFTW3F_FOUND)
set_target_properties(sdrbase PROPERTIES DEFINE_SYMBOL "sdrangelove_EXPORTS")
qt5_use_modules(sdrbase Core Widgets OpenGL Multimedia)
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/include-gpl
${OPENGL_INCLUDE_DIR}
)
if(MSVC)
set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /arch:SSE2" )
set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oi /GL /Ot /Ox /arch:SSE2" )
set( CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG" )
add_definitions (/D "_CRT_SECURE_NO_WARNINGS")
else()
set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -msse2" )
set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -msse2" )
endif()
##############################################################################
set(sdrangelove_SOURCES
main.cpp
)
if(WIN32)
SET(sdrangelove_SOURCES ${sdrangelove_SOURCES} sdrbase/resources/sdrangelove.rc)
endif(WIN32)
add_executable(sdrangelove
${sdrangelove_SOURCES}
)
target_link_libraries(sdrangelove
sdrbase
${QT_LIBRARIES}
${OPENGL_LIBRARIES}
)
if(WIN32)
set_target_properties(sdrangelove PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
set_target_properties(sdrangelove PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE")
set_target_properties(sdrangelove PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
set_target_properties(sdrangelove PROPERTIES COMPILE_DEFINITIONS_RELWITHDEBINFO "_CONSOLE")
set_target_properties(sdrangelove PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
set_target_properties(sdrangelove PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
endif(WIN32)
qt5_use_modules(sdrangelove Widgets Multimedia)
##############################################################################
add_subdirectory(plugins)

View File

@ -0,0 +1,40 @@
# http://tim.klingt.org/code/projects/supernova/repository/revisions/d336dd6f400e381bcfd720e96139656de0c53b6a/entry/cmake_modules/FindFFTW3f.cmake
# Modified to use pkg config and use standard var names
# Find single-precision (float) version of FFTW3
INCLUDE(FindPkgConfig)
PKG_CHECK_MODULES(PC_FFTW3F "fftw3f >= 3.0")
FIND_PATH(
FFTW3F_INCLUDE_DIRS
NAMES fftw3.h
HINTS $ENV{FFTW3_DIR}/include
${PC_FFTW3F_INCLUDE_DIR}
PATHS /usr/local/include
/usr/include
)
FIND_LIBRARY(
FFTW3F_LIBRARIES
NAMES fftw3f libfftw3f
HINTS $ENV{FFTW3_DIR}/lib
${PC_FFTW3F_LIBDIR}
PATHS /usr/local/lib
/usr/lib
/usr/lib64
)
FIND_LIBRARY(
FFTW3F_THREADS_LIBRARIES
NAMES fftw3f_threads libfftw3f_threads
HINTS $ENV{FFTW3_DIR}/lib
${PC_FFTW3F_LIBDIR}
PATHS /usr/local/lib
/usr/lib
/usr/lib64
)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(FFTW3F DEFAULT_MSG FFTW3F_LIBRARIES FFTW3F_INCLUDE_DIRS)
MARK_AS_ADVANCED(FFTW3F_LIBRARIES FFTW3F_INCLUDE_DIRS FFTW3F_THREADS_LIBRARIES)

View File

@ -0,0 +1,29 @@
INCLUDE(FindPkgConfig)
PKG_CHECK_MODULES(PC_GRUEL gruel)
FIND_PATH(
GRUEL_INCLUDE_DIRS
NAMES gruel/attributes.h
HINTS $ENV{GRUEL_DIR}/include
${PC_GRUEL_INCLUDEDIR}
${CMAKE_INSTALL_PREFIX}/include
PATHS /usr/local/include
/usr/include
)
FIND_LIBRARY(
GRUEL_LIBRARIES
NAMES gruel
HINTS $ENV{GRUEL_DIR}/lib
${PC_GRUEL_LIBDIR}
${CMAKE_INSTALL_PREFIX}/lib
${CMAKE_INSTALL_PREFIX}/lib64
PATHS /usr/local/lib
/usr/local/lib64
/usr/lib
/usr/lib64
)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GRUEL DEFAULT_MSG GRUEL_LIBRARIES GRUEL_INCLUDE_DIRS)
MARK_AS_ADVANCED(GRUEL_LIBRARIES GRUEL_INCLUDE_DIRS)

View File

@ -0,0 +1,28 @@
if(NOT LIBOSMOSDR_FOUND)
pkg_check_modules (LIBOSMOSDR_PKG libosmosdr)
find_path(LIBOSMOSDR_INCLUDE_DIR NAMES osmosdr.h
PATHS
${LIBOSMOSDR_PKG_INCLUDE_DIRS}
/usr/include
/usr/local/include
)
find_library(LIBOSMOSDR_LIBRARIES NAMES osmosdr
PATHS
${LIBOSMOSDR_PKG_LIBRARY_DIRS}
/usr/lib
/usr/local/lib
)
if(LIBOSMOSDR_INCLUDE_DIR AND LIBOSMOSDR_LIBRARIES)
set(LIBOSMOSDR_FOUND TRUE CACHE INTERNAL "libosmosdr found")
message(STATUS "Found libosmosdr: ${LIBOSMOSDR_INCLUDE_DIR}, ${LIBOSMOSDR_LIBRARIES}")
else(LIBOSMOSDR_INCLUDE_DIR AND LIBOSMOSDR_LIBRARIES)
set(LIBOSMOSDR_FOUND FALSE CACHE INTERNAL "libosmosdr found")
message(STATUS "libosmosdr not found.")
endif(LIBOSMOSDR_INCLUDE_DIR AND LIBOSMOSDR_LIBRARIES)
mark_as_advanced(LIBOSMOSDR_INCLUDE_DIR LIBOSMOSDR_LIBRARIES)
endif(NOT LIBOSMOSDR_FOUND)

View File

@ -0,0 +1,28 @@
if(NOT LIBRTLSDR_FOUND)
pkg_check_modules (LIBRTLSDR_PKG librtlsdr)
find_path(LIBRTLSDR_INCLUDE_DIR NAMES rtl-sdr.h
PATHS
${LIBRTLSDR_PKG_INCLUDE_DIRS}
/usr/include
/usr/local/include
)
find_library(LIBRTLSDR_LIBRARIES NAMES rtlsdr
PATHS
${LIBRTLSDR_PKG_LIBRARY_DIRS}
/usr/lib
/usr/local/lib
)
if(LIBRTLSDR_INCLUDE_DIR AND LIBRTLSDR_LIBRARIES)
set(LIBRTLSDR_FOUND TRUE CACHE INTERNAL "librtlsdr found")
message(STATUS "Found librtlsdr: ${LIBRTLSDR_INCLUDE_DIR}, ${LIBRTLSDR_LIBRARIES}")
else(LIBRTLSDR_INCLUDE_DIR AND LIBRTLSDR_LIBRARIES)
set(LIBRTLSDR_FOUND FALSE CACHE INTERNAL "librtlsdr found")
message(STATUS "librtlsdr not found.")
endif(LIBRTLSDR_INCLUDE_DIR AND LIBRTLSDR_LIBRARIES)
mark_as_advanced(LIBRTLSDR_INCLUDE_DIR LIBRTLSDR_LIBRARIES)
endif(NOT LIBRTLSDR_FOUND)

View File

@ -0,0 +1,28 @@
if(NOT LIBUSB_FOUND)
pkg_check_modules (LIBUSB_PKG libusb-1.0)
find_path(LIBUSB_INCLUDE_DIR NAMES libusb.h
PATHS
${LIBUSB_PKG_INCLUDE_DIRS}
/usr/include/libusb-1.0
/usr/include
/usr/local/include
)
find_library(LIBUSB_LIBRARIES NAMES usb-1.0
PATHS
${LIBUSB_PKG_LIBRARY_DIRS}
/usr/lib
/usr/local/lib
)
if(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
set(LIBUSB_FOUND TRUE CACHE INTERNAL "libusb-1.0 found")
message(STATUS "Found libusb-1.0: ${LIBUSB_INCLUDE_DIR}, ${LIBUSB_LIBRARIES}")
else(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found")
message(STATUS "libusb-1.0 not found.")
endif(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
mark_as_advanced(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES)
endif(NOT LIBUSB_FOUND)

View File

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_AUDIODEVICEINFO_H
#define INCLUDE_AUDIODEVICEINFO_H
#include <QStringList>
#include "util/export.h"
class SDRANGELOVE_API AudioDeviceInfo {
public:
struct Device {
QString name;
QString api;
int id;
Device(const QString& _name, const QString& _api, int _id) :
name(_name),
api(_api),
id(_id)
{ }
};
typedef QList<Device> Devices;
AudioDeviceInfo();
int match(const QString& api, const QString device) const;
const Devices& getDevices() const { return m_devices; }
private:
Devices m_devices;
};
#endif // INCLUDE_AUDIODEVICEINFO_H

View File

@ -0,0 +1,65 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_AUDIOFIFO_H
#define INCLUDE_AUDIOFIFO_H
#include <QMutex>
#include <QWaitCondition>
#include "util/export.h"
class SDRANGELOVE_API AudioFifo {
public:
AudioFifo();
AudioFifo(uint sampleSize, uint numSamples);
~AudioFifo();
bool setSize(uint sampleSize, uint numSamples);
uint write(const quint8* data, uint numSamples, int timeout = INT_MAX);
uint read(quint8* data, uint numSamples, int timeout = INT_MAX);
uint drain(uint numSamples);
void clear();
inline uint flush() { return drain(m_fill); }
inline uint fill() const { return m_fill; }
inline bool isEmpty() const { return m_fill == 0; }
inline bool isFull() const { return m_fill == m_size; }
inline uint size() const { return m_size; }
private:
QMutex m_mutex;
qint8* m_fifo;
uint m_sampleSize;
uint m_size;
uint m_fill;
uint m_head;
uint m_tail;
QMutex m_writeWaitLock;
QMutex m_readWaitLock;
QWaitCondition m_writeWaitCondition;
QWaitCondition m_readWaitCondition;
bool create(uint sampleSize, uint numSamples);
};
#endif // INCLUDE_AUDIOFIFO_H

View File

@ -0,0 +1,57 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_AUDIOOUTPUT_H
#define INCLUDE_AUDIOOUTPUT_H
#include <QMutex>
#include <QIODevice>
#include <list>
#include <vector>
#include "util/export.h"
class QAudioOutput;
class AudioFifo;
class AudioOutputPipe;
class SDRANGELOVE_API AudioOutput : QIODevice {
public:
AudioOutput();
~AudioOutput();
bool start(int device, int rate);
void stop();
void addFifo(AudioFifo* audioFifo);
void removeFifo(AudioFifo* audioFifo);
private:
QMutex m_mutex;
QAudioOutput* m_audioOutput;
typedef std::list<AudioFifo*> AudioFifos;
AudioFifos m_audioFifos;
std::vector<qint32> m_mixBuffer;
bool open(OpenMode mode);
qint64 readData(char* data, qint64 maxLen);
qint64 writeData(const char* data, qint64 len);
friend class AudioOutputPipe;
};
#endif // INCLUDE_AUDIOOUTPUT_H

View File

@ -0,0 +1,109 @@
#ifndef INCLUDE_CHANNELIZER_H
#define INCLUDE_CHANNELIZER_H
#include <list>
#include "dsp/samplesink.h"
#include "util/export.h"
class MessageQueue;
class IntHalfbandFilter;
class SDRANGELOVE_API Channelizer : public SampleSink {
public:
Channelizer(SampleSink* sampleSink);
~Channelizer();
void configure(MessageQueue* messageQueue, int sampleRate, int centerFrequency);
void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst);
void start();
void stop();
bool handleMessage(Message* cmd);
protected:
struct FilterStage {
enum Mode {
ModeCenter,
ModeLowerHalf,
ModeUpperHalf
};
typedef bool (IntHalfbandFilter::*WorkFunction)(Sample* s);
IntHalfbandFilter* m_filter;
WorkFunction m_workFunction;
FilterStage(Mode mode);
~FilterStage();
bool work(Sample* sample)
{
return (m_filter->*m_workFunction)(sample);
}
};
typedef std::list<FilterStage*> FilterStages;
FilterStages m_filterStages;
SampleSink* m_sampleSink;
int m_inputSampleRate;
int m_requestedOutputSampleRate;
int m_requestedCenterFrequency;
int m_currentOutputSampleRate;
int m_currentCenterFrequency;
SampleVector m_sampleBuffer;
void applyConfiguration();
bool signalContainsChannel(Real sigStart, Real sigEnd, Real chanStart, Real chanEnd) const;
Real createFilterChain(Real sigStart, Real sigEnd, Real chanStart, Real chanEnd);
void freeFilterChain();
};
#endif // INCLUDE_CHANNELIZER_H
#if 0
#ifndef INCLUDE_CHANNELIZER_H
#define INCLUDE_CHANNELIZER_H
#include "samplesink.h"
#include "spectrum.h"
#include "nco.h"
#include "interpolator.h"
#include "pidcontroller.h"
#include "hardware/audiofifo.h"
class AudioOutput;
class Channelizer : public SampleSink {
public:
Channelizer();
~Channelizer();
#if 0
void setGLSpectrum(GLSpectrum* glSpectrum);
#endif
size_t workUnitSize();
size_t work(SampleVector::const_iterator begin, SampleVector::const_iterator end);
private:
#if 0
NCO m_nco;
Interpolator m_interpolator;
Real m_distance;
Interpolator m_interpolator2;
Real m_distance2;
SampleVector m_buffer;
size_t m_bufferFill;
Complex m_lastSample;
AudioOutput* m_audioOutput;
AudioFifo m_audioFifo;
Real m_resampler;
PIDController m_resamplerCtrl;
Spectrum m_spectrum;
#endif
};
#endif // INCLUDE_CHANNELIZER_H
#endif

View File

@ -0,0 +1,255 @@
#ifndef INCLUDE_DSPCOMMANDS_H
#define INCLUDE_DSPCOMMANDS_H
#include <QString>
#include "util/message.h"
#include "fftwindow.h"
#include "util/export.h"
class SampleSource;
class SampleSink;
class AudioFifo;
class SDRANGELOVE_API DSPPing : public Message {
MESSAGE_CLASS_DECLARATION
};
class SDRANGELOVE_API DSPExit : public Message {
MESSAGE_CLASS_DECLARATION
};
class SDRANGELOVE_API DSPAcquisitionStart : public Message {
MESSAGE_CLASS_DECLARATION
};
class SDRANGELOVE_API DSPAcquisitionStop : public Message {
MESSAGE_CLASS_DECLARATION
};
class SDRANGELOVE_API DSPGetDeviceDescription : public Message {
MESSAGE_CLASS_DECLARATION
public:
void setDeviceDescription(const QString& text) { m_deviceDescription = text; }
const QString& getDeviceDescription() const { return m_deviceDescription; }
private:
QString m_deviceDescription;
};
class SDRANGELOVE_API DSPGetErrorMessage : public Message {
MESSAGE_CLASS_DECLARATION
public:
void setErrorMessage(const QString& text) { m_errorMessage = text; }
const QString& getErrorMessage() const { return m_errorMessage; }
private:
QString m_errorMessage;
};
class SDRANGELOVE_API DSPSetSource : public Message {
MESSAGE_CLASS_DECLARATION
public:
DSPSetSource(SampleSource* sampleSource) : Message(), m_sampleSource(sampleSource) { }
SampleSource* getSampleSource() const { return m_sampleSource; }
private:
SampleSource* m_sampleSource;
};
class SDRANGELOVE_API DSPAddSink : public Message {
MESSAGE_CLASS_DECLARATION
public:
DSPAddSink(SampleSink* sampleSink) : Message(), m_sampleSink(sampleSink) { }
SampleSink* getSampleSink() const { return m_sampleSink; }
private:
SampleSink* m_sampleSink;
};
class SDRANGELOVE_API DSPRemoveSink : public Message {
MESSAGE_CLASS_DECLARATION
public:
DSPRemoveSink(SampleSink* sampleSink) : Message(), m_sampleSink(sampleSink) { }
SampleSink* getSampleSink() const { return m_sampleSink; }
private:
SampleSink* m_sampleSink;
};
class SDRANGELOVE_API DSPAddAudioSource : public Message {
MESSAGE_CLASS_DECLARATION
public:
DSPAddAudioSource(AudioFifo* audioFifo) : Message(), m_audioFifo(audioFifo) { }
AudioFifo* getAudioFifo() const { return m_audioFifo; }
private:
AudioFifo* m_audioFifo;
};
class SDRANGELOVE_API DSPRemoveAudioSource : public Message {
MESSAGE_CLASS_DECLARATION
public:
DSPRemoveAudioSource(AudioFifo* audioFifo) : Message(), m_audioFifo(audioFifo) { }
AudioFifo* getAudioFifo() const { return m_audioFifo; }
private:
AudioFifo* m_audioFifo;
};
class SDRANGELOVE_API DSPConfigureSpectrumVis : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getFFTSize() const { return m_fftSize; }
int getOverlapPercent() const { return m_overlapPercent; }
FFTWindow::Function getWindow() const { return m_window; }
static DSPConfigureSpectrumVis* create(int fftSize, int overlapPercent, FFTWindow::Function window)
{
return new DSPConfigureSpectrumVis(fftSize, overlapPercent, window);
}
private:
int m_fftSize;
int m_overlapPercent;
FFTWindow::Function m_window;
DSPConfigureSpectrumVis(int fftSize, int overlapPercent, FFTWindow::Function window) :
Message(),
m_fftSize(fftSize),
m_overlapPercent(overlapPercent),
m_window(window)
{ }
};
class SDRANGELOVE_API DSPConfigureCorrection : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool getDCOffsetCorrection() const { return m_dcOffsetCorrection; }
bool getIQImbalanceCorrection() const { return m_iqImbalanceCorrection; }
static DSPConfigureCorrection* create(bool dcOffsetCorrection, bool iqImbalanceCorrection)
{
return new DSPConfigureCorrection(dcOffsetCorrection, iqImbalanceCorrection);
}
private:
bool m_dcOffsetCorrection;
bool m_iqImbalanceCorrection;
DSPConfigureCorrection(bool dcOffsetCorrection, bool iqImbalanceCorrection) :
Message(),
m_dcOffsetCorrection(dcOffsetCorrection),
m_iqImbalanceCorrection(iqImbalanceCorrection)
{ }
};
class SDRANGELOVE_API DSPEngineReport : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getSampleRate() const { return m_sampleRate; }
quint64 getCenterFrequency() const { return m_centerFrequency; }
static DSPEngineReport* create(int sampleRate, quint64 centerFrequency)
{
return new DSPEngineReport(sampleRate, centerFrequency);
}
private:
int m_sampleRate;
quint64 m_centerFrequency;
DSPEngineReport(int sampleRate, quint64 centerFrequency) :
Message(),
m_sampleRate(sampleRate),
m_centerFrequency(centerFrequency)
{ }
};
class SDRANGELOVE_API DSPConfigureScopeVis : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getTriggerChannel() const { return m_triggerChannel; }
Real getTriggerLevelHigh() const { return m_triggerLevelHigh; }
Real getTriggerLevelLow() const { return m_triggerLevelLow; }
static DSPConfigureScopeVis* create(int triggerChannel, Real triggerLevelHigh, Real triggerLevelLow)
{
return new DSPConfigureScopeVis(triggerChannel, triggerLevelHigh, triggerLevelLow);
}
private:
int m_triggerChannel;
Real m_triggerLevelHigh;
Real m_triggerLevelLow;
DSPConfigureScopeVis(int triggerChannel, Real triggerLevelHigh, Real triggerLevelLow) :
Message(),
m_triggerChannel(triggerChannel),
m_triggerLevelHigh(triggerLevelHigh),
m_triggerLevelLow(triggerLevelLow)
{ }
};
class SDRANGELOVE_API DSPSignalNotification : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getSampleRate() const { return m_sampleRate; }
qint64 getFrequencyOffset() const { return m_frequencyOffset; }
static DSPSignalNotification* create(int sampleRate, quint64 frequencyOffset)
{
return new DSPSignalNotification(sampleRate, frequencyOffset);
}
private:
int m_sampleRate;
qint64 m_frequencyOffset;
DSPSignalNotification(int samplerate, qint64 frequencyOffset) :
Message(),
m_sampleRate(samplerate),
m_frequencyOffset(frequencyOffset)
{ }
};
class SDRANGELOVE_API DSPConfigureChannelizer : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getSampleRate() const { return m_sampleRate; }
int getCenterFrequency() const { return m_centerFrequency; }
static DSPConfigureChannelizer* create(int sampleRate, int centerFrequency)
{
return new DSPConfigureChannelizer(sampleRate, centerFrequency);
}
private:
int m_sampleRate;
int m_centerFrequency;
DSPConfigureChannelizer(int sampleRate, int centerFrequency) :
Message(),
m_sampleRate(sampleRate),
m_centerFrequency(centerFrequency)
{ }
};
#endif // INCLUDE_DSPCOMMANDS_H

119
include-gpl/dsp/dspengine.h Normal file
View File

@ -0,0 +1,119 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_DSPENGINE_H
#define INCLUDE_DSPENGINE_H
#include <QThread>
#include <QTimer>
#include <QMutex>
#include <QWaitCondition>
#include "dsp/dsptypes.h"
#include "dsp/fftwindow.h"
#include "dsp/samplefifo.h"
#include "audio/audiooutput.h"
#include "util/messagequeue.h"
#include "util/export.h"
class SampleSource;
class SampleSink;
class AudioFifo;
class SDRANGELOVE_API DSPEngine : public QThread {
Q_OBJECT
public:
enum State {
StNotStarted,
StIdle,
StRunning,
StError
};
DSPEngine(MessageQueue* reportQueue, QObject* parent = NULL);
~DSPEngine();
MessageQueue* getMessageQueue() { return &m_messageQueue; }
void start();
void stop();
bool startAcquisition();
void stopAcquistion();
void setSource(SampleSource* source);
void addSink(SampleSink* sink);
void removeSink(SampleSink* sink);
void addAudioSource(AudioFifo* audioFifo);
void removeAudioSource(AudioFifo* audioFifo);
void configureCorrections(bool dcOffsetCorrection, bool iqImbalanceCorrection);
State state() const { return m_state; }
QString errorMessage();
QString deviceDescription();
private:
MessageQueue m_messageQueue;
MessageQueue* m_reportQueue;
State m_state;
QString m_errorMessage;
QString m_deviceDescription;
SampleSource* m_sampleSource;
typedef std::list<SampleSink*> SampleSinks;
SampleSinks m_sampleSinks;
AudioOutput m_audioOutput;
uint m_sampleRate;
quint64 m_centerFrequency;
bool m_dcOffsetCorrection;
bool m_iqImbalanceCorrection;
qint32 m_iOffset;
qint32 m_qOffset;
qint32 m_iRange;
qint32 m_qRange;
qint32 m_imbalance;
void run();
void dcOffset(SampleVector::iterator begin, SampleVector::iterator end);
void imbalance(SampleVector::iterator begin, SampleVector::iterator end);
void work();
State gotoIdle();
State gotoRunning();
State gotoError(const QString& errorMsg);
void handleSetSource(SampleSource* source);
void generateReport();
bool distributeMessage(Message* message);
private slots:
void handleData();
void handleMessages();
};
#endif // INCLUDE_DSPENGINE_H

View File

@ -0,0 +1,20 @@
#ifndef INCLUDE_FFTENGINE_H
#define INCLUDE_FFTENGINE_H
#include "dsp/dsptypes.h"
#include "util/export.h"
class SDRANGELOVE_API FFTEngine {
public:
virtual ~FFTEngine();
virtual void configure(int n, bool inverse) = 0;
virtual void transform() = 0;
virtual Complex* in() = 0;
virtual Complex* out() = 0;
static FFTEngine* create();
};
#endif // INCLUDE_FFTENGINE_H

View File

@ -0,0 +1,37 @@
#ifndef INCLUDE_FFTWENGINE_H
#define INCLUDE_FFTWENGINE_H
#include <QMutex>
#include <fftw3.h>
#include <list>
#include "dsp/fftengine.h"
class FFTWEngine : public FFTEngine {
public:
FFTWEngine();
~FFTWEngine();
void configure(int n, bool inverse);
void transform();
Complex* in();
Complex* out();
protected:
static QMutex m_globalPlanMutex;
struct Plan {
int n;
bool inverse;
fftwf_plan plan;
fftwf_complex* in;
fftwf_complex* out;
};
typedef std::list<Plan*> Plans;
Plans m_plans;
Plan* m_currentPlan;
void freeAll();
};
#endif // INCLUDE_FFTWENGINE_H

View File

@ -0,0 +1,82 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_FFTWINDOW_H
#define INCLUDE_FFTWINDOW_H
#include <vector>
#define _USE_MATH_DEFINES
#include <math.h>
#include "dsp/dsptypes.h"
#include "util/export.h"
class SDRANGELOVE_API FFTWindow {
public:
enum Function {
Bartlett,
BlackmanHarris,
Flattop,
Hamming,
Hanning,
Rectangle
};
void create(Function function, int n);
void apply(const std::vector<Real>& in, std::vector<Real>* out);
void apply(const std::vector<Complex>& in, std::vector<Complex>* out);
void apply(const Complex* in, Complex* out);
private:
std::vector<float> m_window;
static inline Real flatTop(Real n, Real i)
{
// correction ?
return 1.0 - 1.93 * cos((2.0 * M_PI * i) / n) + 1.29 * cos((4.0 * M_PI * i) / n) - 0.388 * cos((6.0 * M_PI * i) / n) + 0.03222 * cos((8.0 * M_PI * i) / n);
}
static inline Real bartlett(Real n, Real i)
{
// amplitude correction = 2.0
return (2.0 / (n - 1.0)) * ( (n - 1.0) / 2.0 - fabs(i - (n - 1.0) / 2.0)) * 2.0;
}
static inline Real blackmanHarris(Real n, Real i)
{
// amplitude correction = 2.79
return (0.35875 - 0.48829 * cos((2.0 * M_PI * i) / n) + 0.14128 * cos((4.0 * M_PI * i) / n) - 0.01168 * cos((6.0 * M_PI * i) / n)) * 2.79;
}
static inline Real hamming(Real n, Real i)
{
// amplitude correction = 1.855, energy correction = 1.586
return (0.54 - 0.46 * cos((2.0 * M_PI * i) / n)) * 1.855;
}
static inline Real hanning(Real n, Real i)
{
// amplitude correction = 2.0, energy correction = 1.633
return (0.5 - 0.5 * cos((2.0 * M_PI * i) / n)) * 2.0;
}
static inline Real rectangle(Real, Real)
{
return 1.0;
}
};
#endif // INCLUDE_FFTWINDOWS_H

View File

@ -0,0 +1,130 @@
#ifndef INCLUDE_INTERPOLATOR_H
#define INCLUDE_INTERPOLATOR_H
#include <immintrin.h>
#include "dsp/dsptypes.h"
#include "util/export.h"
#include <stdio.h>
#ifndef WIN32
#include <unistd.h>
#endif
class SDRANGELOVE_API Interpolator {
public:
Interpolator();
~Interpolator();
void create(int phaseSteps, double sampleRate, double cutoff);
void free();
bool interpolate(Real* distance, const Complex& next, bool* consumed, Complex* result)
{
while(*distance >= 1.0) {
if(!(*consumed)) {
advanceFilter(next);
*distance -= 1.0;
*consumed = true;
} else {
return false;
}
}
doInterpolate((int)floor(*distance * (Real)m_phaseSteps), result);
return true;
}
private:
float* m_taps;
float* m_alignedTaps;
float* m_taps2;
float* m_alignedTaps2;
std::vector<Complex> m_samples;
int m_ptr;
int m_phaseSteps;
int m_nTaps;
void createTaps(int nTaps, double sampleRate, double cutoff, std::vector<Real>* taps);
void advanceFilter(const Complex& next)
{
m_ptr--;
if(m_ptr < 0)
m_ptr = m_nTaps - 1;
m_samples[m_ptr] = next;
}
void doInterpolate(int phase, Complex* result)
{
#if 1
// beware of the ringbuffer
if(m_ptr == 0) {
// only one straight block
const float* src = (const float*)&m_samples[0];
const __m128* filter = (const __m128*)&m_alignedTaps[phase * m_nTaps * 2];
__m128 sum = _mm_setzero_ps();
int todo = m_nTaps / 2;
for(int i = 0; i < todo; i++) {
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(src), *filter));
src += 4;
filter += 1;
}
// add upper half to lower half and store
_mm_storel_pi((__m64*)result, _mm_add_ps(sum, _mm_shuffle_ps(sum, _mm_setzero_ps(), _MM_SHUFFLE(1, 0, 3, 2))));
} else {
// two blocks
const float* src = (const float*)&m_samples[m_ptr];
const __m128* filter = (const __m128*)&m_alignedTaps[phase * m_nTaps * 2];
__m128 sum = _mm_setzero_ps();
// first block
int block = m_nTaps - m_ptr;
int todo = block / 2;
if(block & 1)
todo++;
for(int i = 0; i < todo; i++) {
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(src), *filter));
src += 4;
filter += 1;
}
if(block & 1) {
// one sample beyond the end -> switch coefficient table
filter = (const __m128*)&m_alignedTaps2[phase * m_nTaps * 2 + todo * 4 - 4];
}
// second block
src = (const float*)&m_samples[0];
block = m_ptr;
todo = block / 2;
for(int i = 0; i < todo; i++) {
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(src), *filter));
src += 4;
filter += 1;
}
if(block & 1) {
// one sample remaining
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadl_pi(_mm_setzero_ps(), (const __m64*)src), filter[0]));
}
// add upper half to lower half and store
_mm_storel_pi((__m64*)result, _mm_add_ps(sum, _mm_shuffle_ps(sum, _mm_setzero_ps(), _MM_SHUFFLE(1, 0, 3, 2))));
}
#else
int sample = m_ptr;
const Real* coeff = &m_alignedTaps[phase * m_nTaps * 2];
Real rAcc = 0;
Real iAcc = 0;
for(int i = 0; i < m_nTaps; i++) {
rAcc += *coeff * m_samples[sample].real();
iAcc += *coeff * m_samples[sample].imag();
sample = (sample + 1) % m_nTaps;
coeff += 2;
}
*result = Complex(rAcc, iAcc);
#endif
}
};
#endif // INCLUDE_INTERPOLATOR_H

View File

@ -0,0 +1,314 @@
#ifndef INCLUDE_INTHALFBANDFILTER_H
#define INCLUDE_INTHALFBANDFILTER_H
#include <QtGlobal>
#include "dsp/dsptypes.h"
#include "util/export.h"
// uses Q1.14 format internally, input and output are S16
/*
* supported filter orders: 64, 48, 32
*/
#define HB_FILTERORDER 32
#define HB_SHIFT 14
class SDRANGELOVE_API IntHalfbandFilter {
public:
IntHalfbandFilter();
// downsample by 2, return center part of original spectrum
bool workDecimateCenter(Sample* sample)
{
// insert sample into ring-buffer
m_samples[m_ptr][0] = sample->real();
m_samples[m_ptr][1] = sample->imag();
switch(m_state) {
case 0:
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 1;
// tell caller we don't have a new sample
return false;
default:
// save result
doFIR(sample);
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 0;
// tell caller we have a new sample
return true;
}
}
// downsample by 2, return edges of spectrum rotated into center
bool workDecimateFullRotate(Sample* sample)
{
switch(m_state) {
case 0:
// insert sample into ring-buffer
m_samples[m_ptr][0] = sample->real();
m_samples[m_ptr][1] = sample->imag();
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 1;
// tell caller we don't have a new sample
return false;
default:
// insert sample into ring-buffer
m_samples[m_ptr][0] = -sample->real();
m_samples[m_ptr][1] = sample->imag();
// save result
doFIR(sample);
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 0;
// tell caller we have a new sample
return true;
}
}
// downsample by 2, return lower half of original spectrum
bool workDecimateLowerHalf(Sample* sample)
{
switch(m_state) {
case 0:
// insert sample into ring-buffer
m_samples[m_ptr][0] = -sample->imag();
m_samples[m_ptr][1] = sample->real();
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 1;
// tell caller we don't have a new sample
return false;
case 1:
// insert sample into ring-buffer
m_samples[m_ptr][0] = -sample->real();
m_samples[m_ptr][1] = -sample->imag();
// save result
doFIR(sample);
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 2;
// tell caller we have a new sample
return true;
case 2:
// insert sample into ring-buffer
m_samples[m_ptr][0] = sample->imag();
m_samples[m_ptr][1] = -sample->real();
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 3;
// tell caller we don't have a new sample
return false;
default:
// insert sample into ring-buffer
m_samples[m_ptr][0] = sample->real();
m_samples[m_ptr][1] = sample->imag();
// save result
doFIR(sample);
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 0;
// tell caller we have a new sample
return true;
}
}
// downsample by 2, return upper half of original spectrum
bool workDecimateUpperHalf(Sample* sample)
{
switch(m_state) {
case 0:
// insert sample into ring-buffer
m_samples[m_ptr][0] = sample->imag();
m_samples[m_ptr][1] = -sample->real();
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 1;
// tell caller we don't have a new sample
return false;
case 1:
// insert sample into ring-buffer
m_samples[m_ptr][0] = -sample->real();
m_samples[m_ptr][1] = -sample->imag();
// save result
doFIR(sample);
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 2;
// tell caller we have a new sample
return true;
case 2:
// insert sample into ring-buffer
m_samples[m_ptr][0] = -sample->imag();
m_samples[m_ptr][1] = sample->real();
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 3;
// tell caller we don't have a new sample
return false;
default:
// insert sample into ring-buffer
m_samples[m_ptr][0] = sample->real();
m_samples[m_ptr][1] = sample->imag();
// save result
doFIR(sample);
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 0;
// tell caller we have a new sample
return true;
}
}
protected:
qint16 m_samples[HB_FILTERORDER + 1][2];
int m_ptr;
int m_state;
void doFIR(Sample* sample)
{
// coefficents
#if HB_FILTERORDER == 64
static const qint32 COEFF[16] = {
-0.001114417441601693505720538368564120901 * (1 << HB_SHIFT),
0.001268007827185253051302527005361753254 * (1 << HB_SHIFT),
-0.001959831378850490895410230152151598304 * (1 << HB_SHIFT),
0.002878308307661380308073439948657323839 * (1 << HB_SHIFT),
-0.004071361818258721100571850826099762344 * (1 << HB_SHIFT),
0.005597288494657440618973431867289036745 * (1 << HB_SHIFT),
-0.007532345003308904551886371336877346039 * (1 << HB_SHIFT),
0.009980346844667375288961963519795972388 * (1 << HB_SHIFT),
-0.013092614174300500062830820979797863401 * (1 << HB_SHIFT),
0.01710934914871829748417297878404497169 * (1 << HB_SHIFT),
-0.022443558692997273018576720460259821266 * (1 << HB_SHIFT),
0.029875811511593811098386197500076377764 * (1 << HB_SHIFT),
-0.041086352085710403647667021687084343284 * (1 << HB_SHIFT),
0.060465467462665789533104998554335907102 * (1 << HB_SHIFT),
-0.104159517495977321788203084906854201108 * (1 << HB_SHIFT),
0.317657589850154464805598308885237202048 * (1 << HB_SHIFT),
};
#elif HB_FILTERORDER == 48
static const qint32 COEFF[12] = {
-0.004102576237611492253332112767338912818 * (1 << HB_SHIFT),
0.003950551047979387886410762575906119309 * (1 << HB_SHIFT),
-0.005807875789391703583164350277456833282 * (1 << HB_SHIFT),
0.00823497890520805998770814682075069868 * (1 << HB_SHIFT),
-0.011372226513199541059195851744334504474 * (1 << HB_SHIFT),
0.015471557140973646315984524335362948477 * (1 << HB_SHIFT),
-0.020944996398689276484450516591095947661 * (1 << HB_SHIFT),
0.028568078132034283034279553703527199104 * (1 << HB_SHIFT),
-0.040015143905614086738964374490024056286 * (1 << HB_SHIFT),
0.059669519431831075095828964549582451582 * (1 << HB_SHIFT),
-0.103669138691865420076609893840213771909 * (1 << HB_SHIFT),
0.317491986549921390015072120149852707982 * (1 << HB_SHIFT)
};
#elif HB_FILTERORDER == 32
static const qint32 COEFF[8] = {
-0.015956912844043127236437484839370881673 * (1 << HB_SHIFT),
0.013023031678944928940522274274371739011 * (1 << HB_SHIFT),
-0.01866942273717486777684371190844103694 * (1 << HB_SHIFT),
0.026550887571157304190005987720724078827 * (1 << HB_SHIFT),
-0.038350314277854319344740474662103224546 * (1 << HB_SHIFT),
0.058429248652825838128421764849917963147 * (1 << HB_SHIFT),
-0.102889802028955756885153505209018476307 * (1 << HB_SHIFT),
0.317237706405931241260276465254719369113 * (1 << HB_SHIFT)
};
#else
#error unsupported filter order
#endif
// init read-pointer
int a = (m_ptr + 1) % (HB_FILTERORDER + 1);
int b = (m_ptr + (HB_FILTERORDER - 1)) % (HB_FILTERORDER + 1);
// go through samples in buffer
qint32 iAcc = 0;
qint32 qAcc = 0;
for(int i = 0; i < HB_FILTERORDER / 4; i++) {
// do multiply-accumulate
qint32 iTmp = m_samples[a][0] + m_samples[b][0];
qint32 qTmp = m_samples[a][1] + m_samples[b][1];
iAcc += iTmp * COEFF[i];
qAcc += qTmp * COEFF[i];
// update read-pointer
a = (a + 2) % (HB_FILTERORDER + 1);
b = (b + (HB_FILTERORDER - 1)) % (HB_FILTERORDER + 1);
}
a = (a + HB_FILTERORDER) % (HB_FILTERORDER + 1);
iAcc += m_samples[a][0] * (qint32)(0.5 * (1 << HB_SHIFT));
qAcc += m_samples[a][1] * (qint32)(0.5 * (1 << HB_SHIFT));
// done, save result
sample->setReal((iAcc + (qint32)(0.5 * (1 << HB_SHIFT))) >> HB_SHIFT);
sample->setImag((qAcc + (qint32)(0.5 * (1 << HB_SHIFT))) >> HB_SHIFT);
}
};
#endif // INCLUDE_INTHALFBANDFILTER_H

View File

@ -0,0 +1,23 @@
#ifndef INCLUDE_KISSENGINE_H
#define INCLUDE_KISSENGINE_H
#include "dsp/fftengine.h"
#include "dsp/kissfft.h"
class KissEngine : public FFTEngine {
public:
void configure(int n, bool inverse);
void transform();
Complex* in();
Complex* out();
protected:
typedef kissfft<Real, Complex> KissFFT;
KissFFT m_fft;
std::vector<Complex> m_in;
std::vector<Complex> m_out;
};
#endif // INCLUDE_KISSENGINE_H

88
include-gpl/dsp/lowpass.h Normal file
View File

@ -0,0 +1,88 @@
#ifndef INCLUDE_LOWPASS_H
#define INCLUDE_LOWPASS_H
#define _USE_MATH_DEFINES
#include <math.h>
#include "dsp/dsptypes.h"
template <class Type> class Lowpass {
public:
Lowpass() { }
void create(int nTaps, double sampleRate, double cutoff)
{
double wc = 2.0 * M_PI * cutoff;
double Wc = wc / sampleRate;
int i;
// check constraints
if(!(nTaps & 1)) {
qDebug("Lowpass filter has to have an odd number of taps");
nTaps++;
}
// make room
m_samples.resize(nTaps);
for(int i = 0; i < nTaps; i++)
m_samples[i] = 0;
m_ptr = 0;
m_taps.resize(nTaps / 2 + 1);
// generate Sinc filter core
for(i = 0; i < nTaps / 2 + 1; i++) {
if(i == (nTaps - 1) / 2)
m_taps[i] = Wc / M_PI;
else
m_taps[i] = sin(((double)i - ((double)nTaps - 1.0) / 2.0) * Wc) / (((double)i - ((double)nTaps - 1.0) / 2.0) * M_PI);
}
// apply Hamming window
for(i = 0; i < nTaps / 2 + 1; i++)
m_taps[i] *= 0.54 + 0.46 * cos((2.0 * M_PI * ((double)i - ((double)nTaps - 1.0) / 2.0)) / (double)nTaps);
// normalize
Real sum = 0;
for(i = 0; i < (int)m_taps.size() - 1; i++)
sum += m_taps[i] * 2;
sum += m_taps[i];
for(i = 0; i < (int)m_taps.size(); i++)
m_taps[i] /= sum;
}
Type filter(Type sample)
{
Type acc = 0;
int a = m_ptr;
int b = a - 1;
int i;
m_samples[m_ptr] = sample;
while(b < 0)
b += m_samples.size();
for(i = 0; i < (int)m_taps.size() - 1; i++) {
acc += (m_samples[a] + m_samples[b]) * m_taps[i];
a++;
while(a >= (int)m_samples.size())
a -= m_samples.size();
b--;
while(b < 0)
b += m_samples.size();
}
acc += m_samples[a] * m_taps[i];
m_ptr++;
while(m_ptr >= (int)m_samples.size())
m_ptr -= m_samples.size();
return acc;
}
private:
std::vector<Real> m_taps;
std::vector<Type> m_samples;
int m_ptr;
};
#endif // INCLUDE_LOWPASS_H

View File

@ -0,0 +1,53 @@
#ifndef INCLUDE_MOVINGAVERAGE_H
#define INCLUDE_MOVINGAVERAGE_H
#include <vector>
#include "dsp/dsptypes.h"
class MovingAverage {
public:
MovingAverage() :
m_history(),
m_sum(0),
m_ptr(0)
{
}
MovingAverage(int historySize, Real initial) :
m_history(historySize, initial),
m_sum(historySize * initial),
m_ptr(0)
{
}
void resize(int historySize, Real initial)
{
m_history.resize(historySize);
for(size_t i = 0; i < m_history.size(); i++)
m_history[i] = initial;
m_sum = m_history.size() * initial;
m_ptr = 0;
}
void feed(Real value)
{
m_sum -= m_history[m_ptr];
m_history[m_ptr] = value;
m_sum += value;
m_ptr++;
if(m_ptr >= m_history.size())
m_ptr = 0;
}
Real average() const
{
return m_sum / (Real)m_history.size();
}
protected:
std::vector<Real> m_history;
Real m_sum;
uint m_ptr;
};
#endif // INCLUDE_MOVINGAVERAGE_H

45
include-gpl/dsp/nco.h Normal file
View File

@ -0,0 +1,45 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_NCO_H
#define INCLUDE_NCO_H
#include "dsp/dsptypes.h"
#include "util/export.h"
class SDRANGELOVE_API NCO {
private:
enum {
TableSize = (1 << 12),
};
static Real m_table[TableSize];
static bool m_tableInitialized;
static void initTable();
int m_phaseIncrement;
int m_phase;
public:
NCO();
void setFreq(Real freq, Real sampleRate);
Real next();
Complex nextIQ();
};
#endif // INCLUDE_NCO_H

View File

@ -0,0 +1,44 @@
#ifndef INCLUDE_SCOPEVIS_H
#define INCLUDE_SCOPEVIS_H
#include "dsp/samplesink.h"
#include "util/export.h"
class GLScope;
class MessageQueue;
class SDRANGELOVE_API ScopeVis : public SampleSink {
public:
enum TriggerChannel {
TriggerFreeRun,
TriggerChannelI,
TriggerChannelQ
};
ScopeVis(GLScope* glScope = NULL);
void configure(MessageQueue* msgQueue, TriggerChannel triggerChannel, Real triggerLevelHigh, Real triggerLevelLow);
void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst);
void start();
void stop();
bool handleMessage(Message* message);
private:
enum TriggerState {
Untriggered,
Triggered,
WaitForReset
};
GLScope* m_glScope;
std::vector<Complex> m_trace;
uint m_fill;
TriggerState m_triggerState;
TriggerChannel m_triggerChannel;
FixReal m_triggerLevelHigh;
FixReal m_triggerLevelLow;
int m_sampleRate;
};
#endif // INCLUDE_SCOPEVIS_H

View File

@ -0,0 +1,42 @@
#ifndef INCLUDE_SPECTRUMVIS_H
#define INCLUDE_SPECTRUMVIS_H
#include "dsp/samplesink.h"
#include "dsp/fftengine.h"
#include "fftwindow.h"
#include "util/export.h"
class GLSpectrum;
class MessageQueue;
class SDRANGELOVE_API SpectrumVis : public SampleSink {
public:
SpectrumVis(GLSpectrum* glSpectrum = NULL);
~SpectrumVis();
void configure(MessageQueue* msgQueue, int fftSize, int overlapPercent, FFTWindow::Function window);
void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst);
void start();
void stop();
bool handleMessage(Message* message);
private:
FFTEngine* m_fft;
FFTWindow m_window;
std::vector<Complex> m_fftBuffer;
std::vector<Real> m_logPowerSpectrum;
size_t m_fftSize;
size_t m_overlapPercent;
size_t m_overlapSize;
size_t m_refillSize;
size_t m_fftBufferFill;
GLSpectrum* m_glSpectrum;
void handleConfigure(int fftSize, int overlapPercent, FFTWindow::Function window);
};
#endif // INCLUDE_SPECTRUMVIS_H

View File

@ -0,0 +1,21 @@
#ifndef INCLUDE_ABOUTDIALOG_H
#define INCLUDE_ABOUTDIALOG_H
#include <QDialog>
namespace Ui {
class AboutDialog;
}
class AboutDialog : public QDialog {
Q_OBJECT
public:
explicit AboutDialog(QWidget* parent = NULL);
~AboutDialog();
private:
Ui::AboutDialog* ui;
};
#endif // INCLUDE_ABOUTDIALOG_H

View File

@ -0,0 +1,30 @@
#ifndef INCLUDE_ADDPRESETDIALOG_H
#define INCLUDE_ADDPRESETDIALOG_H
#include <QDialog>
namespace Ui {
class AddPresetDialog;
}
class AddPresetDialog : public QDialog {
Q_OBJECT
public:
explicit AddPresetDialog(const QStringList& groups, const QString& group, QWidget* parent = NULL);
~AddPresetDialog();
QString group() const;
QString description() const;
private:
enum Audio {
ATDefault,
ATInterface,
ATDevice
};
Ui::AddPresetDialog* ui;
};
#endif // INCLUDE_ADDPRESETDIALOG_H

View File

@ -0,0 +1,19 @@
#ifndef INCLUDE_BUTTONSWITCH_H
#define INCLUDE_BUTTONSWITCH_H
#include <QToolButton>
class ButtonSwitch : public QToolButton {
Q_OBJECT
public:
ButtonSwitch(QWidget* parent = NULL);
private slots:
void onToggled(bool checked);
private:
QPalette m_originalPalette;
};
#endif // INCLUDE_BUTTONSWITCH_H

View File

@ -0,0 +1,25 @@
#ifndef INCLUDE_CHANNELWINDOW_H
#define INCLUDE_CHANNELWINDOW_H
#include <QScrollArea>
class QBoxLayout;
class QSpacerItem;
class RollupWidget;
class ChannelWindow : public QScrollArea {
Q_OBJECT
public:
ChannelWindow(QWidget* parent = NULL);
void addRollupWidget(QWidget* rollupWidget);
protected:
QWidget* m_container;
QBoxLayout* m_layout;
void resizeEvent(QResizeEvent* event);
};
#endif // INCLUDE_CHANNELWINDOW_H

110
include-gpl/gui/glscope.h Normal file
View File

@ -0,0 +1,110 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_GLSCOPE_H
#define INCLUDE_GLSCOPE_H
#include <QGLWidget>
#include <QPen>
#include <QTimer>
#include <QMutex>
#include "dsp/dsptypes.h"
#include "dsp/scopevis.h"
#include "util/export.h"
class DSPEngine;
class ScopeVis;
class SDRANGELOVE_API GLScope: public QGLWidget {
Q_OBJECT
public:
enum Mode {
ModeIQ,
ModeMagLinPha,
ModeMagdBPha,
ModeDerived12,
ModeCyclostationary
};
GLScope(QWidget* parent = NULL);
~GLScope();
void setDSPEngine(DSPEngine* dspEngine);
void setAmp(Real amp);
void setTimeBase(int timeBase);
void setTimeOfsProMill(int timeOfsProMill);
void setMode(Mode mode);
void setOrientation(Qt::Orientation orientation);
void newTrace(const std::vector<Complex>& trace, int sampleRate);
int getTraceSize() const { return m_rawTrace.size(); }
signals:
void traceSizeChanged(int);
private:
// state
QTimer m_timer;
QMutex m_mutex;
bool m_dataChanged;
bool m_configChanged;
Mode m_mode;
Qt::Orientation m_orientation;
// traces
std::vector<Complex> m_rawTrace;
std::vector<Complex> m_mathTrace;
std::vector<Complex>* m_displayTrace;
int m_oldTraceSize;
int m_sampleRate;
Real m_amp1;
Real m_amp2;
Real m_ofs1;
Real m_ofs2;
// sample sink
DSPEngine* m_dspEngine;
ScopeVis* m_scopeVis;
// config
Real m_amp;
int m_timeBase;
int m_timeOfsProMill;
ScopeVis::TriggerChannel m_triggerChannel;
Real m_triggerLevelHigh;
Real m_triggerLevelLow;
// graphics stuff
QRectF m_glScopeRect1;
QRectF m_glScopeRect2;
void initializeGL();
void resizeGL(int width, int height);
void paintGL();
void mousePressEvent(QMouseEvent*);
void handleMode();
void applyConfig();
protected slots:
void tick();
};
#endif // INCLUDE_GLSCOPE_H

View File

@ -0,0 +1,157 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_GLSPECTRUM_H
#define INCLUDE_GLSPECTRUM_H
#include <QGLWidget>
#include <QTimer>
#include <QMutex>
#include "dsp/dsptypes.h"
#include "gui/scaleengine.h"
#include "dsp/channelmarker.h"
#include "util/export.h"
class SDRANGELOVE_API GLSpectrum : public QGLWidget {
Q_OBJECT
public:
GLSpectrum(QWidget* parent = NULL);
~GLSpectrum();
void setCenterFrequency(quint64 frequency);
void setSampleRate(qint32 sampleRate);
void setReferenceLevel(Real referenceLevel);
void setPowerRange(Real powerRange);
void setDecay(int decay);
void setDisplayWaterfall(bool display);
void setInvertedWaterfall(bool inv);
void setDisplayMaxHold(bool display);
void setDisplayHistogram(bool display);
void setDisplayGrid(bool display);
void addChannelMarker(ChannelMarker* channelMarker);
void removeChannelMarker(ChannelMarker* channelMarker);
void newSpectrum(const std::vector<Real>& spectrum, int fftSize);
private:
struct ChannelMarkerState {
ChannelMarker* m_channelMarker;
QRectF m_glRect;
QRect m_rect;
ChannelMarkerState(ChannelMarker* channelMarker) :
m_channelMarker(channelMarker),
m_glRect()
{ }
};
QList<ChannelMarkerState*> m_channelMarkerStates;
enum CursorState {
CSNormal,
CSSplitter,
CSSplitterMoving,
CSChannel,
CSChannelMoving
};
CursorState m_cursorState;
int m_cursorChannel;
QTimer m_timer;
QMutex m_mutex;
bool m_mouseInside;
bool m_changesPending;
qint64 m_centerFrequency;
Real m_referenceLevel;
Real m_powerRange;
int m_decay;
quint32 m_sampleRate;
int m_fftSize;
bool m_displayGrid;
bool m_invertedWaterfall;
std::vector<Real> m_maxHold;
bool m_displayMaxHold;
Real m_waterfallShare;
QPixmap m_leftMarginPixmap;
bool m_leftMarginTextureAllocated;
GLuint m_leftMarginTexture;
QPixmap m_frequencyPixmap;
bool m_frequencyTextureAllocated;
GLuint m_frequencyTexture;
ScaleEngine m_timeScale;
ScaleEngine m_powerScale;
ScaleEngine m_frequencyScale;
QRectF m_glLeftScaleRect;
QRectF m_glFrequencyScaleRect;
QRect m_frequencyScaleRect;
QRgb m_waterfallPalette[240];
QImage* m_waterfallBuffer;
int m_waterfallBufferPos;
bool m_waterfallTextureAllocated;
GLuint m_waterfallTexture;
int m_waterfallTextureHeight;
int m_waterfallTexturePos;
QRectF m_glWaterfallRect;
bool m_displayWaterfall;
QRgb m_histogramPalette[240];
QImage* m_histogramBuffer;
quint8* m_histogram;
quint8* m_histogramHoldoff;
bool m_histogramTextureAllocated;
GLuint m_histogramTexture;
int m_histogramHoldoffBase;
int m_histogramHoldoffCount;
int m_histogramLateHoldoff;
QRectF m_glHistogramRect;
bool m_displayHistogram;
bool m_displayChanged;
void updateWaterfall(const std::vector<Real>& spectrum);
void updateHistogram(const std::vector<Real>& spectrum);
void initializeGL();
void resizeGL(int width, int height);
void paintGL();
void stopDrag();
void applyChanges();
void mouseMoveEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
private slots:
void tick();
void channelMarkerChanged();
void channelMarkerDestroyed(QObject* object);
};
#endif // INCLUDE_GLSPECTRUM_H

View File

@ -0,0 +1,65 @@
#ifndef INCLUDE_GLSPECTRUMGUI_H
#define INCLUDE_GLSPECTRUMGUI_H
#include <QWidget>
#include "dsp/dsptypes.h"
#include "util/export.h"
namespace Ui {
class GLSpectrumGUI;
}
class MessageQueue;
class SpectrumVis;
class GLSpectrum;
class SDRANGELOVE_API GLSpectrumGUI : public QWidget {
Q_OBJECT
public:
explicit GLSpectrumGUI(QWidget* parent = NULL);
~GLSpectrumGUI();
void setBuddies(MessageQueue* messageQueue, SpectrumVis* spectrumVis, GLSpectrum* glSpectrum);
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
private:
Ui::GLSpectrumGUI* ui;
MessageQueue* m_messageQueue;
SpectrumVis* m_spectrumVis;
GLSpectrum* m_glSpectrum;
qint32 m_fftSize;
qint32 m_fftOverlap;
qint32 m_fftWindow;
Real m_refLevel;
Real m_powerRange;
int m_decay;
bool m_displayWaterfall;
bool m_invertedWaterfall;
bool m_displayMaxHold;
bool m_displayHistogram;
bool m_displayGrid;
bool m_invert;
void applySettings();
private slots:
void on_fftWindow_currentIndexChanged(int index);
void on_fftSize_currentIndexChanged(int index);
void on_refLevel_currentIndexChanged(int index);
void on_levelRange_currentIndexChanged(int index);
void on_decay_currentIndexChanged(int index);
void on_waterfall_toggled(bool checked);
void on_histogram_toggled(bool checked);
void on_maxHold_toggled(bool checked);
void on_invert_toggled(bool checked);
void on_grid_toggled(bool checked);
};
#endif // INCLUDE_GLSPECTRUMGUI_H

View File

@ -0,0 +1,41 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_INDICATOR_H
#define INCLUDE_INDICATOR_H
#include <QWidget>
#include "util/export.h"
class SDRANGELOVE_API Indicator : public QWidget {
private:
Q_OBJECT;
QColor m_color;
QString m_text;
protected:
void paintEvent(QPaintEvent* event);
QSize sizeHint() const;
public:
Indicator(const QString& text, QWidget* parent = NULL);
void setColor(const QColor& color);
};
#endif // INCLUDE_INDICATOR_H

View File

@ -0,0 +1,35 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_PHYSICALUNIT_H
#define INCLUDE_PHYSICALUNIT_H
namespace Unit {
enum Physical {
None,
Frequency,
Information,
Percent,
Decibel,
DecibelMilliWatt,
DecibelMicroVolt,
AngleDegrees,
Time
};
};
#endif // INCLUDE_PHYSICALUNIT_H

View File

@ -0,0 +1,22 @@
#ifndef INCLUDE_PLUGINSDIALOG_H
#define INCLUDE_PLUGINSDIALOG_H
#include <QDialog>
#include "plugin/pluginmanager.h"
namespace Ui {
class PluginsDialog;
}
class PluginsDialog : public QDialog {
Q_OBJECT
public:
explicit PluginsDialog(PluginManager* pluginManager, QWidget* parent = NULL);
~PluginsDialog();
private:
Ui::PluginsDialog* ui;
};
#endif // INCLUDE_PLUGINSDIALOG_H

View File

@ -0,0 +1,34 @@
#ifndef INCLUDE_PREFERENCESDIALOG_H
#define INCLUDE_PREFERENCESDIALOG_H
#include <QDialog>
class AudioDeviceInfo;
namespace Ui {
class PreferencesDialog;
}
class PreferencesDialog : public QDialog {
Q_OBJECT
public:
explicit PreferencesDialog(AudioDeviceInfo* audioDeviceInfo, QWidget* parent = NULL);
~PreferencesDialog();
private:
enum Audio {
ATDefault,
ATInterface,
ATDevice
};
Ui::PreferencesDialog* ui;
AudioDeviceInfo* m_audioDeviceInfo;
private slots:
void accept();
};
#endif // INCLUDE_PREFERENCESDIALOG_H

View File

@ -0,0 +1,27 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <QTreeWidgetItem>
class PresetItem : public QTreeWidgetItem {
public:
PresetItem(QTreeWidgetItem* parent, const QStringList& strings, quint64 frequency, int type);
bool operator<(const QTreeWidgetItem& other) const;
private:
quint64 m_frequency;
};

37
include-gpl/gui/scale.h Normal file
View File

@ -0,0 +1,37 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <QWidget>
#include "gui/scaleengine.h"
#include "util/export.h"
class SDRANGELOVE_API Scale : public QWidget {
Q_OBJECT
public:
Scale(QWidget* parent = NULL);
void setOrientation(Qt::Orientation orientation);
void setRange(Unit::Physical physicalUnit, float rangeMin, float rangeMax);
private:
Qt::Orientation m_orientation;
ScaleEngine m_scaleEngine;
void paintEvent(QPaintEvent*);
void resizeEvent(QResizeEvent*);
};

View File

@ -0,0 +1,90 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_SCALEENGINE_H
#define INCLUDE_SCALEENGINE_H
#include <QFont>
#include <QString>
#include <QList>
#include "physicalunit.h"
#include "util/export.h"
class SDRANGELOVE_API ScaleEngine {
public:
struct Tick {
float pos;
bool major;
float textPos;
float textSize;
QString text;
};
typedef QList<Tick> TickList;
private:
// base configuration
Qt::Orientation m_orientation;
QFont m_font;
float m_charSize;
// graph configuration
float m_size;
Unit::Physical m_physicalUnit;
float m_rangeMin;
float m_rangeMax;
// calculated values
bool m_recalc;
double m_scale;
QString m_unitStr;
TickList m_tickList;
double m_majorTickValueDistance;
double m_firstMajorTickValue;
int m_numMinorTicks;
int m_decimalPlaces;
QString formatTick(double value, int decimalPlaces, bool fancyTime = true);
void calcCharSize();
void calcScaleFactor();
double calcMajorTickUnits(double distance, int* retDecimalPlaces);
int calcTickTextSize();
void forceTwoTicks();
void reCalc();
double majorTickValue(int tick);
double minorTickValue(int tick);
public:
ScaleEngine();
void setOrientation(Qt::Orientation orientation);
void setFont(const QFont& font);
void setSize(float size);
float getSize() { return m_size; }
void setRange(Unit::Physical physicalUnit, float rangeMin, float rangeMax);
float getPosFromValue(double value);
float getValueFromPos(double pos);
const TickList& getTickList();
QString getRangeMinStr();
QString getRangeMaxStr();
float getScaleWidth();
};
#endif // INCLUDE_SCALEENGINE_H

View File

@ -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_SCOPEWINDOW_H
#define INCLUDE_SCOPEWINDOW_H
#include <QWidget>
#include "dsp/dsptypes.h"
#include "util/export.h"
class DSPEngine;
namespace Ui {
class ScopeWindow;
}
class SDRANGELOVE_API ScopeWindow : public QWidget {
Q_OBJECT
public:
explicit ScopeWindow(DSPEngine* dspEngine, QWidget* parent = NULL);
~ScopeWindow();
void setSampleRate(int sampleRate);
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
private slots:
void on_amp_valueChanged(int value);
void on_scope_traceSizeChanged(int value);
void on_time_valueChanged(int value);
void on_timeOfs_valueChanged(int value);
void on_displayMode_currentIndexChanged(int index);
void on_horizView_clicked();
void on_vertView_clicked();
private:
Ui::ScopeWindow *ui;
int m_sampleRate;
qint32 m_displayData;
qint32 m_displayOrientation;
qint32 m_timeBase;
qint32 m_timeOffset;
qint32 m_amplification;
void applySettings();
};
#endif // INCLUDE_SCOPEWINDOW_H

View File

@ -0,0 +1,72 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <QWidget>
#include <QTimer>
#include "util/export.h"
class SDRANGELOVE_API ValueDial : public QWidget {
Q_OBJECT
public:
ValueDial(QWidget* parent = NULL);
void setValue(quint64 value);
void setValueRange(uint numDigits, quint64 min, quint64 max);
void setFont(const QFont& font);
signals:
void changed(quint64 value);
private:
QLinearGradient m_background;
int m_numDigits;
int m_numDecimalPoints;
int m_digitWidth;
int m_digitHeight;
int m_hightlightedDigit;
int m_cursor;
bool m_cursorState;
quint64 m_value;
quint64 m_valueMax;
quint64 m_valueMin;
QString m_text;
quint64 m_valueNew;
QString m_textNew;
int m_animationState;
QTimer m_animationTimer;
QTimer m_blinkTimer;
quint64 findExponent(int digit);
QChar digitNeigh(QChar c, bool dir);
QString formatText(quint64 value);
void paintEvent(QPaintEvent*);
void mousePressEvent(QMouseEvent*);
void mouseMoveEvent(QMouseEvent*);
void wheelEvent(QWheelEvent*);
void leaveEvent(QEvent*);
void keyPressEvent(QKeyEvent*);
void focusInEvent(QFocusEvent*);
void focusOutEvent(QFocusEvent*);
private slots:
void animate();
void blink();
};

136
include-gpl/mainwindow.h Normal file
View File

@ -0,0 +1,136 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_MAINWINDOW_H
#define INCLUDE_MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
#include "settings/settings.h"
#include "util/export.h"
class QLabel;
class QTreeWidgetItem;
class QDir;
class AudioDeviceInfo;
class DSPEngine;
class Indicator;
class ScopeWindow;
class SpectrumVis;
class SampleSource;
class PluginAPI;
class PluginGUI;
class ChannelMarker;
class MessageQueue;
class PluginManager;
class PluginInterface;
namespace Ui {
class MainWindow;
}
class SDRANGELOVE_API MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = NULL);
~MainWindow();
MessageQueue* getMessageQueue() { return m_messageQueue; }
void addChannelCreateAction(QAction* action);
void addChannelRollup(QWidget* widget);
void addViewAction(QAction* action);
void addChannelMarker(ChannelMarker* channelMarker);
void removeChannelMarker(ChannelMarker* channelMarker);
void setInputGUI(QWidget* gui);
private:
enum {
PGroup,
PItem
};
Ui::MainWindow* ui;
AudioDeviceInfo* m_audioDeviceInfo;
MessageQueue* m_messageQueue;
Settings m_settings;
SpectrumVis* m_spectrumVis;
DSPEngine* m_dspEngine;
QTimer m_statusTimer;
int m_lastEngineState;
QLabel* m_sampleRateWidget;
Indicator* m_engineIdle;
Indicator* m_engineRunning;
Indicator* m_engineError;
bool m_startOsmoSDRUpdateAfterStop;
ScopeWindow* m_scopeWindow;
QWidget* m_inputGUI;
int m_sampleRate;
quint64 m_centerFrequency;
PluginManager* m_pluginManager;
void loadSettings();
void loadSettings(const Preset* preset);
void saveSettings(Preset* preset);
void saveSettings();
void createStatusBar();
void closeEvent(QCloseEvent*);
void updateCenterFreqDisplay();
void updateSampleRate();
void updatePresets();
QTreeWidgetItem* addPresetToTree(const Preset* preset);
void applySettings();
private slots:
void handleMessages();
void updateStatus();
void scopeWindowDestroyed();
void on_action_Start_triggered();
void on_action_Stop_triggered();
void on_dcOffset_toggled(bool checked);
void on_iqImbalance_toggled(bool checked);
void on_action_View_Fullscreen_toggled(bool checked);
void on_actionOsmoSDR_Firmware_Upgrade_triggered();
void on_presetSave_clicked();
void on_presetLoad_clicked();
void on_presetDelete_clicked();
void on_presetTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void on_presetTree_itemActivated(QTreeWidgetItem *item, int column);
void on_action_Oscilloscope_triggered();
void on_action_Loaded_Plugins_triggered();
void on_action_Preferences_triggered();
void on_sampleSource_currentIndexChanged(int index);
void on_action_About_triggered();
};
#endif // INCLUDE_MAINWINDOW_H

View File

@ -0,0 +1,131 @@
#ifndef INCLUDE_PLUGINMANAGER_H
#define INCLUDE_PLUGINMANAGER_H
#include <QObject>
#include <QDir>
#include "plugin/plugininterface.h"
#include "plugin/pluginapi.h"
#include "util/export.h"
class QAction;
class QComboBox;
class QPluginLoader;
class Preset;
class MainWindow;
class SampleSource;
class Message;
class SDRANGELOVE_API PluginManager : public QObject {
Q_OBJECT
public:
struct Plugin {
QString filename;
QPluginLoader* loader;
PluginInterface* plugin;
Plugin(const QString& _filename, QPluginLoader* pluginLoader, PluginInterface* _plugin) :
filename(_filename),
loader(pluginLoader),
plugin(_plugin)
{ }
};
typedef QList<Plugin> Plugins;
explicit PluginManager(MainWindow* mainWindow, DSPEngine* dspEngine, QObject* parent = NULL);
~PluginManager();
void loadPlugins();
const Plugins& getPlugins() const { return m_plugins; }
void registerChannel(const QString& channelName, PluginInterface* plugin, QAction* action);
void registerChannelInstance(const QString& channelName, PluginGUI* pluginGUI);
void addChannelRollup(QWidget* pluginGUI);
void removeChannelInstance(PluginGUI* pluginGUI);
void registerSampleSource(const QString& sourceName, PluginInterface* plugin);
void loadSettings(const Preset* preset);
void saveSettings(Preset* preset) const;
void freeAll();
bool handleMessage(Message* message);
void updateSampleSourceDevices();
void fillSampleSourceSelector(QComboBox* comboBox);
int selectSampleSource(int index);
int selectSampleSource(const QString& source);
private:
struct ChannelRegistration {
QString m_channelName;
PluginInterface* m_plugin;
ChannelRegistration(const QString& channelName, PluginInterface* plugin) :
m_channelName(channelName),
m_plugin(plugin)
{ }
};
typedef QList<ChannelRegistration> ChannelRegistrations;
struct ChannelInstanceRegistration {
QString m_channelName;
PluginGUI* m_gui;
ChannelInstanceRegistration() :
m_channelName(),
m_gui(NULL)
{ }
ChannelInstanceRegistration(const QString& channelName, PluginGUI* pluginGUI) :
m_channelName(channelName),
m_gui(pluginGUI)
{ }
};
typedef QList<ChannelInstanceRegistration> ChannelInstanceRegistrations;
struct SampleSourceRegistration {
QString m_sourceName;
PluginInterface* m_plugin;
SampleSourceRegistration(const QString& sourceName, PluginInterface* plugin) :
m_sourceName(sourceName),
m_plugin(plugin)
{ }
};
typedef QList<SampleSourceRegistration> SampleSourceRegistrations;
struct SampleSourceDevice {
PluginInterface* m_plugin;
QString m_displayName;
QString m_sourceName;
QByteArray m_address;
SampleSourceDevice(PluginInterface* plugin, const QString& displayName, const QString& sourceName, const QByteArray& address) :
m_plugin(plugin),
m_displayName(displayName),
m_sourceName(sourceName),
m_address(address)
{ }
};
typedef QList<SampleSourceDevice> SampleSourceDevices;
PluginAPI m_pluginAPI;
MainWindow* m_mainWindow;
DSPEngine* m_dspEngine;
Plugins m_plugins;
ChannelRegistrations m_channelRegistrations;
ChannelInstanceRegistrations m_channelInstanceRegistrations;
SampleSourceRegistrations m_sampleSourceRegistrations;
SampleSourceDevices m_sampleSourceDevices;
QString m_sampleSource;
PluginGUI* m_sampleSourceInstance;
void loadPlugins(const QDir& dir);
void renameChannelInstances();
};
static inline bool operator<(const PluginManager::Plugin& a, const PluginManager::Plugin& b)
{
return a.plugin->getPluginDescriptor().displayedName < b.plugin->getPluginDescriptor().displayedName;
}
#endif // INCLUDE_PLUGINMANAGER_H

View File

@ -0,0 +1,32 @@
#ifndef INCLUDE_PREFERENCES_H
#define INCLUDE_PREFERENCES_H
#include <QString>
class Preferences {
public:
Preferences();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
void setSourceType(const QString& value) { m_sourceType = value; }
const QString& getSourceType() const { return m_sourceType; }
void setSourceDevice(const QString& value) { m_sourceDevice= value; }
const QString& getSourceDevice() const { return m_sourceDevice; }
void setAudioType(const QString& value) { m_audioType = value; }
const QString& getAudioType() const { return m_audioType; }
void setAudioDevice(const QString& value) { m_audioDevice= value; }
const QString& getAudioDevice() const { return m_audioDevice; }
protected:
QString m_sourceType;
QString m_sourceDevice;
QString m_audioType;
QString m_audioDevice;
};
#endif // INCLUDE_PREFERENCES_H

View File

@ -0,0 +1,98 @@
#ifndef INCLUDE_PRESET_H
#define INCLUDE_PRESET_H
#include <QString>
#include <QList>
#include <QMetaType>
class Preset {
public:
struct ChannelConfig {
QString m_channel;
QByteArray m_config;
ChannelConfig(const QString& channel, const QByteArray& config) :
m_channel(channel),
m_config(config)
{ }
};
typedef QList<ChannelConfig> ChannelConfigs;
Preset();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
void setGroup(const QString& group) { m_group = group; }
const QString& getGroup() const { return m_group; }
void setDescription(const QString& description) { m_description = description; }
const QString& getDescription() const { return m_description; }
void setCenterFrequency(const quint64 centerFrequency) { m_centerFrequency = centerFrequency; }
quint64 getCenterFrequency() const { return m_centerFrequency; }
void setSpectrumConfig(const QByteArray& data) { m_spectrumConfig = data; }
const QByteArray& getSpectrumConfig() const { return m_spectrumConfig; }
void setShowScope(bool value) { m_showScope = value; }
bool getShowScope() const { return m_showScope; }
void setLayout(const QByteArray& data) { m_layout = data; }
const QByteArray& getLayout() const { return m_layout; }
void setDCOffsetCorrection(bool value) { m_dcOffsetCorrection = value; }
bool getDCOffsetCorrection() const { return m_dcOffsetCorrection; }
void setIQImbalanceCorrection(bool value) { m_iqImbalanceCorrection = value; }
bool getIQImbalanceCorrection() const { return m_iqImbalanceCorrection; }
void setScopeConfig(const QByteArray& data) { m_scopeConfig = data; }
const QByteArray& getScopeConfig() const { return m_scopeConfig; }
void clearChannels() { m_channelConfigs.clear(); }
void addChannel(const QString& channel, const QByteArray& config) { m_channelConfigs.append(ChannelConfig(channel, config)); }
int getChannelCount() const { return m_channelConfigs.count(); }
const ChannelConfig& getChannelConfig(int index) const { return m_channelConfigs.at(index); }
void setSourceConfig(const QString& source, const QByteArray& generalConfig, const QByteArray& config)
{
m_source = source;
m_sourceGeneralConfig = generalConfig;
m_sourceConfig = config;
}
const QString& getSource() const { return m_source; }
const QByteArray& getSourceGeneralConfig() const { return m_sourceGeneralConfig; }
const QByteArray& getSourceConfig() const { return m_sourceConfig; }
protected:
// group and preset description
QString m_group;
QString m_description;
quint64 m_centerFrequency;
// general configuration
QByteArray m_spectrumConfig;
QByteArray m_scopeConfig;
// dc offset and i/q imbalance correction
bool m_dcOffsetCorrection;
bool m_iqImbalanceCorrection;
// display scope dock
bool m_showScope;
// sample source and sample source configuration
QString m_source;
QByteArray m_sourceGeneralConfig;
QByteArray m_sourceConfig;
// channels and configurations
ChannelConfigs m_channelConfigs;
// screen and dock layout
QByteArray m_layout;
};
Q_DECLARE_METATYPE(const Preset*)
#endif // INCLUDE_PRESET_H

View File

@ -0,0 +1,32 @@
#ifndef INCLUDE_SETTINGS_H
#define INCLUDE_SETTINGS_H
#include <QString>
#include "preferences.h"
#include "preset.h"
class Settings {
public:
Settings();
~Settings();
void load();
void save() const;
void resetToDefaults();
Preset* newPreset(const QString& group, const QString& description);
void deletePreset(const Preset* preset);
int getPresetCount() const { return m_presets.count(); }
const Preset* getPreset(int index) const { return m_presets[index]; }
Preset* getCurrent() { return &m_current; }
protected:
Preferences m_preferences;
Preset m_current;
typedef QList<Preset*> Presets;
Presets m_presets;
};
#endif // INCLUDE_SETTINGS_H

View File

@ -0,0 +1,43 @@
#ifndef INCLUDE_CHANNELMARKER_H
#define INCLUDE_CHANNELMARKER_H
#include <QObject>
#include <QColor>
#include "util/export.h"
class SDRANGELOVE_API ChannelMarker : public QObject {
Q_OBJECT
public:
ChannelMarker(QObject* parent = NULL);
void setTitle(const QString& title);
const QString& getTitle() const { return m_title; }
void setCenterFrequency(int centerFrequency);
int getCenterFrequency() const { return m_centerFrequency; }
void setBandwidth(int bandwidth);
int getBandwidth() const { return m_bandwidth; }
void setVisible(bool visible);
bool getVisible() const { return m_visible; }
void setColor(const QColor& color);
const QColor& getColor() const { return m_color; }
protected:
static QRgb m_colorTable[];
static int m_nextColor;
QString m_title;
int m_centerFrequency;
int m_bandwidth;
bool m_visible;
QColor m_color;
signals:
void changed();
};
#endif // INCLUDE_CHANNELMARKER_H

54
include/dsp/dsptypes.h Normal file
View File

@ -0,0 +1,54 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_DSPTYPES_H
#define INCLUDE_DSPTYPES_H
#include <complex>
#include <vector>
#include <QtGlobal>
typedef float Real;
typedef std::complex<Real> Complex;
typedef qint16 FixReal;
#pragma pack(push, 1)
struct Sample {
Sample() {}
Sample(FixReal real) : m_real(real), m_imag(0) {}
Sample(FixReal real, FixReal imag) : m_real(real), m_imag(imag) {}
Sample(const Sample& other) : m_real(other.m_real), m_imag(other.m_imag) {}
Sample& operator=(const Sample& other) { m_real = other.m_real; m_imag = other.m_imag; return *this; }
Sample& operator+=(const Sample& other) { m_real += other.m_real; m_imag += other.m_imag; return *this; }
Sample& operator-=(const Sample& other) { m_real -= other.m_real; m_imag -= other.m_imag; return *this; }
void setReal(FixReal v) { m_real = v; }
void setImag(FixReal v) { m_imag = v; }
FixReal real() const { return m_real; }
FixReal imag() const { return m_imag; }
FixReal m_real;
FixReal m_imag;
};
#pragma pack(pop)
typedef std::vector<Sample> SampleVector;
#endif // INCLUDE_DSPTYPES_H

391
include/dsp/kissfft.h Normal file
View File

@ -0,0 +1,391 @@
#ifndef INCLUDE_KISSFFT_H
#define INCLUDE_KISSFFT_H
#include <complex>
#include <vector>
/*
Copyright (c) 2003-2010 Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to
endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace kissfft_utils {
template<typename T_scalar, typename T_complex>
struct traits {
typedef T_scalar scalar_type;
typedef T_complex cpx_type;
void fill_twiddles(std::complex<T_scalar>* dst, int nfft, bool inverse)
{
T_scalar phinc = (inverse ? 2 : -2) * acos((T_scalar)-1) / nfft;
for(int i = 0; i < nfft; ++i)
dst[i] = exp(std::complex<T_scalar>(0, i * phinc));
}
void prepare(std::vector<std::complex<T_scalar> >& dst, int nfft, bool inverse, std::vector<int>& stageRadix, std::vector<int>& stageRemainder)
{
_twiddles.resize(nfft);
fill_twiddles(&_twiddles[0], nfft, inverse);
dst = _twiddles;
//factorize
//start factoring out 4's, then 2's, then 3,5,7,9,...
int n = nfft;
int p = 4;
do {
while(n % p) {
switch(p) {
case 4:
p = 2;
break;
case 2:
p = 3;
break;
default:
p += 2;
break;
}
if(p * p > n)
p = n;// no more factors
}
n /= p;
stageRadix.push_back(p);
stageRemainder.push_back(n);
} while(n > 1);
}
std::vector<cpx_type> _twiddles;
const cpx_type twiddle(int i)
{
return _twiddles[i];
}
};
} // namespace
template<typename T_Scalar, typename T_Complex, typename T_traits = kissfft_utils::traits<T_Scalar, T_Complex> >
class kissfft {
public:
typedef T_traits traits_type;
typedef typename traits_type::scalar_type scalar_type;
typedef typename traits_type::cpx_type cpx_type;
kissfft()
{
}
kissfft(int nfft, bool inverse, const traits_type & traits = traits_type()) :
_nfft(nfft), _inverse(inverse), _traits(traits)
{
_traits.prepare(_twiddles, _nfft, _inverse, _stageRadix, _stageRemainder);
}
void configure(int nfft, bool inverse, const traits_type & traits = traits_type())
{
_twiddles.clear();
_stageRadix.clear();
_stageRemainder.clear();
_nfft = nfft;
_inverse = inverse;
_traits = traits;
_traits.prepare(_twiddles, _nfft, _inverse, _stageRadix, _stageRemainder);
}
void transform(const cpx_type* src, cpx_type* dst)
{
kf_work(0, dst, src, 1, 1);
}
private:
void kf_work(int stage, cpx_type* Fout, const cpx_type* f, size_t fstride, size_t in_stride)
{
int p = _stageRadix[stage];
int m = _stageRemainder[stage];
cpx_type * Fout_beg = Fout;
cpx_type * Fout_end = Fout + p * m;
if(m == 1) {
do {
*Fout = *f;
f += fstride * in_stride;
} while(++Fout != Fout_end);
} else {
do {
// recursive call:
// DFT of size m*p performed by doing
// p instances of smaller DFTs of size m,
// each one takes a decimated version of the input
kf_work(stage + 1, Fout, f, fstride * p, in_stride);
f += fstride * in_stride;
} while((Fout += m) != Fout_end);
}
Fout = Fout_beg;
// recombine the p smaller DFTs
switch(p) {
case 2:
kf_bfly2(Fout, fstride, m);
break;
case 3:
kf_bfly3(Fout, fstride, m);
break;
case 4:
kf_bfly4(Fout, fstride, m);
break;
case 5:
kf_bfly5(Fout, fstride, m);
break;
default:
kf_bfly_generic(Fout, fstride, m, p);
break;
}
}
// these were #define macros in the original kiss_fft
void C_ADD(cpx_type& c, const cpx_type& a, const cpx_type& b)
{
c = a + b;
}
void C_MUL(cpx_type& c, const cpx_type& a, const cpx_type& b)
{
//c = a * b;
c = cpx_type(a.real() * b.real() - a.imag() * b.imag(), a.real() * b.imag() + a.imag() * b.real());
}
void C_SUB(cpx_type& c, const cpx_type& a, const cpx_type& b)
{
c = a - b;
}
void C_ADDTO(cpx_type& c, const cpx_type& a)
{
c += a;
}
void C_FIXDIV(cpx_type&, int)
{
} // NO-OP for float types
scalar_type S_MUL(const scalar_type& a, const scalar_type& b)
{
return a * b;
}
scalar_type HALF_OF(const scalar_type& a)
{
return a * .5;
}
void C_MULBYSCALAR(cpx_type& c, const scalar_type& a)
{
c *= a;
}
void kf_bfly2(cpx_type* Fout, const size_t fstride, int m)
{
for(int k = 0; k < m; ++k) {
//cpx_type t = Fout[m + k] * _traits.twiddle(k * fstride);
cpx_type t;
C_MUL(t, Fout[m + k], _traits.twiddle(k * fstride));
Fout[m + k] = Fout[k] - t;
Fout[k] += t;
}
}
void kf_bfly4(cpx_type* Fout, const size_t fstride, const size_t m)
{
cpx_type scratch[7];
int negative_if_inverse = _inverse * -2 + 1;
for(size_t k = 0; k < m; ++k) {
//scratch[0] = Fout[k + m] * _traits.twiddle(k * fstride);
C_MUL(scratch[0], Fout[k + m], _traits.twiddle(k * fstride));
C_MUL(scratch[1], Fout[k + 2 * m], _traits.twiddle(k * fstride * 2));
C_MUL(scratch[2], Fout[k + 3 * m], _traits.twiddle(k * fstride * 3));
scratch[5] = Fout[k] - scratch[1];
Fout[k] += scratch[1];
scratch[3] = scratch[0] + scratch[2];
scratch[4] = scratch[0] - scratch[2];
scratch[4] = cpx_type(scratch[4].imag() * negative_if_inverse, -scratch[4].real() * negative_if_inverse);
Fout[k + 2 * m] = Fout[k] - scratch[3];
Fout[k] += scratch[3];
Fout[k + m] = scratch[5] + scratch[4];
Fout[k + 3 * m] = scratch[5] - scratch[4];
}
}
void kf_bfly3(cpx_type* Fout, const size_t fstride, const size_t m)
{
size_t k = m;
const size_t m2 = 2 * m;
cpx_type* tw1;
cpx_type* tw2;
cpx_type scratch[5];
cpx_type epi3;
epi3 = _twiddles[fstride * m];
tw1 = tw2 = &_twiddles[0];
do {
C_FIXDIV(*Fout, 3);
C_FIXDIV(Fout[m], 3);
C_FIXDIV(Fout[m2], 3);
C_MUL(scratch[1], Fout[m], *tw1);
C_MUL(scratch[2], Fout[m2], *tw2);
C_ADD(scratch[3], scratch[1], scratch[2]);
C_SUB(scratch[0], scratch[1], scratch[2]);
tw1 += fstride;
tw2 += fstride * 2;
Fout[m] = cpx_type(Fout->real() - HALF_OF(scratch[3].real()), Fout->imag() - HALF_OF(scratch[3].imag()));
C_MULBYSCALAR(scratch[0], epi3.imag());
C_ADDTO(*Fout, scratch[3]);
Fout[m2] = cpx_type(Fout[m].real() + scratch[0].imag(), Fout[m].imag() - scratch[0].real());
C_ADDTO(Fout[m], cpx_type(-scratch[0].imag(), scratch[0].real()));
++Fout;
} while(--k);
}
void kf_bfly5(cpx_type* Fout, const size_t fstride, const size_t m)
{
cpx_type* Fout0;
cpx_type* Fout1;
cpx_type* Fout2;
cpx_type* Fout3;
cpx_type* Fout4;
size_t u;
cpx_type scratch[13];
cpx_type* twiddles = &_twiddles[0];
cpx_type* tw;
cpx_type ya, yb;
ya = twiddles[fstride * m];
yb = twiddles[fstride * 2 * m];
Fout0 = Fout;
Fout1 = Fout0 + m;
Fout2 = Fout0 + 2 * m;
Fout3 = Fout0 + 3 * m;
Fout4 = Fout0 + 4 * m;
tw = twiddles;
for(u = 0; u < m; ++u) {
C_FIXDIV(*Fout0, 5);
C_FIXDIV(*Fout1, 5);
C_FIXDIV(*Fout2, 5);
C_FIXDIV(*Fout3, 5);
C_FIXDIV(*Fout4, 5);
scratch[0] = *Fout0;
C_MUL(scratch[1], *Fout1, tw[u * fstride]);
C_MUL(scratch[2], *Fout2, tw[2 * u * fstride]);
C_MUL(scratch[3], *Fout3, tw[3 * u * fstride]);
C_MUL(scratch[4], *Fout4, tw[4 * u * fstride]);
C_ADD(scratch[7], scratch[1], scratch[4]);
C_SUB(scratch[10], scratch[1], scratch[4]);
C_ADD(scratch[8], scratch[2], scratch[3]);
C_SUB(scratch[9], scratch[2], scratch[3]);
C_ADDTO(*Fout0, scratch[7]);
C_ADDTO(*Fout0, scratch[8]);
scratch[5] = scratch[0] + cpx_type(S_MUL(scratch[7].real(), ya.real()) + S_MUL(scratch[8].real(), yb.real()), S_MUL(scratch[7].imag(), ya.real())
+ S_MUL(scratch[8].imag(), yb.real()));
scratch[6] = cpx_type(S_MUL(scratch[10].imag(), ya.imag()) + S_MUL(scratch[9].imag(), yb.imag()), -S_MUL(scratch[10].real(), ya.imag()) - S_MUL(
scratch[9].real(), yb.imag()));
C_SUB(*Fout1, scratch[5], scratch[6]);
C_ADD(*Fout4, scratch[5], scratch[6]);
scratch[11] = scratch[0] + cpx_type(S_MUL(scratch[7].real(), yb.real()) + S_MUL(scratch[8].real(), ya.real()), S_MUL(scratch[7].imag(), yb.real())
+ S_MUL(scratch[8].imag(), ya.real()));
scratch[12] = cpx_type(-S_MUL(scratch[10].imag(), yb.imag()) + S_MUL(scratch[9].imag(), ya.imag()), S_MUL(scratch[10].real(), yb.imag()) - S_MUL(
scratch[9].real(), ya.imag()));
C_ADD(*Fout2, scratch[11], scratch[12]);
C_SUB(*Fout3, scratch[11], scratch[12]);
++Fout0;
++Fout1;
++Fout2;
++Fout3;
++Fout4;
}
}
/* perform the butterfly for one stage of a mixed radix FFT */
void kf_bfly_generic(cpx_type* Fout, const size_t fstride, int m, int p)
{
int u;
int k;
int q1;
int q;
cpx_type* twiddles = &_twiddles[0];
cpx_type t;
int Norig = _nfft;
cpx_type* scratchbuf = new cpx_type[p];
for(u = 0; u < m; ++u) {
k = u;
for(q1 = 0; q1 < p; ++q1) {
scratchbuf[q1] = Fout[k];
C_FIXDIV(scratchbuf[q1], p);
k += m;
}
k = u;
for(q1 = 0; q1 < p; ++q1) {
int twidx = 0;
Fout[k] = scratchbuf[0];
for(q = 1; q < p; ++q) {
twidx += fstride * k;
if(twidx >= Norig)
twidx -= Norig;
C_MUL(t, scratchbuf[q], twiddles[twidx]);
C_ADDTO(Fout[k], t);
}
k += m;
}
}
delete[] scratchbuf;
}
int _nfft;
bool _inverse;
std::vector<cpx_type> _twiddles;
std::vector<int> _stageRadix;
std::vector<int> _stageRemainder;
traits_type _traits;
};
#endif

66
include/dsp/samplefifo.h Normal file
View File

@ -0,0 +1,66 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_SAMPLEFIFO_H
#define INCLUDE_SAMPLEFIFO_H
#include <QObject>
#include <QMutex>
#include <QTime>
#include "dsp/dsptypes.h"
#include "util/export.h"
class SDRANGELOVE_API SampleFifo : public QObject {
Q_OBJECT
private:
QMutex m_mutex;
QTime m_msgRateTimer;
int m_suppressed;
SampleVector m_data;
uint m_size;
uint m_fill;
uint m_head;
uint m_tail;
void create(uint s);
public:
SampleFifo(QObject* parent = NULL);
SampleFifo(int size, QObject* parent = NULL);
~SampleFifo();
bool setSize(int size);
inline uint fill() const { return m_fill; }
uint write(const quint8* data, uint count);
uint write(SampleVector::const_iterator begin, SampleVector::const_iterator end);
uint read(SampleVector::iterator begin, SampleVector::iterator end);
uint readBegin(uint count,
SampleVector::iterator* part1Begin, SampleVector::iterator* part1End,
SampleVector::iterator* part2Begin, SampleVector::iterator* part2End);
uint readCommit(uint count);
signals:
void dataReady();
};
#endif // INCLUDE_SAMPLEFIFO_H

21
include/dsp/samplesink.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef INCLUDE_SAMPLESINK_H
#define INCLUDE_SAMPLESINK_H
#include <QObject>
#include "dsptypes.h"
#include "util/export.h"
class Message;
class SDRANGELOVE_API SampleSink : public QObject {
public:
SampleSink();
virtual ~SampleSink();
virtual void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst) = 0;
virtual void start() = 0;
virtual void stop() = 0;
virtual bool handleMessage(Message* cmd) = 0;
};
#endif // INCLUDE_SAMPLESINK_H

View File

@ -0,0 +1,61 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_SAMPLESOURCE_H
#define INCLUDE_SAMPLESOURCE_H
#include <QtGlobal>
#include "dsp/samplefifo.h"
#include "util/message.h"
#include "util/export.h"
class PluginGUI;
class MessageQueue;
class SDRANGELOVE_API SampleSource {
public:
struct SDRANGELOVE_API GeneralSettings {
quint64 m_centerFrequency;
GeneralSettings();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
SampleSource(MessageQueue* guiMessageQueue);
virtual ~SampleSource();
virtual bool startInput(int device) = 0;
virtual void stopInput() = 0;
virtual const QString& getDeviceDescription() const = 0;
virtual int getSampleRate() const = 0;
virtual quint64 getCenterFrequency() const = 0;
virtual bool handleMessage(Message* message) = 0;
SampleFifo* getSampleFifo() { return &m_sampleFifo; }
protected:
SampleFifo m_sampleFifo;
MessageQueue* m_guiMessageQueue;
GeneralSettings m_generalSettings;
};
#endif // INCLUDE_SAMPLESOURCE_H

View File

@ -0,0 +1,41 @@
#ifndef INCLUDE_THREADEDSAMPLESINK_H
#define INCLUDE_THREADEDSAMPLESINK_H
#include <QMutex>
#include "samplesink.h"
#include "dsp/samplefifo.h"
#include "util/messagequeue.h"
#include "util/export.h"
class QThread;
class SampleSink;
class SDRANGELOVE_API ThreadedSampleSink : public SampleSink {
Q_OBJECT
public:
ThreadedSampleSink(SampleSink* sampleSink);
virtual ~ThreadedSampleSink();
MessageQueue* getMessageQueue() { return &m_messageQueue; }
void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst);
void start();
void stop();
bool handleMessage(Message* cmd);
protected:
QMutex m_mutex;
QThread* m_thread;
MessageQueue m_messageQueue;
SampleFifo m_sampleFifo;
SampleSink* m_sampleSink;
protected slots:
void handleData();
void handleMessages();
void threadStarted();
void threadFinished();
};
#endif // INCLUDE_THREADEDSAMPLESINK_H

View File

@ -0,0 +1,34 @@
#ifndef INCLUDE_BASICCHANNELSETTINGSWIDGET_H
#define INCLUDE_BASICCHANNELSETTINGSWIDGET_H
#include <QWidget>
#include "util/export.h"
namespace Ui {
class BasicChannelSettingsWidget;
}
class ChannelMarker;
class SDRANGELOVE_API BasicChannelSettingsWidget : public QWidget {
Q_OBJECT
public:
explicit BasicChannelSettingsWidget(ChannelMarker* marker, QWidget* parent = NULL);
~BasicChannelSettingsWidget();
private slots:
void on_title_textChanged(const QString& text);
void on_colorBtn_clicked();
void on_red_valueChanged(int value);
void on_green_valueChanged(int value);
void on_blue_valueChanged(int value);
private:
Ui::BasicChannelSettingsWidget* ui;
ChannelMarker* m_channelMarker;
void paintColor();
};
#endif // INCLUDE_BASICCHANNELSETTINGSWIDGET_H

View File

@ -0,0 +1,42 @@
#ifndef INCLUDE_ROLLUPWIDGET_H
#define INCLUDE_ROLLUPWIDGET_H
#include <QWidget>
#include "util/export.h"
class SDRANGELOVE_API RollupWidget : public QWidget {
Q_OBJECT
public:
RollupWidget(QWidget* parent = NULL);
QByteArray saveState(int version = 0) const;
bool restoreState(const QByteArray& state, int version = 0);
void setTitleColor(const QColor& c);
signals:
void widgetRolled(QWidget* widget, bool rollDown);
void menuDoubleClickEvent();
protected:
enum {
VersionMarker = 0xff
};
QColor m_titleColor;
int arrangeRollups();
void paintEvent(QPaintEvent*);
int paintRollup(QWidget* rollup, int pos, QPainter* p, bool last, const QColor& frame);
void resizeEvent(QResizeEvent* size);
void mousePressEvent(QMouseEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
bool event(QEvent* event);
bool eventFilter(QObject* object, QEvent* event);
};
#endif // INCLUDE_ROLLUPWIDGET_H

View File

@ -0,0 +1,60 @@
#ifndef INCLUDE_PLUGINAPI_H
#define INCLUDE_PLUGINAPI_H
#include <QObject>
#include "util/export.h"
class QDockWidget;
class QAction;
class PluginManager;
class PluginInterface;
class SampleSource;
class SampleSink;
class DSPEngine;
class AudioFifo;
class MessageQueue;
class MainWindow;
class ChannelMarker;
class PluginGUI;
class SDRANGELOVE_API PluginAPI : public QObject {
Q_OBJECT
public:
// MainWindow access
QDockWidget* createMainWindowDock(Qt::DockWidgetArea dockWidgetArea, const QString& title);
MessageQueue* getMainWindowMessageQueue();
void setInputGUI(QWidget* inputGUI);
// Channel stuff
void registerChannel(const QString& channelName, PluginInterface* plugin, QAction* action);
void registerChannelInstance(const QString& channelName, PluginGUI* pluginGUI);
void addChannelRollup(QWidget* pluginGUI);
void removeChannelInstance(PluginGUI* pluginGUI);
void addChannelMarker(ChannelMarker* channelMarker);
void removeChannelMarker(ChannelMarker* channelMarker);
// DSPEngine access
void setSampleSource(SampleSource* sampleSource);
void addSampleSink(SampleSink* sampleSink);
void removeSampleSink(SampleSink* sampleSink);
MessageQueue* getDSPEngineMessageQueue();
void addAudioSource(AudioFifo* audioFifo);
void removeAudioSource(AudioFifo* audioFifo);
// Sample Source stuff
void registerSampleSource(const QString& sourceName, PluginInterface* plugin);
protected:
PluginManager* m_pluginManager;
MainWindow* m_mainWindow;
DSPEngine* m_dspEngine;
PluginAPI(PluginManager* pluginManager, MainWindow* mainWindow, DSPEngine* dspEngine);
friend class PluginManager;
};
#endif // INCLUDE_PLUGINAPI_H

View File

@ -0,0 +1,28 @@
#ifndef INCLUDE_PLUGINGUI_H
#define INCLUDE_PLUGINGUI_H
#include <QWidget>
#include "util/export.h"
class Message;
class SDRANGELOVE_API PluginGUI {
public:
PluginGUI() { };
virtual void destroy() = 0;
virtual void setName(const QString& name) = 0;
virtual void resetToDefaults() = 0;
virtual QByteArray serializeGeneral() const;
virtual bool deserializeGeneral(const QByteArray& data);
virtual quint64 getCenterFrequency() const;
virtual QByteArray serialize() const = 0;
virtual bool deserialize(const QByteArray& data) = 0;
virtual bool handleMessage(Message* message) = 0;
};
#endif // INCLUDE_PLUGINGUI_H

View File

@ -0,0 +1,48 @@
#ifndef INCLUDE_PLUGININTERFACE_H
#define INCLUDE_PLUGININTERFACE_H
#include <QtPlugin>
#include <QString>
struct PluginDescriptor {
// general plugin description
const QString displayedName;
const QString version;
const QString copyright;
const QString website;
bool licenseIsGPL;
const QString sourceCodeURL;
};
class PluginAPI;
class PluginGUI;
class PluginInterface {
public:
struct SampleSourceDevice {
QString displayedName;
QString name;
QByteArray address;
SampleSourceDevice(const QString& _displayedName, const QString& _name, const QByteArray& _address) :
displayedName(_displayedName),
name(_name),
address(_address)
{ }
};
typedef QList<SampleSourceDevice> SampleSourceDevices;
virtual ~PluginInterface() { };
virtual const PluginDescriptor& getPluginDescriptor() const = 0;
virtual void initPlugin(PluginAPI* pluginAPI) = 0;
virtual PluginGUI* createChannel(const QString& channelName) { return NULL; }
virtual SampleSourceDevices enumSampleSources() { return SampleSourceDevices(); }
virtual PluginGUI* createSampleSource(const QString& sourceName, const QByteArray& address) { return NULL; }
};
Q_DECLARE_INTERFACE(PluginInterface, "de.maintech.SDRangelove.PluginInterface/0.1");
#endif // INCLUDE_PLUGININTERFACE_H

46
include/util/export.h Normal file
View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2012 by Hoernchen <la@tfc-server.de>
*
* 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, either version 2 of the License, or
* (at your option) any later version.
*
* 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 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 __SDRANGELOVE_EXPORT_H
#define __SDRANGELOVE_EXPORT_H
#if defined __GNUC__
# if __GNUC__ >= 4
# define __SDR_EXPORT __attribute__((visibility("default")))
# define __SDR_IMPORT __attribute__((visibility("default")))
# else
# define __SDR_EXPORT
# define __SDR_IMPORT
# endif
#elif _MSC_VER
# define __SDR_EXPORT __declspec(dllexport)
# define __SDR_IMPORT __declspec(dllimport)
#else
# define __SDR_EXPORT
# define __SDR_IMPORT
#endif
#ifndef sdrangelove_STATIC
# ifdef sdrangelove_EXPORTS
# define SDRANGELOVE_API __SDR_EXPORT
# else
# define SDRANGELOVE_API __SDR_IMPORT
# endif
#else
#define SDRANGELOVE_API
#endif
#endif /* __SDRANGELOVE_EXPORT_H */

58
include/util/message.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef INCLUDE_MESSAGE_H
#define INCLUDE_MESSAGE_H
#include <stdlib.h>
#include <QAtomicInt>
#include "util/export.h"
class MessageQueue;
class QWaitCondition;
class QMutex;
class SDRANGELOVE_API Message {
public:
Message();
virtual ~Message();
virtual const char* getIdentifier() const;
virtual bool matchIdentifier(const char* identifier) const;
static bool match(Message* message);
void* getDestination() const { return m_destination; }
void submit(MessageQueue* queue, void* destination = NULL);
int execute(MessageQueue* queue, void* destination = NULL);
void completed(int result = 0);
protected:
// addressing
static const char* m_identifier;
void* m_destination;
// stuff for synchronous messages
bool m_synchronous;
QWaitCondition* m_waitCondition;
QMutex* m_mutex;
QAtomicInt m_complete;
int m_result;
};
#define MESSAGE_CLASS_DECLARATION \
public: \
const char* getIdentifier() const; \
bool matchIdentifier(const char* identifier) const; \
static bool match(Message* message); \
protected: \
static const char* m_identifier; \
private:
#define MESSAGE_CLASS_DEFINITION(Name, BaseClass) \
const char* Name::m_identifier = #Name; \
const char* Name::getIdentifier() const { return m_identifier; } \
bool Name::matchIdentifier(const char* identifier) const {\
return (m_identifier == identifier) ? true : BaseClass::matchIdentifier(identifier); \
} \
bool Name::match(Message* message) { return message->matchIdentifier(m_identifier); }
#endif // INCLUDE_MESSAGE_H

View File

@ -0,0 +1,31 @@
#ifndef INCLUDE_MESSAGEQUEUE_H
#define INCLUDE_MESSAGEQUEUE_H
#include <QObject>
#include <QQueue>
#include "spinlock.h"
#include "util/export.h"
class Message;
class SDRANGELOVE_API MessageQueue : public QObject {
Q_OBJECT
public:
MessageQueue(QObject* parent = NULL);
~MessageQueue();
void submit(Message* message);
Message* accept();
int countPending();
signals:
void messageEnqueued();
private:
Spinlock m_lock;
QQueue<Message*> m_queue;
};
#endif // INCLUDE_MESSAGEQUEUE_H

7
include/util/miniz.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef INCLUDE_MINIZ_H
#define INCLUDE_MINIZ_H
#define MINIZ_HEADER_FILE_ONLY
#include "../../sdrapp/util/miniz.cpp"
#endif // INCLUDE_MINIZ_H

View File

@ -0,0 +1,112 @@
#ifndef INCLUDE_SIMPLESERIALIZER_H
#define INCLUDE_SIMPLESERIALIZER_H
#include <QString>
#include <QMap>
#include "dsp/dsptypes.h"
#include "util/export.h"
class SDRANGELOVE_API SimpleSerializer {
public:
SimpleSerializer(quint32 version);
void writeS32(quint32 id, qint32 value);
void writeU32(quint32 id, quint32 value);
void writeS64(quint32 id, qint64 value);
void writeU64(quint32 id, quint64 value);
void writeFloat(quint32 id, float value);
void writeDouble(quint32 id, double value);
void writeReal(quint32 id, Real value)
{
if(sizeof(Real) == 4)
writeFloat(id, value);
else writeDouble(id, value);
}
void writeBool(quint32 id, bool value);
void writeString(quint32 id, const QString& value);
void writeBlob(quint32 id, const QByteArray& value);
const QByteArray& final();
protected:
enum Type {
TSigned32 = 0,
TUnsigned32 = 1,
TSigned64 = 2,
TUnsigned64 = 3,
TFloat = 4,
TDouble = 5,
TBool = 6,
TString = 7,
TBlob = 8,
TVersion = 9
};
QByteArray m_data;
bool m_finalized;
bool writeTag(Type type, quint32 id, quint32 length);
};
class SDRANGELOVE_API SimpleDeserializer {
public:
SimpleDeserializer(const QByteArray& data);
bool readS32(quint32 id, qint32* result, qint32 def = 0) const;
bool readU32(quint32 id, quint32* result, quint32 def = 0) const;
bool readS64(quint32 id, qint64* result, qint64 def = 0) const;
bool readU64(quint32 id, quint64* result, quint64 def = 0) const;
bool readFloat(quint32 id, float* result, float def = 0) const;
bool readDouble(quint32 id, double* result, double def = 0) const;
bool readReal(quint32 id, Real* result, Real def = 0) const;
bool readBool(quint32 id, bool* result, bool def = false) const;
bool readString(quint32 id, QString* result, const QString& def = QString::null) const;
bool readBlob(quint32 id, QByteArray* result, const QByteArray& def = QByteArray()) const;
bool isValid() const { return m_valid; }
quint32 getVersion() const { return m_version; }
void dump() const;
private:
enum Type {
TSigned32 = 0,
TUnsigned32 = 1,
TSigned64 = 2,
TUnsigned64 = 3,
TFloat = 4,
TDouble = 5,
TBool = 6,
TString = 7,
TBlob = 8,
TVersion = 9
};
struct Element {
Type type;
quint32 ofs;
quint32 length;
Element(Type _type, quint32 _ofs, quint32 _length) :
type(_type),
ofs(_ofs),
length(_length)
{ }
};
typedef QMap<quint32, Element> Elements;
QByteArray m_data;
bool m_valid;
Elements m_elements;
quint32 m_version;
bool parseAll();
bool readTag(uint* readOfs, uint readEnd, Type* type, quint32* id, quint32* length) const;
quint8 readByte(uint* readOfs) const
{
quint8 res = m_data[*readOfs];
(*readOfs)++;
return res;
}
};
#endif // INCLUDE_SIMPLESERIALIZER_H

39
include/util/spinlock.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef INCLUDE_SPINLOCK_H
#define INCLUDE_SPINLOCK_H
#include <QAtomicInt>
class Spinlock {
public:
void lock()
{
while(!m_atomic.testAndSetAcquire(0, 1)) ;
}
void unlock()
{
while(!m_atomic.testAndSetRelease(1, 0)) ;
}
protected:
QAtomicInt m_atomic;
};
class SpinlockHolder {
public:
SpinlockHolder(Spinlock* spinlock) :
m_spinlock(spinlock)
{
m_spinlock->lock();
}
~SpinlockHolder()
{
m_spinlock->unlock();
}
protected:
Spinlock* m_spinlock;
};
#endif // INCLUDE_SPINLOCK_H

101
main.cpp Normal file
View File

@ -0,0 +1,101 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <QApplication>
#include <QTextCodec>
#include <QProxyStyle>
#include <QStyleFactory>
#include <QFontDatabase>
#include "mainwindow.h"
static int runQtApplication(int argc, char* argv[])
{
QApplication a(argc, argv);
/*
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
*/
QCoreApplication::setOrganizationName("osmocom");
QCoreApplication::setApplicationName("SDRangelove");
#if 1
qApp->setStyle(QStyleFactory::create("fusion"));
QPalette palette;
palette.setColor(QPalette::Window, QColor(53,53,53));
palette.setColor(QPalette::WindowText, Qt::white);
palette.setColor(QPalette::Base, QColor(25,25,25));
palette.setColor(QPalette::AlternateBase, QColor(53,53,53));
palette.setColor(QPalette::ToolTipBase, Qt::white);
palette.setColor(QPalette::ToolTipText, Qt::black);
palette.setColor(QPalette::Text, Qt::white);
palette.setColor(QPalette::Button, QColor(0x40, 0x40, 0x40));
palette.setColor(QPalette::ButtonText, Qt::white);
palette.setColor(QPalette::BrightText, Qt::red);
palette.setColor(QPalette::Light, QColor(53,53,53).lighter(125).lighter());
palette.setColor(QPalette::Mid, QColor(53,53,53).lighter(125));
palette.setColor(QPalette::Dark, QColor(53,53,53).lighter(125).darker());
palette.setColor(QPalette::Link, QColor(0,0xa0,0xa0));
palette.setColor(QPalette::LinkVisited, QColor(0,0xa0,0xa0).lighter());
palette.setColor(QPalette::Highlight, QColor(0xff, 0x8c, 0x00));
palette.setColor(QPalette::HighlightedText, Qt::black);
qApp->setPalette(palette);
#if 0
if(QFontDatabase::addApplicationFont("/tmp/Cuprum.otf") >= 0) {
QFont font("CuprumFFU");
font.setPointSize(10);
qApp->setFont(font);
}
#endif
#if 0
if(QFontDatabase::addApplicationFont("/tmp/PTN57F.ttf") >= 0) {
QFont font("PT Sans Narrow");
font.setPointSize(10);
qApp->setFont(font);
}
#endif
#if 0
if(QFontDatabase::addApplicationFont("/tmp/PTS55F.ttf") >= 0) {
QFont font("PT Sans");
font.setPointSize(10);
qApp->setFont(font);
}
#endif
#if 0
{
QFont font("Ubuntu Condensed");
font.setPointSize(10);
qApp->setFont(font);
}
#endif
#endif
MainWindow w;
w.show();
return a.exec();
}
int main(int argc, char* argv[])
{
int res = runQtApplication(argc, argv);
qDebug("regular program exit");
return res;
}

4
plugins/CMakeLists.txt Normal file
View File

@ -0,0 +1,4 @@
project(plugins)
add_subdirectory(channel)
add_subdirectory(samplesource)

View File

@ -0,0 +1,5 @@
project(demod)
add_subdirectory(nfm)
add_subdirectory(tcpsrc)
#add_subdirectory(tetra)

View File

@ -0,0 +1,47 @@
project(nfm)
set(nfm_SOURCES
nfmdemod.cpp
nfmdemodgui.cpp
nfmplugin.cpp
)
set(nfm_HEADERS
nfmdemod.h
nfmdemodgui.h
nfmplugin.h
)
set(nfm_FORMS
nfmdemodgui.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(nfm_HEADERS_MOC ${nfm_HEADERS})
qt5_wrap_ui(nfm_FORMS_HEADERS ${nfm_FORMS})
add_library(demodnfm SHARED
${nfm_SOURCES}
${nfm_HEADERS_MOC}
${nfm_FORMS_HEADERS}
)
target_link_libraries(demodnfm
${QT_LIBRARIES}
${OPENGL_LIBRARIES}
sdrbase
)
qt5_use_modules(demodnfm Core Widgets OpenGL Multimedia)

View File

@ -0,0 +1,147 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <QTime>
#include <stdio.h>
#include "nfmdemod.h"
#include "audio/audiooutput.h"
#include "dsp/dspcommands.h"
MESSAGE_CLASS_DEFINITION(NFMDemod::MsgConfigureNFMDemod, Message)
NFMDemod::NFMDemod(AudioFifo* audioFifo, SampleSink* sampleSink) :
m_sampleSink(sampleSink),
m_audioFifo(audioFifo)
{
m_rfBandwidth = 12500;
m_volume = 2.0;
m_squelchLevel = pow(10.0, -40.0 / 20.0);
m_sampleRate = 500000;
m_frequency = 0;
m_squelchLevel *= m_squelchLevel;
m_nco.setFreq(m_frequency, m_sampleRate);
m_interpolator.create(16, m_sampleRate, 12500);
m_sampleDistanceRemain = (Real)m_sampleRate / 44100.0;
m_lowpass.create(21, 44100, 3000);
m_audioBuffer.resize(256);
m_audioBufferFill = 0;
m_movingAverage.resize(16, 0);
}
NFMDemod::~NFMDemod()
{
}
void NFMDemod::configure(MessageQueue* messageQueue, Real rfBandwidth, Real afBandwidth, Real volume, Real squelch)
{
Message* cmd = MsgConfigureNFMDemod::create(rfBandwidth, afBandwidth, volume, squelch);
cmd->submit(messageQueue, this);
}
void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst)
{
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_movingAverage.feed(ci.real() * ci.real() + ci.imag() * ci.imag());
if(m_movingAverage.average() >= m_squelchLevel)
m_squelchState = m_sampleRate / 50;
if(m_squelchState > 0) {
m_squelchState--;
Complex d = ci * conj(m_lastSample);
m_lastSample = ci;
Real demod = atan2(d.imag(), d.real()) / M_PI;
demod = m_lowpass.filter(demod);
demod *= m_volume;
qint16 sample = demod * 32767;
m_audioBuffer[m_audioBufferFill].l = sample;
m_audioBuffer[m_audioBufferFill].r = sample;
++m_audioBufferFill;
if(m_audioBufferFill >= m_audioBuffer.size()) {
uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1);
/*
if(res != m_audioBufferFill)
qDebug("lost %u samples", m_audioBufferFill - res);
*/
m_audioBufferFill = 0;
}
}
m_sampleDistanceRemain += (Real)m_sampleRate / 44100.0;
}
}
if(m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 0) != m_audioBufferFill)
;//qDebug("lost samples");
m_audioBufferFill = 0;
if(m_sampleSink != NULL)
m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), firstOfBurst);
m_sampleBuffer.clear();
}
void NFMDemod::start()
{
m_squelchState = 0;
}
void NFMDemod::stop()
{
}
bool NFMDemod::handleMessage(Message* cmd)
{
if(DSPSignalNotification::match(cmd)) {
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(16, m_sampleRate, m_rfBandwidth / 2.1);
m_sampleDistanceRemain = m_sampleRate / 44100.0;
m_squelchState = 0;
cmd->completed();
return true;
} else if(MsgConfigureNFMDemod::match(cmd)) {
MsgConfigureNFMDemod* cfg = (MsgConfigureNFMDemod*)cmd;
m_rfBandwidth = cfg->getRFBandwidth();
m_interpolator.create(16, m_sampleRate, m_rfBandwidth / 2.1);
m_lowpass.create(21, 44100, cfg->getAFBandwidth());
m_squelchLevel = pow(10.0, cfg->getSquelch() / 20.0);
m_squelchLevel *= m_squelchLevel;
m_volume = cfg->getVolume();
cmd->completed();
return true;
} else {
if(m_sampleSink != NULL)
return m_sampleSink->handleMessage(cmd);
else return false;
}
}

View File

@ -0,0 +1,104 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_NFMDEMOD_H
#define INCLUDE_NFMDEMOD_H
#include <vector>
#include "dsp/samplesink.h"
#include "dsp/nco.h"
#include "dsp/interpolator.h"
#include "dsp/lowpass.h"
#include "dsp/movingaverage.h"
#include "audio/audiofifo.h"
#include "util/message.h"
class AudioFifo;
class NFMDemod : public SampleSink {
public:
NFMDemod(AudioFifo* audioFifo, SampleSink* sampleSink);
~NFMDemod();
void configure(MessageQueue* messageQueue, Real rfBandwidth, Real afBandwidth, Real volume, Real squelch);
void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst);
void start();
void stop();
bool handleMessage(Message* cmd);
private:
class MsgConfigureNFMDemod : public Message {
MESSAGE_CLASS_DECLARATION
public:
Real getRFBandwidth() const { return m_rfBandwidth; }
Real getAFBandwidth() const { return m_afBandwidth; }
Real getVolume() const { return m_volume; }
Real getSquelch() const { return m_squelch; }
static MsgConfigureNFMDemod* create(Real rfBandwidth, Real afBandwidth, Real volume, Real squelch)
{
return new MsgConfigureNFMDemod(rfBandwidth, afBandwidth, volume, squelch);
}
private:
Real m_rfBandwidth;
Real m_afBandwidth;
Real m_volume;
Real m_squelch;
MsgConfigureNFMDemod(Real rfBandwidth, Real afBandwidth, Real volume, Real squelch) :
Message(),
m_rfBandwidth(rfBandwidth),
m_afBandwidth(afBandwidth),
m_volume(volume),
m_squelch(squelch)
{ }
};
struct AudioSample {
qint16 l;
qint16 r;
};
typedef std::vector<AudioSample> AudioVector;
Real m_rfBandwidth;
Real m_volume;
Real m_squelchLevel;
int m_sampleRate;
int m_frequency;
NCO m_nco;
Interpolator m_interpolator;
Real m_sampleDistanceRemain;
Lowpass<Real> m_lowpass;
int m_squelchState;
Complex m_lastSample;
MovingAverage m_movingAverage;
AudioVector m_audioBuffer;
uint m_audioBufferFill;
AudioFifo* m_audioFifo;
SampleSink* m_sampleSink;
SampleVector m_sampleBuffer;
};
#endif // INCLUDE_NFMDEMOD_H

View File

@ -0,0 +1,210 @@
#include <QDockWidget>
#include <QMainWindow>
#include "nfmdemodgui.h"
#include "ui_nfmdemodgui.h"
#include "nfmdemodgui.h"
#include "ui_nfmdemodgui.h"
#include "dsp/threadedsamplesink.h"
#include "dsp/channelizer.h"
#include "nfmdemod.h"
#include "dsp/spectrumvis.h"
#include "gui/glspectrum.h"
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
#include "gui/basicchannelsettingswidget.h"
const int NFMDemodGUI::m_rfBW[] = {
5000, 6250, 8330, 10000, 12500, 15000, 20000, 25000, 40000
};
NFMDemodGUI* NFMDemodGUI::create(PluginAPI* pluginAPI)
{
NFMDemodGUI* gui = new NFMDemodGUI(pluginAPI);
return gui;
}
void NFMDemodGUI::destroy()
{
delete this;
}
void NFMDemodGUI::setName(const QString& name)
{
setObjectName(name);
}
void NFMDemodGUI::resetToDefaults()
{
ui->rfBW->setValue(4);
ui->afBW->setValue(3);
ui->volume->setValue(20);
ui->squelch->setValue(-40);
ui->spectrumGUI->resetToDefaults();
applySettings();
}
QByteArray NFMDemodGUI::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->volume->value());
s.writeS32(5, ui->squelch->value());
s.writeBlob(6, ui->spectrumGUI->serialize());
s.writeU32(7, m_channelMarker->getColor().rgb());
return s.final();
}
bool NFMDemodGUI::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid()) {
resetToDefaults();
return false;
}
if(d.getVersion() == 1) {
QByteArray bytetmp;
quint32 u32tmp;
qint32 tmp;
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->volume->setValue(tmp);
d.readS32(5, &tmp, -40);
ui->squelch->setValue(tmp);
d.readBlob(6, &bytetmp);
ui->spectrumGUI->deserialize(bytetmp);
if(d.readU32(7, &u32tmp))
m_channelMarker->setColor(u32tmp);
applySettings();
return true;
} else {
resetToDefaults();
return false;
}
}
bool NFMDemodGUI::handleMessage(Message* message)
{
return false;
}
void NFMDemodGUI::viewChanged()
{
applySettings();
}
void NFMDemodGUI::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 NFMDemodGUI::on_afBW_valueChanged(int value)
{
ui->afBWText->setText(QString("%1 kHz").arg(value));
applySettings();
}
void NFMDemodGUI::on_volume_valueChanged(int value)
{
ui->volumeText->setText(QString("%1").arg(value / 10.0, 0, 'f', 1));
applySettings();
}
void NFMDemodGUI::on_squelch_valueChanged(int value)
{
ui->squelchText->setText(QString("%1 dB").arg(value));
applySettings();
}
void NFMDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
/*
if((widget == ui->spectrumContainer) && (m_nfmDemod != NULL))
m_nfmDemod->setSpectrum(m_threadedSampleSink->getMessageQueue(), rollDown);
*/
}
void NFMDemodGUI::onMenuDoubleClicked()
{
if(!m_basicSettingsShown) {
m_basicSettingsShown = true;
BasicChannelSettingsWidget* bcsw = new BasicChannelSettingsWidget(m_channelMarker, this);
bcsw->show();
}
}
NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, QWidget* parent) :
RollupWidget(parent),
ui(new Ui::NFMDemodGUI),
m_pluginAPI(pluginAPI),
m_basicSettingsShown(false)
{
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_audioFifo = new AudioFifo(4, 44100 / 4);
m_spectrumVis = new SpectrumVis(ui->glSpectrum);
m_nfmDemod = new NFMDemod(m_audioFifo, m_spectrumVis);
m_channelizer = new Channelizer(m_nfmDemod);
m_threadedSampleSink = new ThreadedSampleSink(m_channelizer);
m_pluginAPI->addAudioSource(m_audioFifo);
m_pluginAPI->addSampleSink(m_threadedSampleSink);
ui->glSpectrum->setCenterFrequency(0);
ui->glSpectrum->setSampleRate(44100);
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::red);
m_channelMarker->setBandwidth(12500);
m_channelMarker->setCenterFrequency(0);
m_channelMarker->setVisible(true);
connect(m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged()));
m_pluginAPI->addChannelMarker(m_channelMarker);
ui->spectrumGUI->setBuddies(m_threadedSampleSink->getMessageQueue(), m_spectrumVis, ui->glSpectrum);
applySettings();
}
NFMDemodGUI::~NFMDemodGUI()
{
m_pluginAPI->removeChannelInstance(this);
m_pluginAPI->removeAudioSource(m_audioFifo);
m_pluginAPI->removeSampleSink(m_threadedSampleSink);
delete m_threadedSampleSink;
delete m_channelizer;
delete m_nfmDemod;
delete m_spectrumVis;
delete m_audioFifo;
delete m_channelMarker;
delete ui;
}
void NFMDemodGUI::applySettings()
{
setTitleColor(m_channelMarker->getColor());
m_channelizer->configure(m_threadedSampleSink->getMessageQueue(),
44100,
m_channelMarker->getCenterFrequency());
m_nfmDemod->configure(m_threadedSampleSink->getMessageQueue(),
m_rfBW[ui->rfBW->value()],
ui->afBW->value() * 1000.0,
ui->volume->value() / 10.0,
ui->squelch->value());
}

View File

@ -0,0 +1,64 @@
#ifndef INCLUDE_NFMDEMODGUI_H
#define INCLUDE_NFMDEMODGUI_H
#include "gui/rollupwidget.h"
#include "plugin/plugingui.h"
class PluginAPI;
class ChannelMarker;
class AudioFifo;
class ThreadedSampleSink;
class Channelizer;
class NFMDemod;
class SpectrumVis;
namespace Ui {
class NFMDemodGUI;
}
class NFMDemodGUI : public RollupWidget, public PluginGUI {
Q_OBJECT
public:
static NFMDemodGUI* create(PluginAPI* pluginAPI);
void destroy();
void setName(const QString& name);
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
bool handleMessage(Message* message);
private slots:
void viewChanged();
void on_rfBW_valueChanged(int value);
void on_afBW_valueChanged(int value);
void on_volume_valueChanged(int value);
void on_squelch_valueChanged(int value);
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDoubleClicked();
private:
Ui::NFMDemodGUI* ui;
PluginAPI* m_pluginAPI;
ChannelMarker* m_channelMarker;
bool m_basicSettingsShown;
AudioFifo* m_audioFifo;
ThreadedSampleSink* m_threadedSampleSink;
Channelizer* m_channelizer;
NFMDemod* m_nfmDemod;
SpectrumVis* m_spectrumVis;
static const int m_rfBW[];
explicit NFMDemodGUI(PluginAPI* pluginAPI, QWidget* parent = NULL);
~NFMDemodGUI();
void applySettings();
};
#endif // INCLUDE_NFMDEMODGUI_H

View File

@ -0,0 +1,250 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NFMDemodGUI</class>
<widget class="RollupWidget" name="NFMDemodGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>302</width>
<height>410</height>
</rect>
</property>
<property name="windowTitle">
<string>NFM Demodulator</string>
</property>
<widget class="QWidget" name="settingsContainer" native="true">
<property name="geometry">
<rect>
<x>35</x>
<y>35</y>
<width>242</width>
<height>96</height>
</rect>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="margin">
<number>2</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>RF Bandwidth</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSlider" name="rfBW">
<property name="maximum">
<number>8</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>4</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="rfBWText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>12.5kHz</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>AF Bandwidth</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSlider" name="afBW">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>20</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>3</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="afBWText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>3 kHz</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Volume</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSlider" name="volume">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>20</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="volumeText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>2.0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Squelch</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSlider" name="squelch">
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>0</number>
</property>
<property name="value">
<number>-40</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="squelchText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>-40dB</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="spectrumContainer" native="true">
<property name="geometry">
<rect>
<x>40</x>
<y>140</y>
<width>218</width>
<height>184</height>
</rect>
</property>
<property name="windowTitle">
<string>Channel Spectrum</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="margin">
<number>3</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>
<widget class="GLSpectrumGUI" name="spectrumGUI" native="true"/>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>GLSpectrum</class>
<extends>QWidget</extends>
<header>gui/glspectrum.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>GLSpectrumGUI</class>
<extends>QWidget</extends>
<header>gui/glspectrumgui.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>RollupWidget</class>
<extends>QWidget</extends>
<header>gui/rollupwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,53 @@
#include <QtPlugin>
#include <QAction>
#include "plugin/pluginapi.h"
#include "nfmplugin.h"
#include "nfmdemodgui.h"
const PluginDescriptor NFMPlugin::m_pluginDescriptor = {
QString("NFM Demodulator"),
QString("---"),
QString("(c) maintech GmbH (written by Christian Daniel)"),
QString("http://www.maintech.de"),
true,
QString("http://www.maintech.de")
};
NFMPlugin::NFMPlugin(QObject* parent) :
QObject(parent)
{
}
const PluginDescriptor& NFMPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void NFMPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
// register NFM demodulator
QAction* action = new QAction(tr("&NFM Demodulator"), this);
connect(action, SIGNAL(triggered()), this, SLOT(createInstanceNFM()));
m_pluginAPI->registerChannel("de.maintech.sdrangelove.channel.nfm", this, action);
}
PluginGUI* NFMPlugin::createChannel(const QString& channelName)
{
if(channelName == "de.maintech.sdrangelove.channel.nfm") {
NFMDemodGUI* gui = NFMDemodGUI::create(m_pluginAPI);
m_pluginAPI->registerChannelInstance("de.maintech.sdrangelove.channel.nfm", gui);
m_pluginAPI->addChannelRollup(gui);
return gui;
} else {
return NULL;
}
}
void NFMPlugin::createInstanceNFM()
{
NFMDemodGUI* gui = NFMDemodGUI::create(m_pluginAPI);
m_pluginAPI->registerChannelInstance("de.maintech.sdrangelove.channel.nfm", gui);
m_pluginAPI->addChannelRollup(gui);
}

View File

@ -0,0 +1,29 @@
#ifndef INCLUDE_NFMPLUGIN_H
#define INCLUDE_NFMPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class NFMPlugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "de.maintech.sdrangelove.channel.nfm")
public:
explicit NFMPlugin(QObject* parent = NULL);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
PluginGUI* createChannel(const QString& channelName);
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
private slots:
void createInstanceNFM();
};
#endif // INCLUDE_NFMPLUGIN_H

View File

@ -0,0 +1,47 @@
project(tcpsrc)
set(tcpsrc_SOURCES
tcpsrc.cpp
tcpsrcgui.cpp
tcpsrcplugin.cpp
)
set(tcpsrc_HEADERS
tcpsrc.h
tcpsrcgui.h
tcpsrcplugin.h
)
set(tcpsrc_FORMS
tcpsrcgui.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(tcpsrc_HEADERS_MOC ${tcpsrc_HEADERS})
qt5_wrap_ui(tcpsrc_FORMS_HEADERS ${tcpsrc_FORMS})
add_library(demodtcpsrc SHARED
${tcpsrc_SOURCES}
${tcpsrc_HEADERS_MOC}
${tcpsrc_FORMS_HEADERS}
)
target_link_libraries(demodtcpsrc
${QT_LIBRARIES}
${OPENGL_LIBRARIES}
sdrbase
)
qt5_use_modules(demodtcpsrc Core Widgets OpenGL Network)

View File

@ -0,0 +1,205 @@
#include <QTcpServer>
#include <QTcpSocket>
#include <QThread>
#include "tcpsrc.h"
#include "tcpsrcgui.h"
#include "dsp/dspcommands.h"
MESSAGE_CLASS_DEFINITION(TCPSrc::MsgTCPSrcConfigure, Message)
MESSAGE_CLASS_DEFINITION(TCPSrc::MsgTCPSrcConnection, Message)
MESSAGE_CLASS_DEFINITION(TCPSrc::MsgTCPSrcSpectrum, Message)
TCPSrc::TCPSrc(MessageQueue* uiMessageQueue, TCPSrcGUI* tcpSrcGUI, SampleSink* spectrum)
{
m_inputSampleRate = 100000;
m_sampleFormat = FormatS8;
m_outputSampleRate = 50000;
m_rfBandwidth = 50000;
m_tcpPort = 9999;
m_nco.setFreq(0, m_inputSampleRate);
m_interpolator.create(16, m_inputSampleRate, m_rfBandwidth / 2.1);
m_sampleDistanceRemain = m_inputSampleRate / m_outputSampleRate;
m_uiMessageQueue = uiMessageQueue;
m_tcpSrcGUI = tcpSrcGUI;
m_spectrum = spectrum;
m_spectrumEnabled = false;
m_nextS8Id = 0;
m_nextS16leId = 0;
}
TCPSrc::~TCPSrc()
{
}
void TCPSrc::configure(MessageQueue* messageQueue, SampleFormat sampleFormat, Real outputSampleRate, Real rfBandwidth, int tcpPort)
{
Message* cmd = MsgTCPSrcConfigure::create(sampleFormat, outputSampleRate, rfBandwidth, tcpPort);
cmd->submit(messageQueue, this);
}
void TCPSrc::setSpectrum(MessageQueue* messageQueue, bool enabled)
{
Message* cmd = MsgTCPSrcSpectrum::create(enabled);
cmd->submit(messageQueue, this);
}
void TCPSrc::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst)
{
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 += m_inputSampleRate / m_outputSampleRate;
}
}
if((m_spectrum != NULL) && (m_spectrumEnabled))
m_spectrum->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), firstOfBurst);
for(int i = 0; i < m_s16leSockets.count(); i++)
m_s16leSockets[i].socket->write((const char*)&m_sampleBuffer[0], m_sampleBuffer.size() * 4);
if(m_s8Sockets.count() > 0) {
for(SampleVector::const_iterator it = m_sampleBuffer.begin(); it != m_sampleBuffer.end(); ++it) {
m_sampleBufferS8.push_back(it->real() >> 8);
m_sampleBufferS8.push_back(it->imag() >> 8);
}
for(int i = 0; i < m_s8Sockets.count(); i++)
m_s8Sockets[i].socket->write((const char*)&m_sampleBufferS8[0], m_sampleBufferS8.size());
}
m_sampleBuffer.clear();
m_sampleBufferS8.clear();
}
void TCPSrc::start()
{
m_tcpServer = new QTcpServer();
connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
m_tcpServer->listen(QHostAddress::Any, m_tcpPort);
}
void TCPSrc::stop()
{
closeAllSockets(&m_s8Sockets);
closeAllSockets(&m_s16leSockets);
if(m_tcpServer->isListening())
m_tcpServer->close();
delete m_tcpServer;
}
bool TCPSrc::handleMessage(Message* cmd)
{
if(DSPSignalNotification::match(cmd)) {
DSPSignalNotification* signal = (DSPSignalNotification*)cmd;
qDebug("%d samples/sec, %lld Hz offset", signal->getSampleRate(), signal->getFrequencyOffset());
m_inputSampleRate = signal->getSampleRate();
m_nco.setFreq(-signal->getFrequencyOffset(), m_inputSampleRate);
m_interpolator.create(16, m_inputSampleRate, m_rfBandwidth / 2.1);
m_sampleDistanceRemain = m_inputSampleRate / m_outputSampleRate;
cmd->completed();
return true;
} else if(DSPSignalNotification::match(cmd)) {
MsgTCPSrcConfigure* cfg = (MsgTCPSrcConfigure*)cmd;
m_sampleFormat = cfg->getSampleFormat();
m_outputSampleRate = cfg->getOutputSampleRate();
m_rfBandwidth = cfg->getRFBandwidth();
if(cfg->getTCPPort() != m_tcpPort) {
m_tcpPort = cfg->getTCPPort();
if(m_tcpServer->isListening())
m_tcpServer->close();
m_tcpServer->listen(QHostAddress::Any, m_tcpPort);
}
m_interpolator.create(16, m_inputSampleRate, m_rfBandwidth / 2.1);
m_sampleDistanceRemain = m_inputSampleRate / m_outputSampleRate;
cmd->completed();
return true;
} else if(MsgTCPSrcSpectrum::match(cmd)) {
MsgTCPSrcSpectrum* spc = (MsgTCPSrcSpectrum*)cmd;
m_spectrumEnabled = spc->getEnabled();
cmd->completed();
return true;
} else {
if(m_spectrum != NULL)
return m_spectrum->handleMessage(cmd);
else return false;
}
}
void TCPSrc::closeAllSockets(Sockets* sockets)
{
for(int i = 0; i < sockets->count(); ++i) {
MsgTCPSrcConnection* msg = MsgTCPSrcConnection::create(false, sockets->at(i).id, QHostAddress(), 0);
msg->submit(m_uiMessageQueue, (PluginGUI*)m_tcpSrcGUI);
sockets->at(i).socket->close();
}
}
void TCPSrc::onNewConnection()
{
while(m_tcpServer->hasPendingConnections()) {
QTcpSocket* connection = m_tcpServer->nextPendingConnection();
connect(connection, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
switch(m_sampleFormat) {
case FormatS8: {
quint32 id = (FormatS8 << 24) | m_nextS8Id;
MsgTCPSrcConnection* msg = MsgTCPSrcConnection::create(true, id, connection->peerAddress(), connection->peerPort());
m_nextS8Id = (m_nextS8Id + 1) & 0xffffff;
m_s8Sockets.push_back(Socket(id, connection));
msg->submit(m_uiMessageQueue, (PluginGUI*)m_tcpSrcGUI);
break;
}
case FormatS16LE: {
quint32 id = (FormatS16LE << 24) | m_nextS16leId;
MsgTCPSrcConnection* msg = MsgTCPSrcConnection::create(true, id, connection->peerAddress(), connection->peerPort());
m_nextS16leId = (m_nextS16leId + 1) & 0xffffff;
m_s16leSockets.push_back(Socket(id, connection));
msg->submit(m_uiMessageQueue, (PluginGUI*)m_tcpSrcGUI);
break;
}
default:
delete connection;
break;
}
}
}
void TCPSrc::onDisconnected()
{
quint32 id;
QTcpSocket* socket = NULL;
for(int i = 0; i < m_s8Sockets.count(); i++) {
if(m_s8Sockets[i].socket == sender()) {
id = m_s8Sockets[i].id;
socket = m_s8Sockets[i].socket;
m_s8Sockets.removeAt(i);
break;
}
}
if(socket == NULL) {
for(int i = 0; i < m_s16leSockets.count(); i++) {
if(m_s16leSockets[i].socket == sender()) {
id = m_s16leSockets[i].id;
socket = m_s16leSockets[i].socket;
m_s16leSockets.removeAt(i);
break;
}
}
}
if(socket != NULL) {
MsgTCPSrcConnection* msg = MsgTCPSrcConnection::create(false, id, QHostAddress(), 0);
msg->submit(m_uiMessageQueue, (PluginGUI*)m_tcpSrcGUI);
socket->deleteLater();
}
}

View File

@ -0,0 +1,153 @@
#ifndef INCLUDE_TCPSRC_H
#define INCLUDE_TCPSRC_H
#include <QHostAddress>
#include "dsp/samplesink.h"
#include "dsp/nco.h"
#include "dsp/interpolator.h"
#include "util/message.h"
class QTcpServer;
class QTcpSocket;
class TCPSrcGUI;
class TCPSrc : public SampleSink {
Q_OBJECT
public:
enum SampleFormat {
FormatS8,
FormatS16LE
};
TCPSrc(MessageQueue* uiMessageQueue, TCPSrcGUI* tcpSrcGUI, SampleSink* spectrum);
~TCPSrc();
void configure(MessageQueue* messageQueue, SampleFormat sampleFormat, Real outputSampleRate, Real rfBandwidth, int tcpPort);
void setSpectrum(MessageQueue* messageQueue, bool enabled);
void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst);
void start();
void stop();
bool handleMessage(Message* cmd);
class MsgTCPSrcConnection : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool getConnect() const { return m_connect; }
quint32 getID() const { return m_id; }
const QHostAddress& getPeerAddress() const { return m_peerAddress; }
int getPeerPort() const { return m_peerPort; }
static MsgTCPSrcConnection* create(bool connect, quint32 id, const QHostAddress& peerAddress, int peerPort)
{
return new MsgTCPSrcConnection(connect, id, peerAddress, peerPort);
}
private:
bool m_connect;
quint32 m_id;
QHostAddress m_peerAddress;
int m_peerPort;
MsgTCPSrcConnection(bool connect, quint32 id, const QHostAddress& peerAddress, int peerPort) :
Message(),
m_connect(connect),
m_id(id),
m_peerAddress(peerAddress),
m_peerPort(peerPort)
{ }
};
protected:
class MsgTCPSrcConfigure : public Message {
MESSAGE_CLASS_DECLARATION
public:
SampleFormat getSampleFormat() const { return m_sampleFormat; }
Real getOutputSampleRate() const { return m_outputSampleRate; }
Real getRFBandwidth() const { return m_rfBandwidth; }
int getTCPPort() const { return m_tcpPort; }
static MsgTCPSrcConfigure* create(SampleFormat sampleFormat, Real sampleRate, Real rfBandwidth, int tcpPort)
{
return new MsgTCPSrcConfigure(sampleFormat, sampleRate, rfBandwidth, tcpPort);
}
private:
SampleFormat m_sampleFormat;
Real m_outputSampleRate;
Real m_rfBandwidth;
int m_tcpPort;
MsgTCPSrcConfigure(SampleFormat sampleFormat, Real outputSampleRate, Real rfBandwidth, int tcpPort) :
Message(),
m_sampleFormat(sampleFormat),
m_outputSampleRate(outputSampleRate),
m_rfBandwidth(rfBandwidth),
m_tcpPort(tcpPort)
{ }
};
class MsgTCPSrcSpectrum : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool getEnabled() const { return m_enabled; }
static MsgTCPSrcSpectrum* create(bool enabled)
{
return new MsgTCPSrcSpectrum(enabled);
}
private:
bool m_enabled;
MsgTCPSrcSpectrum(bool enabled) :
Message(),
m_enabled(enabled)
{ }
};
MessageQueue* m_uiMessageQueue;
TCPSrcGUI* m_tcpSrcGUI;
int m_inputSampleRate;
int m_sampleFormat;
Real m_outputSampleRate;
Real m_rfBandwidth;
int m_tcpPort;
NCO m_nco;
Interpolator m_interpolator;
Real m_sampleDistanceRemain;
SampleVector m_sampleBuffer;
std::vector<qint8> m_sampleBufferS8;
SampleSink* m_spectrum;
bool m_spectrumEnabled;
QTcpServer* m_tcpServer;
struct Socket {
quint32 id;
QTcpSocket* socket;
Socket(quint32 _id, QTcpSocket* _socket) :
id(_id),
socket(_socket)
{ }
};
typedef QList<Socket> Sockets;
Sockets m_s8Sockets;
Sockets m_s16leSockets;
quint32 m_nextS8Id;
quint32 m_nextS16leId;
void closeAllSockets(Sockets* sockets);
protected slots:
void onNewConnection();
void onDisconnected();
};
#endif // INCLUDE_TCPSRC_H

View File

@ -0,0 +1,278 @@
#include "tcpsrcgui.h"
#include "plugin/pluginapi.h"
#include "tcpsrc.h"
#include "dsp/channelizer.h"
#include "dsp/spectrumvis.h"
#include "dsp/threadedsamplesink.h"
#include "util/simpleserializer.h"
#include "gui/basicchannelsettingswidget.h"
#include "ui_tcpsrcgui.h"
TCPSrcGUI* TCPSrcGUI::create(PluginAPI* pluginAPI)
{
TCPSrcGUI* gui = new TCPSrcGUI(pluginAPI);
return gui;
}
void TCPSrcGUI::destroy()
{
delete this;
}
void TCPSrcGUI::setName(const QString& name)
{
setObjectName(name);
}
void TCPSrcGUI::resetToDefaults()
{
ui->sampleFormat->setCurrentIndex(0);
ui->sampleRate->setText("25000");
ui->rfBandwidth->setText("20000");
ui->tcpPort->setText("9999");
ui->spectrumGUI->resetToDefaults();
applySettings();
}
QByteArray TCPSrcGUI::serialize() const
{
SimpleSerializer s(1);
s.writeBlob(1, saveState());
s.writeS32(2, m_channelMarker->getCenterFrequency());
s.writeS32(3, m_sampleFormat);
s.writeReal(4, m_outputSampleRate);
s.writeReal(5, m_rfBandwidth);
s.writeS32(6, m_tcpPort);
s.writeBlob(7, ui->spectrumGUI->serialize());
s.writeU32(8, m_channelMarker->getColor().rgb());
return s.final();
}
bool TCPSrcGUI::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid()) {
resetToDefaults();
return false;
}
if(d.getVersion() == 1) {
QByteArray bytetmp;
qint32 s32tmp;
quint32 u32tmp;
Real realtmp;
d.readBlob(1, &bytetmp);
restoreState(bytetmp);
d.readS32(2, &s32tmp, 0);
m_channelMarker->setCenterFrequency(s32tmp);
d.readS32(3, &s32tmp, TCPSrc::FormatS8);
switch(s32tmp) {
case TCPSrc::FormatS8:
ui->sampleFormat->setCurrentIndex(0);
break;
case TCPSrc::FormatS16LE:
ui->sampleFormat->setCurrentIndex(1);
break;
default:
ui->sampleFormat->setCurrentIndex(0);
break;
}
d.readReal(4, &realtmp, 25000);
ui->sampleRate->setText(QString("%1").arg(realtmp, 0));
d.readReal(5, &realtmp, 20000);
ui->rfBandwidth->setText(QString("%1").arg(realtmp, 0));
d.readS32(6, &s32tmp, 9999);
ui->tcpPort->setText(QString("%1").arg(s32tmp));
d.readBlob(7, &bytetmp);
ui->spectrumGUI->deserialize(bytetmp);
if(d.readU32(8, &u32tmp))
m_channelMarker->setColor(u32tmp);
applySettings();
return true;
} else {
resetToDefaults();
return false;
}
}
bool TCPSrcGUI::handleMessage(Message* message)
{
if(TCPSrc::MsgTCPSrcConnection::match(message)) {
TCPSrc::MsgTCPSrcConnection* con = (TCPSrc::MsgTCPSrcConnection*)message;
if(con->getConnect())
addConnection(con->getID(), con->getPeerAddress(), con->getPeerPort());
else delConnection(con->getID());
message->completed();
return true;
} else {
return false;
}
}
void TCPSrcGUI::channelMarkerChanged()
{
applySettings();
}
TCPSrcGUI::TCPSrcGUI(PluginAPI* pluginAPI, QWidget* parent) :
RollupWidget(parent),
ui(new Ui::TCPSrcGUI),
m_pluginAPI(pluginAPI),
m_tcpSrc(NULL),
m_basicSettingsShown(false)
{
ui->setupUi(this);
ui->connectedClientsBox->hide();
connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked()));
setAttribute(Qt::WA_DeleteOnClose, true);
m_spectrumVis = new SpectrumVis(ui->glSpectrum);
m_tcpSrc = new TCPSrc(m_pluginAPI->getMainWindowMessageQueue(), this, m_spectrumVis);
m_channelizer = new Channelizer(m_tcpSrc);
m_threadedSampleSink = new ThreadedSampleSink(m_channelizer);
m_pluginAPI->addSampleSink(m_threadedSampleSink);
ui->glSpectrum->setCenterFrequency(0);
ui->glSpectrum->setSampleRate(ui->sampleRate->text().toInt());
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->setBandwidth(25000);
m_channelMarker->setCenterFrequency(0);
m_channelMarker->setVisible(true);
connect(m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged()));
m_pluginAPI->addChannelMarker(m_channelMarker);
ui->spectrumGUI->setBuddies(m_threadedSampleSink->getMessageQueue(), m_spectrumVis, ui->glSpectrum);
applySettings();
}
TCPSrcGUI::~TCPSrcGUI()
{
m_pluginAPI->removeChannelInstance(this);
m_pluginAPI->removeSampleSink(m_threadedSampleSink);
delete m_threadedSampleSink;
delete m_channelizer;
delete m_tcpSrc;
delete m_spectrumVis;
delete m_channelMarker;
delete ui;
}
void TCPSrcGUI::applySettings()
{
bool ok;
Real outputSampleRate = ui->sampleRate->text().toDouble(&ok);
if((!ok) || (outputSampleRate < 100))
outputSampleRate = 25000;
Real rfBandwidth = ui->rfBandwidth->text().toDouble(&ok);
if((!ok) || (rfBandwidth > outputSampleRate))
rfBandwidth = outputSampleRate;
int tcpPort = ui->tcpPort->text().toInt(&ok);
if((!ok) || (tcpPort < 1) || (tcpPort > 65535))
tcpPort = 9999;
setTitleColor(m_channelMarker->getColor());
ui->sampleRate->setText(QString("%1").arg(outputSampleRate, 0));
ui->rfBandwidth->setText(QString("%1").arg(rfBandwidth, 0));
ui->tcpPort->setText(QString("%1").arg(tcpPort));
m_channelMarker->disconnect(this, SLOT(channelMarkerChanged()));
m_channelMarker->setBandwidth((int)rfBandwidth);
connect(m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged()));
ui->glSpectrum->setSampleRate(outputSampleRate);
m_channelizer->configure(m_threadedSampleSink->getMessageQueue(),
outputSampleRate,
m_channelMarker->getCenterFrequency());
TCPSrc::SampleFormat sampleFormat;
switch(ui->sampleFormat->currentIndex()) {
case 0:
sampleFormat = TCPSrc::FormatS8;
break;
case 1:
sampleFormat = TCPSrc::FormatS16LE;
break;
default:
sampleFormat = TCPSrc::FormatS8;
break;
}
m_sampleFormat = sampleFormat;
m_outputSampleRate = outputSampleRate;
m_rfBandwidth = rfBandwidth;
m_tcpPort = tcpPort;
m_tcpSrc->configure(m_threadedSampleSink->getMessageQueue(),
sampleFormat,
outputSampleRate,
rfBandwidth,
tcpPort);
ui->applyBtn->setEnabled(false);
}
void TCPSrcGUI::on_sampleFormat_currentIndexChanged(int index)
{
ui->applyBtn->setEnabled(true);
}
void TCPSrcGUI::on_sampleRate_textEdited(const QString& arg1)
{
ui->applyBtn->setEnabled(true);
}
void TCPSrcGUI::on_rfBandwidth_textEdited(const QString& arg1)
{
ui->applyBtn->setEnabled(true);
}
void TCPSrcGUI::on_tcpPort_textEdited(const QString& arg1)
{
ui->applyBtn->setEnabled(true);
}
void TCPSrcGUI::on_applyBtn_clicked()
{
applySettings();
}
void TCPSrcGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
if((widget == ui->spectrumBox) && (m_tcpSrc != NULL))
m_tcpSrc->setSpectrum(m_threadedSampleSink->getMessageQueue(), rollDown);
}
void TCPSrcGUI::onMenuDoubleClicked()
{
if(!m_basicSettingsShown) {
m_basicSettingsShown = true;
BasicChannelSettingsWidget* bcsw = new BasicChannelSettingsWidget(m_channelMarker, this);
bcsw->show();
}
}
void TCPSrcGUI::addConnection(quint32 id, const QHostAddress& peerAddress, int peerPort)
{
QStringList l;
l.append(QString("%1:%2").arg(peerAddress.toString()).arg(peerPort));
new QTreeWidgetItem(ui->connections, l, id);
ui->connectedClientsBox->setWindowTitle(tr("Connected Clients (%1)").arg(ui->connections->topLevelItemCount()));
}
void TCPSrcGUI::delConnection(quint32 id)
{
for(int i = 0; i < ui->connections->topLevelItemCount(); i++) {
if(ui->connections->topLevelItem(i)->type() == id) {
delete ui->connections->topLevelItem(i);
ui->connectedClientsBox->setWindowTitle(tr("Connected Clients (%1)").arg(ui->connections->topLevelItemCount()));
return;
}
}
}

View File

@ -0,0 +1,72 @@
#ifndef INCLUDE_TCPSRCGUI_H
#define INCLUDE_TCPSRCGUI_H
#include <QHostAddress>
#include "gui/rollupwidget.h"
#include "plugin/plugingui.h"
#include "tcpsrc.h"
class PluginAPI;
class ChannelMarker;
class ThreadedSampleSink;
class Channelizer;
class TCPSrc;
class SpectrumVis;
namespace Ui {
class TCPSrcGUI;
}
class TCPSrcGUI : public RollupWidget, public PluginGUI {
Q_OBJECT
public:
static TCPSrcGUI* create(PluginAPI* pluginAPI);
void destroy();
void setName(const QString& name);
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
bool handleMessage(Message* message);
private slots:
void channelMarkerChanged();
void on_sampleFormat_currentIndexChanged(int index);
void on_sampleRate_textEdited(const QString& arg1);
void on_rfBandwidth_textEdited(const QString& arg1);
void on_tcpPort_textEdited(const QString& arg1);
void on_applyBtn_clicked();
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDoubleClicked();
private:
Ui::TCPSrcGUI* ui;
PluginAPI* m_pluginAPI;
ChannelMarker* m_channelMarker;
// settings
TCPSrc::SampleFormat m_sampleFormat;
Real m_outputSampleRate;
Real m_rfBandwidth;
int m_tcpPort;
bool m_basicSettingsShown;
// RF path
ThreadedSampleSink* m_threadedSampleSink;
Channelizer* m_channelizer;
TCPSrc* m_tcpSrc;
SpectrumVis* m_spectrumVis;
explicit TCPSrcGUI(PluginAPI* pluginAPI, QWidget* parent = NULL);
~TCPSrcGUI();
void applySettings();
void addConnection(quint32 id, const QHostAddress& peerAddress, int peerPort);
void delConnection(quint32 id);
};
#endif // INCLUDE_TCPSRCGUI_H

View File

@ -0,0 +1,213 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TCPSrcGUI</class>
<widget class="RollupWidget" name="TCPSrcGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>443</height>
</rect>
</property>
<property name="windowTitle">
<string>TCP Sample Source</string>
</property>
<widget class="QWidget" name="widget" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>5</y>
<width>201</width>
<height>142</height>
</rect>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="margin">
<number>2</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Sample Format</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QComboBox" name="sampleFormat">
<item>
<property name="text">
<string>S8 I/Q</string>
</property>
</item>
<item>
<property name="text">
<string>S16LE I/Q</string>
</property>
</item>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="rfBandwidth">
<property name="text">
<string>20000</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_3">
<property name="text">
<string>RF Bandwidth (Hz)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Samplerate (Hz)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="sampleRate">
<property name="text">
<string>25000</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>TCP Port</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLineEdit" name="tcpPort">
<property name="text">
<string>9999</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QPushButton" name="applyBtn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Apply</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="spectrumBox" native="true">
<property name="geometry">
<rect>
<x>15</x>
<y>160</y>
<width>231</width>
<height>156</height>
</rect>
</property>
<property name="windowTitle">
<string>Channel Spectrum</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>3</number>
</property>
<property name="margin">
<number>2</number>
</property>
<item>
<widget class="GLSpectrum" name="glSpectrum" native="true"/>
</item>
<item>
<widget class="GLSpectrumGUI" name="spectrumGUI" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="connectedClientsBox" native="true">
<property name="geometry">
<rect>
<x>15</x>
<y>330</y>
<width>274</width>
<height>101</height>
</rect>
</property>
<property name="windowTitle">
<string>Connected Clients (0)</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="margin">
<number>2</number>
</property>
<item>
<widget class="QTreeWidget" name="connections">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>100</height>
</size>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="itemsExpandable">
<bool>false</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>IP:Port</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>GLSpectrum</class>
<extends>QWidget</extends>
<header>gui/glspectrum.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>GLSpectrumGUI</class>
<extends>QWidget</extends>
<header>gui/glspectrumgui.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>RollupWidget</class>
<extends>QWidget</extends>
<header>gui/rollupwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>sampleFormat</tabstop>
<tabstop>tcpPort</tabstop>
<tabstop>sampleRate</tabstop>
<tabstop>rfBandwidth</tabstop>
<tabstop>applyBtn</tabstop>
<tabstop>connections</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,53 @@
#include <QtPlugin>
#include <QAction>
#include "plugin/pluginapi.h"
#include "tcpsrcplugin.h"
#include "tcpsrcgui.h"
const PluginDescriptor TCPSrcPlugin::m_pluginDescriptor = {
QString("TCP Channel Source"),
QString("---"),
QString("(c) maintech GmbH (written by Christian Daniel)"),
QString("http://www.maintech.de"),
true,
QString("http://www.maintech.de")
};
TCPSrcPlugin::TCPSrcPlugin(QObject* parent) :
QObject(parent)
{
}
const PluginDescriptor& TCPSrcPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void TCPSrcPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
// register TCP Channel Source
QAction* action = new QAction(tr("&TCP Source"), this);
connect(action, SIGNAL(triggered()), this, SLOT(createInstanceTCPSrc()));
m_pluginAPI->registerChannel("de.maintech.sdrangelove.channel.tcpsrc", this, action);
}
PluginGUI* TCPSrcPlugin::createChannel(const QString& channelName)
{
if(channelName == "de.maintech.sdrangelove.channel.tcpsrc") {
TCPSrcGUI* gui = TCPSrcGUI::create(m_pluginAPI);
m_pluginAPI->registerChannelInstance("de.maintech.sdrangelove.channel.tcpsrc", gui);
m_pluginAPI->addChannelRollup(gui);
return gui;
} else {
return NULL;
}
}
void TCPSrcPlugin::createInstanceTCPSrc()
{
TCPSrcGUI* gui = TCPSrcGUI::create(m_pluginAPI);
m_pluginAPI->registerChannelInstance("de.maintech.sdrangelove.channel.tcpsrc", gui);
m_pluginAPI->addChannelRollup(gui);
}

View File

@ -0,0 +1,29 @@
#ifndef INCLUDE_TCPSRCPLUGIN_H
#define INCLUDE_TCPSRCPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class TCPSrcPlugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "de.maintech.sdrangelove.demod.tcpsrc")
public:
explicit TCPSrcPlugin(QObject* parent = NULL);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
PluginGUI* createChannel(const QString& channelName);
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
private slots:
void createInstanceTCPSrc();
};
#endif // INCLUDE_TCPSRCPLUGIN_H

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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

View File

@ -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());
}

View File

@ -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

View File

@ -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>

View File

@ -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));
}

View File

@ -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

View File

@ -0,0 +1,15 @@
project(samplesource)
find_package(LibUSB)
find_package(LibOsmoSDR)
find_package(LibRTLSDR)
add_subdirectory(gnuradio)
if(LIBUSB_FOUND AND LIBOSMOSDR_FOUND)
add_subdirectory(osmosdr)
endif(LIBUSB_FOUND AND LIBOSMOSDR_FOUND)
if(LIBUSB_FOUND AND LIBRTLSDR_FOUND)
add_subdirectory(rtlsdr)
endif(LIBUSB_FOUND AND LIBRTLSDR_FOUND)

View File

@ -0,0 +1,87 @@
project(gnuradio)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules)
find_package(GnuradioRuntime)
find_package(GnuradioOsmosdr)
if(UNIX AND NOT BOOST_ROOT AND EXISTS "/usr/lib64")
list(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix
endif(UNIX AND NOT BOOST_ROOT AND EXISTS "/usr/lib64")
find_package(Boost COMPONENTS system)
if(NOT GNURADIO_RUNTIME_FOUND AND GNURADIO_OSMOSDR_FOUND)
message(STATUS "GNU Radio not found, assuming built-in gr-osmosdr runtime.")
set(GNURADIO_RUNTIME_FOUND TRUE)
set(GNURADIO_RUNTIME_INCLUDE_DIRS "")
set(GNURADIO_RUNTIME_LIBRARIES "")
FOREACH(inc ${GNURADIO_OSMOSDR_INCLUDE_DIRS})
LIST(APPEND GNURADIO_RUNTIME_INCLUDE_DIRS "${inc}/osmosdr/runtime")
ENDFOREACH(inc)
LIST(APPEND GNURADIO_RUNTIME_LIBRARIES ${GNURADIO_OSMOSDR_LIBRARIES})
endif(NOT GNURADIO_RUNTIME_FOUND AND GNURADIO_OSMOSDR_FOUND)
if(Boost_FOUND AND GNURADIO_RUNTIME_FOUND AND GNURADIO_OSMOSDR_FOUND)
set(gnuradio_SOURCES
gnuradiogui.cpp
gnuradioinput.cpp
gnuradioplugin.cpp
gnuradiothread.cpp
)
set(gnuradio_HEADERS
gnuradiogui.h
gnuradioinput.h
gnuradioplugin.h
gnuradiothread.h
)
set(gnuradio_FORMS
gnuradiogui.ui
)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/include-gpl
${Boost_INCLUDE_DIRS}
${GNURADIO_RUNTIME_INCLUDE_DIRS}
${GNURADIO_OSMOSDR_INCLUDE_DIRS}
)
add_definitions(${QT_DEFINITIONS})
add_definitions(-DQT_PLUGIN)
add_definitions(-DQT_SHARED)
add_definitions(-DBOOST_ALL_NO_LIB)
if(MSVC)
add_definitions(-DNOMINMAX)
endif()
qt5_wrap_ui(gnuradio_FORMS_HEADERS ${gnuradio_FORMS})
add_library(inputgnuradio SHARED
${gnuradio_SOURCES}
${gnuradio_HEADERS_MOC}
${gnuradio_FORMS_HEADERS}
)
target_link_libraries(inputgnuradio
${QT_LIBRARIES}
${Boost_LIBRARIES}
${GNURADIO_RUNTIME_LIBRARIES}
${GNURADIO_OSMOSDR_LIBRARIES}
sdrbase
)
qt5_use_modules(inputgnuradio Core Widgets OpenGL Multimedia)
endif(Boost_FOUND AND GNURADIO_RUNTIME_FOUND AND GNURADIO_OSMOSDR_FOUND)

View File

@ -0,0 +1,29 @@
INCLUDE(FindPkgConfig)
PKG_CHECK_MODULES(PC_GNURADIO_OSMOSDR gnuradio-osmosdr)
FIND_PATH(
GNURADIO_OSMOSDR_INCLUDE_DIRS
NAMES osmosdr/api.h
HINTS $ENV{GNURADIO_OSMOSDR_DIR}/include
${PC_GNURADIO_OSMOSDR_INCLUDEDIR}
${CMAKE_INSTALL_PREFIX}/include
PATHS /usr/local/include
/usr/include
)
FIND_LIBRARY(
GNURADIO_OSMOSDR_LIBRARIES
NAMES gnuradio-osmosdr
HINTS $ENV{GNURADIO_OSMOSDR_DIR}/lib
${PC_GNURADIO_OSMOSDR_LIBDIR}
${CMAKE_INSTALL_PREFIX}/lib64
${CMAKE_INSTALL_PREFIX}/lib
PATHS /usr/local/lib
/usr/local/lib64
/usr/lib
/usr/lib64
)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_OSMOSDR DEFAULT_MSG GNURADIO_OSMOSDR_LIBRARIES GNURADIO_OSMOSDR_INCLUDE_DIRS)
MARK_AS_ADVANCED(GNURADIO_OSMOSDR_LIBRARIES GNURADIO_OSMOSDR_INCLUDE_DIRS)

View File

@ -0,0 +1,29 @@
INCLUDE(FindPkgConfig)
PKG_CHECK_MODULES(PC_GNURADIO_RUNTIME gnuradio-runtime)
FIND_PATH(
GNURADIO_RUNTIME_INCLUDE_DIRS
NAMES gnuradio/api.h
HINTS $ENV{GNURADIO_RUNTIME_DIR}/include
${PC_GNURADIO_RUNTIME_INCLUDEDIR}
${CMAKE_INSTALL_PREFIX}/include
PATHS /usr/local/include
/usr/include
)
FIND_LIBRARY(
GNURADIO_RUNTIME_LIBRARIES
NAMES gnuradio-runtime
HINTS $ENV{GNURADIO_RUNTIME_DIR}/lib
${PC_GNURADIO_RUNTIME_LIBDIR}
${CMAKE_INSTALL_PREFIX}/lib64
${CMAKE_INSTALL_PREFIX}/lib
PATHS /usr/local/lib
/usr/local/lib64
/usr/lib
/usr/lib64
)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_RUNTIME DEFAULT_MSG GNURADIO_RUNTIME_LIBRARIES GNURADIO_RUNTIME_INCLUDE_DIRS)
MARK_AS_ADVANCED(GNURADIO_RUNTIME_LIBRARIES GNURADIO_RUNTIME_INCLUDE_DIRS)

View File

@ -0,0 +1,422 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// Copyright (C) 2013 by Dimitri Stolnikov <horiz0n@gmx.net> //
// //
// 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 "gnuradiogui.h"
#include "ui_gnuradiogui.h"
#include <osmosdr/device.h>
#include <iostream>
#include <plugin/pluginapi.h>
#include <QHBoxLayout>
#include <QLabel>
#include <QSlider>
GNURadioGui::GNURadioGui(PluginAPI* pluginAPI, QWidget* parent) :
QWidget(parent),
ui(new Ui::GNURadioGui),
m_pluginAPI(pluginAPI),
m_settings(),
m_sampleSource(NULL)
{
ui->setupUi(this);
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
displaySettings();
m_sampleSource = new GNURadioInput(m_pluginAPI->getMainWindowMessageQueue());
m_pluginAPI->setSampleSource(m_sampleSource);
}
GNURadioGui::~GNURadioGui()
{
delete ui;
}
void GNURadioGui::destroy()
{
delete this;
}
void GNURadioGui::setName(const QString& name)
{
setObjectName(name);
}
void GNURadioGui::resetToDefaults()
{
m_generalSettings.resetToDefaults();
m_settings.resetToDefaults();
displaySettings();
sendSettings();
}
QByteArray GNURadioGui::serializeGeneral() const
{
return m_generalSettings.serialize();
}
bool GNURadioGui::deserializeGeneral(const QByteArray&data)
{
if(m_generalSettings.deserialize(data)) {
displaySettings();
sendSettings();
return true;
} else {
resetToDefaults();
return false;
}
}
quint64 GNURadioGui::getCenterFrequency() const
{
return m_generalSettings.m_centerFrequency;
}
QByteArray GNURadioGui::serialize() const
{
return m_settings.serialize();
}
bool GNURadioGui::deserialize(const QByteArray& data)
{
if(m_settings.deserialize(data)) {
displaySettings();
sendSettings();
return true;
} else {
resetToDefaults();
return false;
}
}
bool GNURadioGui::handleMessage(Message* message)
{
if(GNURadioInput::MsgReportGNURadio::match(message)) {
GNURadioInput::MsgReportGNURadio* rep = (GNURadioInput::MsgReportGNURadio*)message;
m_namedGains = rep->getNamedGains();
m_freqMin = rep->getFreqMin();
m_freqMax = rep->getFreqMax();
m_freqCorr = rep->getFreqCorr();
m_sampRates = rep->getSampRates();
m_antennas = rep->getAntennas();
m_dcoffs = rep->getDCOffs();
m_iqbals = rep->getIQBals();
m_bandwidths = rep->getBandwidths();
/* insert 0 which will become "Auto" in the combo box */
m_bandwidths.insert(m_bandwidths.begin(), 0);
displaySettings();
return true;
} else {
return false;
}
}
void GNURadioGui::displaySettings()
{
int oldIndex = 0;
oldIndex = ui->cboDevices->currentIndex();
ui->cboDevices->clear();
QString oldArgs = ui->txtDeviceArgs->text();
osmosdr::devices_t devices = osmosdr::device::find();
for ( int i = 0; i < devices.size(); i++ )
{
osmosdr::device_t dev = devices[i];
QString label;
if ( dev.count( "label" ) )
{
label = QString(dev[ "label" ].c_str());
dev.erase("label");
}
QPair< QString, QString > pair(label, dev.to_string().c_str());
m_devs.append(pair);
ui->cboDevices->addItem(label);
}
if ( ui->cboDevices->count() && oldIndex >= 0 )
{
if ( oldIndex > ui->cboDevices->count() - 1 )
oldIndex = 0;
ui->cboDevices->setCurrentIndex(oldIndex);
if ( oldArgs.length() == 0 )
ui->txtDeviceArgs->setText( m_devs[oldIndex].second );
}
if ( oldArgs.length() )
ui->txtDeviceArgs->setText( oldArgs );
ui->centerFrequency->setValueRange(7,
unsigned(m_freqMin / 1000.0),
unsigned(m_freqMax / 1000.0));
ui->centerFrequency->setValue(m_generalSettings.m_centerFrequency / 1000);
ui->sldFreqCorr->setRange(-100, +100);
ui->sldFreqCorr->setValue( m_freqCorr );
ui->lblFreqCorr->setText(tr("%1").arg(ui->sldFreqCorr->value()));
m_gainControls.clear();
QVBoxLayout *layoutGains = ui->verticalLayoutGains;
QLayoutItem *layoutItem;
while ( ( layoutItem = layoutGains->takeAt( 0 ) ) != NULL )
{
QLayout *layout = layoutItem->layout();
if ( !layout )
continue;
while ( ( layoutItem = layout->takeAt( 0 ) ) != NULL )
{
delete layoutItem->widget();
delete layoutItem;
}
delete layout;
}
for ( int i = 0; i < m_namedGains.size(); i++ )
{
std::pair< QString, std::vector<double> > pair = m_namedGains[i];
QHBoxLayout *layout = new QHBoxLayout();
QLabel *gainName = new QLabel( pair.first + " Gain" );
QSlider *gainSlider = new QSlider(Qt::Horizontal);
QLabel *gainLabel = new QLabel("0");
gainLabel->setMinimumWidth(30);
gainLabel->setAlignment(Qt::AlignHCenter | Qt::AlignHCenter);
QPair< QSlider*, QLabel* > pair2( gainSlider, gainLabel );
m_gainControls.push_back( pair2 );
connect(gainSlider, SIGNAL(valueChanged(int)),
this, SLOT(on_sldGain_valueChanged(int)));
layout->addWidget(gainName);
layout->addWidget(gainSlider);
layout->addWidget(gainLabel);
layoutGains->addLayout(layout);
std::vector<double> gain_values = pair.second;
if ( gain_values.size() ) {
gainSlider->setRange(0, gain_values.size() - 1);
gainSlider->setValue(gain_values.size() / 4);
gainSlider->setEnabled(true);
} else {
gainSlider->setEnabled(false);
}
}
oldIndex = ui->cboSampleRate->currentIndex();
ui->cboSampleRate->clear();
for ( int i = 0; i < m_sampRates.size(); i++ )
ui->cboSampleRate->addItem( QString::number(m_sampRates[i] / 1e6) );
if ( oldIndex > ui->cboSampleRate->count() - 1 )
oldIndex = 0;
if ( ui->cboSampleRate->count() && oldIndex >= 0 )
ui->cboSampleRate->setCurrentIndex(oldIndex);
if ( ui->cboSampleRate->count() ) {
ui->cboSampleRate->setEnabled(true);
} else {
ui->cboSampleRate->setEnabled(false);
}
oldIndex = ui->cboAntennas->currentIndex();
ui->cboAntennas->clear();
if ( m_antennas.size() ) {
for ( int i = 0; i < m_antennas.size(); i++ )
ui->cboAntennas->addItem( m_antennas[i] );
if ( oldIndex > ui->cboAntennas->count() - 1 )
oldIndex = 0;
if ( ui->cboAntennas->count() && oldIndex >= 0 )
ui->cboAntennas->setCurrentIndex(oldIndex);
ui->cboAntennas->setEnabled(true);
} else {
ui->cboAntennas->setEnabled(false);
}
oldIndex = ui->cboDCOffset->currentIndex();
ui->cboDCOffset->clear();
if ( m_dcoffs.size() ) {
for ( int i = 0; i < m_dcoffs.size(); i++ )
ui->cboDCOffset->addItem( m_dcoffs[i] );
if ( ui->cboDCOffset->count() && oldIndex >= 0 )
ui->cboDCOffset->setCurrentIndex(oldIndex);
ui->cboDCOffset->setEnabled(true);
} else {
ui->cboDCOffset->setEnabled(false);
}
oldIndex = ui->cboIQBalance->currentIndex();
ui->cboIQBalance->clear();
if ( m_iqbals.size() ) {
for ( int i = 0; i < m_iqbals.size(); i++ )
ui->cboIQBalance->addItem( m_iqbals[i] );
if ( ui->cboIQBalance->count() && oldIndex >= 0 )
ui->cboIQBalance->setCurrentIndex(oldIndex);
ui->cboIQBalance->setEnabled(true);
} else {
ui->cboIQBalance->setEnabled(false);
}
oldIndex = ui->cboBandwidth->currentIndex();
ui->cboBandwidth->clear();
for ( int i = 0; i < m_bandwidths.size(); i++ )
if ( 0.0 == m_bandwidths[i] )
ui->cboBandwidth->addItem( "Auto" );
else
ui->cboBandwidth->addItem( QString::number(m_bandwidths[i] / 1e6) );
if ( oldIndex > ui->cboBandwidth->count() - 1 )
oldIndex = 0;
if ( ui->cboBandwidth->count() && oldIndex >= 0 )
ui->cboBandwidth->setCurrentIndex(oldIndex);
if ( ui->cboBandwidth->count() ) {
ui->cboBandwidth->setEnabled(true);
} else {
ui->cboBandwidth->setEnabled(false);
}
}
void GNURadioGui::sendSettings()
{
if(!m_updateTimer.isActive())
m_updateTimer.start(100);
}
void GNURadioGui::updateHardware()
{
m_updateTimer.stop();
GNURadioInput::MsgConfigureGNURadio* msg = GNURadioInput::MsgConfigureGNURadio::create(m_generalSettings, m_settings);
msg->submit(m_pluginAPI->getDSPEngineMessageQueue());
}
void GNURadioGui::on_cboDevices_currentIndexChanged(int index)
{
if ( index < 0 || index >= m_devs.count() )
return;
ui->txtDeviceArgs->setText( m_devs[index].second );
}
void GNURadioGui::on_txtDeviceArgs_textChanged(const QString &arg1)
{
m_settings.m_args = arg1;
sendSettings();
}
void GNURadioGui::on_centerFrequency_changed(quint64 value)
{
m_generalSettings.m_centerFrequency = value * 1000;
sendSettings();
}
void GNURadioGui::on_sldFreqCorr_valueChanged(int value)
{
ui->lblFreqCorr->setText(tr("%1").arg(value));
m_settings.m_freqCorr = value;
sendSettings();
}
void GNURadioGui::on_sldGain_valueChanged(int value)
{
m_settings.m_namedGains.clear();
for ( int i = 0; i < m_gainControls.size(); i++ )
{
QPair< QSlider*, QLabel* > controls = m_gainControls[i];
QSlider *slider = controls.first;
QLabel *label = controls.second;
std::pair< QString, std::vector<double> > named_gain = m_namedGains[ i ];
int index = slider->value();
double gain = named_gain.second[index];
label->setText(tr("%1").arg(gain));
QPair< QString, double > named_gain2( named_gain.first, gain );
m_settings.m_namedGains.push_back( named_gain2 );
}
sendSettings();
}
void GNURadioGui::on_cboSampleRate_currentIndexChanged(int index)
{
if ( index < 0 || index >= m_sampRates.size() )
return;
m_settings.m_sampRate = m_sampRates[index];
sendSettings();
}
void GNURadioGui::on_cboAntennas_currentIndexChanged(const QString &arg1)
{
m_settings.m_antenna = arg1;
sendSettings();
}
void GNURadioGui::on_cboDCOffset_currentIndexChanged(const QString &arg1)
{
m_settings.m_dcoff = arg1;
sendSettings();
}
void GNURadioGui::on_cboIQBalance_currentIndexChanged(const QString &arg1)
{
m_settings.m_iqbal = arg1;
sendSettings();
}
void GNURadioGui::on_cboBandwidth_currentIndexChanged(int index)
{
if ( index < 0 || index >= m_bandwidths.size() )
return;
m_settings.m_bandwidth = m_bandwidths[index];
sendSettings();
}

View File

@ -0,0 +1,99 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// Copyright (C) 2013 by Dimitri Stolnikov <horiz0n@gmx.net> //
// //
// 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_GNURADIOGUI_H
#define INCLUDE_GNURADIOGUI_H
#include <QTimer>
#include <QPair>
#include <QList>
#include <QString>
#include <QSlider>
#include <QLabel>
#include "plugin/plugingui.h"
#include "gnuradioinput.h"
namespace Ui {
class GNURadioGui;
}
class PluginAPI;
class GNURadioGui : public QWidget, public PluginGUI {
Q_OBJECT
public:
explicit GNURadioGui(PluginAPI* pluginAPI, QWidget* parent = NULL);
~GNURadioGui();
void destroy();
void setName(const QString& name);
void resetToDefaults();
QByteArray serializeGeneral() const;
bool deserializeGeneral(const QByteArray&data);
quint64 getCenterFrequency() const;
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
bool handleMessage(Message* message);
private:
Ui::GNURadioGui* ui;
PluginAPI* m_pluginAPI;
SampleSource* m_sampleSource;
QList< QPair<QString, QString> > m_devs;
std::vector< std::pair< QString, std::vector<double> > > m_namedGains;
double m_freqMin;
double m_freqMax;
double m_freqCorr;
std::vector<double> m_sampRates;
std::vector<QString> m_antennas;
std::vector<QString> m_dcoffs;
std::vector<QString> m_iqbals;
std::vector<double> m_bandwidths;
std::vector< QSlider* > m_gainSliders;
std::vector< QLabel* > m_gainLabels;
QList< QPair< QSlider*, QLabel* > > m_gainControls;
SampleSource::GeneralSettings m_generalSettings;
GNURadioInput::Settings m_settings;
QTimer m_updateTimer;
void displaySettings();
void sendSettings();
private slots:
void updateHardware();
void on_cboDevices_currentIndexChanged(int index);
void on_txtDeviceArgs_textChanged(const QString &arg1);
void on_centerFrequency_changed(quint64 value);
void on_sldFreqCorr_valueChanged(int value);
void on_sldGain_valueChanged(int value);
void on_cboSampleRate_currentIndexChanged(int index);
void on_cboAntennas_currentIndexChanged(const QString &arg1);
void on_cboDCOffset_currentIndexChanged(const QString &arg1);
void on_cboIQBalance_currentIndexChanged(const QString &arg1);
void on_cboBandwidth_currentIndexChanged(int index);
};
#endif // INCLUDE_GNURADIOGUI_H

View File

@ -0,0 +1,301 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GNURadioGui</class>
<widget class="QWidget" name="GNURadioGui">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>186</width>
<height>261</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Gnuradio Source</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>2</number>
</property>
<item>
<widget class="QComboBox" name="cboDevices">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>false</bool>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txtDeviceArgs">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="ValueDial" name="centerFrequency" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<family>Monospace</family>
<pointsize>20</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
<string>Tuner center frequency in kHz</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>3</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Freq. Corr.</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="sldFreqCorr">
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="tracking">
<bool>true</bool>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblFreqCorr">
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayoutGains"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="spacing">
<number>3</number>
</property>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Sample Rate (MHz)</string>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cboSampleRate">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="spacing">
<number>3</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Selected Antenna</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cboAntennas">
<property name="frame">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>DC Offset Corr. </string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cboDCOffset"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="spacing">
<number>3</number>
</property>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>IQ Imbalance Corr.</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cboIQBalance"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Bandwidth (MHz)</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cboBandwidth"/>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ValueDial</class>
<extends>QWidget</extends>
<header>gui/valuedial.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,332 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// Copyright (C) 2013 by Dimitri Stolnikov <horiz0n@gmx.net> //
// //
// 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 <string.h>
#include <errno.h>
#include "util/simpleserializer.h"
#include "gnuradioinput.h"
#include "gnuradiothread.h"
#include "gnuradiogui.h"
MESSAGE_CLASS_DEFINITION(GNURadioInput::MsgConfigureGNURadio, Message)
MESSAGE_CLASS_DEFINITION(GNURadioInput::MsgReportGNURadio, Message)
GNURadioInput::Settings::Settings() :
m_args(""),
m_freqCorr(0),
m_sampRate(0),
m_antenna(""),
m_dcoff(""),
m_iqbal(""),
m_bandwidth(0)
{
}
void GNURadioInput::Settings::resetToDefaults()
{
m_args = "";
m_sampRate = 0;
m_freqCorr = 0;
m_antenna = "";
m_dcoff = "";
m_iqbal = "";
m_bandwidth = 0;
}
QByteArray GNURadioInput::Settings::serialize() const
{
SimpleSerializer s(1);
// s.writeString(1, m_args);
// s.writeDouble(2, m_freqCorr);
// s.writeDouble(3, m_sampRate);
// s.writeString(4, m_antenna);
// s.writeString(5, m_dcoff);
// s.writeString(5, m_iqbal);
return s.final();
}
bool GNURadioInput::Settings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid()) {
resetToDefaults();
return false;
}
if(d.getVersion() == 1) {
// d.readString(1, &m_args, "");
// d.readDouble(2, &m_freqCorr, 0);
// d.readDouble(3, &m_sampRate, 0);
// d.readString(4, &m_antenna, "");
// d.readString(5, &m_dcoff, "");
// d.readString(5, &m_iqbal, "");
return true;
} else {
resetToDefaults();
return false;
}
}
GNURadioInput::GNURadioInput(MessageQueue* msgQueueToGUI) :
SampleSource(msgQueueToGUI),
m_settings(),
m_GnuradioThread(NULL),
m_deviceDescription()
{
}
GNURadioInput::~GNURadioInput()
{
stopInput();
}
bool GNURadioInput::startInput(int device)
{
double freqMin = 0, freqMax = 0, freqCorr = 0;
std::vector< std::pair< QString, std::vector<double> > > namedGains;
std::vector< double > sampRates;
std::vector< QString > antennas;
std::vector< double > bandwidths;
QMutexLocker mutexLocker(&m_mutex);
if(m_GnuradioThread != NULL)
stopInput();
if(!m_sampleFifo.setSize( 2 * 1024 * 1024 )) {
qCritical("Could not allocate SampleFifo");
return false;
}
m_deviceDescription = m_settings.m_args;
// pass device arguments from the gui
m_GnuradioThread = new GnuradioThread(m_settings.m_args, &m_sampleFifo);
if(m_GnuradioThread == NULL) {
qFatal("out of memory");
goto failed;
}
m_GnuradioThread->startWork();
mutexLocker.unlock();
applySettings(m_generalSettings, m_settings, true);
if(m_GnuradioThread != NULL) {
osmosdr::source::sptr radio = m_GnuradioThread->radio();
try {
osmosdr::freq_range_t freq_rage = radio->get_freq_range();
freqMin = freq_rage.start();
freqMax = freq_rage.stop();
} catch ( std::exception &ex ) {
qDebug("%s", ex.what());
}
freqCorr = radio->get_freq_corr();
namedGains.clear();
m_settings.m_namedGains.clear();
std::vector< std::string > gain_names = radio->get_gain_names();
for ( int i = 0; i < gain_names.size(); i++ )
{
std::string gain_name = gain_names[i];
try {
std::vector< double > gain_values = \
radio->get_gain_range( gain_name ).values();
std::pair< QString, std::vector<double> > pair( gain_name.c_str(),
gain_values );
namedGains.push_back( pair );
QPair< QString, double > pair2( gain_name.c_str(), 0 );
m_settings.m_namedGains.push_back( pair2 );
} catch ( std::exception &ex ) {
qDebug("%s", ex.what());
}
}
try {
sampRates = radio->get_sample_rates().values();
} catch ( std::exception &ex ) {
qDebug("%s", ex.what());
}
antennas.clear();
std::vector< std::string > ant = radio->get_antennas();
for ( int i = 0; i < ant.size(); i++ )
antennas.push_back( QString( ant[i].c_str() ) );
m_dcoffs.clear();
m_dcoffs.push_back( "Off" );
m_dcoffs.push_back( "Keep" );
m_dcoffs.push_back( "Auto" );
m_iqbals.clear();
m_iqbals.push_back( "Off" );
m_iqbals.push_back( "Keep" );
m_iqbals.push_back( "Auto" );
try {
bandwidths = radio->get_bandwidth_range().values();
} catch ( std::exception &ex ) {
qDebug("%s", ex.what());
}
}
qDebug("GnuradioInput: start");
MsgReportGNURadio::create(freqMin, freqMax, freqCorr, namedGains,
sampRates, antennas, m_dcoffs, m_iqbals,
bandwidths)
->submit(m_guiMessageQueue);
return true;
failed:
stopInput();
return false;
}
void GNURadioInput::stopInput()
{
QMutexLocker mutexLocker(&m_mutex);
if(m_GnuradioThread != NULL) {
m_GnuradioThread->stopWork();
delete m_GnuradioThread;
m_GnuradioThread = NULL;
}
m_deviceDescription.clear();
}
const QString& GNURadioInput::getDeviceDescription() const
{
return m_deviceDescription;
}
int GNURadioInput::getSampleRate() const
{
return m_settings.m_sampRate;
}
quint64 GNURadioInput::getCenterFrequency() const
{
return m_generalSettings.m_centerFrequency;
}
bool GNURadioInput::handleMessage(Message* message)
{
if(MsgConfigureGNURadio::match(message)) {
MsgConfigureGNURadio* conf = (MsgConfigureGNURadio*)message;
if(!applySettings(conf->getGeneralSettings(), conf->getSettings(), false))
qDebug("Gnuradio config error");
return true;
} else {
return false;
}
}
bool GNURadioInput::applySettings(const GeneralSettings& generalSettings,
const Settings& settings,
bool force)
{
QMutexLocker mutexLocker(&m_mutex);
m_settings.m_args = settings.m_args;
if ( NULL == m_GnuradioThread )
return true;
osmosdr::source::sptr radio = m_GnuradioThread->radio();
try {
if((m_settings.m_freqCorr != settings.m_freqCorr) || force) {
m_settings.m_freqCorr = settings.m_freqCorr;
radio->set_freq_corr( m_settings.m_freqCorr );
}
if((m_generalSettings.m_centerFrequency != generalSettings.m_centerFrequency) || force) {
m_generalSettings.m_centerFrequency = generalSettings.m_centerFrequency;
radio->set_center_freq( m_generalSettings.m_centerFrequency );
}
for ( int i = 0; i < settings.m_namedGains.size(); i++ )
{
if((m_settings.m_namedGains[i].second != settings.m_namedGains[i].second) || force) {
m_settings.m_namedGains[i].second = settings.m_namedGains[i].second;
radio->set_gain( settings.m_namedGains[i].second,
settings.m_namedGains[i].first.toStdString() );
}
}
if((m_settings.m_sampRate != settings.m_sampRate) || force) {
m_settings.m_sampRate = settings.m_sampRate;
radio->set_sample_rate( m_settings.m_sampRate );
}
if((m_settings.m_antenna != settings.m_antenna) || force) {
m_settings.m_antenna = settings.m_antenna;
radio->set_antenna( m_settings.m_antenna.toStdString() );
}
if((m_settings.m_dcoff != settings.m_dcoff) || force) {
m_settings.m_dcoff = settings.m_dcoff;
for ( int i = 0; i < m_dcoffs.size(); i++ )
{
if ( m_dcoffs[i] != m_settings.m_dcoff )
continue;
radio->set_dc_offset_mode( i );
break;
}
}
if((m_settings.m_iqbal != settings.m_iqbal) || force) {
m_settings.m_iqbal = settings.m_iqbal;
for ( int i = 0; i < m_iqbals.size(); i++ )
{
if ( m_iqbals[i] != m_settings.m_iqbal )
continue;
radio->set_iq_balance_mode( i );
break;
}
}
if((m_settings.m_bandwidth != settings.m_bandwidth) ||
(0.0f == settings.m_bandwidth) || force) {
m_settings.m_bandwidth = settings.m_bandwidth;
/* setting the BW to 0.0 triggers automatic bandwidth
* selection when supported by device */
radio->set_bandwidth( m_settings.m_bandwidth );
}
} catch ( std::exception &ex ) {
qDebug("%s", ex.what());
return false;
}
return true;
}

View File

@ -0,0 +1,156 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// Copyright (C) 2013 by Dimitri Stolnikov <horiz0n@gmx.net> //
// //
// 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_GNURADIOINPUT_H
#define INCLUDE_GNURADIOINPUT_H
#include "dsp/samplesource/samplesource.h"
#include <QString>
#include <QPair>
class GnuradioThread;
class GNURadioInput : public SampleSource {
public:
struct Settings {
QString m_args;
double m_freqMin;
double m_freqMax;
double m_freqCorr;
QList< QPair< QString, double > > m_namedGains;
double m_sampRate;
QString m_antenna;
QString m_dcoff;
QString m_iqbal;
double m_bandwidth;
Settings();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
class MsgConfigureGNURadio : public Message {
MESSAGE_CLASS_DECLARATION
public:
const GeneralSettings& getGeneralSettings() const { return m_generalSettings; }
const Settings& getSettings() const { return m_settings; }
static MsgConfigureGNURadio* create(const GeneralSettings& generalSettings, const Settings& settings)
{
return new MsgConfigureGNURadio(generalSettings, settings);
}
protected:
GeneralSettings m_generalSettings;
Settings m_settings;
MsgConfigureGNURadio(const GeneralSettings& generalSettings, const Settings& settings) :
Message(),
m_generalSettings(generalSettings),
m_settings(settings)
{ }
};
class MsgReportGNURadio : public Message {
MESSAGE_CLASS_DECLARATION
public:
const std::vector< std::pair< QString, std::vector<double> > >& getNamedGains() const { return m_namedGains; }
const double getFreqMin() const { return m_freqMin; }
const double getFreqMax() const { return m_freqMax; }
const double getFreqCorr() const { return m_freqCorr; }
const std::vector<double>& getSampRates() const { return m_sampRates; }
const std::vector<QString>& getAntennas() const { return m_antennas; }
const std::vector<QString>& getDCOffs() const { return m_dcoffs; }
const std::vector<QString>& getIQBals() const { return m_iqbals; }
const std::vector<double>& getBandwidths() const { return m_bandwidths; }
static MsgReportGNURadio* create(const double freqMin,
const double freqMax,
const double freqCorr,
const std::vector< std::pair< QString, std::vector<double> > >& namedGains,
const std::vector<double>& sampRates,
const std::vector<QString>& antennas,
const std::vector<QString>& dcoffs,
const std::vector<QString>& iqbals,
const std::vector<double>& bandwidths)
{
return new MsgReportGNURadio(freqMin, freqMax, freqCorr, namedGains, sampRates, antennas, dcoffs, iqbals, bandwidths);
}
protected:
double m_freqMin;
double m_freqMax;
double m_freqCorr;
std::vector< std::pair< QString, std::vector<double> > > m_namedGains;
std::vector<double> m_sampRates;
std::vector<QString> m_antennas;
std::vector<QString> m_dcoffs;
std::vector<QString> m_iqbals;
std::vector<double> m_bandwidths;
MsgReportGNURadio(const double freqMin,
const double freqMax,
const double freqCorr,
const std::vector< std::pair< QString, std::vector<double> > >& namedGains,
const std::vector<double>& sampRates,
const std::vector<QString>& antennas,
const std::vector<QString>& dcoffs,
const std::vector<QString>& iqbals,
const std::vector<double>& bandwidths) :
Message(),
m_freqMin(freqMin),
m_freqMax(freqMax),
m_freqCorr(freqCorr),
m_namedGains(namedGains),
m_sampRates(sampRates),
m_antennas(antennas),
m_dcoffs(dcoffs),
m_iqbals(iqbals),
m_bandwidths(bandwidths)
{ }
};
GNURadioInput(MessageQueue* msgQueueToGUI);
~GNURadioInput();
bool startInput(int device);
void stopInput();
const QString& getDeviceDescription() const;
int getSampleRate() const;
quint64 getCenterFrequency() const;
bool handleMessage(Message* message);
private:
QMutex m_mutex;
Settings m_settings;
GnuradioThread* m_GnuradioThread;
QString m_deviceDescription;
std::vector< QString > m_dcoffs;
std::vector< QString > m_iqbals;
bool applySettings(const GeneralSettings& generalSettings,
const Settings& settings,
bool force);
};
#endif // INCLUDE_GNURADIOINPUT_H

View File

@ -0,0 +1,52 @@
#include <QtPlugin>
#include <QAction>
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
#include "gnuradioplugin.h"
#include "gnuradiogui.h"
const PluginDescriptor GNURadioPlugin::m_pluginDescriptor = {
QString("GR-OsmoSDR Input"),
QString("1.0"),
QString("(c) Dimitri Stolnikov <horiz0n@gmx.net>"),
QString("http://sdr.osmocom.org/trac/wiki/GrOsmoSDR"),
true,
QString("http://cgit.osmocom.org/cgit/gr-osmosdr")
};
GNURadioPlugin::GNURadioPlugin(QObject* parent) :
QObject(parent)
{
}
const PluginDescriptor& GNURadioPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void GNURadioPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
m_pluginAPI->registerSampleSource("org.osmocom.sdr.samplesource.gr-osmosdr", this);
}
PluginInterface::SampleSourceDevices GNURadioPlugin::enumSampleSources()
{
SampleSourceDevices result;
result.append(SampleSourceDevice("GNURadio OsmoSDR Driver", "org.osmocom.sdr.samplesource.gr-osmosdr", QByteArray()));
return result;
}
PluginGUI* GNURadioPlugin::createSampleSource(const QString& sourceName, const QByteArray& address)
{
if(sourceName == "org.osmocom.sdr.samplesource.gr-osmosdr") {
GNURadioGui* gui = new GNURadioGui(m_pluginAPI);
m_pluginAPI->setInputGUI(gui);
return gui;
} else {
return NULL;
}
}

Some files were not shown because too many files have changed in this diff Show More