diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b25b3d16..e4798d1db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,89 +155,89 @@ set(sdrbase_SOURCES ) set(sdrbase_HEADERS - include/mainwindow.h + sdrbase/mainwindow.h - include/audio/audiodeviceinfo.h - include/audio/audiofifo.h - include/audio/audiooutput.h + sdrbase/audio/audiodeviceinfo.h + sdrbase/audio/audiofifo.h + sdrbase/audio/audiooutput.h - include/dsp/afsquelch.h - include/dsp/channelizer.h - include/dsp/channelmarker.h - include/dsp/complex.h - include/dsp/decimators.h - include/dsp/dspcommands.h - include/dsp/dspengine.h - include/dsp/dspdeviceengine.h - include/dsp/dsptypes.h - include/dsp/fftengine.h - include/dsp/fftfilt.h - include/dsp/fftwengine.h - include/dsp/fftwindow.h - include/dsp/filterrc.h - include/dsp/filesink.h - include/dsp/gfft.h - include/dsp/interpolator.h - include/dsp/inthalfbandfilter.h - include/dsp/kissfft.h - include/dsp/kissengine.h - include/dsp/lowpass.h - include/dsp/misc.h - include/dsp/movingaverage.h - include/dsp/nco.h - include/dsp/phasediscri.h - include/dsp/phaselock.h + sdrbase/dsp/afsquelch.h + sdrbase/dsp/channelizer.h + sdrbase/dsp/channelmarker.h + sdrbase/dsp/complex.h + sdrbase/dsp/decimators.h + sdrbase/dsp/dspcommands.h + sdrbase/dsp/dspengine.h + sdrbase/dsp/dspdeviceengine.h + sdrbase/dsp/dsptypes.h + sdrbase/dsp/fftengine.h + sdrbase/dsp/fftfilt.h + sdrbase/dsp/fftwengine.h + sdrbase/dsp/fftwindow.h + sdrbase/dsp/filterrc.h + sdrbase/dsp/filesink.h + sdrbase/dsp/gfft.h + sdrbase/dsp/interpolator.h + sdrbase/dsp/inthalfbandfilter.h + sdrbase/dsp/kissfft.h + sdrbase/dsp/kissengine.h + sdrbase/dsp/lowpass.h + sdrbase/dsp/misc.h + sdrbase/dsp/movingaverage.h + sdrbase/dsp/nco.h + sdrbase/dsp/phasediscri.h + sdrbase/dsp/phaselock.h sdrbase/dsp/pidcontroller.h - include/dsp/samplefifo.h - include/dsp/samplesink.h - include/dsp/nullsink.h - include/dsp/scopevis.h - include/dsp/spectrumvis.h - include/dsp/threadedsamplesink.h + sdrbase/dsp/samplefifo.h + sdrbase/dsp/samplesink.h + sdrbase/dsp/nullsink.h + sdrbase/dsp/scopevis.h + sdrbase/dsp/spectrumvis.h + sdrbase/dsp/threadedsamplesink.h - include/gui/aboutdialog.h - include/gui/addpresetdialog.h - include/gui/basicchannelsettingswidget.h - include/gui/buttonswitch.h - include/gui/channelwindow.h - include/gui/colormapper.h - include/gui/glscope.h - include/gui/glscopegui.h - include/gui/glshadersimple.h - include/gui/glshadertextured.h - include/gui/glspectrum.h - include/gui/glspectrumgui.h - include/gui/indicator.h - include/gui/physicalunit.h - include/gui/pluginsdialog.h - include/gui/preferencesdialog.h - include/gui/presetitem.h - include/gui/rollupwidget.h - include/gui/scale.h - include/gui/scaleengine.h - include/gui/valuedial.h + sdrbase/gui/aboutdialog.h + sdrbase/gui/addpresetdialog.h + sdrbase/gui/basicchannelsettingswidget.h + sdrbase/gui/buttonswitch.h + sdrbase/gui/channelwindow.h + sdrbase/gui/colormapper.h + sdrbase/gui/glscope.h + sdrbase/gui/glscopegui.h + sdrbase/gui/glshadersimple.h + sdrbase/gui/glshadertextured.h + sdrbase/gui/glspectrum.h + sdrbase/gui/glspectrumgui.h + sdrbase/gui/indicator.h + sdrbase/gui/physicalunit.h + sdrbase/gui/pluginsdialog.h + sdrbase/gui/preferencesdialog.h + sdrbase/gui/presetitem.h + sdrbase/gui/rollupwidget.h + sdrbase/gui/scale.h + sdrbase/gui/scaleengine.h + sdrbase/gui/valuedial.h - include/dsp/samplesource.h + sdrbase/dsp/samplesource.h - include/plugin/pluginapi.h - include/plugin/plugingui.h - include/plugin/plugininterface.h - include/plugin/pluginmanager.h + sdrbase/plugin/pluginapi.h + sdrbase/plugin/plugingui.h + sdrbase/plugin/plugininterface.h + sdrbase/plugin/pluginmanager.h - include/settings/preferences.h - include/settings/preset.h - include/settings/mainsettings.h + sdrbase/settings/preferences.h + sdrbase/settings/preset.h + sdrbase/settings/mainsettings.h - include/util/CRC64.h - include/util/db.h - include/util/export.h - include/util/message.h - include/util/messagequeue.h - include/util/prettyprint.h - include/util/syncmessenger.h - include/util/samplesourceserializer.h - include/util/simpleserializer.h - #include/util/spinlock.h + sdrbase/util/CRC64.h + sdrbase/util/db.h + sdrbase/util/export.h + sdrbase/util/message.h + sdrbase/util/messagequeue.h + sdrbase/util/prettyprint.h + sdrbase/util/syncmessenger.h + sdrbase/util/samplesourceserializer.h + sdrbase/util/simpleserializer.h + #sdrbase/util/spinlock.h ) set(sdrbase_SOURCES @@ -268,7 +268,7 @@ if(FFTW3F_FOUND) ) set(sdrbase_HEADERS ${sdrbase_HEADERS} - include/dsp/fftwengine.h + sdrbase/dsp/fftwengine.h ) add_definitions(-DUSE_FFTW) include_directories(${FFTW3F_INCLUDE_DIRS}) @@ -276,11 +276,11 @@ else(FFTW3F_FOUND) set(sdrbase_SOURCES ${sdrbase_SOURCES} sdrbase/dsp/kissengine.cpp - include/dsp/kissfft.h + sdrbase/dsp/kissfft.h ) set(sdrbase_HEADERS ${sdrbase_HEADERS} - include/dsp/kissengine.h + sdrbase/dsp/kissengine.h ) add_definitions(-DUSE_KISSFFT) endif(FFTW3F_FOUND) @@ -319,8 +319,7 @@ qt5_use_modules(sdrbase Core Widgets OpenGL Multimedia) include_directories( ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_SOURCE_DIR}/include - ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/sdrbase ${OPENGL_INCLUDE_DIR} ) @@ -341,7 +340,7 @@ endif() ############################################################################## set(sdrangel_SOURCES - main.cpp + app/main.cpp ) if(WIN32) diff --git a/main.cpp b/app/main.cpp similarity index 100% rename from main.cpp rename to app/main.cpp diff --git a/include/audio/audiodeviceinfo.h b/include.old/audio/audiodeviceinfo.h similarity index 100% rename from include/audio/audiodeviceinfo.h rename to include.old/audio/audiodeviceinfo.h diff --git a/include/audio/audiofifo.h b/include.old/audio/audiofifo.h similarity index 100% rename from include/audio/audiofifo.h rename to include.old/audio/audiofifo.h diff --git a/include/audio/audiooutput.h b/include.old/audio/audiooutput.h similarity index 100% rename from include/audio/audiooutput.h rename to include.old/audio/audiooutput.h diff --git a/include/dsp/afsquelch.h b/include.old/dsp/afsquelch.h similarity index 100% rename from include/dsp/afsquelch.h rename to include.old/dsp/afsquelch.h diff --git a/include/dsp/agc.h b/include.old/dsp/agc.h similarity index 100% rename from include/dsp/agc.h rename to include.old/dsp/agc.h diff --git a/include/dsp/bandpass.h b/include.old/dsp/bandpass.h similarity index 100% rename from include/dsp/bandpass.h rename to include.old/dsp/bandpass.h diff --git a/include/dsp/channelizer.h b/include.old/dsp/channelizer.h similarity index 100% rename from include/dsp/channelizer.h rename to include.old/dsp/channelizer.h diff --git a/include/dsp/channelmarker.h b/include.old/dsp/channelmarker.h similarity index 100% rename from include/dsp/channelmarker.h rename to include.old/dsp/channelmarker.h diff --git a/include/dsp/complex.h b/include.old/dsp/complex.h similarity index 100% rename from include/dsp/complex.h rename to include.old/dsp/complex.h diff --git a/include/dsp/ctcssdetector.h b/include.old/dsp/ctcssdetector.h similarity index 100% rename from include/dsp/ctcssdetector.h rename to include.old/dsp/ctcssdetector.h diff --git a/include/dsp/decimators.h b/include.old/dsp/decimators.h similarity index 100% rename from include/dsp/decimators.h rename to include.old/dsp/decimators.h diff --git a/include/dsp/dspcommands.h b/include.old/dsp/dspcommands.h similarity index 100% rename from include/dsp/dspcommands.h rename to include.old/dsp/dspcommands.h diff --git a/include/dsp/dspdeviceengine.h b/include.old/dsp/dspdeviceengine.h similarity index 100% rename from include/dsp/dspdeviceengine.h rename to include.old/dsp/dspdeviceengine.h diff --git a/include/dsp/dspengine.h b/include.old/dsp/dspengine.h similarity index 100% rename from include/dsp/dspengine.h rename to include.old/dsp/dspengine.h diff --git a/include/dsp/dsptypes.h b/include.old/dsp/dsptypes.h similarity index 100% rename from include/dsp/dsptypes.h rename to include.old/dsp/dsptypes.h diff --git a/include/dsp/fftengine.h b/include.old/dsp/fftengine.h similarity index 100% rename from include/dsp/fftengine.h rename to include.old/dsp/fftengine.h diff --git a/include/dsp/fftfilt.h b/include.old/dsp/fftfilt.h similarity index 100% rename from include/dsp/fftfilt.h rename to include.old/dsp/fftfilt.h diff --git a/include/dsp/fftwengine.h b/include.old/dsp/fftwengine.h similarity index 100% rename from include/dsp/fftwengine.h rename to include.old/dsp/fftwengine.h diff --git a/include/dsp/fftwindow.h b/include.old/dsp/fftwindow.h similarity index 100% rename from include/dsp/fftwindow.h rename to include.old/dsp/fftwindow.h diff --git a/include/dsp/filesink.h b/include.old/dsp/filesink.h similarity index 100% rename from include/dsp/filesink.h rename to include.old/dsp/filesink.h diff --git a/include/dsp/filterrc.h b/include.old/dsp/filterrc.h similarity index 100% rename from include/dsp/filterrc.h rename to include.old/dsp/filterrc.h diff --git a/include/dsp/gfft.h b/include.old/dsp/gfft.h similarity index 100% rename from include/dsp/gfft.h rename to include.old/dsp/gfft.h diff --git a/include/dsp/highpass.h b/include.old/dsp/highpass.h similarity index 100% rename from include/dsp/highpass.h rename to include.old/dsp/highpass.h diff --git a/include/dsp/interpolator.h b/include.old/dsp/interpolator.h similarity index 100% rename from include/dsp/interpolator.h rename to include.old/dsp/interpolator.h diff --git a/include/dsp/inthalfbandfilter.h b/include.old/dsp/inthalfbandfilter.h similarity index 100% rename from include/dsp/inthalfbandfilter.h rename to include.old/dsp/inthalfbandfilter.h diff --git a/include/dsp/kissengine.h b/include.old/dsp/kissengine.h similarity index 100% rename from include/dsp/kissengine.h rename to include.old/dsp/kissengine.h diff --git a/include/dsp/kissfft.h b/include.old/dsp/kissfft.h similarity index 100% rename from include/dsp/kissfft.h rename to include.old/dsp/kissfft.h diff --git a/include/dsp/lowpass.h b/include.old/dsp/lowpass.h similarity index 100% rename from include/dsp/lowpass.h rename to include.old/dsp/lowpass.h diff --git a/include/dsp/misc.h b/include.old/dsp/misc.h similarity index 100% rename from include/dsp/misc.h rename to include.old/dsp/misc.h diff --git a/include/dsp/movingaverage.h b/include.old/dsp/movingaverage.h similarity index 100% rename from include/dsp/movingaverage.h rename to include.old/dsp/movingaverage.h diff --git a/include/dsp/nco.h b/include.old/dsp/nco.h similarity index 100% rename from include/dsp/nco.h rename to include.old/dsp/nco.h diff --git a/include/dsp/nullsink.h b/include.old/dsp/nullsink.h similarity index 100% rename from include/dsp/nullsink.h rename to include.old/dsp/nullsink.h diff --git a/include/dsp/phasediscri.h b/include.old/dsp/phasediscri.h similarity index 100% rename from include/dsp/phasediscri.h rename to include.old/dsp/phasediscri.h diff --git a/include/dsp/phaselock.h b/include.old/dsp/phaselock.h similarity index 100% rename from include/dsp/phaselock.h rename to include.old/dsp/phaselock.h diff --git a/include/dsp/pidcontroller.h b/include.old/dsp/pidcontroller.h similarity index 100% rename from include/dsp/pidcontroller.h rename to include.old/dsp/pidcontroller.h diff --git a/include/dsp/samplefifo.h b/include.old/dsp/samplefifo.h similarity index 100% rename from include/dsp/samplefifo.h rename to include.old/dsp/samplefifo.h diff --git a/include/dsp/samplesink.h b/include.old/dsp/samplesink.h similarity index 100% rename from include/dsp/samplesink.h rename to include.old/dsp/samplesink.h diff --git a/include/dsp/samplesource.h b/include.old/dsp/samplesource.h similarity index 100% rename from include/dsp/samplesource.h rename to include.old/dsp/samplesource.h diff --git a/include/dsp/scopevis.h b/include.old/dsp/scopevis.h similarity index 100% rename from include/dsp/scopevis.h rename to include.old/dsp/scopevis.h diff --git a/include/dsp/spectrumscopecombovis.h b/include.old/dsp/spectrumscopecombovis.h similarity index 100% rename from include/dsp/spectrumscopecombovis.h rename to include.old/dsp/spectrumscopecombovis.h diff --git a/include/dsp/spectrumvis.h b/include.old/dsp/spectrumvis.h similarity index 100% rename from include/dsp/spectrumvis.h rename to include.old/dsp/spectrumvis.h diff --git a/include/dsp/threadedsamplesink.h b/include.old/dsp/threadedsamplesink.h similarity index 100% rename from include/dsp/threadedsamplesink.h rename to include.old/dsp/threadedsamplesink.h diff --git a/include/gui/aboutdialog.h b/include.old/gui/aboutdialog.h similarity index 100% rename from include/gui/aboutdialog.h rename to include.old/gui/aboutdialog.h diff --git a/include/gui/addpresetdialog.h b/include.old/gui/addpresetdialog.h similarity index 100% rename from include/gui/addpresetdialog.h rename to include.old/gui/addpresetdialog.h diff --git a/include/gui/basicchannelsettingswidget.h b/include.old/gui/basicchannelsettingswidget.h similarity index 100% rename from include/gui/basicchannelsettingswidget.h rename to include.old/gui/basicchannelsettingswidget.h diff --git a/include/gui/buttonswitch.h b/include.old/gui/buttonswitch.h similarity index 100% rename from include/gui/buttonswitch.h rename to include.old/gui/buttonswitch.h diff --git a/include/gui/channelwindow.h b/include.old/gui/channelwindow.h similarity index 100% rename from include/gui/channelwindow.h rename to include.old/gui/channelwindow.h diff --git a/include/gui/colormapper.h b/include.old/gui/colormapper.h similarity index 100% rename from include/gui/colormapper.h rename to include.old/gui/colormapper.h diff --git a/include/gui/glscope.h b/include.old/gui/glscope.h similarity index 100% rename from include/gui/glscope.h rename to include.old/gui/glscope.h diff --git a/include/gui/glscopegui.h b/include.old/gui/glscopegui.h similarity index 100% rename from include/gui/glscopegui.h rename to include.old/gui/glscopegui.h diff --git a/include/gui/glshadersimple.h b/include.old/gui/glshadersimple.h similarity index 100% rename from include/gui/glshadersimple.h rename to include.old/gui/glshadersimple.h diff --git a/include/gui/glshadertextured.h b/include.old/gui/glshadertextured.h similarity index 100% rename from include/gui/glshadertextured.h rename to include.old/gui/glshadertextured.h diff --git a/include/gui/glspectrum.h b/include.old/gui/glspectrum.h similarity index 100% rename from include/gui/glspectrum.h rename to include.old/gui/glspectrum.h diff --git a/include/gui/glspectrumgui.h b/include.old/gui/glspectrumgui.h similarity index 100% rename from include/gui/glspectrumgui.h rename to include.old/gui/glspectrumgui.h diff --git a/include/gui/indicator.h b/include.old/gui/indicator.h similarity index 100% rename from include/gui/indicator.h rename to include.old/gui/indicator.h diff --git a/include/gui/physicalunit.h b/include.old/gui/physicalunit.h similarity index 100% rename from include/gui/physicalunit.h rename to include.old/gui/physicalunit.h diff --git a/include/gui/pluginsdialog.h b/include.old/gui/pluginsdialog.h similarity index 100% rename from include/gui/pluginsdialog.h rename to include.old/gui/pluginsdialog.h diff --git a/include/gui/preferencesdialog.h b/include.old/gui/preferencesdialog.h similarity index 100% rename from include/gui/preferencesdialog.h rename to include.old/gui/preferencesdialog.h diff --git a/include/gui/presetitem.h b/include.old/gui/presetitem.h similarity index 100% rename from include/gui/presetitem.h rename to include.old/gui/presetitem.h diff --git a/include/gui/rollupwidget.h b/include.old/gui/rollupwidget.h similarity index 100% rename from include/gui/rollupwidget.h rename to include.old/gui/rollupwidget.h diff --git a/include/gui/scale.h b/include.old/gui/scale.h similarity index 100% rename from include/gui/scale.h rename to include.old/gui/scale.h diff --git a/include/gui/scaleengine.h b/include.old/gui/scaleengine.h similarity index 100% rename from include/gui/scaleengine.h rename to include.old/gui/scaleengine.h diff --git a/include/gui/valuedial.h b/include.old/gui/valuedial.h similarity index 100% rename from include/gui/valuedial.h rename to include.old/gui/valuedial.h diff --git a/include/mainwindow.h b/include.old/mainwindow.h similarity index 100% rename from include/mainwindow.h rename to include.old/mainwindow.h diff --git a/include/plugin/pluginapi.h b/include.old/plugin/pluginapi.h similarity index 100% rename from include/plugin/pluginapi.h rename to include.old/plugin/pluginapi.h diff --git a/include/plugin/plugingui.h b/include.old/plugin/plugingui.h similarity index 100% rename from include/plugin/plugingui.h rename to include.old/plugin/plugingui.h diff --git a/include/plugin/plugininterface.h b/include.old/plugin/plugininterface.h similarity index 100% rename from include/plugin/plugininterface.h rename to include.old/plugin/plugininterface.h diff --git a/include/plugin/pluginmanager.h b/include.old/plugin/pluginmanager.h similarity index 100% rename from include/plugin/pluginmanager.h rename to include.old/plugin/pluginmanager.h diff --git a/include/settings/mainsettings.h b/include.old/settings/mainsettings.h similarity index 100% rename from include/settings/mainsettings.h rename to include.old/settings/mainsettings.h diff --git a/include/settings/preferences.h b/include.old/settings/preferences.h similarity index 100% rename from include/settings/preferences.h rename to include.old/settings/preferences.h diff --git a/include/settings/preset.h b/include.old/settings/preset.h similarity index 100% rename from include/settings/preset.h rename to include.old/settings/preset.h diff --git a/include/util/CRC64.h b/include.old/util/CRC64.h similarity index 100% rename from include/util/CRC64.h rename to include.old/util/CRC64.h diff --git a/include/util/bitfieldindex.h b/include.old/util/bitfieldindex.h similarity index 100% rename from include/util/bitfieldindex.h rename to include.old/util/bitfieldindex.h diff --git a/include/util/db.h b/include.old/util/db.h similarity index 100% rename from include/util/db.h rename to include.old/util/db.h diff --git a/include/util/export.h b/include.old/util/export.h similarity index 100% rename from include/util/export.h rename to include.old/util/export.h diff --git a/include/util/message.h b/include.old/util/message.h similarity index 100% rename from include/util/message.h rename to include.old/util/message.h diff --git a/include/util/messagequeue.h b/include.old/util/messagequeue.h similarity index 100% rename from include/util/messagequeue.h rename to include.old/util/messagequeue.h diff --git a/include/util/prettyprint.h b/include.old/util/prettyprint.h similarity index 100% rename from include/util/prettyprint.h rename to include.old/util/prettyprint.h diff --git a/include/util/samplesourceserializer.h b/include.old/util/samplesourceserializer.h similarity index 100% rename from include/util/samplesourceserializer.h rename to include.old/util/samplesourceserializer.h diff --git a/include/util/simpleserializer.h b/include.old/util/simpleserializer.h similarity index 100% rename from include/util/simpleserializer.h rename to include.old/util/simpleserializer.h diff --git a/include/util/spinlock.h b/include.old/util/spinlock.h similarity index 100% rename from include/util/spinlock.h rename to include.old/util/spinlock.h diff --git a/include/util/stacktrace.h b/include.old/util/stacktrace.h similarity index 100% rename from include/util/stacktrace.h rename to include.old/util/stacktrace.h diff --git a/include/util/syncmessenger.h b/include.old/util/syncmessenger.h similarity index 100% rename from include/util/syncmessenger.h rename to include.old/util/syncmessenger.h diff --git a/include/util/threadsafesingleton.h b/include.old/util/threadsafesingleton.h similarity index 100% rename from include/util/threadsafesingleton.h rename to include.old/util/threadsafesingleton.h diff --git a/include/util/udpsink.h b/include.old/util/udpsink.h similarity index 100% rename from include/util/udpsink.h rename to include.old/util/udpsink.h diff --git a/sdrbase/audio/audiodeviceinfo.h b/sdrbase/audio/audiodeviceinfo.h new file mode 100644 index 000000000..c2a018651 --- /dev/null +++ b/sdrbase/audio/audiodeviceinfo.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_AUDIODEVICEINFO_H +#define INCLUDE_AUDIODEVICEINFO_H + +#include +#include "util/export.h" + +class SDRANGEL_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 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 diff --git a/sdrbase/audio/audiofifo.h b/sdrbase/audio/audiofifo.h new file mode 100644 index 000000000..ce6b1ba89 --- /dev/null +++ b/sdrbase/audio/audiofifo.h @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // +// written by Christian Daniel // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_AUDIOFIFO_H +#define INCLUDE_AUDIOFIFO_H + +#include +#include +#include +#include "util/export.h" + +class SDRANGEL_API AudioFifo : public QObject { + Q_OBJECT +public: + AudioFifo(); + AudioFifo(uint sampleSize, uint numSamples); + ~AudioFifo(); + + bool setSize(uint sampleSize, uint numSamples); + + uint write(const quint8* data, uint numSamples, int timeout_ms = INT_MAX); + uint read(quint8* data, uint numSamples, int timeout_ms = 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 diff --git a/sdrbase/audio/audiooutput.h b/sdrbase/audio/audiooutput.h new file mode 100644 index 000000000..eeb802333 --- /dev/null +++ b/sdrbase/audio/audiooutput.h @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_AUDIOOUTPUT_H +#define INCLUDE_AUDIOOUTPUT_H + +#include +#include +#include +#include +#include +#include "util/export.h" + +class QAudioOutput; +class AudioFifo; +class AudioOutputPipe; + +class SDRANGEL_API AudioOutput : QIODevice { +public: + AudioOutput(); + virtual ~AudioOutput(); + + bool start(int device, int rate); + void stop(); + + void addFifo(AudioFifo* audioFifo); + void removeFifo(AudioFifo* audioFifo); + + uint getRate() const { return m_audioFormat.sampleRate(); } + +private: + QMutex m_mutex; + QAudioOutput* m_audioOutput; + + typedef std::list AudioFifos; + AudioFifos m_audioFifos; + std::vector m_mixBuffer; + + QAudioFormat m_audioFormat; + + //virtual bool open(OpenMode mode); + virtual qint64 readData(char* data, qint64 maxLen); + virtual qint64 writeData(const char* data, qint64 len); + + friend class AudioOutputPipe; +}; + +#endif // INCLUDE_AUDIOOUTPUT_H diff --git a/sdrbase/dsp/afsquelch.h b/sdrbase/dsp/afsquelch.h new file mode 100644 index 000000000..eb587995d --- /dev/null +++ b/sdrbase/dsp/afsquelch.h @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB. // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_GPL_DSP_AFSQUELCH_H_ +#define INCLUDE_GPL_DSP_AFSQUELCH_H_ + +#include "dsp/dsptypes.h" +#include "dsp/movingaverage.h" + +/** AFSquelch: AF squelch class based on the Modified Goertzel + * algorithm. + */ +class AFSquelch { +public: + // Constructors and Destructor + AFSquelch(); + // allows user defined tone pair + AFSquelch(unsigned int nbTones, + const Real *tones); + virtual ~AFSquelch(); + + // setup the basic parameters and coefficients + void setCoefficients( + int N, //!< the algorithm "block" size + unsigned int nbAvg, //!< averaging size + int SampleRate, //!< input signal sample rate + int _samplesAttack, //!< number of results before squelch opens + int _samplesDecay); //!< number of results keeping squelch open + + // set the detection threshold + void setThreshold(double _threshold); + + // analyze a sample set and optionally filter + // the tone frequencies. + bool analyze(Real sample); // input signal sample + bool evaluate(); // evaluate result + + // get the tone set + const Real *getToneSet() const + { + return m_toneSet; + } + + bool open() const { + return m_isOpen; + } + + void reset(); // reset the analysis algorithm + +protected: + void feedback(Real sample); + void feedForward(); + +private: + unsigned int m_nbAvg; //!< number of power samples taken for moving average + int m_N; + int m_sampleRate; + int m_samplesProcessed; + int m_maxPowerIndex; + int m_nTones; + int m_samplesAttack; + int m_attackCount; + int m_samplesDecay; + int m_decayCount; + bool m_isOpen; + double m_threshold; + double *m_k; + double *m_coef; + Real *m_toneSet; + double *m_u0; + double *m_u1; + double *m_power; + std::vector > m_movingAverages; +}; + + +#endif /* INCLUDE_GPL_DSP_CTCSSDETECTOR_H_ */ diff --git a/sdrbase/dsp/agc.h b/sdrbase/dsp/agc.h new file mode 100644 index 000000000..e6e320800 --- /dev/null +++ b/sdrbase/dsp/agc.h @@ -0,0 +1,128 @@ +/* + * kissagc.h + * + * Created on: May 12, 2015 + * Author: f4exb + */ + +#ifndef INCLUDE_GPL_DSP_AGC_H_ +#define INCLUDE_GPL_DSP_AGC_H_ + +#include "movingaverage.h" + +class AGC +{ +public: + + AGC(); + AGC(int historySize, Real R); + virtual ~AGC(); + + void resize(int historySize, Real R); + Real getValue(); + Real getAverage(); + virtual void feed(Complex& ci) = 0; + +protected: + Real m_u0; + Real m_R; // objective mag + MovingAverage m_moving_average; // Averaging engine. The stack length conditions the smoothness of AGC. + int m_historySize; + int m_count; +}; + +class MagSquaredAGC : public AGC +{ +public: + MagSquaredAGC(); + MagSquaredAGC(int historySize, Real R); + virtual ~MagSquaredAGC(); + virtual void feed(Complex& ci); + Real getMagSq() const { return m_magsq; } +private: + Real m_magsq; +}; + +class MagAGC : public AGC +{ +public: + MagAGC(); + MagAGC(int historySize, Real R); + virtual ~MagAGC(); + virtual void feed(Complex& ci); + Real getMagSq() const { return m_magsq; } +private: + Real m_magsq; +}; + +class AlphaAGC : public AGC +{ +public: + AlphaAGC(); + AlphaAGC(int historySize, Real R); + AlphaAGC(int historySize, Real R, Real alpha); + virtual ~AlphaAGC(); + void resize(int historySize, Real R, Real alpha); + virtual void feed(Complex& ci); + Real getMagSq() const { return m_magsq; } +private: + Real m_alpha; + Real m_magsq; + bool m_squelchOpen; +}; + +class SimpleAGC +{ +public: + SimpleAGC() : + m_squelchOpen(false), + m_fill(0), + m_cutoff(0), + m_clip(0), + m_moving_average() + {} + + SimpleAGC(int historySize, Real initial, Real cutoff=0, Real clip=0) : + m_squelchOpen(false), + m_fill(initial), + m_cutoff(cutoff), + m_clip(clip), + m_moving_average(historySize, initial) + {} + + void resize(int historySize, Real initial, Real cutoff=0, Real clip=0) + { + m_fill = initial; + m_cutoff = cutoff; + m_clip = clip; + m_moving_average.resize(historySize, initial); + } + + Real getValue() + { + if (m_moving_average.average() > m_clip) + { + return m_moving_average.average(); + } else + { + return m_clip; + } + } + + void feed(Real value) + { + if (value > m_cutoff) + { + m_moving_average.feed(value); + } + } + +private: + bool m_squelchOpen; // open for processing + Real m_fill; // refill average at this level + Real m_cutoff; // consider samples only above this level + Real m_clip; // never go below this level + MovingAverage m_moving_average; // Averaging engine. The stack length conditions the smoothness of AGC. +}; + +#endif /* INCLUDE_GPL_DSP_AGC_H_ */ diff --git a/sdrbase/dsp/bandpass.h b/sdrbase/dsp/bandpass.h new file mode 100644 index 000000000..3e0ffb147 --- /dev/null +++ b/sdrbase/dsp/bandpass.h @@ -0,0 +1,127 @@ +#ifndef INCLUDE_BANDPASS_H +#define INCLUDE_BANDPASS_H + +#define _USE_MATH_DEFINES +#include +#include "dsp/dsptypes.h" + +template class Bandpass { +public: + Bandpass() { } + + void create(int nTaps, double sampleRate, double lowCutoff, double highCutoff) + { + std::vector taps_lp; + std::vector taps_hp; + double wcl = 2.0 * M_PI * lowCutoff; + double Wcl = wcl / sampleRate; + double wch = 2.0 * M_PI * highCutoff; + double Wch = wch / sampleRate; + int i; + + // check constraints + if(!(nTaps & 1)) { + qDebug("Bandpass 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); + taps_lp.resize(nTaps / 2 + 1); + taps_hp.resize(nTaps / 2 + 1); + + // generate Sinc filter core + for(i = 0; i < nTaps / 2 + 1; i++) { + if(i == (nTaps - 1) / 2) { + taps_lp[i] = Wch / M_PI; + taps_hp[i] = -(Wcl / M_PI); + } + else { + taps_lp[i] = sin(((double)i - ((double)nTaps - 1.0) / 2.0) * Wch) / (((double)i - ((double)nTaps - 1.0) / 2.0) * M_PI); + taps_hp[i] = -sin(((double)i - ((double)nTaps - 1.0) / 2.0) * Wcl) / (((double)i - ((double)nTaps - 1.0) / 2.0) * M_PI); + } + } + + taps_hp[(nTaps - 1) / 2] += 1; + + // apply Hamming window and combine lowpass and highpass + for(i = 0; i < nTaps / 2 + 1; i++) { + taps_lp[i] *= 0.54 + 0.46 * cos((2.0 * M_PI * ((double)i - ((double)nTaps - 1.0) / 2.0)) / (double)nTaps); + taps_hp[i] *= 0.54 + 0.46 * cos((2.0 * M_PI * ((double)i - ((double)nTaps - 1.0) / 2.0)) / (double)nTaps); + m_taps[i] = -(taps_lp[i]+taps_hp[i]); + } + + m_taps[(nTaps - 1) / 2] += 1; + + // 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, n_taps, size; + + m_samples[m_ptr] = sample; + size = m_samples.size(); // Valgrind optim (2) + + while(b < 0) + { + b += size; + } + + n_taps = m_taps.size() - 1; // Valgrind optim + + for(i = 0; i < n_taps; i++) + { + acc += (m_samples[a] + m_samples[b]) * m_taps[i]; + a++; + + while (a >= size) + { + a -= size; + } + + b--; + + while(b < 0) + { + b += size; + } + } + + acc += m_samples[a] * m_taps[i]; + + m_ptr++; + + while (m_ptr >= size) + { + m_ptr -= size; + } + + return acc; + } + +private: + std::vector m_taps; + std::vector m_samples; + int m_ptr; +}; + +#endif // INCLUDE_BANDPASS_H diff --git a/sdrbase/dsp/channelizer.h b/sdrbase/dsp/channelizer.h new file mode 100644 index 000000000..a45b21fed --- /dev/null +++ b/sdrbase/dsp/channelizer.h @@ -0,0 +1,85 @@ +#ifndef INCLUDE_CHANNELIZER_H +#define INCLUDE_CHANNELIZER_H + +#include +#include +#include "dsp/samplesink.h" +#include "util/export.h" +#include "util/message.h" + +class MessageQueue; +class IntHalfbandFilter; + +class SDRANGEL_API Channelizer : public SampleSink { + Q_OBJECT +public: + class SDRANGEL_API MsgChannelizerNotification : public Message { + MESSAGE_CLASS_DECLARATION + + public: + MsgChannelizerNotification(int samplerate, qint64 frequencyOffset) : + Message(), + m_sampleRate(samplerate), + m_frequencyOffset(frequencyOffset) + { } + + int getSampleRate() const { return m_sampleRate; } + qint64 getFrequencyOffset() const { return m_frequencyOffset; } + + private: + int m_sampleRate; + qint64 m_frequencyOffset; + }; + + Channelizer(SampleSink* sampleSink); + virtual ~Channelizer(); + + void configure(MessageQueue* messageQueue, int sampleRate, int centerFrequency); + int getInputSampleRate() const { return m_inputSampleRate; } + + virtual void start(); + virtual void stop(); + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); + virtual bool handleMessage(const 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 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; + QMutex m_mutex; + + 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(); + +signals: + void inputSampleRateChanged(); +}; + +#endif // INCLUDE_CHANNELIZER_H diff --git a/sdrbase/dsp/channelmarker.h b/sdrbase/dsp/channelmarker.h new file mode 100644 index 000000000..66ed138ab --- /dev/null +++ b/sdrbase/dsp/channelmarker.h @@ -0,0 +1,62 @@ +#ifndef INCLUDE_CHANNELMARKER_H +#define INCLUDE_CHANNELMARKER_H + +#include +#include +#include "util/export.h" + +class SDRANGEL_API ChannelMarker : public QObject { + Q_OBJECT + +public: + typedef enum sidebands_e + { + dsb, + lsb, + usb + } sidebands_t; + + 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 setLowCutoff(int lowCutoff); + int getLowCutoff() const { return m_lowCutoff; } + + void setSidebands(sidebands_t sidebands); + sidebands_t getSidebands() const { return m_sidebands; } + + void setVisible(bool visible); + bool getVisible() const { return m_visible; } + + void setHighlighted(bool highlighted); + bool getHighlighted() const { return m_highlighted; } + + 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; + int m_lowCutoff; + sidebands_t m_sidebands; + bool m_visible; + bool m_highlighted; + QColor m_color; + +signals: + void changed(); +}; + +#endif // INCLUDE_CHANNELMARKER_H diff --git a/sdrbase/dsp/complex.h b/sdrbase/dsp/complex.h new file mode 100644 index 000000000..2653a591c --- /dev/null +++ b/sdrbase/dsp/complex.h @@ -0,0 +1,43 @@ +// ---------------------------------------------------------------------------- +// complex.h -- Complex arithmetic +// +// Copyright (C) 2006-2008 +// Dave Freese, W1HKJ +// Copyright (C) 2008 +// Stelios Bounanos, M0GLD +// +// This file is part of fldigi. +// +// Fldigi 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 3 of the License, or +// (at your option) any later version. +// +// Fldigi 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 fldigi. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef _COMPLEX_H +#define _COMPLEX_H + +#include +#include + +typedef std::complex cmplx; + +inline cmplx cmac (const cmplx *a, const cmplx *b, int ptr, int len) { + cmplx z; + ptr %= len; + for (int i = 0; i < len; i++) { + z += a[i] * b[ptr]; + ptr = (ptr + 1) % len; + } + return z; +} + +#endif diff --git a/sdrbase/dsp/ctcssdetector.h b/sdrbase/dsp/ctcssdetector.h new file mode 100644 index 000000000..e3799c07c --- /dev/null +++ b/sdrbase/dsp/ctcssdetector.h @@ -0,0 +1,88 @@ +/* + * ctcssdetector.h + * + * Created on: Jun 16, 2015 + * Author: f4exb + * See: http://www.embedded.com/design/connectivity/4025660/Detecting-CTCSS-tones-with-Goertzel-s-algorithm + */ + +#ifndef INCLUDE_GPL_DSP_CTCSSDETECTOR_H_ +#define INCLUDE_GPL_DSP_CTCSSDETECTOR_H_ + +#include "dsp/dsptypes.h" + +/** CTCSSDetector: Continuous Tone Coded Squelch System + * tone detector class based on the Modified Goertzel + * algorithm. + */ +class CTCSSDetector { +public: + // Constructors and Destructor + CTCSSDetector(); + // allows user defined CTCSS tone set + CTCSSDetector(int _nTones, Real *tones); + virtual ~CTCSSDetector(); + + // setup the basic parameters and coefficients + void setCoefficients( + int zN, // the algorithm "block" size + int SampleRate); // input signal sample rate + + // set the detection threshold + void setThreshold(double thold); + + // analyze a sample set and optionally filter + // the tone frequencies. + bool analyze(Real *sample); // input signal sample + + // get the number of defined tones. + int getNTones() const { + return nTones; + } + + // get the tone set + const Real *getToneSet() const + { + return toneSet; + } + + // get the currently detected tone, if any + bool getDetectedTone(int &maxTone) const + { + maxTone = maxPowerIndex; + return toneDetected; + } + + // Get the max power at the detected tone. + Real getMaxPower() const + { + return maxPower; + } + + void reset(); // reset the analysis algorithm + +protected: + // Override these to change behavior of the detector + virtual void initializePower(); + virtual void evaluatePower(); + void feedback(Real sample); + void feedForward(); + +private: + int N; + int sampleRate; + int nTones; + int samplesProcessed; + int maxPowerIndex; + bool toneDetected; + Real maxPower; + Real *k; + Real *coef; + Real *toneSet; + Real *u0; + Real *u1; + Real *power; +}; + + +#endif /* INCLUDE_GPL_DSP_CTCSSDETECTOR_H_ */ diff --git a/sdrbase/dsp/decimators.h b/sdrbase/dsp/decimators.h new file mode 100644 index 000000000..403e908fc --- /dev/null +++ b/sdrbase/dsp/decimators.h @@ -0,0 +1,1538 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_GPL_DSP_DECIMATORS_H_ +#define INCLUDE_GPL_DSP_DECIMATORS_H_ + +#include "dsp/dsptypes.h" +#include "dsp/inthalfbandfilter.h" + +template +struct decimation_shifts +{ + static const uint pre1 = 0; + static const uint pre2 = 0; + static const uint post2 = 0; + static const uint pre4 = 0; + static const uint post4 = 0; + static const uint pre8 = 0; + static const uint post8 = 0; + static const uint pre16 = 0; + static const uint post16 = 0; + static const uint pre32 = 0; + static const uint post32 = 0; + static const uint pre64 = 0; + static const uint post64 = 0; +}; + +template<> +struct decimation_shifts<16, 16> +{ + static const uint pre1 = 0; + static const uint pre2 = 0; + static const uint post2 = 1; + static const uint pre4 = 0; + static const uint post4 = 2; + static const uint pre8 = 0; + static const uint post8 = 3; + static const uint pre16 = 0; + static const uint post16 = 4; + static const uint pre32 = 0; + static const uint post32 = 5; + static const uint pre64 = 0; + static const uint post64 = 6; +}; + +template<> +struct decimation_shifts<16, 12> +{ + static const uint pre1 = 4; + static const uint pre2 = 3; + static const uint post2 = 0; + static const uint pre4 = 2; + static const uint post4 = 0; + static const uint pre8 = 1; + static const uint post8 = 0; + static const uint pre16 = 0; + static const uint post16 = 0; + static const uint pre32 = 0; + static const uint post32 = 1; + static const uint pre64 = 0; + static const uint post64 = 2; +}; + +template<> +struct decimation_shifts<16, 8> +{ + static const uint pre1 = 6; + static const uint pre2 = 5; + static const uint post2 = 0; + static const uint pre4 = 4; + static const uint post4 = 0; + static const uint pre8 = 3; + static const uint post8 = 0; + static const uint pre16 = 2; + static const uint post16 = 0; + static const uint pre32 = 1; + static const uint post32 = 0; + static const uint pre64 = 0; + static const uint post64 = 0; +}; + +template +class Decimators +{ +public: + void decimate1(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate2_u(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate2_inf(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate2_sup(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate2_cen(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate4_inf(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate4_sup(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate4_cen(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate8_inf(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate8_sup(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate8_cen(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate16_inf(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate16_sup(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate16_cen(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate32_inf(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate32_sup(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate32_cen(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate64_inf(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate64_sup(SampleVector::iterator* it, const T* buf, qint32 len); + void decimate64_cen(SampleVector::iterator* it, const T* buf, qint32 len); + +private: + IntHalfbandFilter m_decimator2; // 1st stages + IntHalfbandFilter m_decimator4; // 2nd stages + IntHalfbandFilter m_decimator8; // 3rd stages + IntHalfbandFilter m_decimator16; // 4th stages + IntHalfbandFilter m_decimator32; // 5th stages + IntHalfbandFilter m_decimator64; // 6th stages +}; + +template +void Decimators::decimate1(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 xreal, yimag; + + for (int pos = 0; pos < len - 1; pos += 2) + { + xreal = buf[pos+0]; + yimag = buf[pos+1]; + (**it).setReal(xreal << decimation_shifts::pre1); // Valgrind optim (2 - comment not repeated) + (**it).setImag(yimag << decimation_shifts::pre1); + ++(*it); // Valgrind optim (comment not repeated) + } +} + +template +void Decimators::decimate2_u(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 xreal, yimag; + + for (int pos = 0; pos < len - 7; pos += 8) + { + xreal = (buf[pos+0] - buf[pos+3]) << decimation_shifts::pre2; + yimag = (buf[pos+1] + buf[pos+2] - 255) << decimation_shifts::pre2; + (**it).setReal(xreal >> decimation_shifts::post2); + (**it).setImag(yimag >> decimation_shifts::post2); + ++(*it); + + xreal = (buf[pos+7] - buf[pos+4]) << decimation_shifts::pre2; + yimag = (255 - buf[pos+5] - buf[pos+6]) << decimation_shifts::pre2; + (**it).setReal(xreal >> decimation_shifts::post2); + (**it).setImag(yimag >> decimation_shifts::post2); + ++(*it); + } +} + +template +void Decimators::decimate2_inf(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 xreal, yimag; + + for (int pos = 0; pos < len - 7; pos += 8) + { + xreal = (buf[pos+0] - buf[pos+3]) << decimation_shifts::pre2; + yimag = (buf[pos+1] + buf[pos+2]) << decimation_shifts::pre2; + (**it).setReal(xreal >> decimation_shifts::post2); + (**it).setImag(yimag >> decimation_shifts::post2); + ++(*it); + + xreal = (buf[pos+7] - buf[pos+4]) << decimation_shifts::pre2; + yimag = (- buf[pos+5] - buf[pos+6]) << decimation_shifts::pre2; + (**it).setReal(xreal >> decimation_shifts::post2); + (**it).setImag(yimag >> decimation_shifts::post2); + ++(*it); + } +} + +template +void Decimators::decimate2_sup(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 xreal, yimag; + + for (int pos = 0; pos < len - 7; pos += 8) + { + xreal = (buf[pos+1] - buf[pos+2]) << decimation_shifts::pre2; + yimag = (- buf[pos+0] - buf[pos+3]) << decimation_shifts::pre2; + (**it).setReal(xreal >> decimation_shifts::post2); + (**it).setImag(yimag >> decimation_shifts::post2); + ++(*it); + + xreal = (buf[pos+6] - buf[pos+5]) << decimation_shifts::pre2; + yimag = (buf[pos+4] + buf[pos+7]) << decimation_shifts::pre2; + (**it).setReal(xreal >> decimation_shifts::post2); + (**it).setImag(yimag >> decimation_shifts::post2); + ++(*it); + } +} + +template +void Decimators::decimate4_inf(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 xreal, yimag; + + for (int pos = 0; pos < len - 7; pos += 8) + { + xreal = (buf[pos+0] - buf[pos+3] + buf[pos+7] - buf[pos+4]) << decimation_shifts::pre4; + yimag = (buf[pos+1] - buf[pos+5] + buf[pos+2] - buf[pos+6]) << decimation_shifts::pre4; + + (**it).setReal(xreal >> decimation_shifts::post4); + (**it).setImag(yimag >> decimation_shifts::post4); + + ++(*it); + } +} + +template +void Decimators::decimate4_sup(SampleVector::iterator* it, const T* buf, qint32 len) +{ + // Sup (USB): + // x y x y x y x y / x -> 1,-2,-5,6 / y -> -0,-3,4,7 + // [ rotate: 1, 0, -2, 3, -5, -4, 6, -7] + // Inf (LSB): + // x y x y x y x y / x -> 0,-3,-4,7 / y -> 1,2,-5,-6 + // [ rotate: 0, 1, -3, 2, -4, -5, 7, -6] + qint32 xreal, yimag; + + for (int pos = 0; pos < len - 7; pos += 8) + { + xreal = (buf[pos+1] - buf[pos+2] - buf[pos+5] + buf[pos+6]) << decimation_shifts::pre4; + yimag = (- buf[pos+0] - buf[pos+3] + buf[pos+4] + buf[pos+7]) << decimation_shifts::pre4; + + (**it).setReal(xreal >> decimation_shifts::post4); + (**it).setImag(yimag >> decimation_shifts::post4); + + ++(*it); + } +} + +template +void Decimators::decimate8_inf(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 xreal[2], yimag[2]; + + for (int pos = 0; pos < len - 15; pos += 8) + { + xreal[0] = (buf[pos+0] - buf[pos+3] + buf[pos+7] - buf[pos+4]) << decimation_shifts::pre8; + yimag[0] = (buf[pos+1] - buf[pos+5] + buf[pos+2] - buf[pos+6]) << decimation_shifts::pre8; + pos += 8; + + xreal[1] = (buf[pos+0] - buf[pos+3] + buf[pos+7] - buf[pos+4]) << decimation_shifts::pre8; + yimag[1] = (buf[pos+1] - buf[pos+5] + buf[pos+2] - buf[pos+6]) << decimation_shifts::pre8; + + m_decimator2.myDecimate(xreal[0], yimag[0], &xreal[1], &yimag[1]); + + (**it).setReal(xreal[1] >> decimation_shifts::post8); + (**it).setImag(yimag[1] >> decimation_shifts::post8); + + ++(*it); + } +} + +template +void Decimators::decimate8_sup(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 xreal[2], yimag[2]; + + for (int pos = 0; pos < len - 15; pos += 8) + { + xreal[0] = (buf[pos+1] - buf[pos+2] - buf[pos+5] + buf[pos+6]) << decimation_shifts::pre8; + yimag[0] = (- buf[pos+0] - buf[pos+3] + buf[pos+4] + buf[pos+7]) << decimation_shifts::pre8; + pos += 8; + + xreal[1] = (buf[pos+1] - buf[pos+2] - buf[pos+5] + buf[pos+6]) << decimation_shifts::pre8; + yimag[1] = (- buf[pos+0] - buf[pos+3] + buf[pos+4] + buf[pos+7]) << decimation_shifts::pre8; + + m_decimator2.myDecimate(xreal[0], yimag[0], &xreal[1], &yimag[1]); + + (**it).setReal(xreal[1] >> decimation_shifts::post8); + (**it).setImag(yimag[1] >> decimation_shifts::post8); + + ++(*it); + } +} + +template +void Decimators::decimate16_inf(SampleVector::iterator* it, const T* buf, qint32 len) +{ + // Offset tuning: 4x downsample and rotate, then + // downsample 4x more. [ rotate: 0, 1, -3, 2, -4, -5, 7, -6] + qint32 xreal[4], yimag[4]; + + for (int pos = 0; pos < len - 31; ) + { + for (int i = 0; i < 4; i++) + { + xreal[i] = (buf[pos+0] - buf[pos+3] + buf[pos+7] - buf[pos+4]) << decimation_shifts::pre16; + yimag[i] = (buf[pos+1] - buf[pos+5] + buf[pos+2] - buf[pos+6]) << decimation_shifts::pre16; + pos += 8; + } + + m_decimator2.myDecimate(xreal[0], yimag[0], &xreal[1], &yimag[1]); + m_decimator2.myDecimate(xreal[2], yimag[2], &xreal[3], &yimag[3]); + + m_decimator4.myDecimate(xreal[1], yimag[1], &xreal[3], &yimag[3]); + + (**it).setReal(xreal[3] >> decimation_shifts::post16); + (**it).setImag(yimag[3] >> decimation_shifts::post16); + + ++(*it); + } +} + +template +void Decimators::decimate16_sup(SampleVector::iterator* it, const T* buf, qint32 len) +{ + // Offset tuning: 4x downsample and rotate, then + // downsample 4x more. [ rotate: 1, 0, -2, 3, -5, -4, 6, -7] + qint32 xreal[4], yimag[4]; + + for (int pos = 0; pos < len - 31; ) + { + for (int i = 0; i < 4; i++) + { + xreal[i] = (buf[pos+1] - buf[pos+2] - buf[pos+5] + buf[pos+6]) << decimation_shifts::pre16; + yimag[i] = (buf[pos+4] + buf[pos+7] - buf[pos+0] - buf[pos+3]) << decimation_shifts::pre16; + pos += 8; + } + + m_decimator2.myDecimate(xreal[0], yimag[0], &xreal[1], &yimag[1]); + m_decimator2.myDecimate(xreal[2], yimag[2], &xreal[3], &yimag[3]); + + m_decimator4.myDecimate(xreal[1], yimag[1], &xreal[3], &yimag[3]); + + (**it).setReal(xreal[3] >> decimation_shifts::post16); + (**it).setImag(yimag[3] >> decimation_shifts::post16); + + ++(*it); + } +} + +template +void Decimators::decimate32_inf(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 xreal[8], yimag[8]; + + for (int pos = 0; pos < len - 63; ) + { + for (int i = 0; i < 8; i++) + { + xreal[i] = (buf[pos+0] - buf[pos+3] + buf[pos+7] - buf[pos+4]) << decimation_shifts::pre32; + yimag[i] = (buf[pos+1] - buf[pos+5] + buf[pos+2] - buf[pos+6]) << decimation_shifts::pre32; + pos += 8; + } + + m_decimator2.myDecimate(xreal[0], yimag[0], &xreal[1], &yimag[1]); + m_decimator2.myDecimate(xreal[2], yimag[2], &xreal[3], &yimag[3]); + m_decimator2.myDecimate(xreal[4], yimag[4], &xreal[5], &yimag[5]); + m_decimator2.myDecimate(xreal[6], yimag[6], &xreal[7], &yimag[7]); + + m_decimator4.myDecimate(xreal[1], yimag[1], &xreal[3], &yimag[3]); + m_decimator4.myDecimate(xreal[5], yimag[5], &xreal[7], &yimag[7]); + + m_decimator8.myDecimate(xreal[3], yimag[3], &xreal[7], &yimag[7]); + + (**it).setReal(xreal[7] >> decimation_shifts::post32); + (**it).setImag(yimag[7] >> decimation_shifts::post32); + + ++(*it); + } +} + +template +void Decimators::decimate32_sup(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 xreal[8], yimag[8]; + + for (int pos = 0; pos < len - 63; ) + { + for (int i = 0; i < 8; i++) + { + xreal[i] = (buf[pos+1] - buf[pos+2] - buf[pos+5] + buf[pos+6]) << decimation_shifts::pre32; + yimag[i] = (buf[pos+4] + buf[pos+7] - buf[pos+0] - buf[pos+3]) << decimation_shifts::pre32; + pos += 8; + } + + m_decimator2.myDecimate(xreal[0], yimag[0], &xreal[1], &yimag[1]); + m_decimator2.myDecimate(xreal[2], yimag[2], &xreal[3], &yimag[3]); + m_decimator2.myDecimate(xreal[4], yimag[4], &xreal[5], &yimag[5]); + m_decimator2.myDecimate(xreal[6], yimag[6], &xreal[7], &yimag[7]); + + m_decimator4.myDecimate(xreal[1], yimag[1], &xreal[3], &yimag[3]); + m_decimator4.myDecimate(xreal[5], yimag[5], &xreal[7], &yimag[7]); + + m_decimator8.myDecimate(xreal[3], yimag[3], &xreal[7], &yimag[7]); + + (**it).setReal(xreal[7] >> decimation_shifts::post32); + (**it).setImag(yimag[7] >> decimation_shifts::post32); + + ++(*it); + } +} + +template +void Decimators::decimate64_inf(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 xreal[16], yimag[16]; + + for (int pos = 0; pos < len - 127; ) + { + for (int i = 0; i < 16; i++) + { + xreal[i] = (buf[pos+0] - buf[pos+3] + buf[pos+7] - buf[pos+4]) << decimation_shifts::pre64; + yimag[i] = (buf[pos+1] - buf[pos+5] + buf[pos+2] - buf[pos+6]) << decimation_shifts::pre64; + pos += 8; + } + + m_decimator2.myDecimate(xreal[0], yimag[0], &xreal[1], &yimag[1]); + m_decimator2.myDecimate(xreal[2], yimag[2], &xreal[3], &yimag[3]); + m_decimator2.myDecimate(xreal[4], yimag[4], &xreal[5], &yimag[5]); + m_decimator2.myDecimate(xreal[6], yimag[6], &xreal[7], &yimag[7]); + m_decimator2.myDecimate(xreal[8], yimag[8], &xreal[9], &yimag[9]); + m_decimator2.myDecimate(xreal[10], yimag[10], &xreal[11], &yimag[11]); + m_decimator2.myDecimate(xreal[12], yimag[12], &xreal[13], &yimag[13]); + m_decimator2.myDecimate(xreal[14], yimag[14], &xreal[15], &yimag[15]); + + m_decimator4.myDecimate(xreal[1], yimag[1], &xreal[3], &yimag[3]); + m_decimator4.myDecimate(xreal[5], yimag[5], &xreal[7], &yimag[7]); + m_decimator4.myDecimate(xreal[9], yimag[9], &xreal[11], &yimag[11]); + m_decimator4.myDecimate(xreal[13], yimag[13], &xreal[15], &yimag[15]); + + m_decimator8.myDecimate(xreal[3], yimag[3], &xreal[7], &yimag[7]); + m_decimator8.myDecimate(xreal[11], yimag[11], &xreal[15], &yimag[15]); + + m_decimator16.myDecimate(xreal[7], yimag[7], &xreal[15], &yimag[15]); + + (**it).setReal(xreal[15] >> decimation_shifts::post64); + (**it).setImag(yimag[15] >> decimation_shifts::post64); + + ++(*it); + } +} + +template +void Decimators::decimate64_sup(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 xreal[16], yimag[16]; + + for (int pos = 0; pos < len - 127; ) + { + for (int i = 0; i < 16; i++) + { + xreal[i] = (buf[pos+1] - buf[pos+2] - buf[pos+5] + buf[pos+6]) << decimation_shifts::pre32; + yimag[i] = (buf[pos+4] + buf[pos+7] - buf[pos+0] - buf[pos+3]) << decimation_shifts::pre32; + pos += 8; + } + + m_decimator2.myDecimate(xreal[0], yimag[0], &xreal[1], &yimag[1]); + m_decimator2.myDecimate(xreal[2], yimag[2], &xreal[3], &yimag[3]); + m_decimator2.myDecimate(xreal[4], yimag[4], &xreal[5], &yimag[5]); + m_decimator2.myDecimate(xreal[6], yimag[6], &xreal[7], &yimag[7]); + m_decimator2.myDecimate(xreal[8], yimag[8], &xreal[9], &yimag[9]); + m_decimator2.myDecimate(xreal[10], yimag[10], &xreal[11], &yimag[11]); + m_decimator2.myDecimate(xreal[12], yimag[12], &xreal[13], &yimag[13]); + m_decimator2.myDecimate(xreal[14], yimag[14], &xreal[15], &yimag[15]); + + m_decimator4.myDecimate(xreal[1], yimag[1], &xreal[3], &yimag[3]); + m_decimator4.myDecimate(xreal[5], yimag[5], &xreal[7], &yimag[7]); + m_decimator4.myDecimate(xreal[9], yimag[9], &xreal[11], &yimag[11]); + m_decimator4.myDecimate(xreal[13], yimag[13], &xreal[15], &yimag[15]); + + m_decimator8.myDecimate(xreal[3], yimag[3], &xreal[7], &yimag[7]); + m_decimator8.myDecimate(xreal[11], yimag[11], &xreal[15], &yimag[15]); + + m_decimator16.myDecimate(xreal[7], yimag[7], &xreal[15], &yimag[15]); + + (**it).setReal(xreal[15] >> decimation_shifts::post64); + (**it).setImag(yimag[15] >> decimation_shifts::post64); + + ++(*it); + } +} + +template +void Decimators::decimate2_cen(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 intbuf[2]; + + for (int pos = 0; pos < len - 3; pos += 4) + { + intbuf[0] = buf[pos+2] << decimation_shifts::pre2; + intbuf[1] = buf[pos+3] << decimation_shifts::pre2; + + m_decimator2.myDecimate( + buf[pos+0] << decimation_shifts::pre2, + buf[pos+1] << decimation_shifts::pre2, + &intbuf[0], + &intbuf[1]); + + (**it).setReal(intbuf[0] >> decimation_shifts::post2); + (**it).setImag(intbuf[1] >> decimation_shifts::post2); + ++(*it); + } +} + +template +void Decimators::decimate4_cen(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 intbuf[4]; + + for (int pos = 0; pos < len - 7; pos += 8) + { + intbuf[0] = buf[pos+2] << decimation_shifts::pre4; + intbuf[1] = buf[pos+3] << decimation_shifts::pre4; + intbuf[2] = buf[pos+6] << decimation_shifts::pre4; + intbuf[3] = buf[pos+7] << decimation_shifts::pre4; + + m_decimator2.myDecimate( + buf[pos+0] << decimation_shifts::pre4, + buf[pos+1] << decimation_shifts::pre4, + &intbuf[0], + &intbuf[1]); + m_decimator2.myDecimate( + buf[pos+4] << decimation_shifts::pre4, + buf[pos+5] << decimation_shifts::pre4, + &intbuf[2], + &intbuf[3]); + + m_decimator4.myDecimate( + intbuf[0], + intbuf[1], + &intbuf[2], + &intbuf[3]); + + (**it).setReal(intbuf[2] >> decimation_shifts::post4); + (**it).setImag(intbuf[3] >> decimation_shifts::post4); + ++(*it); + } +} + +template +void Decimators::decimate8_cen(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 intbuf[8]; + + for (int pos = 0; pos < len - 15; pos += 16) + { + intbuf[0] = buf[pos+2] << decimation_shifts::pre8; + intbuf[1] = buf[pos+3] << decimation_shifts::pre8; + intbuf[2] = buf[pos+6] << decimation_shifts::pre8; + intbuf[3] = buf[pos+7] << decimation_shifts::pre8; + intbuf[4] = buf[pos+10] << decimation_shifts::pre8; + intbuf[5] = buf[pos+11] << decimation_shifts::pre8; + intbuf[6] = buf[pos+14] << decimation_shifts::pre8; + intbuf[7] = buf[pos+15] << decimation_shifts::pre8; + + m_decimator2.myDecimate( + buf[pos+0] << decimation_shifts::pre8, + buf[pos+1] << decimation_shifts::pre8, + &intbuf[0], + &intbuf[1]); + m_decimator2.myDecimate( + buf[pos+4] << decimation_shifts::pre8, + buf[pos+5] << decimation_shifts::pre8, + &intbuf[2], + &intbuf[3]); + m_decimator2.myDecimate( + buf[pos+8] << decimation_shifts::pre8, + buf[pos+9] << decimation_shifts::pre8, + &intbuf[4], + &intbuf[5]); + m_decimator2.myDecimate( + buf[pos+12] << decimation_shifts::pre8, + buf[pos+13] << decimation_shifts::pre8, + &intbuf[6], + &intbuf[7]); + + m_decimator4.myDecimate( + intbuf[0], + intbuf[1], + &intbuf[2], + &intbuf[3]); + m_decimator4.myDecimate( + intbuf[4], + intbuf[5], + &intbuf[6], + &intbuf[7]); + + m_decimator8.myDecimate( + intbuf[2], + intbuf[3], + &intbuf[6], + &intbuf[7]); + + (**it).setReal(intbuf[6] >> decimation_shifts::post8); + (**it).setImag(intbuf[7] >> decimation_shifts::post8); + ++(*it); + } +} + +template +void Decimators::decimate16_cen(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 intbuf[16]; + + for (int pos = 0; pos < len - 31; pos += 32) + { + intbuf[0] = buf[pos+2] << decimation_shifts::pre16; + intbuf[1] = buf[pos+3] << decimation_shifts::pre16; + intbuf[2] = buf[pos+6] << decimation_shifts::pre16; + intbuf[3] = buf[pos+7] << decimation_shifts::pre16; + intbuf[4] = buf[pos+10] << decimation_shifts::pre16; + intbuf[5] = buf[pos+11] << decimation_shifts::pre16; + intbuf[6] = buf[pos+14] << decimation_shifts::pre16; + intbuf[7] = buf[pos+15] << decimation_shifts::pre16; + intbuf[8] = buf[pos+18] << decimation_shifts::pre16; + intbuf[9] = buf[pos+19] << decimation_shifts::pre16; + intbuf[10] = buf[pos+22] << decimation_shifts::pre16; + intbuf[11] = buf[pos+23] << decimation_shifts::pre16; + intbuf[12] = buf[pos+26] << decimation_shifts::pre16; + intbuf[13] = buf[pos+27] << decimation_shifts::pre16; + intbuf[14] = buf[pos+30] << decimation_shifts::pre16; + intbuf[15] = buf[pos+31] << decimation_shifts::pre16; + + m_decimator2.myDecimate( + buf[pos+0] << decimation_shifts::pre16, + buf[pos+1] << decimation_shifts::pre16, + &intbuf[0], + &intbuf[1]); + m_decimator2.myDecimate( + buf[pos+4] << decimation_shifts::pre16, + buf[pos+5] << decimation_shifts::pre16, + &intbuf[2], + &intbuf[3]); + m_decimator2.myDecimate( + buf[pos+8] << decimation_shifts::pre16, + buf[pos+9] << decimation_shifts::pre16, + &intbuf[4], + &intbuf[5]); + m_decimator2.myDecimate( + buf[pos+12] << decimation_shifts::pre16, + buf[pos+13] << decimation_shifts::pre16, + &intbuf[6], + &intbuf[7]); + m_decimator2.myDecimate( + buf[pos+16] << decimation_shifts::pre16, + buf[pos+17] << decimation_shifts::pre16, + &intbuf[8], + &intbuf[9]); + m_decimator2.myDecimate( + buf[pos+20] << decimation_shifts::pre16, + buf[pos+21] << decimation_shifts::pre16, + &intbuf[10], + &intbuf[11]); + m_decimator2.myDecimate( + buf[pos+24] << decimation_shifts::pre16, + buf[pos+25] << decimation_shifts::pre16, + &intbuf[12], + &intbuf[13]); + m_decimator2.myDecimate( + buf[pos+28] << decimation_shifts::pre16, + buf[pos+29] << decimation_shifts::pre16, + &intbuf[14], + &intbuf[15]); + + m_decimator4.myDecimate( + intbuf[0], + intbuf[1], + &intbuf[2], + &intbuf[3]); + m_decimator4.myDecimate( + intbuf[4], + intbuf[5], + &intbuf[6], + &intbuf[7]); + m_decimator4.myDecimate( + intbuf[8], + intbuf[9], + &intbuf[10], + &intbuf[11]); + m_decimator4.myDecimate( + intbuf[12], + intbuf[13], + &intbuf[14], + &intbuf[15]); + + m_decimator8.myDecimate( + intbuf[2], + intbuf[3], + &intbuf[6], + &intbuf[7]); + m_decimator8.myDecimate( + intbuf[10], + intbuf[11], + &intbuf[14], + &intbuf[15]); + + m_decimator16.myDecimate( + intbuf[6], + intbuf[7], + &intbuf[14], + &intbuf[15]); + + (**it).setReal(intbuf[14] >> decimation_shifts::post16); + (**it).setImag(intbuf[15] >> decimation_shifts::post16); + ++(*it); + } +} + +template +void Decimators::decimate32_cen(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 intbuf[32]; + + for (int pos = 0; pos < len - 63; pos += 64) + { + intbuf[0] = buf[pos+2] << decimation_shifts::pre32; + intbuf[1] = buf[pos+3] << decimation_shifts::pre32; + intbuf[2] = buf[pos+6] << decimation_shifts::pre32; + intbuf[3] = buf[pos+7] << decimation_shifts::pre32; + intbuf[4] = buf[pos+10] << decimation_shifts::pre32; + intbuf[5] = buf[pos+11] << decimation_shifts::pre32; + intbuf[6] = buf[pos+14] << decimation_shifts::pre32; + intbuf[7] = buf[pos+15] << decimation_shifts::pre32; + intbuf[8] = buf[pos+18] << decimation_shifts::pre32; + intbuf[9] = buf[pos+19] << decimation_shifts::pre32; + intbuf[10] = buf[pos+22] << decimation_shifts::pre32; + intbuf[11] = buf[pos+23] << decimation_shifts::pre32; + intbuf[12] = buf[pos+26] << decimation_shifts::pre32; + intbuf[13] = buf[pos+27] << decimation_shifts::pre32; + intbuf[14] = buf[pos+30] << decimation_shifts::pre32; + intbuf[15] = buf[pos+31] << decimation_shifts::pre32; + intbuf[16] = buf[pos+34] << decimation_shifts::pre32; + intbuf[17] = buf[pos+35] << decimation_shifts::pre32; + intbuf[18] = buf[pos+38] << decimation_shifts::pre32; + intbuf[19] = buf[pos+39] << decimation_shifts::pre32; + intbuf[20] = buf[pos+42] << decimation_shifts::pre32; + intbuf[21] = buf[pos+43] << decimation_shifts::pre32; + intbuf[22] = buf[pos+46] << decimation_shifts::pre32; + intbuf[23] = buf[pos+47] << decimation_shifts::pre32; + intbuf[24] = buf[pos+50] << decimation_shifts::pre32; + intbuf[25] = buf[pos+51] << decimation_shifts::pre32; + intbuf[26] = buf[pos+54] << decimation_shifts::pre32; + intbuf[27] = buf[pos+55] << decimation_shifts::pre32; + intbuf[28] = buf[pos+58] << decimation_shifts::pre32; + intbuf[29] = buf[pos+59] << decimation_shifts::pre32; + intbuf[30] = buf[pos+62] << decimation_shifts::pre32; + intbuf[31] = buf[pos+63] << decimation_shifts::pre32; + + m_decimator2.myDecimate( + buf[pos+0] << decimation_shifts::pre32, + buf[pos+1] << decimation_shifts::pre32, + &intbuf[0], + &intbuf[1]); + m_decimator2.myDecimate( + buf[pos+4] << decimation_shifts::pre32, + buf[pos+5] << decimation_shifts::pre32, + &intbuf[2], + &intbuf[3]); + m_decimator2.myDecimate( + buf[pos+8] << decimation_shifts::pre32, + buf[pos+9] << decimation_shifts::pre32, + &intbuf[4], + &intbuf[5]); + m_decimator2.myDecimate( + buf[pos+12] << decimation_shifts::pre32, + buf[pos+13] << decimation_shifts::pre32, + &intbuf[6], + &intbuf[7]); + m_decimator2.myDecimate( + buf[pos+16] << decimation_shifts::pre32, + buf[pos+17] << decimation_shifts::pre32, + &intbuf[8], + &intbuf[9]); + m_decimator2.myDecimate( + buf[pos+20] << decimation_shifts::pre32, + buf[pos+21] << decimation_shifts::pre32, + &intbuf[10], + &intbuf[11]); + m_decimator2.myDecimate( + buf[pos+24] << decimation_shifts::pre32, + buf[pos+25] << decimation_shifts::pre32, + &intbuf[12], + &intbuf[13]); + m_decimator2.myDecimate( + buf[pos+28] << decimation_shifts::pre32, + buf[pos+29] << decimation_shifts::pre32, + &intbuf[14], + &intbuf[15]); + m_decimator2.myDecimate( + buf[pos+32] << decimation_shifts::pre32, + buf[pos+33] << decimation_shifts::pre32, + &intbuf[16], + &intbuf[17]); + m_decimator2.myDecimate( + buf[pos+36] << decimation_shifts::pre32, + buf[pos+37] << decimation_shifts::pre32, + &intbuf[18], + &intbuf[19]); + m_decimator2.myDecimate( + buf[pos+40] << decimation_shifts::pre32, + buf[pos+41] << decimation_shifts::pre32, + &intbuf[20], + &intbuf[21]); + m_decimator2.myDecimate( + buf[pos+44] << decimation_shifts::pre32, + buf[pos+45] << decimation_shifts::pre32, + &intbuf[22], + &intbuf[23]); + m_decimator2.myDecimate( + buf[pos+48] << decimation_shifts::pre32, + buf[pos+49] << decimation_shifts::pre32, + &intbuf[24], + &intbuf[25]); + m_decimator2.myDecimate( + buf[pos+52] << decimation_shifts::pre32, + buf[pos+53] << decimation_shifts::pre32, + &intbuf[26], + &intbuf[27]); + m_decimator2.myDecimate( + buf[pos+56] << decimation_shifts::pre32, + buf[pos+57] << decimation_shifts::pre32, + &intbuf[28], + &intbuf[29]); + m_decimator2.myDecimate( + buf[pos+60] << decimation_shifts::pre32, + buf[pos+61] << decimation_shifts::pre32, + &intbuf[30], + &intbuf[31]); + + m_decimator4.myDecimate( + intbuf[0], + intbuf[1], + &intbuf[2], + &intbuf[3]); + m_decimator4.myDecimate( + intbuf[4], + intbuf[5], + &intbuf[6], + &intbuf[7]); + m_decimator4.myDecimate( + intbuf[8], + intbuf[9], + &intbuf[10], + &intbuf[11]); + m_decimator4.myDecimate( + intbuf[12], + intbuf[13], + &intbuf[14], + &intbuf[15]); + m_decimator4.myDecimate( + intbuf[16], + intbuf[17], + &intbuf[18], + &intbuf[19]); + m_decimator4.myDecimate( + intbuf[20], + intbuf[21], + &intbuf[22], + &intbuf[23]); + m_decimator4.myDecimate( + intbuf[24], + intbuf[25], + &intbuf[26], + &intbuf[27]); + m_decimator4.myDecimate( + intbuf[28], + intbuf[29], + &intbuf[30], + &intbuf[31]); + + m_decimator8.myDecimate( + intbuf[2], + intbuf[3], + &intbuf[6], + &intbuf[7]); + m_decimator8.myDecimate( + intbuf[10], + intbuf[11], + &intbuf[14], + &intbuf[15]); + m_decimator8.myDecimate( + intbuf[18], + intbuf[19], + &intbuf[22], + &intbuf[23]); + m_decimator8.myDecimate( + intbuf[26], + intbuf[27], + &intbuf[30], + &intbuf[31]); + + m_decimator16.myDecimate( + intbuf[6], + intbuf[7], + &intbuf[14], + &intbuf[15]); + m_decimator16.myDecimate( + intbuf[22], + intbuf[23], + &intbuf[30], + &intbuf[31]); + + m_decimator32.myDecimate( + intbuf[14], + intbuf[15], + &intbuf[30], + &intbuf[31]); + + (**it).setReal(intbuf[30] >> decimation_shifts::post32); + (**it).setImag(intbuf[31] >> decimation_shifts::post32); + ++(*it); + } +} + + +template +void Decimators::decimate64_cen(SampleVector::iterator* it, const T* buf, qint32 len) +{ + qint32 intbuf[64]; + + for (int pos = 0; pos < len - 127; pos += 128) + { + intbuf[0] = buf[pos+2] << decimation_shifts::pre64; + intbuf[1] = buf[pos+3] << decimation_shifts::pre64; + intbuf[2] = buf[pos+6] << decimation_shifts::pre64; + intbuf[3] = buf[pos+7] << decimation_shifts::pre64; + intbuf[4] = buf[pos+10] << decimation_shifts::pre64; + intbuf[5] = buf[pos+11] << decimation_shifts::pre64; + intbuf[6] = buf[pos+14] << decimation_shifts::pre64; + intbuf[7] = buf[pos+15] << decimation_shifts::pre64; + intbuf[8] = buf[pos+18] << decimation_shifts::pre64; + intbuf[9] = buf[pos+19] << decimation_shifts::pre64; + intbuf[10] = buf[pos+22] << decimation_shifts::pre64; + intbuf[11] = buf[pos+23] << decimation_shifts::pre64; + intbuf[12] = buf[pos+26] << decimation_shifts::pre64; + intbuf[13] = buf[pos+27] << decimation_shifts::pre64; + intbuf[14] = buf[pos+30] << decimation_shifts::pre64; + intbuf[15] = buf[pos+31] << decimation_shifts::pre64; + intbuf[16] = buf[pos+34] << decimation_shifts::pre64; + intbuf[17] = buf[pos+35] << decimation_shifts::pre64; + intbuf[18] = buf[pos+38] << decimation_shifts::pre64; + intbuf[19] = buf[pos+39] << decimation_shifts::pre64; + intbuf[20] = buf[pos+42] << decimation_shifts::pre64; + intbuf[21] = buf[pos+43] << decimation_shifts::pre64; + intbuf[22] = buf[pos+46] << decimation_shifts::pre64; + intbuf[23] = buf[pos+47] << decimation_shifts::pre64; + intbuf[24] = buf[pos+50] << decimation_shifts::pre64; + intbuf[25] = buf[pos+51] << decimation_shifts::pre64; + intbuf[26] = buf[pos+54] << decimation_shifts::pre64; + intbuf[27] = buf[pos+55] << decimation_shifts::pre64; + intbuf[28] = buf[pos+58] << decimation_shifts::pre64; + intbuf[29] = buf[pos+59] << decimation_shifts::pre64; + intbuf[30] = buf[pos+62] << decimation_shifts::pre64; + intbuf[31] = buf[pos+63] << decimation_shifts::pre64; + + intbuf[32] = buf[pos+66] << decimation_shifts::pre64; + intbuf[33] = buf[pos+67] << decimation_shifts::pre64; + intbuf[34] = buf[pos+70] << decimation_shifts::pre64; + intbuf[35] = buf[pos+71] << decimation_shifts::pre64; + intbuf[36] = buf[pos+74] << decimation_shifts::pre64; + intbuf[37] = buf[pos+75] << decimation_shifts::pre64; + intbuf[38] = buf[pos+78] << decimation_shifts::pre64; + intbuf[39] = buf[pos+79] << decimation_shifts::pre64; + intbuf[40] = buf[pos+82] << decimation_shifts::pre64; + intbuf[41] = buf[pos+83] << decimation_shifts::pre64; + intbuf[42] = buf[pos+86] << decimation_shifts::pre64; + intbuf[43] = buf[pos+87] << decimation_shifts::pre64; + intbuf[44] = buf[pos+90] << decimation_shifts::pre64; + intbuf[45] = buf[pos+91] << decimation_shifts::pre64; + intbuf[46] = buf[pos+94] << decimation_shifts::pre64; + intbuf[47] = buf[pos+95] << decimation_shifts::pre64; + intbuf[48] = buf[pos+98] << decimation_shifts::pre64; + intbuf[49] = buf[pos+99] << decimation_shifts::pre64; + intbuf[50] = buf[pos+102] << decimation_shifts::pre64; + intbuf[51] = buf[pos+103] << decimation_shifts::pre64; + intbuf[52] = buf[pos+106] << decimation_shifts::pre64; + intbuf[53] = buf[pos+107] << decimation_shifts::pre64; + intbuf[54] = buf[pos+110] << decimation_shifts::pre64; + intbuf[55] = buf[pos+111] << decimation_shifts::pre64; + intbuf[56] = buf[pos+114] << decimation_shifts::pre64; + intbuf[57] = buf[pos+115] << decimation_shifts::pre64; + intbuf[58] = buf[pos+118] << decimation_shifts::pre64; + intbuf[59] = buf[pos+119] << decimation_shifts::pre64; + intbuf[60] = buf[pos+122] << decimation_shifts::pre64; + intbuf[61] = buf[pos+123] << decimation_shifts::pre64; + intbuf[62] = buf[pos+126] << decimation_shifts::pre64; + intbuf[63] = buf[pos+127] << decimation_shifts::pre64; + + m_decimator2.myDecimate( + buf[pos+0] << decimation_shifts::pre64, + buf[pos+1] << decimation_shifts::pre64, + &intbuf[0], + &intbuf[1]); + m_decimator2.myDecimate( + buf[pos+4] << decimation_shifts::pre64, + buf[pos+5] << decimation_shifts::pre64, + &intbuf[2], + &intbuf[3]); + m_decimator2.myDecimate( + buf[pos+8] << decimation_shifts::pre64, + buf[pos+9] << decimation_shifts::pre64, + &intbuf[4], + &intbuf[5]); + m_decimator2.myDecimate( + buf[pos+12] << decimation_shifts::pre64, + buf[pos+13] << decimation_shifts::pre64, + &intbuf[6], + &intbuf[7]); + m_decimator2.myDecimate( + buf[pos+16] << decimation_shifts::pre64, + buf[pos+17] << decimation_shifts::pre64, + &intbuf[8], + &intbuf[9]); + m_decimator2.myDecimate( + buf[pos+20] << decimation_shifts::pre64, + buf[pos+21] << decimation_shifts::pre64, + &intbuf[10], + &intbuf[11]); + m_decimator2.myDecimate( + buf[pos+24] << decimation_shifts::pre64, + buf[pos+25] << decimation_shifts::pre64, + &intbuf[12], + &intbuf[13]); + m_decimator2.myDecimate( + buf[pos+28] << decimation_shifts::pre64, + buf[pos+29] << decimation_shifts::pre64, + &intbuf[14], + &intbuf[15]); + m_decimator2.myDecimate( + buf[pos+32] << decimation_shifts::pre64, + buf[pos+33] << decimation_shifts::pre64, + &intbuf[16], + &intbuf[17]); + m_decimator2.myDecimate( + buf[pos+36] << decimation_shifts::pre64, + buf[pos+37] << decimation_shifts::pre64, + &intbuf[18], + &intbuf[19]); + m_decimator2.myDecimate( + buf[pos+40] << decimation_shifts::pre64, + buf[pos+41] << decimation_shifts::pre64, + &intbuf[20], + &intbuf[21]); + m_decimator2.myDecimate( + buf[pos+44] << decimation_shifts::pre64, + buf[pos+45] << decimation_shifts::pre64, + &intbuf[22], + &intbuf[23]); + m_decimator2.myDecimate( + buf[pos+48] << decimation_shifts::pre64, + buf[pos+49] << decimation_shifts::pre64, + &intbuf[24], + &intbuf[25]); + m_decimator2.myDecimate( + buf[pos+52] << decimation_shifts::pre64, + buf[pos+53] << decimation_shifts::pre64, + &intbuf[26], + &intbuf[27]); + m_decimator2.myDecimate( + buf[pos+56] << decimation_shifts::pre64, + buf[pos+57] << decimation_shifts::pre64, + &intbuf[28], + &intbuf[29]); + m_decimator2.myDecimate( + buf[pos+60] << decimation_shifts::pre64, + buf[pos+61] << decimation_shifts::pre64, + &intbuf[30], + &intbuf[31]); + m_decimator2.myDecimate( + buf[pos+64] << decimation_shifts::pre64, + buf[pos+65] << decimation_shifts::pre64, + &intbuf[32], + &intbuf[33]); + m_decimator2.myDecimate( + buf[pos+68] << decimation_shifts::pre64, + buf[pos+69] << decimation_shifts::pre64, + &intbuf[34], + &intbuf[35]); + m_decimator2.myDecimate( + buf[pos+72] << decimation_shifts::pre64, + buf[pos+73] << decimation_shifts::pre64, + &intbuf[36], + &intbuf[37]); + m_decimator2.myDecimate( + buf[pos+76] << decimation_shifts::pre64, + buf[pos+77] << decimation_shifts::pre64, + &intbuf[38], + &intbuf[39]); + m_decimator2.myDecimate( + buf[pos+80] << decimation_shifts::pre64, + buf[pos+81] << decimation_shifts::pre64, + &intbuf[40], + &intbuf[41]); + m_decimator2.myDecimate( + buf[pos+84] << decimation_shifts::pre64, + buf[pos+85] << decimation_shifts::pre64, + &intbuf[42], + &intbuf[43]); + m_decimator2.myDecimate( + buf[pos+88] << decimation_shifts::pre64, + buf[pos+89] << decimation_shifts::pre64, + &intbuf[44], + &intbuf[45]); + m_decimator2.myDecimate( + buf[pos+92] << decimation_shifts::pre64, + buf[pos+93] << decimation_shifts::pre64, + &intbuf[46], + &intbuf[47]); + m_decimator2.myDecimate( + buf[pos+96] << decimation_shifts::pre64, + buf[pos+97] << decimation_shifts::pre64, + &intbuf[48], + &intbuf[49]); + m_decimator2.myDecimate( + buf[pos+100] << decimation_shifts::pre64, + buf[pos+101] << decimation_shifts::pre64, + &intbuf[50], + &intbuf[51]); + m_decimator2.myDecimate( + buf[pos+104] << decimation_shifts::pre64, + buf[pos+105] << decimation_shifts::pre64, + &intbuf[52], + &intbuf[53]); + m_decimator2.myDecimate( + buf[pos+108] << decimation_shifts::pre64, + buf[pos+109] << decimation_shifts::pre64, + &intbuf[54], + &intbuf[55]); + m_decimator2.myDecimate( + buf[pos+112] << decimation_shifts::pre64, + buf[pos+113] << decimation_shifts::pre64, + &intbuf[56], + &intbuf[57]); + m_decimator2.myDecimate( + buf[pos+116] << decimation_shifts::pre64, + buf[pos+117] << decimation_shifts::pre64, + &intbuf[58], + &intbuf[59]); + m_decimator2.myDecimate( + buf[pos+120] << decimation_shifts::pre64, + buf[pos+121] << decimation_shifts::pre64, + &intbuf[60], + &intbuf[61]); + m_decimator2.myDecimate( + buf[pos+124] << decimation_shifts::pre64, + buf[pos+125] << decimation_shifts::pre64, + &intbuf[62], + &intbuf[63]); + + m_decimator4.myDecimate( + intbuf[0], + intbuf[1], + &intbuf[2], + &intbuf[3]); + m_decimator4.myDecimate( + intbuf[4], + intbuf[5], + &intbuf[6], + &intbuf[7]); + m_decimator4.myDecimate( + intbuf[8], + intbuf[9], + &intbuf[10], + &intbuf[11]); + m_decimator4.myDecimate( + intbuf[12], + intbuf[13], + &intbuf[14], + &intbuf[15]); + m_decimator4.myDecimate( + intbuf[16], + intbuf[17], + &intbuf[18], + &intbuf[19]); + m_decimator4.myDecimate( + intbuf[20], + intbuf[21], + &intbuf[22], + &intbuf[23]); + m_decimator4.myDecimate( + intbuf[24], + intbuf[25], + &intbuf[26], + &intbuf[27]); + m_decimator4.myDecimate( + intbuf[28], + intbuf[29], + &intbuf[30], + &intbuf[31]); + m_decimator4.myDecimate( + intbuf[32], + intbuf[33], + &intbuf[34], + &intbuf[35]); + m_decimator4.myDecimate( + intbuf[36], + intbuf[37], + &intbuf[38], + &intbuf[39]); + m_decimator4.myDecimate( + intbuf[40], + intbuf[41], + &intbuf[42], + &intbuf[43]); + m_decimator4.myDecimate( + intbuf[44], + intbuf[45], + &intbuf[46], + &intbuf[47]); + m_decimator4.myDecimate( + intbuf[48], + intbuf[49], + &intbuf[50], + &intbuf[51]); + m_decimator4.myDecimate( + intbuf[52], + intbuf[53], + &intbuf[54], + &intbuf[55]); + m_decimator4.myDecimate( + intbuf[56], + intbuf[57], + &intbuf[58], + &intbuf[59]); + m_decimator4.myDecimate( + intbuf[60], + intbuf[61], + &intbuf[62], + &intbuf[63]); + + m_decimator8.myDecimate( + intbuf[2], + intbuf[3], + &intbuf[6], + &intbuf[7]); + m_decimator8.myDecimate( + intbuf[10], + intbuf[11], + &intbuf[14], + &intbuf[15]); + m_decimator8.myDecimate( + intbuf[18], + intbuf[19], + &intbuf[22], + &intbuf[23]); + m_decimator8.myDecimate( + intbuf[26], + intbuf[27], + &intbuf[30], + &intbuf[31]); + m_decimator8.myDecimate( + intbuf[34], + intbuf[35], + &intbuf[38], + &intbuf[39]); + m_decimator8.myDecimate( + intbuf[42], + intbuf[43], + &intbuf[46], + &intbuf[47]); + m_decimator8.myDecimate( + intbuf[50], + intbuf[51], + &intbuf[54], + &intbuf[55]); + m_decimator8.myDecimate( + intbuf[58], + intbuf[59], + &intbuf[62], + &intbuf[63]); + + m_decimator16.myDecimate( + intbuf[6], + intbuf[7], + &intbuf[14], + &intbuf[15]); + m_decimator16.myDecimate( + intbuf[22], + intbuf[23], + &intbuf[30], + &intbuf[31]); + m_decimator16.myDecimate( + intbuf[38], + intbuf[39], + &intbuf[46], + &intbuf[47]); + m_decimator16.myDecimate( + intbuf[54], + intbuf[55], + &intbuf[62], + &intbuf[63]); + + m_decimator32.myDecimate( + intbuf[14], + intbuf[15], + &intbuf[30], + &intbuf[31]); + m_decimator32.myDecimate( + intbuf[46], + intbuf[47], + &intbuf[62], + &intbuf[63]); + + m_decimator64.myDecimate( + intbuf[30], + intbuf[31], + &intbuf[62], + &intbuf[63]); + + (**it).setReal(intbuf[62] >> decimation_shifts::post64); + (**it).setImag(intbuf[63] >> decimation_shifts::post64); + ++(*it); + } +} + +/* +template +void Decimators::decimate2_cen(SampleVector::iterator* it, const T* buf, qint32 len) +{ + int pos = 0; + + while (pos < len - 1) + { + qint32 x0 = buf[pos+0] << decimation_shifts::pre2; + qint32 y0 = buf[pos+1] << decimation_shifts::pre2; + pos += 2; + + if (m_decimator2.workDecimateCenter(&x0, &y0)) + { + (**it).setReal(x0 >> decimation_shifts::post2); + (**it).setImag(y0 >> decimation_shifts::post2); + ++(*it); + } + } +} + +template +void Decimators::decimate4_cen(SampleVector::iterator* it, const T* buf, qint32 len) +{ + int pos = 0; + + while (pos < len - 1) + { + qint32 x0 = buf[pos+0] << decimation_shifts::pre4; + qint32 y0 = buf[pos+1] << decimation_shifts::pre4; + pos += 2; + + if (m_decimator2.workDecimateCenter(&x0, &y0)) + { + qint32 x1 = x0; + qint32 y1 = y0; + + if (m_decimator4.workDecimateCenter(&x1, &y1)) + { + (**it).setReal(x0 >> decimation_shifts::post4); + (**it).setImag(y0 >> decimation_shifts::post4); + ++(*it); + } + } + } +} + +template +void Decimators::decimate8_cen(SampleVector::iterator* it, const T* buf, qint32 len) +{ + int pos = 0; + + while (pos < len - 1) + { + qint32 x0 = buf[pos+0] << decimation_shifts::pre8; + qint32 y0 = buf[pos+1] << decimation_shifts::pre8; + pos += 2; + + if (m_decimator2.workDecimateCenter(&x0, &y0)) + { + qint32 x1 = x0; + qint32 y1 = y0; + + if (m_decimator4.workDecimateCenter(&x1, &y1)) + { + qint32 x2 = x1; + qint32 y2 = y1; + + if (m_decimator8.workDecimateCenter(&x2, &y2)) + { + (**it).setReal(x2 >> decimation_shifts::post8); + (**it).setImag(y2 >> decimation_shifts::post8); + ++(*it); + } + } + } + } +} + +template +void Decimators::decimate16_cen(SampleVector::iterator* it, const T* buf, qint32 len) +{ + int pos = 0; + + while (pos < len - 1) + { + qint32 x0 = buf[pos+0] << decimation_shifts::pre16; + qint32 y0 = buf[pos+1] << decimation_shifts::pre16; + pos += 2; + + if (m_decimator2.workDecimateCenter(&x0, &y0)) + { + qint32 x1 = x0; + qint32 y1 = y0; + + if (m_decimator4.workDecimateCenter(&x1, &y1)) + { + qint32 x2 = x1; + qint32 y2 = y1; + + if (m_decimator8.workDecimateCenter(&x2, &y2)) + { + qint32 x3 = x2; + qint32 y3 = y2; + + if (m_decimator16.workDecimateCenter(&x3, &y3)) + { + (**it).setReal(x3 >> decimation_shifts::post16); + (**it).setImag(y3 >> decimation_shifts::post16); + ++(*it); + } + } + } + } + } +} + +template +void Decimators::decimate32_cen(SampleVector::iterator* it, const T* buf, qint32 len) +{ + int pos = 0; + + while (pos < len - 1) + { + qint32 x0 = buf[pos+0] << decimation_shifts::pre32; + qint32 y0 = buf[pos+1] << decimation_shifts::pre32; + pos += 2; + + if (m_decimator2.workDecimateCenter(&x0, &y0)) + { + qint32 x1 = x0; + qint32 y1 = y0; + + if (m_decimator4.workDecimateCenter(&x1, &y1)) + { + qint32 x2 = x1; + qint32 y2 = y1; + + if (m_decimator8.workDecimateCenter(&x2, &y2)) + { + qint32 x3 = x2; + qint32 y3 = y2; + + if (m_decimator16.workDecimateCenter(&x3, &y3)) + { + qint32 x4 = x3; + qint32 y4 = y3; + + if (m_decimator32.workDecimateCenter(&x4, &y4)) + { + (**it).setReal(x4 >> decimation_shifts::post32); + (**it).setImag(y4 >> decimation_shifts::post32); + ++(*it); + } + } + } + } + } + } +} + + +template +void Decimators::decimate64_cen(SampleVector::iterator* it, const T* buf, qint32 len) +{ + int pos = 0; + + while (pos < len - 1) + { + qint32 x0 = buf[pos+0] << decimation_shifts::pre64; + qint32 y0 = buf[pos+1] << decimation_shifts::pre64; + pos += 2; + + if (m_decimator2.workDecimateCenter(&x0, &y0)) + { + qint32 x1 = x0; + qint32 y1 = y0; + + if (m_decimator4.workDecimateCenter(&x1, &y1)) + { + qint32 x2 = x1; + qint32 y2 = y1; + + if (m_decimator8.workDecimateCenter(&x2, &y2)) + { + qint32 x3 = x2; + qint32 y3 = y2; + + if (m_decimator16.workDecimateCenter(&x3, &y3)) + { + qint32 x4 = x3; + qint32 y4 = y3; + + if (m_decimator32.workDecimateCenter(&x4, &y4)) + { + qint32 x5 = x4; + qint32 y5 = y4; + + if (m_decimator64.workDecimateCenter(&x5, &y5)) + { + (**it).setReal(x5 >> decimation_shifts::post64); + (**it).setImag(y5 >> decimation_shifts::post64); + ++(*it); + } + } + } + } + } + } + } +} + + */ + +#endif /* INCLUDE_GPL_DSP_DECIMATORS_H_ */ diff --git a/sdrbase/dsp/dspcommands.h b/sdrbase/dsp/dspcommands.h new file mode 100644 index 000000000..a3140ea8e --- /dev/null +++ b/sdrbase/dsp/dspcommands.h @@ -0,0 +1,268 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_DSPCOMMANDS_H +#define INCLUDE_DSPCOMMANDS_H + +#include +#include "util/message.h" +#include "fftwindow.h" +#include "util/export.h" + +class SampleSource; +class SampleSink; +class ThreadedSampleSink; +class AudioFifo; + +class SDRANGEL_API DSPExit : public Message { + MESSAGE_CLASS_DECLARATION +}; + +class SDRANGEL_API DSPAcquisitionInit : public Message { + MESSAGE_CLASS_DECLARATION +}; + +class SDRANGEL_API DSPAcquisitionStart : public Message { + MESSAGE_CLASS_DECLARATION +}; + +class SDRANGEL_API DSPAcquisitionStop : public Message { + MESSAGE_CLASS_DECLARATION +}; + +class SDRANGEL_API DSPGetSourceDeviceDescription : 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 SDRANGEL_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 SDRANGEL_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 SDRANGEL_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 SDRANGEL_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 SDRANGEL_API DSPAddThreadedSampleSink : public Message { + MESSAGE_CLASS_DECLARATION + +public: + DSPAddThreadedSampleSink(ThreadedSampleSink* threadedSampleSink) : Message(), m_threadedSampleSink(threadedSampleSink) { } + + ThreadedSampleSink* getThreadedSampleSink() const { return m_threadedSampleSink; } + +private: + ThreadedSampleSink* m_threadedSampleSink; +}; + +class SDRANGEL_API DSPRemoveThreadedSampleSink : public Message { + MESSAGE_CLASS_DECLARATION + +public: + DSPRemoveThreadedSampleSink(ThreadedSampleSink* threadedSampleSink) : Message(), m_threadedSampleSink(threadedSampleSink) { } + + ThreadedSampleSink* getThreadedSampleSink() const { return m_threadedSampleSink; } + +private: + ThreadedSampleSink* m_threadedSampleSink; +}; + +class SDRANGEL_API DSPAddAudioSink : public Message { + MESSAGE_CLASS_DECLARATION + +public: + DSPAddAudioSink(AudioFifo* audioFifo) : Message(), m_audioFifo(audioFifo) { } + + AudioFifo* getAudioFifo() const { return m_audioFifo; } + +private: + AudioFifo* m_audioFifo; +}; + +class SDRANGEL_API DSPRemoveAudioSink : public Message { + MESSAGE_CLASS_DECLARATION + +public: + DSPRemoveAudioSink(AudioFifo* audioFifo) : Message(), m_audioFifo(audioFifo) { } + + AudioFifo* getAudioFifo() const { return m_audioFifo; } + +private: + AudioFifo* m_audioFifo; +}; + +class SDRANGEL_API DSPConfigureSpectrumVis : public Message { + MESSAGE_CLASS_DECLARATION + +public: + DSPConfigureSpectrumVis(int fftSize, int overlapPercent, FFTWindow::Function window) : + Message(), + m_fftSize(fftSize), + m_overlapPercent(overlapPercent), + m_window(window) + { } + + int getFFTSize() const { return m_fftSize; } + int getOverlapPercent() const { return m_overlapPercent; } + FFTWindow::Function getWindow() const { return m_window; } + +private: + int m_fftSize; + int m_overlapPercent; + FFTWindow::Function m_window; +}; + +class SDRANGEL_API DSPConfigureCorrection : public Message { + MESSAGE_CLASS_DECLARATION + +public: + DSPConfigureCorrection(bool dcOffsetCorrection, bool iqImbalanceCorrection) : + Message(), + m_dcOffsetCorrection(dcOffsetCorrection), + m_iqImbalanceCorrection(iqImbalanceCorrection) + { } + + bool getDCOffsetCorrection() const { return m_dcOffsetCorrection; } + bool getIQImbalanceCorrection() const { return m_iqImbalanceCorrection; } + +private: + bool m_dcOffsetCorrection; + bool m_iqImbalanceCorrection; + +}; + +class SDRANGEL_API DSPEngineReport : public Message { + MESSAGE_CLASS_DECLARATION + +public: + DSPEngineReport(int sampleRate, quint64 centerFrequency) : + Message(), + m_sampleRate(sampleRate), + m_centerFrequency(centerFrequency) + { } + + int getSampleRate() const { return m_sampleRate; } + quint64 getCenterFrequency() const { return m_centerFrequency; } + +private: + int m_sampleRate; + quint64 m_centerFrequency; +}; + +class SDRANGEL_API DSPConfigureScopeVis : public Message { + MESSAGE_CLASS_DECLARATION + +public: + DSPConfigureScopeVis(int triggerChannel, Real triggerLevelHigh, Real triggerLevelLow) : + Message(), + m_triggerChannel(triggerChannel), + m_triggerLevelHigh(triggerLevelHigh), + m_triggerLevelLow(triggerLevelLow) + { } + + int getTriggerChannel() const { return m_triggerChannel; } + Real getTriggerLevelHigh() const { return m_triggerLevelHigh; } + Real getTriggerLevelLow() const { return m_triggerLevelLow; } + +private: + int m_triggerChannel; + Real m_triggerLevelHigh; + Real m_triggerLevelLow; +}; + +class SDRANGEL_API DSPSignalNotification : public Message { + MESSAGE_CLASS_DECLARATION + +public: + DSPSignalNotification(int samplerate, qint64 centerFrequency) : + Message(), + m_sampleRate(samplerate), + m_centerFrequency(centerFrequency) + { } + + int getSampleRate() const { return m_sampleRate; } + qint64 getCenterFrequency() const { return m_centerFrequency; } + +private: + int m_sampleRate; + qint64 m_centerFrequency; +}; + +class SDRANGEL_API DSPConfigureChannelizer : public Message { + MESSAGE_CLASS_DECLARATION + +public: + DSPConfigureChannelizer(int sampleRate, int centerFrequency) : + Message(), + m_sampleRate(sampleRate), + m_centerFrequency(centerFrequency) + { } + + int getSampleRate() const { return m_sampleRate; } + int getCenterFrequency() const { return m_centerFrequency; } + +private: + int m_sampleRate; + int m_centerFrequency; +}; + +#endif // INCLUDE_DSPCOMMANDS_H diff --git a/sdrbase/dsp/dspdeviceengine.h b/sdrbase/dsp/dspdeviceengine.h new file mode 100644 index 000000000..1f7e40a0e --- /dev/null +++ b/sdrbase/dsp/dspdeviceengine.h @@ -0,0 +1,125 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_DSPDEVICEENGINE_H +#define INCLUDE_DSPDEVICEENGINE_H + +#include +#include +#include +#include +#include "dsp/dsptypes.h" +#include "dsp/fftwindow.h" +#include "dsp/samplefifo.h" +#include "util/messagequeue.h" +#include "util/syncmessenger.h" +#include "util/export.h" + +class SampleSource; +class SampleSink; +class ThreadedSampleSink; + +class SDRANGEL_API DSPDeviceEngine : public QThread { + Q_OBJECT + +public: + enum State { + StNotStarted, //!< engine is before initialization + StIdle, //!< engine is idle + StReady, //!< engine is ready to run + StRunning, //!< engine is running + StError //!< engine is in error + }; + + DSPDeviceEngine(QObject* parent = NULL); + ~DSPDeviceEngine(); + + MessageQueue* getInputMessageQueue() { return &m_inputMessageQueue; } + MessageQueue* getOutputMessageQueue() { return &m_outputMessageQueue; } + + void start(); //!< This thread start + void stop(); //!< This thread stop + + bool initAcquisition(); //!< Initialize acquisition sequence + bool startAcquisition(); //!< Start acquisition sequence + void stopAcquistion(); //!< Stop acquisition sequence + + void setSource(SampleSource* source); //!< Set the sample source type + void setSourceSequence(int sequence); //!< Set the sample source sequence in type + + void addSink(SampleSink* sink); //!< Add a sample sink + void removeSink(SampleSink* sink); //!< Remove a sample sink + + void addThreadedSink(ThreadedSampleSink* sink); //!< Add a sample sink that will run on its own thread + void removeThreadedSink(ThreadedSampleSink* sink); //!< Remove a sample sink that runs on its own thread + + void configureCorrections(bool dcOffsetCorrection, bool iqImbalanceCorrection); //!< Configure DSP corrections + + State state() const { return m_state; } //!< Return DSP engine current state + + QString errorMessage(); //!< Return the current error message + QString sourceDeviceDescription(); //!< Return the source device description + +private: + MessageQueue m_inputMessageQueue; // SampleSinks; + SampleSinks m_sampleSinks; //!< sample sinks within main thread (usually spectrum, file output) + + typedef std::list ThreadedSampleSinks; + ThreadedSampleSinks m_threadedSampleSinks; //!< sample sinks on their own threads (usually channels) + + uint m_sampleRate; + quint64 m_centerFrequency; + + bool m_dcOffsetCorrection; + bool m_iqImbalanceCorrection; + double m_iOffset, 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(); //!< transfer samples from source to sinks if in running state + + State gotoIdle(); //!< Go to the idle state + State gotoInit(); //!< Go to the acquisition init state from idle + State gotoRunning(); //!< Go to the running state from ready state + State gotoError(const QString& errorMsg); //!< Go to an error state + + void handleSetSource(SampleSource* source); //!< Manage source setting + +private slots: + void handleData(); //!< Handle data when samples from source FIFO are ready to be processed + void handleInputMessages(); //!< Handle input message queue + void handleSynchronousMessages(); //!< Handle synchronous messages with the thread +}; + +#endif // INCLUDE_DSPDEVICEENGINE_H diff --git a/sdrbase/dsp/dspengine.h b/sdrbase/dsp/dspengine.h new file mode 100644 index 000000000..acd0b10cd --- /dev/null +++ b/sdrbase/dsp/dspengine.h @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_DSPENGINE_H +#define INCLUDE_DSPENGINE_H + +#include +#include "dsp/dspdeviceengine.h" +#include "audio/audiooutput.h" +#include "util/export.h" + +class DSPDeviceEngine; +class ThreadedSampleSink; + +class SDRANGEL_API DSPEngine : public QObject { + Q_OBJECT +public: + DSPEngine(); + ~DSPEngine(); + + static DSPEngine *instance(); + + MessageQueue* getInputMessageQueue(); + MessageQueue* getOutputMessageQueue(); + + uint getAudioSampleRate() const { return m_audioSampleRate; } + + void start(); //!< Device engine(s) start + void stop(); //!< Device engine(s) stop + + bool initAcquisition(); //!< Initialize acquisition sequence + bool startAcquisition(); //!< Start acquisition sequence + void stopAcquistion(); //!< Stop acquisition sequence + + void setSource(SampleSource* source); //!< Set the sample source type + void setSourceSequence(int sequence); //!< Set the sample source sequence in type + + void addSink(SampleSink* sink); //!< Add a sample sink + void removeSink(SampleSink* sink); //!< Remove a sample sink + + void addThreadedSink(ThreadedSampleSink* sink); //!< Add a sample sink that will run on its own thread + void removeThreadedSink(ThreadedSampleSink* sink); //!< Remove a sample sink that runs on its own thread + + void addAudioSink(AudioFifo* audioFifo); //!< Add the audio sink + void removeAudioSink(AudioFifo* audioFifo); //!< Remove the audio sink + + void configureCorrections(bool dcOffsetCorrection, bool iqImbalanceCorrection); //!< Configure DSP corrections + + DSPDeviceEngine::State state() const; + + QString errorMessage(); //!< Return the current error message + QString sourceDeviceDescription(); //!< Return the source device description + +private: + DSPDeviceEngine *m_deviceEngine; + AudioOutput m_audioOutput; + uint m_audioSampleRate; +}; + +#endif // INCLUDE_DSPENGINE_H diff --git a/sdrbase/dsp/dsptypes.h b/sdrbase/dsp/dsptypes.h new file mode 100644 index 000000000..b090997bb --- /dev/null +++ b/sdrbase/dsp/dsptypes.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_DSPTYPES_H +#define INCLUDE_DSPTYPES_H + +#include +#include +#include + +#define SDR_SAMP_SZ 16 // internal fixed arithmetic sample size + +typedef float Real; +typedef std::complex 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) {} + inline Sample& operator=(const Sample& other) { m_real = other.m_real; m_imag = other.m_imag; return *this; } + + inline Sample& operator+=(const Sample& other) { m_real += other.m_real; m_imag += other.m_imag; return *this; } + inline Sample& operator-=(const Sample& other) { m_real -= other.m_real; m_imag -= other.m_imag; return *this; } + + inline void setReal(FixReal v) { m_real = v; } + inline void setImag(FixReal v) { m_imag = v; } + + inline FixReal real() const { return m_real; } + inline FixReal imag() const { return m_imag; } + + FixReal m_real; + FixReal m_imag; +}; +#pragma pack(pop) + +typedef std::vector SampleVector; + +#endif // INCLUDE_DSPTYPES_H diff --git a/sdrbase/dsp/fftengine.h b/sdrbase/dsp/fftengine.h new file mode 100644 index 000000000..05665241a --- /dev/null +++ b/sdrbase/dsp/fftengine.h @@ -0,0 +1,20 @@ +#ifndef INCLUDE_FFTENGINE_H +#define INCLUDE_FFTENGINE_H + +#include "dsp/dsptypes.h" +#include "util/export.h" + +class SDRANGEL_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 diff --git a/sdrbase/dsp/fftfilt.h b/sdrbase/dsp/fftfilt.h new file mode 100644 index 000000000..4af4613fb --- /dev/null +++ b/sdrbase/dsp/fftfilt.h @@ -0,0 +1,82 @@ +/* + * Filters from Fldigi. +*/ + +#ifndef _FFTFILT_H +#define _FFTFILT_H + +#include +#include "gfft.h" + +//---------------------------------------------------------------------- + +class fftfilt { +enum {NONE, BLACKMAN, HAMMING, HANNING}; + +public: + typedef std::complex cmplx; + + fftfilt(float f1, float f2, int len); + fftfilt(float f2, int len); + ~fftfilt(); +// f1 < f2 ==> bandpass +// f1 > f2 ==> band reject + void create_filter(float f1, float f2); + void create_dsb_filter(float f2); + + int noFilt(const cmplx& in, cmplx **out); + int runFilt(const cmplx& in, cmplx **out); + int runSSB(const cmplx& in, cmplx **out, bool usb); + int runDSB(const cmplx& in, cmplx **out); + +protected: + int flen; + int flen2; + g_fft *fft; + g_fft *ift; + cmplx *filter; + cmplx *data; + cmplx *ovlbuf; + cmplx *output; + int inptr; + int pass; + int window; + + inline float fsinc(float fc, int i, int len) { + return (i == len/2) ? 2.0 * fc: + sin(2 * M_PI * fc * (i - len/2)) / (M_PI * (i - len/2)); + } + + inline float _blackman(int i, int len) { + return (0.42 - + 0.50 * cos(2.0 * M_PI * i / len) + + 0.08 * cos(4.0 * M_PI * i / len)); + } + + void init_filter(); + void init_dsb_filter(); +}; + + + +/* Sliding FFT filter from Fldigi */ +class sfft { +#define K1 0.99999 +public: + typedef std::complex cmplx; + sfft(int len); + ~sfft(); + void run(const cmplx& input); + void fetch(float *result); +private: + int fftlen; + int first; + int last; + int ptr; + struct vrot_bins_pair; + vrot_bins_pair *vrot_bins; + cmplx *delay; + float k2; +}; + +#endif diff --git a/sdrbase/dsp/fftwengine.h b/sdrbase/dsp/fftwengine.h new file mode 100644 index 000000000..9224ba4ad --- /dev/null +++ b/sdrbase/dsp/fftwengine.h @@ -0,0 +1,37 @@ +#ifndef INCLUDE_FFTWENGINE_H +#define INCLUDE_FFTWENGINE_H + +#include +#include +#include +#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 Plans; + Plans m_plans; + Plan* m_currentPlan; + + void freeAll(); +}; + +#endif // INCLUDE_FFTWENGINE_H diff --git a/sdrbase/dsp/fftwindow.h b/sdrbase/dsp/fftwindow.h new file mode 100644 index 000000000..89da43478 --- /dev/null +++ b/sdrbase/dsp/fftwindow.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FFTWINDOW_H +#define INCLUDE_FFTWINDOW_H + +#include +#define _USE_MATH_DEFINES +#include +#include "dsp/dsptypes.h" +#include "util/export.h" + +class SDRANGEL_API FFTWindow { +public: + enum Function { + Bartlett, + BlackmanHarris, + Flattop, + Hamming, + Hanning, + Rectangle + }; + + void create(Function function, int n); + void apply(const std::vector& in, std::vector* out); + void apply(const std::vector& in, std::vector* out); + void apply(const Complex* in, Complex* out); + +private: + std::vector 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 diff --git a/sdrbase/dsp/filesink.h b/sdrbase/dsp/filesink.h new file mode 100644 index 000000000..52c1f4be2 --- /dev/null +++ b/sdrbase/dsp/filesink.h @@ -0,0 +1,72 @@ +#ifndef INCLUDE_FILESINK_H +#define INCLUDE_FILESINK_H + +#include +#include +#include + +#include +#include "dsp/samplesink.h" +#include "util/export.h" +#include "util/message.h" +#include "util/messagequeue.h" + +class SDRANGEL_API FileSink : public SampleSink { +public: + + struct Header + { + int sampleRate; + quint64 centerFrequency; + std::time_t startTimeStamp; + }; + + FileSink(); + virtual ~FileSink(); + + quint64 getByteCount() const { return m_byteCount; } + + void configure(MessageQueue* msgQueue, const std::string& filename); + + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); + virtual void start(); + virtual void stop(); + virtual bool handleMessage(const Message& message); + void startRecording(); + void stopRecording(); + static void readHeader(std::ifstream& samplefile, Header& header); + +private: + class MsgConfigureFileSink : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const std::string& getFileName() const { return m_fileName; } + + static MsgConfigureFileSink* create(const std::string& fileName) + { + return new MsgConfigureFileSink(fileName); + } + + private: + std::string m_fileName; + + MsgConfigureFileSink(const std::string& fileName) : + Message(), + m_fileName(fileName) + { } + }; + + std::string m_fileName; + int m_sampleRate; + quint64 m_centerFrequency; + bool m_recordOn; + bool m_recordStart; + std::ofstream m_sampleFile; + quint64 m_byteCount; + + void handleConfigure(const std::string& fileName); + void writeHeader(); +}; + +#endif // INCLUDE_FILESINK_H diff --git a/sdrbase/dsp/filterrc.h b/sdrbase/dsp/filterrc.h new file mode 100644 index 000000000..7f1af7c89 --- /dev/null +++ b/sdrbase/dsp/filterrc.h @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_DSP_FILTERRC_H_ +#define INCLUDE_DSP_FILTERRC_H_ + +#include "dsp/dsptypes.h" + +/** First order low-pass IIR filter for real-valued signals. */ +class LowPassFilterRC +{ +public: + + /** + * Construct 1st order low-pass IIR filter. + * + * timeconst :: RC time constant in seconds (1 / (2 * PI * cutoff_freq) + */ + LowPassFilterRC(Real timeconst); + + /** + * Reconfigure filter with new time constant + */ + void configure(Real timeout); + + /** Process samples. */ + void process(const Real& sample_in, Real& sample_out); + +private: + Real m_timeconst; + Real m_y1; + Real m_a1; + Real m_b0; +}; + +#endif /* INCLUDE_DSP_FILTERRC_H_ */ diff --git a/sdrbase/dsp/gfft.h b/sdrbase/dsp/gfft.h new file mode 100644 index 000000000..fbde30eeb --- /dev/null +++ b/sdrbase/dsp/gfft.h @@ -0,0 +1,3383 @@ +//============================================================================== +// g_fft.h: +// +// FFT library +// Copyright (C) 2013 +// Dave Freese, W1HKJ +// +// based on public domain code by John Green +// original version is available at +// http://hyperarchive.lcs.mit.edu/ +// /HyperArchive/Archive/dev/src/ffts-for-risc-2-c.hqx +// +// ported to C++ for fldigi by Dave Freese, W1HKJ +// +// This file is part of fldigi. +// +// Fldigi is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Fldigi 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 fldigi. If not, see . +//============================================================================== + +#ifndef CGREEN_FFT_H +#define CGREEN_FFT_H + +#include + +template +class g_fft { + +#define FFT_RECIPLN2 1.442695040888963407359924681001892137426 // 1.0/log(2) + +// some useful conversions between a number and its power of 2 +#define LOG2(a) (FFT_RECIPLN2*log(a)) // floating point logarithm base 2 +#define POW2(m) ((unsigned int) 1 << (m)) // integer power of 2 for m<32 + +// fft's with M bigger than this bust primary cache +#define MCACHE (11 - (sizeof(FFT_TYPE) / 8)) + +// some math constants to 40 decimal places +#define FFT_PI 3.141592653589793238462643383279502884197 // pi +#define FFT_ROOT2 1.414213562373095048801688724209698078569 // sqrt(2) +#define FFT_COSPID8 0.9238795325112867561281831893967882868224 // cos(pi/8) +#define FFT_SINPID8 0.3826834323650897717284599840303988667613 // sin(pi/8) + +private: + int FFT_size; + int FFT_N; + FFT_TYPE *FFT_table_1[32]; + short int *FFT_table_2[32]; + + FFT_TYPE *Utbl; + short *BRLow; + + void fftInit(); + int ConvertFFTSize(int); + +// base fft methods + void riffts1(FFT_TYPE *ioptr, int M, FFT_TYPE *Utbl, short *BRLow); + void ifrstage(FFT_TYPE *ioptr, int M, FFT_TYPE *Utbl); + void rifft8pt(FFT_TYPE *ioptr, FFT_TYPE scale); + void rifft4pt(FFT_TYPE *ioptr, FFT_TYPE scale); + void rifft2pt(FFT_TYPE *ioptr, FFT_TYPE scale); + void rifft1pt(FFT_TYPE *ioptr, FFT_TYPE scale); + void rffts1(FFT_TYPE *ioptr, int M, FFT_TYPE *Utbl, short *BRLow); + void frstage(FFT_TYPE *ioptr, int M, FFT_TYPE *Utbl); + void rfft8pt(FFT_TYPE *ioptr); + void rfft4pt(FFT_TYPE *ioptr); + void rfft2pt(FFT_TYPE *ioptr); + void rfft1pt(FFT_TYPE *ioptr); + void iffts1(FFT_TYPE *ioptr, int M, FFT_TYPE *Utbl, short *BRLow); + void ifftrecurs(FFT_TYPE *ioptr, int M, FFT_TYPE *Utbl, int Ustride, + int NDiffU, int StageCnt); + void ibfstages(FFT_TYPE *ioptr, int M, FFT_TYPE *Utbl, int Ustride, + int NDiffU, int StageCnt); + void ibfR4(FFT_TYPE *ioptr, int M, int NDiffU); + void ibfR2(FFT_TYPE *ioptr, int M, int NDiffU); + void ifft8pt(FFT_TYPE *ioptr, FFT_TYPE scale); + void ifft4pt(FFT_TYPE *ioptr, FFT_TYPE scale); + void ifft2pt(FFT_TYPE *ioptr, FFT_TYPE scale); + void scbitrevR2(FFT_TYPE *ioptr, int M, short *BRLow, FFT_TYPE scale); + void ffts1(FFT_TYPE *ioptr, int M, FFT_TYPE *Utbl, short *BRLow); + void fftrecurs(FFT_TYPE *ioptr, int M, + FFT_TYPE *Utbl, int Ustride, int NDiffU, + int StageCnt); + void bfstages(FFT_TYPE *ioptr, int M, + FFT_TYPE *Utbl, int Ustride, + int NDiffU, int StageCnt); + void bfR4(FFT_TYPE *ioptr, int M, int NDiffU); + void bfR2(FFT_TYPE *ioptr, int M, int NDiffU); + void fft8pt(FFT_TYPE *ioptr); + void fft4pt(FFT_TYPE *ioptr); + void fft2pt(FFT_TYPE *ioptr); + void bitrevR2(FFT_TYPE *ioptr, int M, short *BRLow); + void fftBRInit(int M, short *BRLow); + void fftCosInit(int M, FFT_TYPE *Utbl); + +public: + g_fft (int M = 8192) + { + if (M < 16) M = 16; + if (M > 268435456) M = 268435456; + FFT_size = M; + fftInit(); + } + + ~g_fft() + { + for (int i = 0; i < 32; i++) + { + if (FFT_table_1[i] != 0) delete [] FFT_table_1[i]; + if (FFT_table_2[i] != 0) delete [] FFT_table_2[i]; + } + } + + void ComplexFFT(std::complex *buf); + void InverseComplexFFT(std::complex *buf); + void RealFFT(std::complex *buf); + void InverseRealFFT(std::complex *buf); + FFT_TYPE GetInverseComplexFFTScale(); + FFT_TYPE GetInverseRealFFTScale(); +}; + +//------------------------------------------------------------------------------ +// Compute Utbl, the cosine table for ffts +// of size (pow(2,M)/4 +1) +// INPUTS +// M = log2 of fft size +// OUTPUTS +// *Utbl = cosine table +//------------------------------------------------------------------------------ +template +void g_fft::fftCosInit(int M, FFT_TYPE *Utbl) +{ + unsigned int fftN = POW2(M); + unsigned int i1; + + Utbl[0] = FFT_TYPE(1.0); + for (i1 = 1; i1 < fftN/4; i1++) + Utbl[i1] = (FFT_TYPE)cos((2.0 * FFT_PI * (float)i1) / (float)fftN); + Utbl[fftN/4] = FFT_TYPE(0.0); +} + +//------------------------------------------------------------------------------ +// Compute BRLow, the bit reversed table for ffts +// of size pow(2,M/2 -1) +// INPUTS +// M = log2 of fft size +// OUTPUTS +// *BRLow = bit reversed counter table +//------------------------------------------------------------------------------ +template +void g_fft::fftBRInit(int M, short *BRLow) +{ + int Mroot_1 = M / 2 - 1; + int Nroot_1 = POW2(Mroot_1); + int i1; + int bitsum; + int bitmask; + int bit; + + for (i1 = 0; i1 < Nroot_1; i1++) { + bitsum = 0; + bitmask = 1; + for (bit = 1; bit <= Mroot_1; bitmask <<= 1, bit++) + if (i1 & bitmask) + bitsum = bitsum + (Nroot_1 >> bit); + BRLow[i1] = bitsum; + } +} + +//------------------------------------------------------------------------------ +// parts of ffts1 +// bit reverse and first radix 2 stage of forward or inverse fft +//------------------------------------------------------------------------------ +template +void g_fft::bitrevR2(FFT_TYPE *ioptr, int M, short *BRLow) +{ + FFT_TYPE f0r; + FFT_TYPE f0i; + FFT_TYPE f1r; + FFT_TYPE f1i; + FFT_TYPE f2r; + FFT_TYPE f2i; + FFT_TYPE f3r; + FFT_TYPE f3i; + FFT_TYPE f4r; + FFT_TYPE f4i; + FFT_TYPE f5r; + FFT_TYPE f5i; + FFT_TYPE f6r; + FFT_TYPE f6i; + FFT_TYPE f7r; + FFT_TYPE f7i; + FFT_TYPE t0r; + FFT_TYPE t0i; + FFT_TYPE t1r; + FFT_TYPE t1i; + FFT_TYPE *p0r; + FFT_TYPE *p1r; + FFT_TYPE *IOP; + FFT_TYPE *iolimit; + int Colstart; + int iCol; + unsigned int posA; + unsigned int posAi; + unsigned int posB; + unsigned int posBi; + + const unsigned int Nrems2 = POW2((M + 3) / 2); + const unsigned int Nroot_1_ColInc = POW2(M) - Nrems2; + const unsigned int Nroot_1 = POW2(M / 2 - 1) - 1; + const unsigned int ColstartShift = (M + 1) / 2 + 1; + + posA = POW2(M); // 1/2 of POW2(M) complex + posAi = posA + 1; + posB = posA + 2; + posBi = posB + 1; + + iolimit = ioptr + Nrems2; + for (; ioptr < iolimit; ioptr += POW2(M / 2 + 1)) { + for (Colstart = Nroot_1; Colstart >= 0; Colstart--) { + iCol = Nroot_1; + p0r = ioptr + Nroot_1_ColInc + BRLow[Colstart] * 4; + IOP = ioptr + (Colstart << ColstartShift); + p1r = IOP + BRLow[iCol] * 4; + f0r = *(p0r); + f0i = *(p0r + 1); + f1r = *(p0r + posA); + f1i = *(p0r + posAi); + for (; iCol > Colstart;) { + f2r = *(p0r + 2); + f2i = *(p0r + (2 + 1)); + f3r = *(p0r + posB); + f3i = *(p0r + posBi); + f4r = *(p1r); + f4i = *(p1r + 1); + f5r = *(p1r + posA); + f5i = *(p1r + posAi); + f6r = *(p1r + 2); + f6i = *(p1r + (2 + 1)); + f7r = *(p1r + posB); + f7i = *(p1r + posBi); + + t0r = f0r + f1r; + t0i = f0i + f1i; + f1r = f0r - f1r; + f1i = f0i - f1i; + t1r = f2r + f3r; + t1i = f2i + f3i; + f3r = f2r - f3r; + f3i = f2i - f3i; + f0r = f4r + f5r; + f0i = f4i + f5i; + f5r = f4r - f5r; + f5i = f4i - f5i; + f2r = f6r + f7r; + f2i = f6i + f7i; + f7r = f6r - f7r; + f7i = f6i - f7i; + + *(p1r) = t0r; + *(p1r + 1) = t0i; + *(p1r + 2) = f1r; + *(p1r + (2 + 1)) = f1i; + *(p1r + posA) = t1r; + *(p1r + posAi) = t1i; + *(p1r + posB) = f3r; + *(p1r + posBi) = f3i; + *(p0r) = f0r; + *(p0r + 1) = f0i; + *(p0r + 2) = f5r; + *(p0r + (2 + 1)) = f5i; + *(p0r + posA) = f2r; + *(p0r + posAi) = f2i; + *(p0r + posB) = f7r; + *(p0r + posBi) = f7i; + + p0r -= Nrems2; + f0r = *(p0r); + f0i = *(p0r + 1); + f1r = *(p0r + posA); + f1i = *(p0r + posAi); + iCol -= 1; + p1r = IOP + BRLow[iCol] * 4; + } + f2r = *(p0r + 2); + f2i = *(p0r + (2 + 1)); + f3r = *(p0r + posB); + f3i = *(p0r + posBi); + + t0r = f0r + f1r; + t0i = f0i + f1i; + f1r = f0r - f1r; + f1i = f0i - f1i; + t1r = f2r + f3r; + t1i = f2i + f3i; + f3r = f2r - f3r; + f3i = f2i - f3i; + + *(p0r) = t0r; + *(p0r + 1) = t0i; + *(p0r + 2) = f1r; + *(p0r + (2 + 1)) = f1i; + *(p0r + posA) = t1r; + *(p0r + posAi) = t1i; + *(p0r + posB) = f3r; + *(p0r + posBi) = f3i; + } + } +} + +//------------------------------------------------------------------------------ +// RADIX 2 fft +//------------------------------------------------------------------------------ +template +void g_fft::fft2pt(FFT_TYPE *ioptr) +{ + FFT_TYPE f0r, f0i, f1r, f1i; + FFT_TYPE t0r, t0i; + +// bit reversed load + f0r = ioptr[0]; + f0i = ioptr[1]; + f1r = ioptr[2]; + f1i = ioptr[3]; + +// Butterflys +// f0 - - - t0 +// f1 - 1 - f1 + + t0r = f0r + f1r; + t0i = f0i + f1i; + f1r = f0r - f1r; + f1i = f0i - f1i; + +// store result + ioptr[0] = t0r; + ioptr[1] = t0i; + ioptr[2] = f1r; + ioptr[3] = f1i; +} + +//------------------------------------------------------------------------------ +// RADIX 4 fft +//------------------------------------------------------------------------------ +template +void g_fft::fft4pt(FFT_TYPE *ioptr) +{ + FFT_TYPE f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i; + FFT_TYPE t0r, t0i, t1r, t1i; + +// bit reversed load + f0r = ioptr[0]; + f0i = ioptr[1]; + f1r = ioptr[4]; + f1i = ioptr[5]; + f2r = ioptr[2]; + f2i = ioptr[3]; + f3r = ioptr[6]; + f3i = ioptr[7]; + +// Butterflys +// f0 - - t0 - - f0 +// f1 - 1 - f1 - - f1 +// f2 - - f2 - 1 - f2 +// f3 - 1 - t1 - -i - f3 + + t0r = f0r + f1r; + t0i = f0i + f1i; + f1r = f0r - f1r; + f1i = f0i - f1i; + + t1r = f2r - f3r; + t1i = f2i - f3i; + f2r = f2r + f3r; + f2i = f2i + f3i; + + f0r = t0r + f2r; + f0i = t0i + f2i; + f2r = t0r - f2r; + f2i = t0i - f2i; + + f3r = f1r - t1i; + f3i = f1i + t1r; + f1r = f1r + t1i; + f1i = f1i - t1r; + +// store result + ioptr[0] = f0r; + ioptr[1] = f0i; + ioptr[2] = f1r; + ioptr[3] = f1i; + ioptr[4] = f2r; + ioptr[5] = f2i; + ioptr[6] = f3r; + ioptr[7] = f3i; +} + +//------------------------------------------------------------------------------ +// RADIX 8 fft +//------------------------------------------------------------------------------ +template +void g_fft::fft8pt(FFT_TYPE *ioptr) +{ + FFT_TYPE w0r = 1.0 / FFT_ROOT2; // cos(pi/4) + FFT_TYPE f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i; + FFT_TYPE f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i; + FFT_TYPE t0r, t0i, t1r, t1i; + const FFT_TYPE Two = 2.0; + +// bit reversed load + f0r = ioptr[0]; + f0i = ioptr[1]; + f1r = ioptr[8]; + f1i = ioptr[9]; + f2r = ioptr[4]; + f2i = ioptr[5]; + f3r = ioptr[12]; + f3i = ioptr[13]; + f4r = ioptr[2]; + f4i = ioptr[3]; + f5r = ioptr[10]; + f5i = ioptr[11]; + f6r = ioptr[6]; + f6i = ioptr[7]; + f7r = ioptr[14]; + f7i = ioptr[15]; + +// Butterflys +// f0 - - t0 - - f0 - - f0 +// f1 - 1 - f1 - - f1 - - f1 +// f2 - - f2 - 1 - f2 - - f2 +// f3 - 1 - t1 - -i - f3 - - f3 +// f4 - - t0 - - f4 - 1 - t0 +// f5 - 1 - f5 - - f5 - w3 - f4 +// f6 - - f6 - 1 - f6 - -i - t1 +// f7 - 1 - t1 - -i - f7 - iw3- f6 + + t0r = f0r + f1r; + t0i = f0i + f1i; + f1r = f0r - f1r; + f1i = f0i - f1i; + + t1r = f2r - f3r; + t1i = f2i - f3i; + f2r = f2r + f3r; + f2i = f2i + f3i; + + f0r = t0r + f2r; + f0i = t0i + f2i; + f2r = t0r - f2r; + f2i = t0i - f2i; + + f3r = f1r - t1i; + f3i = f1i + t1r; + f1r = f1r + t1i; + f1i = f1i - t1r; + + t0r = f4r + f5r; + t0i = f4i + f5i; + f5r = f4r - f5r; + f5i = f4i - f5i; + + t1r = f6r - f7r; + t1i = f6i - f7i; + f6r = f6r + f7r; + f6i = f6i + f7i; + + f4r = t0r + f6r; + f4i = t0i + f6i; + f6r = t0r - f6r; + f6i = t0i - f6i; + + f7r = f5r - t1i; + f7i = f5i + t1r; + f5r = f5r + t1i; + f5i = f5i - t1r; + + t0r = f0r - f4r; + t0i = f0i - f4i; + f0r = f0r + f4r; + f0i = f0i + f4i; + + t1r = f2r - f6i; + t1i = f2i + f6r; + f2r = f2r + f6i; + f2i = f2i - f6r; + + f4r = f1r - f5r * w0r - f5i * w0r; + f4i = f1i + f5r * w0r - f5i * w0r; + f1r = f1r * Two - f4r; + f1i = f1i * Two - f4i; + + f6r = f3r + f7r * w0r - f7i * w0r; + f6i = f3i + f7r * w0r + f7i * w0r; + f3r = f3r * Two - f6r; + f3i = f3i * Two - f6i; + +// store result + ioptr[0] = f0r; + ioptr[1] = f0i; + ioptr[2] = f1r; + ioptr[3] = f1i; + ioptr[4] = f2r; + ioptr[5] = f2i; + ioptr[6] = f3r; + ioptr[7] = f3i; + ioptr[8] = t0r; + ioptr[9] = t0i; + ioptr[10] = f4r; + ioptr[11] = f4i; + ioptr[12] = t1r; + ioptr[13] = t1i; + ioptr[14] = f6r; + ioptr[15] = f6i; +} + +//------------------------------------------------------------------------------ +// 2nd radix 2 stage +//------------------------------------------------------------------------------ +template +void g_fft::bfR2(FFT_TYPE *ioptr, int M, int NDiffU) +{ + unsigned int pos; + unsigned int posi; + unsigned int pinc; + unsigned int pnext; + unsigned int NSameU; + unsigned int SameUCnt; + + FFT_TYPE *pstrt; + FFT_TYPE *p0r, *p1r, *p2r, *p3r; + + FFT_TYPE f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i; + FFT_TYPE f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i; + + pinc = NDiffU * 2; // 2 floats per complex + pnext = pinc * 4; + pos = 2; + posi = pos + 1; + NSameU = POW2(M) / 4 / NDiffU; // 4 Us at a time + pstrt = ioptr; + p0r = pstrt; + p1r = pstrt + pinc; + p2r = p1r + pinc; + p3r = p2r + pinc; + +// Butterflys +// f0 - - f4 +// f1 - 1 - f5 +// f2 - - f6 +// f3 - 1 - f7 +// Butterflys +// f0 - - f4 +// f1 - 1 - f5 +// f2 - - f6 +// f3 - 1 - f7 + + for (SameUCnt = NSameU; SameUCnt > 0; SameUCnt--) { + + f0r = *p0r; + f1r = *p1r; + f0i = *(p0r + 1); + f1i = *(p1r + 1); + f2r = *p2r; + f3r = *p3r; + f2i = *(p2r + 1); + f3i = *(p3r + 1); + + f4r = f0r + f1r; + f4i = f0i + f1i; + f5r = f0r - f1r; + f5i = f0i - f1i; + + f6r = f2r + f3r; + f6i = f2i + f3i; + f7r = f2r - f3r; + f7i = f2i - f3i; + + *p0r = f4r; + *(p0r + 1) = f4i; + *p1r = f5r; + *(p1r + 1) = f5i; + *p2r = f6r; + *(p2r + 1) = f6i; + *p3r = f7r; + *(p3r + 1) = f7i; + + f0r = *(p0r + pos); + f1i = *(p1r + posi); + f0i = *(p0r + posi); + f1r = *(p1r + pos); + f2r = *(p2r + pos); + f3i = *(p3r + posi); + f2i = *(p2r + posi); + f3r = *(p3r + pos); + + f4r = f0r + f1i; + f4i = f0i - f1r; + f5r = f0r - f1i; + f5i = f0i + f1r; + + f6r = f2r + f3i; + f6i = f2i - f3r; + f7r = f2r - f3i; + f7i = f2i + f3r; + + *(p0r + pos) = f4r; + *(p0r + posi) = f4i; + *(p1r + pos) = f5r; + *(p1r + posi) = f5i; + *(p2r + pos) = f6r; + *(p2r + posi) = f6i; + *(p3r + pos) = f7r; + *(p3r + posi) = f7i; + + p0r += pnext; + p1r += pnext; + p2r += pnext; + p3r += pnext; + } +} + +//------------------------------------------------------------------------------ +// 1 radix 4 stage +//------------------------------------------------------------------------------ +template +void g_fft::bfR4(FFT_TYPE *ioptr, int M, int NDiffU) +{ + unsigned int pos; + unsigned int posi; + unsigned int pinc; + unsigned int pnext; + unsigned int pnexti; + unsigned int NSameU; + unsigned int SameUCnt; + + FFT_TYPE *pstrt; + FFT_TYPE *p0r, *p1r, *p2r, *p3r; + + FFT_TYPE w1r = 1.0 / FFT_ROOT2; // cos(pi/4) + FFT_TYPE f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i; + FFT_TYPE f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i; + FFT_TYPE t1r, t1i; + const FFT_TYPE Two = 2.0; + + pinc = NDiffU * 2; // 2 floats per complex + pnext = pinc * 4; + pnexti = pnext + 1; + pos = 2; + posi = pos + 1; + NSameU = POW2(M) / 4 / NDiffU; // 4 pts per butterfly + pstrt = ioptr; + p0r = pstrt; + p1r = pstrt + pinc; + p2r = p1r + pinc; + p3r = p2r + pinc; + +// Butterflys +// f0 - - f0 - - f4 +// f1 - 1 - f5 - - f5 +// f2 - - f6 - 1 - f6 +// f3 - 1 - f3 - -i - f7 +// Butterflys +// f0 - - f4 - - f4 +// f1 - -i - t1 - - f5 +// f2 - - f2 - w1 - f6 +// f3 - -i - f7 - iw1- f7 + + f0r = *p0r; + f1r = *p1r; + f2r = *p2r; + f3r = *p3r; + f0i = *(p0r + 1); + f1i = *(p1r + 1); + f2i = *(p2r + 1); + f3i = *(p3r + 1); + + f5r = f0r - f1r; + f5i = f0i - f1i; + f0r = f0r + f1r; + f0i = f0i + f1i; + + f6r = f2r + f3r; + f6i = f2i + f3i; + f3r = f2r - f3r; + f3i = f2i - f3i; + + for (SameUCnt = NSameU - 1; SameUCnt > 0; SameUCnt--) { + + f7r = f5r - f3i; + f7i = f5i + f3r; + f5r = f5r + f3i; + f5i = f5i - f3r; + + f4r = f0r + f6r; + f4i = f0i + f6i; + f6r = f0r - f6r; + f6i = f0i - f6i; + + f2r = *(p2r + pos); + f2i = *(p2r + posi); + f1r = *(p1r + pos); + f1i = *(p1r + posi); + f3i = *(p3r + posi); + f0r = *(p0r + pos); + f3r = *(p3r + pos); + f0i = *(p0r + posi); + + *p3r = f7r; + *p0r = f4r; + *(p3r + 1) = f7i; + *(p0r + 1) = f4i; + *p1r = f5r; + *p2r = f6r; + *(p1r + 1) = f5i; + *(p2r + 1) = f6i; + + f7r = f2r - f3i; + f7i = f2i + f3r; + f2r = f2r + f3i; + f2i = f2i - f3r; + + f4r = f0r + f1i; + f4i = f0i - f1r; + t1r = f0r - f1i; + t1i = f0i + f1r; + + f5r = t1r - f7r * w1r + f7i * w1r; + f5i = t1i - f7r * w1r - f7i * w1r; + f7r = t1r * Two - f5r; + f7i = t1i * Two - f5i; + + f6r = f4r - f2r * w1r - f2i * w1r; + f6i = f4i + f2r * w1r - f2i * w1r; + f4r = f4r * Two - f6r; + f4i = f4i * Two - f6i; + + f3r = *(p3r + pnext); + f0r = *(p0r + pnext); + f3i = *(p3r + pnexti); + f0i = *(p0r + pnexti); + f2r = *(p2r + pnext); + f2i = *(p2r + pnexti); + f1r = *(p1r + pnext); + f1i = *(p1r + pnexti); + + *(p2r + pos) = f6r; + *(p1r + pos) = f5r; + *(p2r + posi) = f6i; + *(p1r + posi) = f5i; + *(p3r + pos) = f7r; + *(p0r + pos) = f4r; + *(p3r + posi) = f7i; + *(p0r + posi) = f4i; + + f6r = f2r + f3r; + f6i = f2i + f3i; + f3r = f2r - f3r; + f3i = f2i - f3i; + + f5r = f0r - f1r; + f5i = f0i - f1i; + f0r = f0r + f1r; + f0i = f0i + f1i; + + p3r += pnext; + p0r += pnext; + p1r += pnext; + p2r += pnext; + } + f7r = f5r - f3i; + f7i = f5i + f3r; + f5r = f5r + f3i; + f5i = f5i - f3r; + + f4r = f0r + f6r; + f4i = f0i + f6i; + f6r = f0r - f6r; + f6i = f0i - f6i; + + f2r = *(p2r + pos); + f2i = *(p2r + posi); + f1r = *(p1r + pos); + f1i = *(p1r + posi); + f3i = *(p3r + posi); + f0r = *(p0r + pos); + f3r = *(p3r + pos); + f0i = *(p0r + posi); + + *p3r = f7r; + *p0r = f4r; + *(p3r + 1) = f7i; + *(p0r + 1) = f4i; + *p1r = f5r; + *p2r = f6r; + *(p1r + 1) = f5i; + *(p2r + 1) = f6i; + + f7r = f2r - f3i; + f7i = f2i + f3r; + f2r = f2r + f3i; + f2i = f2i - f3r; + + f4r = f0r + f1i; + f4i = f0i - f1r; + t1r = f0r - f1i; + t1i = f0i + f1r; + + f5r = t1r - f7r * w1r + f7i * w1r; + f5i = t1i - f7r * w1r - f7i * w1r; + f7r = t1r * Two - f5r; + f7i = t1i * Two - f5i; + + f6r = f4r - f2r * w1r - f2i * w1r; + f6i = f4i + f2r * w1r - f2i * w1r; + f4r = f4r * Two - f6r; + f4i = f4i * Two - f6i; + + *(p2r + pos) = f6r; + *(p1r + pos) = f5r; + *(p2r + posi) = f6i; + *(p1r + posi) = f5i; + *(p3r + pos) = f7r; + *(p0r + pos) = f4r; + *(p3r + posi) = f7i; + *(p0r + posi) = f4i; +} + +//------------------------------------------------------------------------------ +// RADIX 8 Stages +//------------------------------------------------------------------------------ +template +void g_fft::bfstages(FFT_TYPE *ioptr, int M, FFT_TYPE *Utbl, int Ustride, + int NDiffU, int StageCnt) +{ + unsigned int pos; + unsigned int posi; + unsigned int pinc; + unsigned int pnext; + unsigned int NSameU; + int Uinc; + int Uinc2; + int Uinc4; + unsigned int DiffUCnt; + unsigned int SameUCnt; + unsigned int U2toU3; + + FFT_TYPE *pstrt; + FFT_TYPE *p0r, *p1r, *p2r, *p3r; + FFT_TYPE *u0r, *u0i, *u1r, *u1i, *u2r, *u2i; + + FFT_TYPE w0r, w0i, w1r, w1i, w2r, w2i, w3r, w3i; + FFT_TYPE f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i; + FFT_TYPE f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i; + FFT_TYPE t0r, t0i, t1r, t1i; + const FFT_TYPE Two = FFT_TYPE(2.0); + + pinc = NDiffU * 2; // 2 floats per complex + pnext = pinc * 8; + pos = pinc * 4; + posi = pos + 1; + NSameU = POW2(M) / 8 / NDiffU; // 8 pts per butterfly + Uinc = (int) NSameU * Ustride; + Uinc2 = Uinc * 2; + Uinc4 = Uinc * 4; + U2toU3 = (POW2(M) / 8) * Ustride; + for (; StageCnt > 0; StageCnt--) { + + u0r = &Utbl[0]; + u0i = &Utbl[POW2(M - 2) * Ustride]; + u1r = u0r; + u1i = u0i; + u2r = u0r; + u2i = u0i; + + w0r = *u0r; + w0i = *u0i; + w1r = *u1r; + w1i = *u1i; + w2r = *u2r; + w2i = *u2i; + w3r = *(u2r + U2toU3); + w3i = *(u2i - U2toU3); + + pstrt = ioptr; + + p0r = pstrt; + p1r = pstrt + pinc; + p2r = p1r + pinc; + p3r = p2r + pinc; + +// Butterflys +// f0 - - t0 - - f0 - - f0 +// f1 - w0- f1 - - f1 - - f1 +// f2 - - f2 - w1- f2 - - f4 +// f3 - w0- t1 - iw1- f3 - - f5 +// +// f4 - - t0 - - f4 - w2- t0 +// f5 - w0- f5 - - f5 - w3- t1 +// f6 - - f6 - w1- f6 - iw2- f6 +// f7 - w0- t1 - iw1- f7 - iw3- f7 + + for (DiffUCnt = NDiffU; DiffUCnt > 0; DiffUCnt--) { + f0r = *p0r; + f0i = *(p0r + 1); + f1r = *p1r; + f1i = *(p1r + 1); + for (SameUCnt = NSameU - 1; SameUCnt > 0; SameUCnt--) { + f2r = *p2r; + f2i = *(p2r + 1); + f3r = *p3r; + f3i = *(p3r + 1); + + t0r = f0r + f1r * w0r + f1i * w0i; + t0i = f0i - f1r * w0i + f1i * w0r; + f1r = f0r * Two - t0r; + f1i = f0i * Two - t0i; + + f4r = *(p0r + pos); + f4i = *(p0r + posi); + f5r = *(p1r + pos); + f5i = *(p1r + posi); + + f6r = *(p2r + pos); + f6i = *(p2r + posi); + f7r = *(p3r + pos); + f7i = *(p3r + posi); + + t1r = f2r - f3r * w0r - f3i * w0i; + t1i = f2i + f3r * w0i - f3i * w0r; + f2r = f2r * Two - t1r; + f2i = f2i * Two - t1i; + + f0r = t0r + f2r * w1r + f2i * w1i; + f0i = t0i - f2r * w1i + f2i * w1r; + f2r = t0r * Two - f0r; + f2i = t0i * Two - f0i; + + f3r = f1r + t1r * w1i - t1i * w1r; + f3i = f1i + t1r * w1r + t1i * w1i; + f1r = f1r * Two - f3r; + f1i = f1i * Two - f3i; + + t0r = f4r + f5r * w0r + f5i * w0i; + t0i = f4i - f5r * w0i + f5i * w0r; + f5r = f4r * Two - t0r; + f5i = f4i * Two - t0i; + + t1r = f6r - f7r * w0r - f7i * w0i; + t1i = f6i + f7r * w0i - f7i * w0r; + f6r = f6r * Two - t1r; + f6i = f6i * Two - t1i; + + f4r = t0r + f6r * w1r + f6i * w1i; + f4i = t0i - f6r * w1i + f6i * w1r; + f6r = t0r * Two - f4r; + f6i = t0i * Two - f4i; + + f7r = f5r + t1r * w1i - t1i * w1r; + f7i = f5i + t1r * w1r + t1i * w1i; + f5r = f5r * Two - f7r; + f5i = f5i * Two - f7i; + + t0r = f0r - f4r * w2r - f4i * w2i; + t0i = f0i + f4r * w2i - f4i * w2r; + f0r = f0r * Two - t0r; + f0i = f0i * Two - t0i; + + t1r = f1r - f5r * w3r - f5i * w3i; + t1i = f1i + f5r * w3i - f5i * w3r; + f1r = f1r * Two - t1r; + f1i = f1i * Two - t1i; + + *(p0r + pos) = t0r; + *(p1r + pos) = t1r; + *(p0r + posi) = t0i; + *(p1r + posi) = t1i; + *p0r = f0r; + *p1r = f1r; + *(p0r + 1) = f0i; + *(p1r + 1) = f1i; + + p0r += pnext; + f0r = *p0r; + f0i = *(p0r + 1); + + p1r += pnext; + + f1r = *p1r; + f1i = *(p1r + 1); + + f4r = f2r - f6r * w2i + f6i * w2r; + f4i = f2i - f6r * w2r - f6i * w2i; + f6r = f2r * Two - f4r; + f6i = f2i * Two - f4i; + + f5r = f3r - f7r * w3i + f7i * w3r; + f5i = f3i - f7r * w3r - f7i * w3i; + f7r = f3r * Two - f5r; + f7i = f3i * Two - f5i; + + *p2r = f4r; + *p3r = f5r; + *(p2r + 1) = f4i; + *(p3r + 1) = f5i; + *(p2r + pos) = f6r; + *(p3r + pos) = f7r; + *(p2r + posi) = f6i; + *(p3r + posi) = f7i; + + p2r += pnext; + p3r += pnext; + } + + f2r = *p2r; + f2i = *(p2r + 1); + f3r = *p3r; + f3i = *(p3r + 1); + + t0r = f0r + f1r * w0r + f1i * w0i; + t0i = f0i - f1r * w0i + f1i * w0r; + f1r = f0r * Two - t0r; + f1i = f0i * Two - t0i; + + f4r = *(p0r + pos); + f4i = *(p0r + posi); + f5r = *(p1r + pos); + f5i = *(p1r + posi); + + f6r = *(p2r + pos); + f6i = *(p2r + posi); + f7r = *(p3r + pos); + f7i = *(p3r + posi); + + t1r = f2r - f3r * w0r - f3i * w0i; + t1i = f2i + f3r * w0i - f3i * w0r; + f2r = f2r * Two - t1r; + f2i = f2i * Two - t1i; + + f0r = t0r + f2r * w1r + f2i * w1i; + f0i = t0i - f2r * w1i + f2i * w1r; + f2r = t0r * Two - f0r; + f2i = t0i * Two - f0i; + + f3r = f1r + t1r * w1i - t1i * w1r; + f3i = f1i + t1r * w1r + t1i * w1i; + f1r = f1r * Two - f3r; + f1i = f1i * Two - f3i; + + if ((int) DiffUCnt == NDiffU / 2) + Uinc4 = -Uinc4; + + u0r += Uinc4; + u0i -= Uinc4; + u1r += Uinc2; + u1i -= Uinc2; + u2r += Uinc; + u2i -= Uinc; + + pstrt += 2; + + t0r = f4r + f5r * w0r + f5i * w0i; + t0i = f4i - f5r * w0i + f5i * w0r; + f5r = f4r * Two - t0r; + f5i = f4i * Two - t0i; + + t1r = f6r - f7r * w0r - f7i * w0i; + t1i = f6i + f7r * w0i - f7i * w0r; + f6r = f6r * Two - t1r; + f6i = f6i * Two - t1i; + + f4r = t0r + f6r * w1r + f6i * w1i; + f4i = t0i - f6r * w1i + f6i * w1r; + f6r = t0r * Two - f4r; + f6i = t0i * Two - f4i; + + f7r = f5r + t1r * w1i - t1i * w1r; + f7i = f5i + t1r * w1r + t1i * w1i; + f5r = f5r * Two - f7r; + f5i = f5i * Two - f7i; + + w0r = *u0r; + w0i = *u0i; + w1r = *u1r; + w1i = *u1i; + + if ((int) DiffUCnt <= NDiffU / 2) + w0r = -w0r; + + t0r = f0r - f4r * w2r - f4i * w2i; + t0i = f0i + f4r * w2i - f4i * w2r; + f0r = f0r * Two - t0r; + f0i = f0i * Two - t0i; + + f4r = f2r - f6r * w2i + f6i * w2r; + f4i = f2i - f6r * w2r - f6i * w2i; + f6r = f2r * Two - f4r; + f6i = f2i * Two - f4i; + + *(p0r + pos) = t0r; + *p2r = f4r; + *(p0r + posi) = t0i; + *(p2r + 1) = f4i; + w2r = *u2r; + w2i = *u2i; + *p0r = f0r; + *(p2r + pos) = f6r; + *(p0r + 1) = f0i; + *(p2r + posi) = f6i; + + p0r = pstrt; + p2r = pstrt + pinc + pinc; + + t1r = f1r - f5r * w3r - f5i * w3i; + t1i = f1i + f5r * w3i - f5i * w3r; + f1r = f1r * Two - t1r; + f1i = f1i * Two - t1i; + + f5r = f3r - f7r * w3i + f7i * w3r; + f5i = f3i - f7r * w3r - f7i * w3i; + f7r = f3r * Two - f5r; + f7i = f3i * Two - f5i; + + *(p1r + pos) = t1r; + *p3r = f5r; + *(p1r + posi) = t1i; + *(p3r + 1) = f5i; + w3r = *(u2r + U2toU3); + w3i = *(u2i - U2toU3); + *p1r = f1r; + *(p3r + pos) = f7r; + *(p1r + 1) = f1i; + *(p3r + posi) = f7i; + + p1r = pstrt + pinc; + p3r = p2r + pinc; + } + NSameU /= 8; + Uinc /= 8; + Uinc2 /= 8; + Uinc4 = Uinc * 4; + NDiffU *= 8; + pinc *= 8; + pnext *= 8; + pos *= 8; + posi = pos + 1; + } +} + +template +void g_fft::fftrecurs(FFT_TYPE *ioptr, int M, + FFT_TYPE *Utbl, int Ustride, int NDiffU, + int StageCnt) +{ +// recursive bfstages calls to maximize on chip cache efficiency + int i1; + + if (M <= (int) MCACHE) // fits on chip ? + bfstages(ioptr, M, Utbl, Ustride, NDiffU, StageCnt); // RADIX 8 Stages + else { + for (i1 = 0; i1 < 8; i1++) { + fftrecurs(&ioptr[i1 * POW2(M - 3) * 2], M - 3, Utbl, 8 * Ustride, + NDiffU, StageCnt - 1); // RADIX 8 Stages + } + bfstages(ioptr, M, Utbl, Ustride, POW2(M - 3), 1); // RADIX 8 Stage + } +} + +//------------------------------------------------------------------------------ +// Compute in-place complex fft on the rows of the input array +// INPUTS +// *ioptr = input data array +// M = log2 of fft size (ex M=10 for 1024 point fft) +// *Utbl = cosine table +// *BRLow = bit reversed counter table +// OUTPUTS +// *ioptr = output data array +//------------------------------------------------------------------------------ +template +void g_fft::ffts1(FFT_TYPE *ioptr, int M, FFT_TYPE *Utbl, short *BRLow) +{ + int StageCnt; + int NDiffU; + + switch (M) { + case 0: + break; + case 1: + fft2pt(ioptr); // a 2 pt fft + break; + case 2: + fft4pt(ioptr); // a 4 pt fft + break; + case 3: + fft8pt(ioptr); // an 8 pt fft + break; + default: + bitrevR2(ioptr, M, BRLow); // bit reverse and first radix 2 stage + StageCnt = (M - 1) / 3; // number of radix 8 stages + NDiffU = 2; // one radix 2 stage already complete + if ((M - 1 - (StageCnt * 3)) == 1) { + bfR2(ioptr, M, NDiffU); // 1 radix 2 stage + NDiffU *= 2; + } + if ((M - 1 - (StageCnt * 3)) == 2) { + bfR4(ioptr, M, NDiffU); // 1 radix 4 stage + NDiffU *= 4; + } + if (M <= (int) MCACHE) + bfstages(ioptr, M, Utbl, 1, NDiffU, StageCnt); // RADIX 8 Stages + else + fftrecurs(ioptr, M, Utbl, 1, NDiffU, StageCnt); // RADIX 8 Stages + } +} + +//------------------------------------------------------------------------------ +// parts of iffts1 +// scaled bit reverse and first radix 2 stage forward or inverse fft +//------------------------------------------------------------------------------ +template +void g_fft::scbitrevR2(FFT_TYPE *ioptr, int M, short *BRLow, FFT_TYPE scale) +{ + FFT_TYPE f0r; + FFT_TYPE f0i; + FFT_TYPE f1r; + FFT_TYPE f1i; + FFT_TYPE f2r; + FFT_TYPE f2i; + FFT_TYPE f3r; + FFT_TYPE f3i; + FFT_TYPE f4r; + FFT_TYPE f4i; + FFT_TYPE f5r; + FFT_TYPE f5i; + FFT_TYPE f6r; + FFT_TYPE f6i; + FFT_TYPE f7r; + FFT_TYPE f7i; + FFT_TYPE t0r; + FFT_TYPE t0i; + FFT_TYPE t1r; + FFT_TYPE t1i; + FFT_TYPE *p0r; + FFT_TYPE *p1r; + FFT_TYPE *IOP; + FFT_TYPE *iolimit; + int Colstart; + int iCol; + unsigned int posA; + unsigned int posAi; + unsigned int posB; + unsigned int posBi; + + const unsigned int Nrems2 = POW2((M + 3) / 2); + const unsigned int Nroot_1_ColInc = POW2(M) - Nrems2; + const unsigned int Nroot_1 = POW2(M / 2 - 1) - 1; + const unsigned int ColstartShift = (M + 1) / 2 + 1; + + posA = POW2(M); // 1/2 of POW2(M) complexes + posAi = posA + 1; + posB = posA + 2; + posBi = posB + 1; + + iolimit = ioptr + Nrems2; + for (; ioptr < iolimit; ioptr += POW2(M / 2 + 1)) { + for (Colstart = Nroot_1; Colstart >= 0; Colstart--) { + iCol = Nroot_1; + p0r = ioptr + Nroot_1_ColInc + BRLow[Colstart] * 4; + IOP = ioptr + (Colstart << ColstartShift); + p1r = IOP + BRLow[iCol] * 4; + f0r = *(p0r); + f0i = *(p0r + 1); + f1r = *(p0r + posA); + f1i = *(p0r + posAi); + for (; iCol > Colstart;) { + f2r = *(p0r + 2); + f2i = *(p0r + (2 + 1)); + f3r = *(p0r + posB); + f3i = *(p0r + posBi); + f4r = *(p1r); + f4i = *(p1r + 1); + f5r = *(p1r + posA); + f5i = *(p1r + posAi); + f6r = *(p1r + 2); + f6i = *(p1r + (2 + 1)); + f7r = *(p1r + posB); + f7i = *(p1r + posBi); + + t0r = f0r + f1r; + t0i = f0i + f1i; + f1r = f0r - f1r; + f1i = f0i - f1i; + t1r = f2r + f3r; + t1i = f2i + f3i; + f3r = f2r - f3r; + f3i = f2i - f3i; + f0r = f4r + f5r; + f0i = f4i + f5i; + f5r = f4r - f5r; + f5i = f4i - f5i; + f2r = f6r + f7r; + f2i = f6i + f7i; + f7r = f6r - f7r; + f7i = f6i - f7i; + + *(p1r) = scale * t0r; + *(p1r + 1) = scale * t0i; + *(p1r + 2) = scale * f1r; + *(p1r + (2 + 1)) = scale * f1i; + *(p1r + posA) = scale * t1r; + *(p1r + posAi) = scale * t1i; + *(p1r + posB) = scale * f3r; + *(p1r + posBi) = scale * f3i; + *(p0r) = scale * f0r; + *(p0r + 1) = scale * f0i; + *(p0r + 2) = scale * f5r; + *(p0r + (2 + 1)) = scale * f5i; + *(p0r + posA) = scale * f2r; + *(p0r + posAi) = scale * f2i; + *(p0r + posB) = scale * f7r; + *(p0r + posBi) = scale * f7i; + + p0r -= Nrems2; + f0r = *(p0r); + f0i = *(p0r + 1); + f1r = *(p0r + posA); + f1i = *(p0r + posAi); + iCol -= 1; + p1r = IOP + BRLow[iCol] * 4; + } + f2r = *(p0r + 2); + f2i = *(p0r + (2 + 1)); + f3r = *(p0r + posB); + f3i = *(p0r + posBi); + + t0r = f0r + f1r; + t0i = f0i + f1i; + f1r = f0r - f1r; + f1i = f0i - f1i; + t1r = f2r + f3r; + t1i = f2i + f3i; + f3r = f2r - f3r; + f3i = f2i - f3i; + + *(p0r) = scale * t0r; + *(p0r + 1) = scale * t0i; + *(p0r + 2) = scale * f1r; + *(p0r + (2 + 1)) = scale * f1i; + *(p0r + posA) = scale * t1r; + *(p0r + posAi) = scale * t1i; + *(p0r + posB) = scale * f3r; + *(p0r + posBi) = scale * f3i; + } + } +} + +//------------------------------------------------------------------------------ +// RADIX 2 ifft +//------------------------------------------------------------------------------ +template +void g_fft::ifft2pt(FFT_TYPE *ioptr, FFT_TYPE scale) +{ + FFT_TYPE f0r, f0i, f1r, f1i; + FFT_TYPE t0r, t0i; + +// bit reversed load + f0r = ioptr[0]; + f0i = ioptr[1]; + f1r = ioptr[2]; + f1i = ioptr[3]; + +// Butterflys +// f0 - - t0 +// f1 - 1 - f1 + + t0r = f0r + f1r; + t0i = f0i + f1i; + f1r = f0r - f1r; + f1i = f0i - f1i; + +// store result + ioptr[0] = scale * t0r; + ioptr[1] = scale * t0i; + ioptr[2] = scale * f1r; + ioptr[3] = scale * f1i; +} + +//------------------------------------------------------------------------------ +// RADIX 4 ifft +//------------------------------------------------------------------------------ +template +void g_fft::ifft4pt(FFT_TYPE *ioptr, FFT_TYPE scale) +{ + FFT_TYPE f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i; + FFT_TYPE t0r, t0i, t1r, t1i; + +// bit reversed load + f0r = ioptr[0]; + f0i = ioptr[1]; + f1r = ioptr[4]; + f1i = ioptr[5]; + f2r = ioptr[2]; + f2i = ioptr[3]; + f3r = ioptr[6]; + f3i = ioptr[7]; + +// Butterflys +// f0 - - t0 - - f0 +// f1 - 1 - f1 - - f1 +// f2 - - f2 - 1 - f2 +// f3 - 1 - t1 - i - f3 + + t0r = f0r + f1r; + t0i = f0i + f1i; + f1r = f0r - f1r; + f1i = f0i - f1i; + + t1r = f2r - f3r; + t1i = f2i - f3i; + f2r = f2r + f3r; + f2i = f2i + f3i; + + f0r = t0r + f2r; + f0i = t0i + f2i; + f2r = t0r - f2r; + f2i = t0i - f2i; + + f3r = f1r + t1i; + f3i = f1i - t1r; + f1r = f1r - t1i; + f1i = f1i + t1r; + +// store result + ioptr[0] = scale * f0r; + ioptr[1] = scale * f0i; + ioptr[2] = scale * f1r; + ioptr[3] = scale * f1i; + ioptr[4] = scale * f2r; + ioptr[5] = scale * f2i; + ioptr[6] = scale * f3r; + ioptr[7] = scale * f3i; +} + +//------------------------------------------------------------------------------ +// RADIX 8 ifft +//------------------------------------------------------------------------------ +template +void g_fft::ifft8pt(FFT_TYPE *ioptr, FFT_TYPE scale) +{ + FFT_TYPE w0r = 1.0 / FFT_ROOT2; // cos(pi/4) + FFT_TYPE f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i; + FFT_TYPE f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i; + FFT_TYPE t0r, t0i, t1r, t1i; + const FFT_TYPE Two = 2.0; + +// bit reversed load + f0r = ioptr[0]; + f0i = ioptr[1]; + f1r = ioptr[8]; + f1i = ioptr[9]; + f2r = ioptr[4]; + f2i = ioptr[5]; + f3r = ioptr[12]; + f3i = ioptr[13]; + f4r = ioptr[2]; + f4i = ioptr[3]; + f5r = ioptr[10]; + f5i = ioptr[11]; + f6r = ioptr[6]; + f6i = ioptr[7]; + f7r = ioptr[14]; + f7i = ioptr[15]; + +// Butterflys +// f0 - - t0 - - f0 - - f0 +// f1 - 1 - f1 - - f1 - - f1 +// f2 - - f2 - 1 - f2 - - f2 +// f3 - 1 - t1 - i - f3 - - f3 +// f4 - - t0 - - f4 - 1 - t0 +// f5 - 1 - f5 - - f5 - w3 - f4 +// f6 - - f6 - 1 - f6 - i - t1 +// f7 - 1 - t1 - i - f7 - iw3- f6 + + t0r = f0r + f1r; + t0i = f0i + f1i; + f1r = f0r - f1r; + f1i = f0i - f1i; + + t1r = f2r - f3r; + t1i = f2i - f3i; + f2r = f2r + f3r; + f2i = f2i + f3i; + + f0r = t0r + f2r; + f0i = t0i + f2i; + f2r = t0r - f2r; + f2i = t0i - f2i; + + f3r = f1r + t1i; + f3i = f1i - t1r; + f1r = f1r - t1i; + f1i = f1i + t1r; + + t0r = f4r + f5r; + t0i = f4i + f5i; + f5r = f4r - f5r; + f5i = f4i - f5i; + + t1r = f6r - f7r; + t1i = f6i - f7i; + f6r = f6r + f7r; + f6i = f6i + f7i; + + f4r = t0r + f6r; + f4i = t0i + f6i; + f6r = t0r - f6r; + f6i = t0i - f6i; + + f7r = f5r + t1i; + f7i = f5i - t1r; + f5r = f5r - t1i; + f5i = f5i + t1r; + + t0r = f0r - f4r; + t0i = f0i - f4i; + f0r = f0r + f4r; + f0i = f0i + f4i; + + t1r = f2r + f6i; + t1i = f2i - f6r; + f2r = f2r - f6i; + f2i = f2i + f6r; + + f4r = f1r - f5r * w0r + f5i * w0r; + f4i = f1i - f5r * w0r - f5i * w0r; + f1r = f1r * Two - f4r; + f1i = f1i * Two - f4i; + + f6r = f3r + f7r * w0r + f7i * w0r; + f6i = f3i - f7r * w0r + f7i * w0r; + f3r = f3r * Two - f6r; + f3i = f3i * Two - f6i; + +// store result + ioptr[0] = scale * f0r; + ioptr[1] = scale * f0i; + ioptr[2] = scale * f1r; + ioptr[3] = scale * f1i; + ioptr[4] = scale * f2r; + ioptr[5] = scale * f2i; + ioptr[6] = scale * f3r; + ioptr[7] = scale * f3i; + ioptr[8] = scale * t0r; + ioptr[9] = scale * t0i; + ioptr[10] = scale * f4r; + ioptr[11] = scale * f4i; + ioptr[12] = scale * t1r; + ioptr[13] = scale * t1i; + ioptr[14] = scale * f6r; + ioptr[15] = scale * f6i; +} + +//------------------------------------------------------------------------------ +// 2nd radix 2 stage +//------------------------------------------------------------------------------ +template +void g_fft::ibfR2(FFT_TYPE *ioptr, int M, int NDiffU) +{ + unsigned int pos; + unsigned int posi; + unsigned int pinc; + unsigned int pnext; + unsigned int NSameU; + unsigned int SameUCnt; + + FFT_TYPE *pstrt; + FFT_TYPE *p0r, *p1r, *p2r, *p3r; + + FFT_TYPE f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i; + FFT_TYPE f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i; + + pinc = NDiffU * 2; // 2 floats per complex + pnext = pinc * 4; + pos = 2; + posi = pos + 1; + NSameU = POW2(M) / 4 / NDiffU; // 4 Us at a time + pstrt = ioptr; + p0r = pstrt; + p1r = pstrt + pinc; + p2r = p1r + pinc; + p3r = p2r + pinc; + +// Butterflys +// f0 - - f4 +// f1 - 1 - f5 +// f2 - - f6 +// f3 - 1 - f7 +// Butterflys +// f0 - - f4 +// f1 - 1 - f5 +// f2 - - f6 +// f3 - 1 - f7 + + for (SameUCnt = NSameU; SameUCnt > 0; SameUCnt--) { + + f0r = *p0r; + f1r = *p1r; + f0i = *(p0r + 1); + f1i = *(p1r + 1); + f2r = *p2r; + f3r = *p3r; + f2i = *(p2r + 1); + f3i = *(p3r + 1); + + f4r = f0r + f1r; + f4i = f0i + f1i; + f5r = f0r - f1r; + f5i = f0i - f1i; + + f6r = f2r + f3r; + f6i = f2i + f3i; + f7r = f2r - f3r; + f7i = f2i - f3i; + + *p0r = f4r; + *(p0r + 1) = f4i; + *p1r = f5r; + *(p1r + 1) = f5i; + *p2r = f6r; + *(p2r + 1) = f6i; + *p3r = f7r; + *(p3r + 1) = f7i; + + f0r = *(p0r + pos); + f1i = *(p1r + posi); + f0i = *(p0r + posi); + f1r = *(p1r + pos); + f2r = *(p2r + pos); + f3i = *(p3r + posi); + f2i = *(p2r + posi); + f3r = *(p3r + pos); + + f4r = f0r - f1i; + f4i = f0i + f1r; + f5r = f0r + f1i; + f5i = f0i - f1r; + + f6r = f2r - f3i; + f6i = f2i + f3r; + f7r = f2r + f3i; + f7i = f2i - f3r; + + *(p0r + pos) = f4r; + *(p0r + posi) = f4i; + *(p1r + pos) = f5r; + *(p1r + posi) = f5i; + *(p2r + pos) = f6r; + *(p2r + posi) = f6i; + *(p3r + pos) = f7r; + *(p3r + posi) = f7i; + + p0r += pnext; + p1r += pnext; + p2r += pnext; + p3r += pnext; + } +} + +//------------------------------------------------------------------------------ +// 1 radix 4 stage +//------------------------------------------------------------------------------ +template +void g_fft::ibfR4(FFT_TYPE *ioptr, int M, int NDiffU) +{ + unsigned int pos; + unsigned int posi; + unsigned int pinc; + unsigned int pnext; + unsigned int pnexti; + unsigned int NSameU; + unsigned int SameUCnt; + + FFT_TYPE *pstrt; + FFT_TYPE *p0r, *p1r, *p2r, *p3r; + + FFT_TYPE w1r = 1.0 / FFT_ROOT2; // cos(pi/4) + FFT_TYPE f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i; + FFT_TYPE f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i; + FFT_TYPE t1r, t1i; + const FFT_TYPE Two = 2.0; + + pinc = NDiffU * 2; // 2 floats per complex + pnext = pinc * 4; + pnexti = pnext + 1; + pos = 2; + posi = pos + 1; + NSameU = POW2(M) / 4 / NDiffU; // 4 pts per butterfly + pstrt = ioptr; + p0r = pstrt; + p1r = pstrt + pinc; + p2r = p1r + pinc; + p3r = p2r + pinc; + +// Butterflys +// f0 - - f0 - - f4 +// f1 - 1 - f5 - - f5 +// f2 - - f6 - 1 - f6 +// f3 - 1 - f3 - -i - f7 +// Butterflys +// f0 - - f4 - - f4 +// f1 - -i - t1 - - f5 +// f2 - - f2 - w1 - f6 +// f3 - -i - f7 - iw1- f7 + + f0r = *p0r; + f1r = *p1r; + f2r = *p2r; + f3r = *p3r; + f0i = *(p0r + 1); + f1i = *(p1r + 1); + f2i = *(p2r + 1); + f3i = *(p3r + 1); + + f5r = f0r - f1r; + f5i = f0i - f1i; + f0r = f0r + f1r; + f0i = f0i + f1i; + + f6r = f2r + f3r; + f6i = f2i + f3i; + f3r = f2r - f3r; + f3i = f2i - f3i; + + for (SameUCnt = NSameU - 1; SameUCnt > 0; SameUCnt--) { + + f7r = f5r + f3i; + f7i = f5i - f3r; + f5r = f5r - f3i; + f5i = f5i + f3r; + + f4r = f0r + f6r; + f4i = f0i + f6i; + f6r = f0r - f6r; + f6i = f0i - f6i; + + f2r = *(p2r + pos); + f2i = *(p2r + posi); + f1r = *(p1r + pos); + f1i = *(p1r + posi); + f3i = *(p3r + posi); + f0r = *(p0r + pos); + f3r = *(p3r + pos); + f0i = *(p0r + posi); + + *p3r = f7r; + *p0r = f4r; + *(p3r + 1) = f7i; + *(p0r + 1) = f4i; + *p1r = f5r; + *p2r = f6r; + *(p1r + 1) = f5i; + *(p2r + 1) = f6i; + + f7r = f2r + f3i; + f7i = f2i - f3r; + f2r = f2r - f3i; + f2i = f2i + f3r; + + f4r = f0r - f1i; + f4i = f0i + f1r; + t1r = f0r + f1i; + t1i = f0i - f1r; + + f5r = t1r - f7r * w1r - f7i * w1r; + f5i = t1i + f7r * w1r - f7i * w1r; + f7r = t1r * Two - f5r; + f7i = t1i * Two - f5i; + + f6r = f4r - f2r * w1r + f2i * w1r; + f6i = f4i - f2r * w1r - f2i * w1r; + f4r = f4r * Two - f6r; + f4i = f4i * Two - f6i; + + f3r = *(p3r + pnext); + f0r = *(p0r + pnext); + f3i = *(p3r + pnexti); + f0i = *(p0r + pnexti); + f2r = *(p2r + pnext); + f2i = *(p2r + pnexti); + f1r = *(p1r + pnext); + f1i = *(p1r + pnexti); + + *(p2r + pos) = f6r; + *(p1r + pos) = f5r; + *(p2r + posi) = f6i; + *(p1r + posi) = f5i; + *(p3r + pos) = f7r; + *(p0r + pos) = f4r; + *(p3r + posi) = f7i; + *(p0r + posi) = f4i; + + f6r = f2r + f3r; + f6i = f2i + f3i; + f3r = f2r - f3r; + f3i = f2i - f3i; + + f5r = f0r - f1r; + f5i = f0i - f1i; + f0r = f0r + f1r; + f0i = f0i + f1i; + + p3r += pnext; + p0r += pnext; + p1r += pnext; + p2r += pnext; + } + f7r = f5r + f3i; + f7i = f5i - f3r; + f5r = f5r - f3i; + f5i = f5i + f3r; + + f4r = f0r + f6r; + f4i = f0i + f6i; + f6r = f0r - f6r; + f6i = f0i - f6i; + + f2r = *(p2r + pos); + f2i = *(p2r + posi); + f1r = *(p1r + pos); + f1i = *(p1r + posi); + f3i = *(p3r + posi); + f0r = *(p0r + pos); + f3r = *(p3r + pos); + f0i = *(p0r + posi); + + *p3r = f7r; + *p0r = f4r; + *(p3r + 1) = f7i; + *(p0r + 1) = f4i; + *p1r = f5r; + *p2r = f6r; + *(p1r + 1) = f5i; + *(p2r + 1) = f6i; + + f7r = f2r + f3i; + f7i = f2i - f3r; + f2r = f2r - f3i; + f2i = f2i + f3r; + + f4r = f0r - f1i; + f4i = f0i + f1r; + t1r = f0r + f1i; + t1i = f0i - f1r; + + f5r = t1r - f7r * w1r - f7i * w1r; + f5i = t1i + f7r * w1r - f7i * w1r; + f7r = t1r * Two - f5r; + f7i = t1i * Two - f5i; + + f6r = f4r - f2r * w1r + f2i * w1r; + f6i = f4i - f2r * w1r - f2i * w1r; + f4r = f4r * Two - f6r; + f4i = f4i * Two - f6i; + + *(p2r + pos) = f6r; + *(p1r + pos) = f5r; + *(p2r + posi) = f6i; + *(p1r + posi) = f5i; + *(p3r + pos) = f7r; + *(p0r + pos) = f4r; + *(p3r + posi) = f7i; + *(p0r + posi) = f4i; +} + +//------------------------------------------------------------------------------ +// RADIX 8 Stages +//------------------------------------------------------------------------------ +template +void g_fft::ibfstages(FFT_TYPE *ioptr, int M, FFT_TYPE *Utbl, int Ustride, + int NDiffU, int StageCnt) +{ + unsigned int pos; + unsigned int posi; + unsigned int pinc; + unsigned int pnext; + unsigned int NSameU; + int Uinc; + int Uinc2; + int Uinc4; + unsigned int DiffUCnt; + unsigned int SameUCnt; + unsigned int U2toU3; + + FFT_TYPE *pstrt; + FFT_TYPE *p0r, *p1r, *p2r, *p3r; + FFT_TYPE *u0r, *u0i, *u1r, *u1i, *u2r, *u2i; + + FFT_TYPE w0r, w0i, w1r, w1i, w2r, w2i, w3r, w3i; + FFT_TYPE f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i; + FFT_TYPE f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i; + FFT_TYPE t0r, t0i, t1r, t1i; + const FFT_TYPE Two = 2.0; + + pinc = NDiffU * 2; // 2 floats per complex + pnext = pinc * 8; + pos = pinc * 4; + posi = pos + 1; + NSameU = POW2(M) / 8 / NDiffU; // 8 pts per butterfly + Uinc = (int) NSameU * Ustride; + Uinc2 = Uinc * 2; + Uinc4 = Uinc * 4; + U2toU3 = (POW2(M) / 8) * Ustride; + for (; StageCnt > 0; StageCnt--) { + + u0r = &Utbl[0]; + u0i = &Utbl[POW2(M - 2) * Ustride]; + u1r = u0r; + u1i = u0i; + u2r = u0r; + u2i = u0i; + + w0r = *u0r; + w0i = *u0i; + w1r = *u1r; + w1i = *u1i; + w2r = *u2r; + w2i = *u2i; + w3r = *(u2r + U2toU3); + w3i = *(u2i - U2toU3); + + pstrt = ioptr; + + p0r = pstrt; + p1r = pstrt + pinc; + p2r = p1r + pinc; + p3r = p2r + pinc; + +// Butterflys +// f0 - - t0 - - f0 - - f0 +// f1 - w0- f1 - - f1 - - f1 +// f2 - - f2 - w1- f2 - - f4 +// f3 - w0- t1 - iw1- f3 - - f5 +// f4 - - t0 - - f4 - w2- t0 +// f5 - w0- f5 - - f5 - w3- t1 +// f6 - - f6 - w1- f6 - iw2- f6 +// f7 - w0- t1 - iw1- f7 - iw3- f7 + + for (DiffUCnt = NDiffU; DiffUCnt > 0; DiffUCnt--) { + f0r = *p0r; + f0i = *(p0r + 1); + f1r = *p1r; + f1i = *(p1r + 1); + for (SameUCnt = NSameU - 1; SameUCnt > 0; SameUCnt--) { + f2r = *p2r; + f2i = *(p2r + 1); + f3r = *p3r; + f3i = *(p3r + 1); + + t0r = f0r + f1r * w0r - f1i * w0i; + t0i = f0i + f1r * w0i + f1i * w0r; + f1r = f0r * Two - t0r; + f1i = f0i * Two - t0i; + + f4r = *(p0r + pos); + f4i = *(p0r + posi); + f5r = *(p1r + pos); + f5i = *(p1r + posi); + + f6r = *(p2r + pos); + f6i = *(p2r + posi); + f7r = *(p3r + pos); + f7i = *(p3r + posi); + + t1r = f2r - f3r * w0r + f3i * w0i; + t1i = f2i - f3r * w0i - f3i * w0r; + f2r = f2r * Two - t1r; + f2i = f2i * Two - t1i; + + f0r = t0r + f2r * w1r - f2i * w1i; + f0i = t0i + f2r * w1i + f2i * w1r; + f2r = t0r * Two - f0r; + f2i = t0i * Two - f0i; + + f3r = f1r + t1r * w1i + t1i * w1r; + f3i = f1i - t1r * w1r + t1i * w1i; + f1r = f1r * Two - f3r; + f1i = f1i * Two - f3i; + + t0r = f4r + f5r * w0r - f5i * w0i; + t0i = f4i + f5r * w0i + f5i * w0r; + f5r = f4r * Two - t0r; + f5i = f4i * Two - t0i; + + t1r = f6r - f7r * w0r + f7i * w0i; + t1i = f6i - f7r * w0i - f7i * w0r; + f6r = f6r * Two - t1r; + f6i = f6i * Two - t1i; + + f4r = t0r + f6r * w1r - f6i * w1i; + f4i = t0i + f6r * w1i + f6i * w1r; + f6r = t0r * Two - f4r; + f6i = t0i * Two - f4i; + + f7r = f5r + t1r * w1i + t1i * w1r; + f7i = f5i - t1r * w1r + t1i * w1i; + f5r = f5r * Two - f7r; + f5i = f5i * Two - f7i; + + t0r = f0r - f4r * w2r + f4i * w2i; + t0i = f0i - f4r * w2i - f4i * w2r; + f0r = f0r * Two - t0r; + f0i = f0i * Two - t0i; + + t1r = f1r - f5r * w3r + f5i * w3i; + t1i = f1i - f5r * w3i - f5i * w3r; + f1r = f1r * Two - t1r; + f1i = f1i * Two - t1i; + + *(p0r + pos) = t0r; + *(p0r + posi) = t0i; + *p0r = f0r; + *(p0r + 1) = f0i; + + p0r += pnext; + f0r = *p0r; + f0i = *(p0r + 1); + + *(p1r + pos) = t1r; + *(p1r + posi) = t1i; + *p1r = f1r; + *(p1r + 1) = f1i; + + p1r += pnext; + + f1r = *p1r; + f1i = *(p1r + 1); + + f4r = f2r - f6r * w2i - f6i * w2r; + f4i = f2i + f6r * w2r - f6i * w2i; + f6r = f2r * Two - f4r; + f6i = f2i * Two - f4i; + + f5r = f3r - f7r * w3i - f7i * w3r; + f5i = f3i + f7r * w3r - f7i * w3i; + f7r = f3r * Two - f5r; + f7i = f3i * Two - f5i; + + *p2r = f4r; + *(p2r + 1) = f4i; + *(p2r + pos) = f6r; + *(p2r + posi) = f6i; + + p2r += pnext; + + *p3r = f5r; + *(p3r + 1) = f5i; + *(p3r + pos) = f7r; + *(p3r + posi) = f7i; + + p3r += pnext; + } + + f2r = *p2r; + f2i = *(p2r + 1); + f3r = *p3r; + f3i = *(p3r + 1); + + t0r = f0r + f1r * w0r - f1i * w0i; + t0i = f0i + f1r * w0i + f1i * w0r; + f1r = f0r * Two - t0r; + f1i = f0i * Two - t0i; + + f4r = *(p0r + pos); + f4i = *(p0r + posi); + f5r = *(p1r + pos); + f5i = *(p1r + posi); + + f6r = *(p2r + pos); + f6i = *(p2r + posi); + f7r = *(p3r + pos); + f7i = *(p3r + posi); + + t1r = f2r - f3r * w0r + f3i * w0i; + t1i = f2i - f3r * w0i - f3i * w0r; + f2r = f2r * Two - t1r; + f2i = f2i * Two - t1i; + + f0r = t0r + f2r * w1r - f2i * w1i; + f0i = t0i + f2r * w1i + f2i * w1r; + f2r = t0r * Two - f0r; + f2i = t0i * Two - f0i; + + f3r = f1r + t1r * w1i + t1i * w1r; + f3i = f1i - t1r * w1r + t1i * w1i; + f1r = f1r * Two - f3r; + f1i = f1i * Two - f3i; + + if ((int) DiffUCnt == NDiffU / 2) + Uinc4 = -Uinc4; + + u0r += Uinc4; + u0i -= Uinc4; + u1r += Uinc2; + u1i -= Uinc2; + u2r += Uinc; + u2i -= Uinc; + + pstrt += 2; + + t0r = f4r + f5r * w0r - f5i * w0i; + t0i = f4i + f5r * w0i + f5i * w0r; + f5r = f4r * Two - t0r; + f5i = f4i * Two - t0i; + + t1r = f6r - f7r * w0r + f7i * w0i; + t1i = f6i - f7r * w0i - f7i * w0r; + f6r = f6r * Two - t1r; + f6i = f6i * Two - t1i; + + f4r = t0r + f6r * w1r - f6i * w1i; + f4i = t0i + f6r * w1i + f6i * w1r; + f6r = t0r * Two - f4r; + f6i = t0i * Two - f4i; + + f7r = f5r + t1r * w1i + t1i * w1r; + f7i = f5i - t1r * w1r + t1i * w1i; + f5r = f5r * Two - f7r; + f5i = f5i * Two - f7i; + + w0r = *u0r; + w0i = *u0i; + w1r = *u1r; + w1i = *u1i; + + if ((int) DiffUCnt <= NDiffU / 2) + w0r = -w0r; + + t0r = f0r - f4r * w2r + f4i * w2i; + t0i = f0i - f4r * w2i - f4i * w2r; + f0r = f0r * Two - t0r; + f0i = f0i * Two - t0i; + + f4r = f2r - f6r * w2i - f6i * w2r; + f4i = f2i + f6r * w2r - f6i * w2i; + f6r = f2r * Two - f4r; + f6i = f2i * Two - f4i; + + *(p0r + pos) = t0r; + *p2r = f4r; + *(p0r + posi) = t0i; + *(p2r + 1) = f4i; + w2r = *u2r; + w2i = *u2i; + *p0r = f0r; + *(p2r + pos) = f6r; + *(p0r + 1) = f0i; + *(p2r + posi) = f6i; + + p0r = pstrt; + p2r = pstrt + pinc + pinc; + + t1r = f1r - f5r * w3r + f5i * w3i; + t1i = f1i - f5r * w3i - f5i * w3r; + f1r = f1r * Two - t1r; + f1i = f1i * Two - t1i; + + f5r = f3r - f7r * w3i - f7i * w3r; + f5i = f3i + f7r * w3r - f7i * w3i; + f7r = f3r * Two - f5r; + f7i = f3i * Two - f5i; + + *(p1r + pos) = t1r; + *p3r = f5r; + *(p1r + posi) = t1i; + *(p3r + 1) = f5i; + w3r = *(u2r + U2toU3); + w3i = *(u2i - U2toU3); + *p1r = f1r; + *(p3r + pos) = f7r; + *(p1r + 1) = f1i; + *(p3r + posi) = f7i; + + p1r = pstrt + pinc; + p3r = p2r + pinc; + } + NSameU /= 8; + Uinc /= 8; + Uinc2 /= 8; + Uinc4 = Uinc * 4; + NDiffU *= 8; + pinc *= 8; + pnext *= 8; + pos *= 8; + posi = pos + 1; + } +} + +//------------------------------------------------------------------------------ +// recursive bfstages calls to maximize on chip cache efficiency +//------------------------------------------------------------------------------ +template +void g_fft::ifftrecurs(FFT_TYPE *ioptr, int M, FFT_TYPE *Utbl, int Ustride, + int NDiffU, int StageCnt) +{ + int i1; + + if (M <= (int) MCACHE) + ibfstages(ioptr, M, Utbl, Ustride, NDiffU, StageCnt); // RADIX 8 Stages + else { + for (i1 = 0; i1 < 8; i1++) { + ifftrecurs(&ioptr[i1 * POW2(M - 3) * 2], M - 3, Utbl, 8 * Ustride, + NDiffU, StageCnt - 1); // RADIX 8 Stages + } + ibfstages(ioptr, M, Utbl, Ustride, POW2(M - 3), 1); // RADIX 8 Stage + } +} + +//------------------------------------------------------------------------------ +// Compute in-place inverse complex fft on the rows of the input array +// INPUTS +// *ioptr = input data array +// M = log2 of fft size +// *Utbl = cosine table +// *BRLow = bit reversed counter table +// OUTPUTS +// *ioptr = output data array +//------------------------------------------------------------------------------ +template +void g_fft::iffts1(FFT_TYPE *ioptr, int M, FFT_TYPE *Utbl, short *BRLow) +{ + int StageCnt; + int NDiffU; + const FFT_TYPE scale = 1.0 / POW2(M); + + switch (M) { + case 0: + break; + case 1: + ifft2pt(ioptr, scale); // a 2 pt fft + break; + case 2: + ifft4pt(ioptr, scale); // a 4 pt fft + break; + case 3: + ifft8pt(ioptr, scale); // an 8 pt fft + break; + default: +// bit reverse and first radix 2 stage + scbitrevR2(ioptr, M, BRLow, scale); + StageCnt = (M - 1) / 3; // number of radix 8 stages + NDiffU = 2; // one radix 2 stage already complete + if ((M - 1 - (StageCnt * 3)) == 1) { + ibfR2(ioptr, M, NDiffU); // 1 radix 2 stage + NDiffU *= 2; + } + if ((M - 1 - (StageCnt * 3)) == 2) { + ibfR4(ioptr, M, NDiffU); // 1 radix 4 stage + NDiffU *= 4; + } + if (M <= (int) MCACHE) + ibfstages(ioptr, M, Utbl, 1, NDiffU, StageCnt); // RADIX 8 Stages + else + ifftrecurs(ioptr, M, Utbl, 1, NDiffU, StageCnt); // RADIX 8 Stages + } +} + +//------------------------------------------------------------------------------ +// parts of rffts1 +// RADIX 2 rfft +//------------------------------------------------------------------------------ +template +void g_fft::rfft1pt(FFT_TYPE *ioptr) +{ + FFT_TYPE f0r, f0i; + FFT_TYPE t0r, t0i; + +// bit reversed load + f0r = ioptr[0]; + f0i = ioptr[1]; + +// finish rfft + t0r = f0r + f0i; + t0i = f0r - f0i; + +// store result + ioptr[0] = t0r; + ioptr[1] = t0i; +} + +//------------------------------------------------------------------------------ +// RADIX 4 rfft +//------------------------------------------------------------------------------ +template +void g_fft::rfft2pt(FFT_TYPE *ioptr) +{ + FFT_TYPE f0r, f0i, f1r, f1i; + FFT_TYPE t0r, t0i; + +// bit reversed load + f0r = ioptr[0]; + f0i = ioptr[1]; + f1r = ioptr[2]; + f1i = ioptr[3]; + +// Butterflys +// f0 - - t0 +// f1 - 1 - f1 + + t0r = f0r + f1r; + t0i = f0i + f1i; + f1r = f0r - f1r; + f1i = f1i - f0i; +// finish rfft + f0r = t0r + t0i; + f0i = t0r - t0i; + +// store result + ioptr[0] = f0r; + ioptr[1] = f0i; + ioptr[2] = f1r; + ioptr[3] = f1i; +} + +//------------------------------------------------------------------------------ +// RADIX 8 rfft +//------------------------------------------------------------------------------ +template +void g_fft::rfft4pt(FFT_TYPE *ioptr) +{ + FFT_TYPE f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i; + FFT_TYPE t0r, t0i, t1r, t1i; + FFT_TYPE w0r = 1.0 / FFT_ROOT2; // cos(pi/4) + const FFT_TYPE Two = 2.0; + const FFT_TYPE scale = 0.5; + +// bit reversed load + f0r = ioptr[0]; + f0i = ioptr[1]; + f1r = ioptr[4]; + f1i = ioptr[5]; + f2r = ioptr[2]; + f2i = ioptr[3]; + f3r = ioptr[6]; + f3i = ioptr[7]; + +// Butterflys +// f0 - - t0 - - f0 +// f1 - 1 - f1 - - f1 +// f2 - - f2 - 1 - f2 +// f3 - 1 - t1 - -i - f3 + + t0r = f0r + f1r; + t0i = f0i + f1i; + f1r = f0r - f1r; + f1i = f0i - f1i; + + t1r = f2r - f3r; + t1i = f2i - f3i; + f2r = f2r + f3r; + f2i = f2i + f3i; + + f0r = t0r + f2r; + f0i = t0i + f2i; + f2r = t0r - f2r; + f2i = f2i - t0i; // neg for rfft + + f3r = f1r - t1i; + f3i = f1i + t1r; + f1r = f1r + t1i; + f1i = f1i - t1r; + +// finish rfft + t0r = f0r + f0i; // compute Re(x[0]) + t0i = f0r - f0i; // compute Re(x[N/2]) + + t1r = f1r + f3r; + t1i = f1i - f3i; + f0r = f1i + f3i; + f0i = f3r - f1r; + + f1r = t1r + w0r * f0r + w0r * f0i; + f1i = t1i - w0r * f0r + w0r * f0i; + f3r = Two * t1r - f1r; + f3i = f1i - Two * t1i; + +// store result + ioptr[4] = f2r; + ioptr[5] = f2i; + ioptr[0] = t0r; + ioptr[1] = t0i; + + ioptr[2] = scale * f1r; + ioptr[3] = scale * f1i; + ioptr[6] = scale * f3r; + ioptr[7] = scale * f3i; +} + +//------------------------------------------------------------------------------ +// RADIX 16 rfft +//------------------------------------------------------------------------------ +template +void g_fft::rfft8pt(FFT_TYPE *ioptr) +{ + FFT_TYPE w0r = 1.0 / FFT_ROOT2; // cos(pi/4) + FFT_TYPE w1r = FFT_COSPID8; // cos(pi/8) + FFT_TYPE w1i = FFT_SINPID8; // sin(pi/8) + FFT_TYPE f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i; + FFT_TYPE f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i; + FFT_TYPE t0r, t0i, t1r, t1i; + const FFT_TYPE Two = 2.0; + const FFT_TYPE scale = 0.5; + +// bit reversed load + f0r = ioptr[0]; + f0i = ioptr[1]; + f1r = ioptr[8]; + f1i = ioptr[9]; + f2r = ioptr[4]; + f2i = ioptr[5]; + f3r = ioptr[12]; + f3i = ioptr[13]; + f4r = ioptr[2]; + f4i = ioptr[3]; + f5r = ioptr[10]; + f5i = ioptr[11]; + f6r = ioptr[6]; + f6i = ioptr[7]; + f7r = ioptr[14]; + f7i = ioptr[15]; + +// Butterflys +// f0 - - t0 - - f0 - - f0 +// f1 - 1 - f1 - - f1 - - f1 +// f2 - - f2 - 1 - f2 - - f2 +// f3 - 1 - t1 - -i - f3 - - f3 +// f4 - - t0 - - f4 - 1 - t0 +// f5 - 1 - f5 - - f5 - w3 - f4 +// f6 - - f6 - 1 - f6 - -i - t1 +// f7 - 1 - t1 - -i - f7 - iw3- f6 + + t0r = f0r + f1r; + t0i = f0i + f1i; + f1r = f0r - f1r; + f1i = f0i - f1i; + + t1r = f2r - f3r; + t1i = f2i - f3i; + f2r = f2r + f3r; + f2i = f2i + f3i; + + f0r = t0r + f2r; + f0i = t0i + f2i; + f2r = t0r - f2r; + f2i = t0i - f2i; + + f3r = f1r - t1i; + f3i = f1i + t1r; + f1r = f1r + t1i; + f1i = f1i - t1r; + + t0r = f4r + f5r; + t0i = f4i + f5i; + f5r = f4r - f5r; + f5i = f4i - f5i; + + t1r = f6r - f7r; + t1i = f6i - f7i; + f6r = f6r + f7r; + f6i = f6i + f7i; + + f4r = t0r + f6r; + f4i = t0i + f6i; + f6r = t0r - f6r; + f6i = t0i - f6i; + + f7r = f5r - t1i; + f7i = f5i + t1r; + f5r = f5r + t1i; + f5i = f5i - t1r; + + t0r = f0r - f4r; + t0i = f4i - f0i; // neg for rfft + f0r = f0r + f4r; + f0i = f0i + f4i; + + t1r = f2r - f6i; + t1i = f2i + f6r; + f2r = f2r + f6i; + f2i = f2i - f6r; + + f4r = f1r - f5r * w0r - f5i * w0r; + f4i = f1i + f5r * w0r - f5i * w0r; + f1r = f1r * Two - f4r; + f1i = f1i * Two - f4i; + + f6r = f3r + f7r * w0r - f7i * w0r; + f6i = f3i + f7r * w0r + f7i * w0r; + f3r = f3r * Two - f6r; + f3i = f3i * Two - f6i; + +// finish rfft + f5r = f0r + f0i; // compute Re(x[0]) + f5i = f0r - f0i; // compute Re(x[N/2]) + + f0r = f2r + t1r; + f0i = f2i - t1i; + f7r = f2i + t1i; + f7i = t1r - f2r; + + f2r = f0r + w0r * f7r + w0r * f7i; + f2i = f0i - w0r * f7r + w0r * f7i; + t1r = Two * f0r - f2r; + t1i = f2i - Two * f0i; + + f0r = f1r + f6r; + f0i = f1i - f6i; + f7r = f1i + f6i; + f7i = f6r - f1r; + + f1r = f0r + w1r * f7r + w1i * f7i; + f1i = f0i - w1i * f7r + w1r * f7i; + f6r = Two * f0r - f1r; + f6i = f1i - Two * f0i; + + f0r = f3r + f4r; + f0i = f3i - f4i; + f7r = f3i + f4i; + f7i = f4r - f3r; + + f3r = f0r + w1i * f7r + w1r * f7i; + f3i = f0i - w1r * f7r + w1i * f7i; + f4r = Two * f0r - f3r; + f4i = f3i - Two * f0i; + +// store result + ioptr[8] = t0r; + ioptr[9] = t0i; + ioptr[0] = f5r; + ioptr[1] = f5i; + + ioptr[4] = scale * f2r; + ioptr[5] = scale * f2i; + ioptr[12] = scale * t1r; + ioptr[13] = scale * t1i; + + ioptr[2] = scale * f1r; + ioptr[3] = scale * f1i; + ioptr[6] = scale * f3r; + ioptr[7] = scale * f3i; + ioptr[10] = scale * f4r; + ioptr[11] = scale * f4i; + ioptr[14] = scale * f6r; + ioptr[15] = scale * f6i; +} + +//------------------------------------------------------------------------------ +// Finish RFFT +//------------------------------------------------------------------------------ +template +void g_fft::frstage(FFT_TYPE *ioptr, int M, FFT_TYPE *Utbl) +{ + unsigned int pos; + unsigned int posi; + unsigned int diffUcnt; + + FFT_TYPE *p0r, *p1r; + FFT_TYPE *u0r, *u0i; + + FFT_TYPE w0r, w0i; + FFT_TYPE f0r, f0i, f1r, f1i, f4r, f4i, f5r, f5i; + FFT_TYPE t0r, t0i, t1r, t1i; + const FFT_TYPE Two = 2.0; + + pos = POW2(M - 1); + posi = pos + 1; + + p0r = ioptr; + p1r = ioptr + pos / 2; + + u0r = Utbl + POW2(M - 3); + + w0r = *u0r, f0r = *(p0r); + f0i = *(p0r + 1); + f4r = *(p0r + pos); + f4i = *(p0r + posi); + f1r = *(p1r); + f1i = *(p1r + 1); + f5r = *(p1r + pos); + f5i = *(p1r + posi); + + t0r = Two * f0r + Two * f0i; // compute Re(x[0]) + t0i = Two * f0r - Two * f0i; // compute Re(x[N/2]) + t1r = f4r + f4r; + t1i = -f4i - f4i; + + f0r = f1r + f5r; + f0i = f1i - f5i; + f4r = f1i + f5i; + f4i = f5r - f1r; + + f1r = f0r + w0r * f4r + w0r * f4i; + f1i = f0i - w0r * f4r + w0r * f4i; + f5r = Two * f0r - f1r; + f5i = f1i - Two * f0i; + + *(p0r) = t0r; + *(p0r + 1) = t0i; + *(p0r + pos) = t1r; + *(p0r + posi) = t1i; + *(p1r) = f1r; + *(p1r + 1) = f1i; + *(p1r + pos) = f5r; + *(p1r + posi) = f5i; + + u0r = Utbl + 1; + u0i = Utbl + (POW2(M - 2) - 1); + + w0r = *u0r, w0i = *u0i; + + p0r = (ioptr + 2); + p1r = (ioptr + (POW2(M - 2) - 1) * 2); + +// Butterflys +// f0 - t0 - - f0 +// f5 - t1 - w0 - f5 +// f1 - t0 - - f1 +// f4 - t1 -iw0 - f4 + + for (diffUcnt = POW2(M - 3) - 1; diffUcnt > 0; diffUcnt--) { + + f0r = *(p0r); + f0i = *(p0r + 1); + f5r = *(p1r + pos); + f5i = *(p1r + posi); + f1r = *(p1r); + f1i = *(p1r + 1); + f4r = *(p0r + pos); + f4i = *(p0r + posi); + + t0r = f0r + f5r; + t0i = f0i - f5i; + t1r = f0i + f5i; + t1i = f5r - f0r; + + f0r = t0r + w0r * t1r + w0i * t1i; + f0i = t0i - w0i * t1r + w0r * t1i; + f5r = Two * t0r - f0r; + f5i = f0i - Two * t0i; + + t0r = f1r + f4r; + t0i = f1i - f4i; + t1r = f1i + f4i; + t1i = f4r - f1r; + + f1r = t0r + w0i * t1r + w0r * t1i; + f1i = t0i - w0r * t1r + w0i * t1i; + f4r = Two * t0r - f1r; + f4i = f1i - Two * t0i; + + *(p0r) = f0r; + *(p0r + 1) = f0i; + *(p1r + pos) = f5r; + *(p1r + posi) = f5i; + + w0r = *++u0r; + w0i = *--u0i; + + *(p1r) = f1r; + *(p1r + 1) = f1i; + *(p0r + pos) = f4r; + *(p0r + posi) = f4i; + + p0r += 2; + p1r -= 2; + } +} + +//------------------------------------------------------------------------------ +// Compute in-place real fft on the rows of the input array +// The result is the complex spectra of the positive frequencies +// except the location for the first complex number contains the real +// values for DC and Nyquest +// INPUTS +// *ioptr = real input data array +// M = log2 of fft size +// *Utbl = cosine table +// *BRLow = bit reversed counter table +// OUTPUTS +// *ioptr = output data array in the following order +// Re(x[0]), Re(x[N/2]), Re(x[1]), Im(x[1]), Re(x[2]), Im(x[2]), +// ... Re(x[N/2-1]), Im(x[N/2-1]). +//------------------------------------------------------------------------------ +template +void g_fft::rffts1(FFT_TYPE *ioptr, int M, FFT_TYPE *Utbl, short *BRLow) +{ + FFT_TYPE scale; + int StageCnt; + int NDiffU; + + M = M - 1; + switch (M) { + case -1: + break; + case 0: + rfft1pt(ioptr); // a 2 pt fft + break; + case 1: + rfft2pt(ioptr); // a 4 pt fft + break; + case 2: + rfft4pt(ioptr); // an 8 pt fft + break; + case 3: + rfft8pt(ioptr); // a 16 pt fft + break; + default: + scale = 0.5; +// bit reverse and first radix 2 stage + scbitrevR2(ioptr, M, BRLow, scale); + StageCnt = (M - 1) / 3; // number of radix 8 stages + NDiffU = 2; // one radix 2 stage already complete + if ((M - 1 - (StageCnt * 3)) == 1) { + bfR2(ioptr, M, NDiffU); // 1 radix 2 stage + NDiffU *= 2; + } + if ((M - 1 - (StageCnt * 3)) == 2) { + bfR4(ioptr, M, NDiffU); // 1 radix 4 stage + NDiffU *= 4; + } + if (M <= (int) MCACHE) + bfstages(ioptr, M, Utbl, 2, NDiffU, StageCnt); // RADIX 8 Stages + else + fftrecurs(ioptr, M, Utbl, 2, NDiffU, StageCnt); // RADIX 8 Stages + frstage(ioptr, M + 1, Utbl); + } +} + +//------------------------------------------------------------------------------ +// parts of riffts1 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// RADIX 2 rifft +//------------------------------------------------------------------------------ +template +void g_fft::rifft1pt(FFT_TYPE *ioptr, FFT_TYPE scale) +{ + FFT_TYPE f0r, f0i; + FFT_TYPE t0r, t0i; + +// bit reversed load + f0r = ioptr[0]; + f0i = ioptr[1]; + +// finish rfft + t0r = f0r + f0i; + t0i = f0r - f0i; + +// store result + ioptr[0] = scale * t0r; + ioptr[1] = scale * t0i; +} + +//------------------------------------------------------------------------------ +// RADIX 4 rifft +//------------------------------------------------------------------------------ +template +void g_fft::rifft2pt(FFT_TYPE *ioptr, FFT_TYPE scale) +{ + FFT_TYPE f0r, f0i, f1r, f1i; + FFT_TYPE t0r, t0i; + const FFT_TYPE Two = FFT_TYPE(2.0); + +// bit reversed load + t0r = ioptr[0]; + t0i = ioptr[1]; + f1r = Two * ioptr[2]; + f1i = Two * ioptr[3]; + +// start rifft + f0r = t0r + t0i; + f0i = t0r - t0i; + +// Butterflys +// f0 - - t0 +// f1 - 1 - f1 + + t0r = f0r + f1r; + t0i = f0i - f1i; + f1r = f0r - f1r; + f1i = f0i + f1i; + +// store result + ioptr[0] = scale * t0r; + ioptr[1] = scale * t0i; + ioptr[2] = scale * f1r; + ioptr[3] = scale * f1i; +} + +//------------------------------------------------------------------------------ +// RADIX 8 rifft +//------------------------------------------------------------------------------ +template +void g_fft::rifft4pt(FFT_TYPE *ioptr, FFT_TYPE scale) +{ + FFT_TYPE f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i; + FFT_TYPE t0r, t0i, t1r, t1i; + FFT_TYPE w0r = 1.0 / FFT_ROOT2; // cos(pi/4) + const FFT_TYPE Two = FFT_TYPE(2.0); + +// bit reversed load + t0r = ioptr[0]; + t0i = ioptr[1]; + f2r = ioptr[2]; + f2i = ioptr[3]; + f1r = Two * ioptr[4]; + f1i = Two * ioptr[5]; + f3r = ioptr[6]; + f3i = ioptr[7]; + +// start rfft + f0r = t0r + t0i; // compute Re(x[0]) + f0i = t0r - t0i; // compute Re(x[N/2]) + + t1r = f2r + f3r; + t1i = f2i - f3i; + t0r = f2r - f3r; + t0i = f2i + f3i; + + f2r = t1r - w0r * t0r - w0r * t0i; + f2i = t1i + w0r * t0r - w0r * t0i; + f3r = Two * t1r - f2r; + f3i = f2i - Two * t1i; + +// Butterflys +// f0 - - t0 - - f0 +// f1 - 1 - f1 - - f1 +// f2 - - f2 - 1 - f2 +// f3 - 1 - t1 - i - f3 + + t0r = f0r + f1r; + t0i = f0i - f1i; + f1r = f0r - f1r; + f1i = f0i + f1i; + + t1r = f2r - f3r; + t1i = f2i - f3i; + f2r = f2r + f3r; + f2i = f2i + f3i; + + f0r = t0r + f2r; + f0i = t0i + f2i; + f2r = t0r - f2r; + f2i = t0i - f2i; + + f3r = f1r + t1i; + f3i = f1i - t1r; + f1r = f1r - t1i; + f1i = f1i + t1r; + +// store result + ioptr[0] = scale * f0r; + ioptr[1] = scale * f0i; + ioptr[2] = scale * f1r; + ioptr[3] = scale * f1i; + ioptr[4] = scale * f2r; + ioptr[5] = scale * f2i; + ioptr[6] = scale * f3r; + ioptr[7] = scale * f3i; +} + +//------------------------------------------------------------------------------ +// RADIX 16 rifft +//------------------------------------------------------------------------------ +template +void g_fft::rifft8pt(FFT_TYPE *ioptr, FFT_TYPE scale) +{ + FFT_TYPE w0r = (FFT_TYPE) (1.0 / FFT_ROOT2); // cos(pi/4) + FFT_TYPE w1r = FFT_COSPID8; // cos(pi/8) + FFT_TYPE w1i = FFT_SINPID8; // sin(pi/8) + FFT_TYPE f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i; + FFT_TYPE f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i; + FFT_TYPE t0r, t0i, t1r, t1i; + const FFT_TYPE Two = FFT_TYPE(2.0); + +// bit reversed load + t0r = ioptr[0]; + t0i = ioptr[1]; + f4r = ioptr[2]; + f4i = ioptr[3]; + f2r = ioptr[4]; + f2i = ioptr[5]; + f6r = ioptr[6]; + f6i = ioptr[7]; + f1r = Two * ioptr[8]; + f1i = Two * ioptr[9]; + f5r = ioptr[10]; + f5i = ioptr[11]; + f3r = ioptr[12]; + f3i = ioptr[13]; + f7r = ioptr[14]; + f7i = ioptr[15]; + +// start rfft + f0r = t0r + t0i; // compute Re(x[0]) + f0i = t0r - t0i; // compute Re(x[N/2]) + + t0r = f2r + f3r; + t0i = f2i - f3i; + t1r = f2r - f3r; + t1i = f2i + f3i; + + f2r = t0r - w0r * t1r - w0r * t1i; + f2i = t0i + w0r * t1r - w0r * t1i; + f3r = Two * t0r - f2r; + f3i = f2i - Two * t0i; + + t0r = f4r + f7r; + t0i = f4i - f7i; + t1r = f4r - f7r; + t1i = f4i + f7i; + + f4r = t0r - w1i * t1r - w1r * t1i; + f4i = t0i + w1r * t1r - w1i * t1i; + f7r = Two * t0r - f4r; + f7i = f4i - Two * t0i; + + t0r = f6r + f5r; + t0i = f6i - f5i; + t1r = f6r - f5r; + t1i = f6i + f5i; + + f6r = t0r - w1r * t1r - w1i * t1i; + f6i = t0i + w1i * t1r - w1r * t1i; + f5r = Two * t0r - f6r; + f5i = f6i - Two * t0i; + +// Butterflys +// f0 - - t0 - - f0 - - f0 +// f1* - 1 - f1 - - f1 - - f1 +// f2 - - f2 - 1 - f2 - - f2 +// f3 - 1 - t1 - i - f3 - - f3 +// f4 - - t0 - - f4 - 1 - t0 +// f5 - 1 - f5 - - f5 - w3 - f4 +// f6 - - f6 - 1 - f6 - i - t1 +// f7 - 1 - t1 - i - f7 - iw3- f6 + + t0r = f0r + f1r; + t0i = f0i - f1i; + f1r = f0r - f1r; + f1i = f0i + f1i; + + t1r = f2r - f3r; + t1i = f2i - f3i; + f2r = f2r + f3r; + f2i = f2i + f3i; + + f0r = t0r + f2r; + f0i = t0i + f2i; + f2r = t0r - f2r; + f2i = t0i - f2i; + + f3r = f1r + t1i; + f3i = f1i - t1r; + f1r = f1r - t1i; + f1i = f1i + t1r; + + t0r = f4r + f5r; + t0i = f4i + f5i; + f5r = f4r - f5r; + f5i = f4i - f5i; + + t1r = f6r - f7r; + t1i = f6i - f7i; + f6r = f6r + f7r; + f6i = f6i + f7i; + + f4r = t0r + f6r; + f4i = t0i + f6i; + f6r = t0r - f6r; + f6i = t0i - f6i; + + f7r = f5r + t1i; + f7i = f5i - t1r; + f5r = f5r - t1i; + f5i = f5i + t1r; + + t0r = f0r - f4r; + t0i = f0i - f4i; + f0r = f0r + f4r; + f0i = f0i + f4i; + + t1r = f2r + f6i; + t1i = f2i - f6r; + f2r = f2r - f6i; + f2i = f2i + f6r; + + f4r = f1r - f5r * w0r + f5i * w0r; + f4i = f1i - f5r * w0r - f5i * w0r; + f1r = f1r * Two - f4r; + f1i = f1i * Two - f4i; + + f6r = f3r + f7r * w0r + f7i * w0r; + f6i = f3i - f7r * w0r + f7i * w0r; + f3r = f3r * Two - f6r; + f3i = f3i * Two - f6i; + +// store result + ioptr[0] = scale * f0r; + ioptr[1] = scale * f0i; + ioptr[2] = scale * f1r; + ioptr[3] = scale * f1i; + ioptr[4] = scale * f2r; + ioptr[5] = scale * f2i; + ioptr[6] = scale * f3r; + ioptr[7] = scale * f3i; + ioptr[8] = scale * t0r; + ioptr[9] = scale * t0i; + ioptr[10] = scale * f4r; + ioptr[11] = scale * f4i; + ioptr[12] = scale * t1r; + ioptr[13] = scale * t1i; + ioptr[14] = scale * f6r; + ioptr[15] = scale * f6i; +} + +//------------------------------------------------------------------------------ +// Start RIFFT +//------------------------------------------------------------------------------ +template +void g_fft::ifrstage(FFT_TYPE *ioptr, int M, FFT_TYPE *Utbl) +{ + unsigned int pos; + unsigned int posi; + unsigned int diffUcnt; + + FFT_TYPE *p0r, *p1r; + FFT_TYPE *u0r, *u0i; + + FFT_TYPE w0r, w0i; + FFT_TYPE f0r, f0i, f1r, f1i, f4r, f4i, f5r, f5i; + FFT_TYPE t0r, t0i, t1r, t1i; + const FFT_TYPE Two = FFT_TYPE(2.0); + + pos = POW2(M - 1); + posi = pos + 1; + + p0r = ioptr; + p1r = ioptr + pos / 2; + + u0r = Utbl + POW2(M - 3); + + w0r = *u0r, f0r = *(p0r); + f0i = *(p0r + 1); + f4r = *(p0r + pos); + f4i = *(p0r + posi); + f1r = *(p1r); + f1i = *(p1r + 1); + f5r = *(p1r + pos); + f5i = *(p1r + posi); + + t0r = f0r + f0i; + t0i = f0r - f0i; + t1r = f4r + f4r; + t1i = -f4i - f4i; + + f0r = f1r + f5r; + f0i = f1i - f5i; + f4r = f1r - f5r; + f4i = f1i + f5i; + + f1r = f0r - w0r * f4r - w0r * f4i; + f1i = f0i + w0r * f4r - w0r * f4i; + f5r = Two * f0r - f1r; + f5i = f1i - Two * f0i; + + *(p0r) = t0r; + *(p0r + 1) = t0i; + *(p0r + pos) = t1r; + *(p0r + posi) = t1i; + *(p1r) = f1r; + *(p1r + 1) = f1i; + *(p1r + pos) = f5r; + *(p1r + posi) = f5i; + + u0r = Utbl + 1; + u0i = Utbl + (POW2(M - 2) - 1); + + w0r = *u0r, w0i = *u0i; + + p0r = (ioptr + 2); + p1r = (ioptr + (POW2(M - 2) - 1) * 2); + +// Butterflys +// f0 - t0 - f0 +// f1 - t1 -w0- f1 +// f2 - t0 - f2 +// f3 - t1 -iw0- f3 + + for (diffUcnt = POW2(M - 3) - 1; diffUcnt > 0; diffUcnt--) { + + f0r = *(p0r); + f0i = *(p0r + 1); + f5r = *(p1r + pos); + f5i = *(p1r + posi); + f1r = *(p1r); + f1i = *(p1r + 1); + f4r = *(p0r + pos); + f4i = *(p0r + posi); + + t0r = f0r + f5r; + t0i = f0i - f5i; + t1r = f0r - f5r; + t1i = f0i + f5i; + + f0r = t0r - w0i * t1r - w0r * t1i; + f0i = t0i + w0r * t1r - w0i * t1i; + f5r = Two * t0r - f0r; + f5i = f0i - Two * t0i; + + t0r = f1r + f4r; + t0i = f1i - f4i; + t1r = f1r - f4r; + t1i = f1i + f4i; + + f1r = t0r - w0r * t1r - w0i * t1i; + f1i = t0i + w0i * t1r - w0r * t1i; + f4r = Two * t0r - f1r; + f4i = f1i - Two * t0i; + + *(p0r) = f0r; + *(p0r + 1) = f0i; + *(p1r + pos) = f5r; + *(p1r + posi) = f5i; + + w0r = *++u0r; + w0i = *--u0i; + + *(p1r) = f1r; + *(p1r + 1) = f1i; + *(p0r + pos) = f4r; + *(p0r + posi) = f4i; + + p0r += 2; + p1r -= 2; + } +} + +//------------------------------------------------------------------------------ +// Compute in-place real ifft on the rows of the input array +// data order as from rffts1 +// INPUTS +// *ioptr = input data array in the following order +// M = log2 of fft size +// Re(x[0]), Re(x[N/2]), Re(x[1]), Im(x[1]), +// Re(x[2]), Im(x[2]), ... Re(x[N/2-1]), Im(x[N/2-1]). +// *Utbl = cosine table +// *BRLow = bit reversed counter table +// OUTPUTS +// *ioptr = real output data array +//------------------------------------------------------------------------------ +template +void g_fft::riffts1(FFT_TYPE *ioptr, int M, FFT_TYPE *Utbl, short *BRLow) +{ + FFT_TYPE scale; + int StageCnt; + int NDiffU; + + scale = (FFT_TYPE)(1.0 / (float)((int)POW2(M))); + M = M - 1; + switch (M) { + case -1: + break; + case 0: + rifft1pt(ioptr, scale); // a 2 pt fft + break; + case 1: + rifft2pt(ioptr, scale); // a 4 pt fft + break; + case 2: + rifft4pt(ioptr, scale); // an 8 pt fft + break; + case 3: + rifft8pt(ioptr, scale); // a 16 pt fft + break; + default: + ifrstage(ioptr, M + 1, Utbl); +// bit reverse and first radix 2 stage + scbitrevR2(ioptr, M, BRLow, scale); + StageCnt = (M - 1) / 3; // number of radix 8 stages + NDiffU = 2; // one radix 2 stage already complete + if ((M - 1 - (StageCnt * 3)) == 1) { + ibfR2(ioptr, M, NDiffU); // 1 radix 2 stage + NDiffU *= 2; + } + if ((M - 1 - (StageCnt * 3)) == 2) { + ibfR4(ioptr, M, NDiffU); // 1 radix 4 stage + NDiffU *= 4; + } + if (M <= (int) MCACHE) + ibfstages(ioptr, M, Utbl, 2, NDiffU, StageCnt); // RADIX 8 Stages + else + ifftrecurs(ioptr, M, Utbl, 2, NDiffU, StageCnt); // RADIX 8 Stages + } +} + +//============================================================================== +// End of original C functions +// +// Wrapper methods for simple class access +//============================================================================== + +//------------------------------------------------------------------------------ +// malloc and init cosine and bit reversed tables for a given size +// fft, ifft, rfft, rifft +// INPUTS +// M = log2 of fft size (ex M=10 for 1024 point fft) +// OUTPUTS +// private cosine and bit reversed tables +//------------------------------------------------------------------------------ +template +void g_fft::fftInit() +{ + for (int i = 0; i < 32; i++) + { + FFT_table_1[i] = (FFT_TYPE*)0; + FFT_table_2[i] = (short int*)0; + } + + FFT_N = ConvertFFTSize(FFT_size); + +// create and initialize cos table + FFT_table_1[FFT_N] = new FFT_TYPE[(POW2(FFT_N) / 4 + 1)]; + fftCosInit(FFT_N, FFT_table_1[FFT_N]); + +// create and initialize bit reverse tables + FFT_table_2[FFT_N/2] = new short[POW2(FFT_N/2 - 1)]; + fftBRInit(FFT_N, FFT_table_2[FFT_N/2]); + + FFT_table_2[(FFT_N - 1) / 2] = new short[POW2((FFT_N - 1) / 2 - 1)]; + fftBRInit(FFT_N - 1, FFT_table_2[(FFT_N - 1) / 2]); + + Utbl = ((FFT_TYPE**) FFT_table_1)[FFT_N]; + BRLow = ((short**) FFT_table_2)[FFT_N / 2]; + +} + +//------------------------------------------------------------------------------ +// convert from N to LOG2(N) +//------------------------------------------------------------------------------ +template +int g_fft::ConvertFFTSize(int N) +{ + if (N <= 0) N = -N; + + switch (N) { + case 0x00000001: return 0; // 1 + case 0x00000002: return 1; // 2 + case 0x00000004: return 2; // 4 + case 0x00000008: return 3; // 8 + case 0x00000010: return 4; // 16 + case 0x00000020: return 5; // 32 + case 0x00000040: return 6; // 64 + case 0x00000080: return 7; // 128 + case 0x00000100: return 8; // 256 + case 0x00000200: return 9; // 512 + case 0x00000400: return 10; // 1024 + case 0x00000800: return 11; // 2048 + case 0x00001000: return 12; // 4096 + case 0x00002000: return 13; // 8192 + case 0x00004000: return 14; // 16384 + case 0x00008000: return 15; // 32768 + case 0x00010000: return 16; // 65536 + case 0x00020000: return 17; // 131072 + case 0x00040000: return 18; // 262144 + case 0x00080000: return 19; // 525288 + case 0x00100000: return 20; // 1048576 + case 0x00200000: return 21; // 2097152 + case 0x00400000: return 22; // 4194304 + case 0x00800000: return 23; // 8388608 + case 0x01000000: return 24; // 16777216 + case 0x02000000: return 25; // 33554432 + case 0x04000000: return 26; // 67108864 + case 0x08000000: return 27; // 134217728 + case 0x10000000: return 28; // 268435456 + } + return 0; +} + +//------------------------------------------------------------------------------ +// Compute in-place complex FFT +// FFTsize: FFT length in samples +// buf: array of FFTsize*2 FFT_TYPE values, +// in interleaved real/imaginary format +//------------------------------------------------------------------------------ +template +void g_fft::ComplexFFT(std::complex *buf) +{ + void *ptr = buf; + FFT_TYPE *nbuf = static_cast(ptr); + ffts1(nbuf, FFT_N, Utbl, BRLow); +} + +//------------------------------------------------------------------------------ +// Compute in-place inverse complex FFT +// FFTsize: FFT length in samples +// buf: array of FFTsize*2 FFT_TYPE values, +// in interleaved real/imaginary format +// Output should be scaled by the return value of +// GetInverseComplexFFTScale(fft_struct, FFTsize). +//------------------------------------------------------------------------------ +template +void g_fft::InverseComplexFFT(std::complex *buf) +{ + void *ptr = buf; + FFT_TYPE *nbuf = static_cast(ptr); + iffts1(nbuf, FFT_N, Utbl, BRLow); +} + +//------------------------------------------------------------------------------ +// Compute in-place real FFT +// FFTsize: FFT length in samples +// buf: array of FFTsize FFT_TYPE values; output is in interleaved +// real/imaginary format, except for buf[1] which is the real +// part for the Nyquist frequency +//------------------------------------------------------------------------------ +template +void g_fft::RealFFT(std::complex *buf) +{ + void *ptr = buf; + FFT_TYPE *nbuf = static_cast(ptr); + rffts1(nbuf, FFT_N, Utbl, BRLow); +} + +//------------------------------------------------------------------------------ +// Compute in-place inverse real FFT +// FFTsize: FFT length in samples +// buf: array of FFTsize FFT_TYPE values; input is expected to be in +// interleaved real/imaginary format, except for buf[1] which +// is the real part for the Nyquist frequency +// Output should be scaled by the return value of +// GetInverseRealFFTScale(fft_struct, FFTsize). +//------------------------------------------------------------------------------ +template +void g_fft::InverseRealFFT(std::complex *buf) +{ + void *ptr = buf; + FFT_TYPE *nbuf = static_cast(ptr); + riffts1(nbuf, FFT_N, Utbl, BRLow); +} + +//------------------------------------------------------------------------------ +// Returns the amplitude scale that should be applied to the result of +// an inverse complex FFT with a length of 'FFTsize' samples. +//------------------------------------------------------------------------------ +template +FFT_TYPE g_fft::GetInverseComplexFFTScale() +{ + return FFT_TYPE(1.0); +} + +//------------------------------------------------------------------------------ +// Returns the amplitude scale that should be applied to the result of +// an inverse real FFT with a length of 'FFTsize' samples. +//------------------------------------------------------------------------------ +template +FFT_TYPE g_fft::GetInverseRealFFTScale() +{ + return FFT_TYPE(1.0); +} + +#endif + diff --git a/sdrbase/dsp/highpass.h b/sdrbase/dsp/highpass.h new file mode 100644 index 000000000..de293b4b1 --- /dev/null +++ b/sdrbase/dsp/highpass.h @@ -0,0 +1,106 @@ +#ifndef INCLUDE_HIGHPASS_H +#define INCLUDE_HIGHPASS_H + +#define _USE_MATH_DEFINES +#include +#include "dsp/dsptypes.h" + +template class Highpass { +public: + Highpass() { } + + 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("Highpass 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 lowpass but inverting every other tap for highpass keeping center tap + 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); + } + + m_taps[(nTaps - 1) / 2] += 1; + + // 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, n_taps, size; + + m_samples[m_ptr] = sample; + size = m_samples.size(); // Valgrind optim (2) + + while(b < 0) + { + b += size; + } + + n_taps = m_taps.size() - 1; // Valgrind optim + + for (i = 0; i < n_taps; i++) + { + acc += (m_samples[a] + m_samples[b]) * m_taps[i]; + a++; + + while (a >= size) + { + a -= size; + } + + b--; + + while (b < 0) + { + b += size; + } + } + + acc += m_samples[a] * m_taps[i]; + m_ptr++; + + while (m_ptr >= size) + { + m_ptr -= size; + } + + return acc; + } + +private: + std::vector m_taps; + std::vector m_samples; + int m_ptr; +}; + +#endif // INCLUDE_HIGHPASS_H diff --git a/sdrbase/dsp/interpolator.h b/sdrbase/dsp/interpolator.h new file mode 100644 index 000000000..ed6d7b0d4 --- /dev/null +++ b/sdrbase/dsp/interpolator.h @@ -0,0 +1,135 @@ +#ifndef INCLUDE_INTERPOLATOR_H +#define INCLUDE_INTERPOLATOR_H + +#ifdef USE_SIMD +#include +#endif +#include "dsp/dsptypes.h" +#include "util/export.h" +#include +#ifndef WIN32 +#include +#endif + +class SDRANGEL_API Interpolator { +public: + Interpolator(); + ~Interpolator(); + + void create(int phaseSteps, double sampleRate, double cutoff); + void free(); + + // Original code allowed for upsampling, but was never used that way + bool interpolate(Real *distance, const Complex& next, Complex* result) + { + advanceFilter(next); + *distance -= 1.0; + + if (*distance >= 1.0) + { + 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 m_samples; + int m_ptr; + int m_phaseSteps; + int m_nTaps; + + void createTaps(int nTaps, double sampleRate, double cutoff, std::vector* 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 (phase < 0) + phase = 0; +#if USE_SIMD + // 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 diff --git a/sdrbase/dsp/inthalfbandfilter.h b/sdrbase/dsp/inthalfbandfilter.h new file mode 100644 index 000000000..25a9ff3b6 --- /dev/null +++ b/sdrbase/dsp/inthalfbandfilter.h @@ -0,0 +1,476 @@ +#ifndef INCLUDE_INTHALFBANDFILTER_H +#define INCLUDE_INTHALFBANDFILTER_H + +#include +#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 SDRANGEL_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; + } + } + + bool workDecimateCenter(qint32 *x, qint32 *y) + { + // insert sample into ring-buffer + m_samples[m_ptr][0] = *x; + m_samples[m_ptr][1] = *y; + + 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(x, y); + + // 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; + } + } + + void myDecimate(const Sample* sample1, Sample* sample2) + { + static const qint16 mod33[38] = { 31,32,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19, + 20,21,22,23,24,25,26,27,28,29,30,31,32,0,1,2}; + + m_samples[m_ptr][0] = sample1->real(); + m_samples[m_ptr][1] = sample1->imag(); + m_ptr = mod33[m_ptr + 2 - 1]; + + m_samples[m_ptr][0] = sample2->real(); + m_samples[m_ptr][1] = sample2->imag(); + + doFIR(sample2); + + m_ptr = mod33[m_ptr + 2 - 1]; + } + + void myDecimate(qint32 x1, qint32 y1, qint32 *x2, qint32 *y2) + { + static const qint16 mod33[38] = { 31,32,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19, + 20,21,22,23,24,25,26,27,28,29,30,31,32,0,1,2}; + + m_samples[m_ptr][0] = x1; + m_samples[m_ptr][1] = y1; + m_ptr = mod33[m_ptr + 2 - 1]; + + m_samples[m_ptr][0] = *x2; + m_samples[m_ptr][1] = *y2; + + doFIR(x2, y2); + + m_ptr = mod33[m_ptr + 2 - 1]; + } + +protected: + qint32 m_samples[HB_FILTERORDER + 1][2]; // Valgrind optim (from qint16) + qint16 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 qint16 mod33[38] = { 31,32,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19, + 20,21,22,23,24,25,26,27,28,29,30,31,32,0,1,2} ; + static const qint32 COEFF[8] = { + (qint32)(-0.015956912844043127236437484839370881673 * (1 << HB_SHIFT)), + (qint32)( 0.013023031678944928940522274274371739011 * (1 << HB_SHIFT)), + (qint32)(-0.01866942273717486777684371190844103694 * (1 << HB_SHIFT)), + (qint32)( 0.026550887571157304190005987720724078827 * (1 << HB_SHIFT)), + (qint32)(-0.038350314277854319344740474662103224546 * (1 << HB_SHIFT)), + (qint32)( 0.058429248652825838128421764849917963147 * (1 << HB_SHIFT)), + (qint32)(-0.102889802028955756885153505209018476307 * (1 << HB_SHIFT)), + (qint32)( 0.317237706405931241260276465254719369113 * (1 << HB_SHIFT)) + }; +#else +#error unsupported filter order +#endif + + + // init read-pointer + int a = mod33[m_ptr + 2 + 1]; // 0 + 1 + int b = mod33[m_ptr + 2 - 2]; //-1 - 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]; // Valgrind optim + //qint32 qTmp = m_samples[a][1] + m_samples[b][1]; // Valgrind optim + iAcc += (m_samples[a][0] + m_samples[b][0]) * COEFF[i]; + qAcc += (m_samples[a][1] + m_samples[b][1]) * COEFF[i]; + + // update read-pointer + a = mod33[a + 2 + 2]; + b = mod33[b + 2 - 2]; + } + + a = mod33[a + 2 - 1]; + + iAcc += ((qint32)m_samples[a][0] + 1) << (HB_SHIFT - 1); + qAcc += ((qint32)m_samples[a][1] + 1) << (HB_SHIFT - 1); + + sample->setReal(iAcc >> HB_SHIFT); + sample->setImag(qAcc >> HB_SHIFT); + } + + void doFIR(qint32 *x, qint32 *y) + { + // 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 int mod33[38] = { 31,32,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19, + 20,21,22,23,24,25,26,27,28,29,30,31,32,0,1,2} ; + static const qint32 COEFF[8] = { + (qint32)(-0.015956912844043127236437484839370881673 * (1 << HB_SHIFT)), + (qint32)( 0.013023031678944928940522274274371739011 * (1 << HB_SHIFT)), + (qint32)(-0.01866942273717486777684371190844103694 * (1 << HB_SHIFT)), + (qint32)( 0.026550887571157304190005987720724078827 * (1 << HB_SHIFT)), + (qint32)(-0.038350314277854319344740474662103224546 * (1 << HB_SHIFT)), + (qint32)( 0.058429248652825838128421764849917963147 * (1 << HB_SHIFT)), + (qint32)(-0.102889802028955756885153505209018476307 * (1 << HB_SHIFT)), + (qint32)( 0.317237706405931241260276465254719369113 * (1 << HB_SHIFT)) + }; + #else + #error unsupported filter order + #endif + + + // init read-pointer + int a = mod33[m_ptr + 2 + 1]; // 0 + 1 + int b = mod33[m_ptr + 2 - 2]; //-1 - 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]; // Valgrind optim + //qint32 qTmp = m_samples[a][1] + m_samples[b][1]; // Valgrind optim + iAcc += (m_samples[a][0] + m_samples[b][0]) * COEFF[i]; + qAcc += (m_samples[a][1] + m_samples[b][1]) * COEFF[i]; + + // update read-pointer + a = mod33[a + 2 + 2]; + b = mod33[b + 2 - 2]; + } + + a = mod33[a + 2 - 1]; + + iAcc += ((qint32)m_samples[a][0] + 1) << (HB_SHIFT - 1); + qAcc += ((qint32)m_samples[a][1] + 1) << (HB_SHIFT - 1); + + *x = iAcc >> (HB_SHIFT -1); // HB_SHIFT incorrect do not loose the gained bit + *y = qAcc >> (HB_SHIFT -1); + } + +}; + +#endif // INCLUDE_INTHALFBANDFILTER_H diff --git a/sdrbase/dsp/kissengine.h b/sdrbase/dsp/kissengine.h new file mode 100644 index 000000000..ad8e53c32 --- /dev/null +++ b/sdrbase/dsp/kissengine.h @@ -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 KissFFT; + KissFFT m_fft; + + std::vector m_in; + std::vector m_out; +}; + +#endif // INCLUDE_KISSENGINE_H diff --git a/sdrbase/dsp/kissfft.h b/sdrbase/dsp/kissfft.h new file mode 100644 index 000000000..ba1256c30 --- /dev/null +++ b/sdrbase/dsp/kissfft.h @@ -0,0 +1,391 @@ +#ifndef INCLUDE_KISSFFT_H +#define INCLUDE_KISSFFT_H + +#include +#include + +/* +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 + struct traits { + typedef T_scalar scalar_type; + typedef T_complex cpx_type; + void fill_twiddles(std::complex* 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(0, i * phinc)); + } + + void prepare(std::vector >& dst, int nfft, bool inverse, std::vector& stageRadix, std::vector& 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 _twiddles; + + const cpx_type twiddle(int i) + { + return _twiddles[i]; + } + }; + +} // namespace + +template > +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 _twiddles; + std::vector _stageRadix; + std::vector _stageRemainder; + traits_type _traits; +}; +#endif diff --git a/sdrbase/dsp/lowpass.h b/sdrbase/dsp/lowpass.h new file mode 100644 index 000000000..355941210 --- /dev/null +++ b/sdrbase/dsp/lowpass.h @@ -0,0 +1,104 @@ +#ifndef INCLUDE_LOWPASS_H +#define INCLUDE_LOWPASS_H + +#define _USE_MATH_DEFINES +#include +#include "dsp/dsptypes.h" + +template 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, n_taps, size; + + m_samples[m_ptr] = sample; + size = m_samples.size(); // Valgrind optim (2) + + while (b < 0) + { + b += size; + } + + n_taps = m_taps.size() - 1; // Valgrind optim + + for (i = 0; i < n_taps; i++) + { + acc += (m_samples[a] + m_samples[b]) * m_taps[i]; + a++; + + while (a >= size) + { + a -= size; + } + + b--; + + while(b < 0) + { + b += size; + } + } + + acc += m_samples[a] * m_taps[i]; + m_ptr++; + + while(m_ptr >= size) + { + m_ptr -= size; + } + + return acc; + } + +private: + std::vector m_taps; + std::vector m_samples; + int m_ptr; +}; + +#endif // INCLUDE_LOWPASS_H diff --git a/sdrbase/dsp/misc.h b/sdrbase/dsp/misc.h new file mode 100644 index 000000000..f0c73f1f0 --- /dev/null +++ b/sdrbase/dsp/misc.h @@ -0,0 +1,75 @@ +// ---------------------------------------------------------------------------- +// misc.h -- Miscellaneous helper functions +// +// Copyright (C) 2006-2008 +// Dave Freese, W1HKJ +// +// This file is part of fldigi. These filters were adapted from code contained +// in the gmfsk source code distribution. +// +// Fldigi 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 3 of the License, or +// (at your option) any later version. +// +// Fldigi 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 fldigi. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef _MISC_H +#define _MISC_H + +#include + +inline float sinc(float x) +{ + return (fabs(x) < 1e-10) ? 1.0 : (sin(M_PI * x) / (M_PI * x)); +} + +inline float cosc(float x) +{ + return (fabs(x) < 1e-10) ? 0.0 : ((1.0 - cos(M_PI * x)) / (M_PI * x)); +} + +inline float clamp(float x, float min, float max) +{ + return (x < min) ? min : ((x > max) ? max : x); +} + +/// This is always called with an int weight +inline float decayavg(float average, float input, int weight) +{ + if (weight <= 1) return input; + return ( ( input - average ) / (float)weight ) + average ; +} + +// following are defined inline to provide best performance +inline float blackman(float x) +{ + return (0.42 - 0.50 * cos(2 * M_PI * x) + 0.08 * cos(4 * M_PI * x)); +} + +inline float hamming(float x) +{ + return 0.54 - 0.46 * cos(2 * M_PI * x); +} + +inline float hanning(float x) +{ + return 0.5 - 0.5 * cos(2 * M_PI * x); +} + +inline float rcos( float t, float T, float alpha=1.0 ) +{ + if( t == 0 ) return 1.0; + float taT = T / (2.0 * alpha); + if( fabs(t) == taT ) return ((alpha/2.0) * sin(M_PI/(2.0*alpha))); + return (sin(M_PI*t/T)/(M_PI*t/T))*cos(alpha*M_PI*t/T)/(1.0-(t/taT)*(t/taT)); +} + +#endif diff --git a/sdrbase/dsp/movingaverage.h b/sdrbase/dsp/movingaverage.h new file mode 100644 index 000000000..dfe284856 --- /dev/null +++ b/sdrbase/dsp/movingaverage.h @@ -0,0 +1,65 @@ +#ifndef INCLUDE_MOVINGAVERAGE_H +#define INCLUDE_MOVINGAVERAGE_H + +#include +#include "dsp/dsptypes.h" + +template class MovingAverage { +public: + MovingAverage() : + m_history(), + m_sum(0), + m_ptr(0) + { + } + + MovingAverage(int historySize, Type initial) : + m_history(historySize, initial), + m_sum((float) historySize * initial), + m_ptr(0) + { + } + + void resize(int historySize, Type initial) + { + m_history.resize(historySize); + for(size_t i = 0; i < m_history.size(); i++) + m_history[i] = initial; + m_sum = (float) m_history.size() * initial; + m_ptr = 0; + } + + void feed(Type 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; + } + + void fill(Type value) + { + for(size_t i = 0; i < m_history.size(); i++) + m_history[i] = value; + m_sum = (float) m_history.size() * value; + } + + Type average() const + { + return m_sum / (float) m_history.size(); + } + + Type sum() const + { + return m_sum; + } + +protected: + std::vector m_history; + Type m_sum; + uint m_ptr; +}; + +#endif // INCLUDE_MOVINGAVERAGE_H diff --git a/sdrbase/dsp/nco.h b/sdrbase/dsp/nco.h new file mode 100644 index 000000000..7a0ed9c81 --- /dev/null +++ b/sdrbase/dsp/nco.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_NCO_H +#define INCLUDE_NCO_H + +#include "dsp/dsptypes.h" +#include "util/export.h" + +class SDRANGEL_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 diff --git a/sdrbase/dsp/nullsink.h b/sdrbase/dsp/nullsink.h new file mode 100644 index 000000000..4f87ae9b6 --- /dev/null +++ b/sdrbase/dsp/nullsink.h @@ -0,0 +1,22 @@ +#ifndef INCLUDE_NULLSINK_H +#define INCLUDE_NULLSINK_H + +#include "dsp/samplesink.h" +#include "util/export.h" + +class Message; + +class SDRANGEL_API NullSink : public SampleSink { +public: + + NullSink(); + virtual ~NullSink(); + + virtual bool init(const Message& cmd); + virtual void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly); + virtual void start(); + virtual void stop(); + virtual bool handleMessage(const Message& message); +}; + +#endif // INCLUDE_NULLSINK_H diff --git a/sdrbase/dsp/phasediscri.h b/sdrbase/dsp/phasediscri.h new file mode 100644 index 000000000..3530a439a --- /dev/null +++ b/sdrbase/dsp/phasediscri.h @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_DSP_PHASEDISCRI_H_ +#define INCLUDE_DSP_PHASEDISCRI_H_ + +#include "dsp/dsptypes.h" + +class PhaseDiscriminators +{ +public: + /** + * Reset stored values + */ + void reset() + { + m_m1Sample = 0; + m_m2Sample = 0; + } + + /** + * Scaling factor so that resulting excursion maps to [-1,+1] + */ + void setFMScaling(Real fmScaling) + { + m_fmScaling = fmScaling; + } + + /** + * Standard discriminator using atan2. On modern processors this is as efficient as the non atan2 one. + * This is better for high fidelity. + */ + Real phaseDiscriminator(const Complex& sample) + { + Complex d(std::conj(m_m1Sample) * sample); + m_m1Sample = sample; + return (std::atan2(d.imag(), d.real()) / M_PI_2) * m_fmScaling; + } + + /** + * Alternative without atan at the expense of a slight distorsion on very wideband signals + * http://www.embedded.com/design/configurable-systems/4212086/DSP-Tricks--Frequency-demodulation-algorithms- + * in addition it needs scaling by instantaneous magnitude squared and volume (0..10) adjustment factor + */ + Real phaseDiscriminator2(const Complex& sample) + { + Real ip = sample.real() - m_m2Sample.real(); + Real qp = sample.imag() - m_m2Sample.imag(); + Real h1 = m_m1Sample.real() * qp; + Real h2 = m_m1Sample.imag() * ip; + + m_m2Sample = m_m1Sample; + m_m1Sample = sample; + + return ((h1 - h2) / M_PI_2) * m_fmScaling; + } + +private: + Complex m_m1Sample; + Complex m_m2Sample; + Real m_fmScaling; +}; + +#endif /* INCLUDE_DSP_PHASEDISCRI_H_ */ diff --git a/sdrbase/dsp/phaselock.h b/sdrbase/dsp/phaselock.h new file mode 100644 index 000000000..66092b153 --- /dev/null +++ b/sdrbase/dsp/phaselock.h @@ -0,0 +1,159 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include "dsp/dsptypes.h" + +/** Phase-locked loop mainly for broadcadt FM stereo pilot. */ +class PhaseLock +{ +public: + + /** Expected pilot frequency (used for PPS events). */ + static const int pilot_frequency = 19000; + + /** Timestamp event produced once every 19000 pilot periods. */ + struct PpsEvent + { + quint64 pps_index; + quint64 sample_index; + double block_position; + }; + + /** + * Construct phase-locked loop. + * + * freq :: 19 kHz center frequency relative to sample freq + * (0.5 is Nyquist) + * bandwidth :: bandwidth relative to sample frequency + * minsignal :: minimum pilot amplitude + */ + PhaseLock(Real freq, Real bandwidth, Real minsignal); + + virtual ~PhaseLock() + {} + + /** + * Change phase locked loop parameters + * + * freq :: 19 kHz center frequency relative to sample freq + * (0.5 is Nyquist) + * bandwidth :: bandwidth relative to sample frequency + * minsignal :: minimum pilot amplitude + */ + void configure(Real freq, Real bandwidth, Real minsignal); + + /** + * Process samples and extract 19 kHz pilot tone. + * Generate phase-locked 38 kHz tone with unit amplitude. + * Bufferized version with input and output vectors + */ + void process(const std::vector& samples_in, std::vector& samples_out); + + /** + * Process samples and track a pilot tone. Generate samples for single or multiple phase-locked + * signals. Implement the processPhase virtual method to produce the output samples. + * In flow version. Ex: Use 19 kHz stereo pilot tone to generate 38 kHz (stereo) and 57 kHz + * pilots (see RDSPhaseLock class below). + * This is the in flow version + */ + void process(const Real& sample_in, Real *samples_out); + + /** Return true if the phase-locked loop is locked. */ + bool locked() const + { + return m_lock_cnt >= m_lock_delay; + } + + /** Return detected amplitude of pilot signal. */ + Real get_pilot_level() const + { + return 2 * m_pilot_level; + } + +protected: + Real m_phase; + Real m_psin; + Real m_pcos; + /** + * Callback method to produce multiple outputs from the current phase value in m_phase + * and/or the sin and cos values in m_psin and m_pcos + */ + virtual void processPhase(Real *samples_out) const {}; + +private: + Real m_minfreq, m_maxfreq; + Real m_phasor_b0, m_phasor_a1, m_phasor_a2; + Real m_phasor_i1, m_phasor_i2, m_phasor_q1, m_phasor_q2; + Real m_loopfilter_b0, m_loopfilter_b1; + Real m_loopfilter_x1; + Real m_freq; + Real m_minsignal; + Real m_pilot_level; + int m_lock_delay; + int m_lock_cnt; + int m_pilot_periods; + quint64 m_pps_cnt; + quint64 m_sample_cnt; + std::vector m_pps_events; +}; + +class StereoPhaseLock : public PhaseLock +{ +public: + StereoPhaseLock(Real freq, Real bandwidth, Real minsignal) : + PhaseLock(freq, bandwidth, minsignal) + {} + + virtual ~StereoPhaseLock() + {} + +protected: + virtual void processPhase(Real *samples_out) const + { + samples_out[0] = m_psin; // f Pilot + // Generate double-frequency output. + // sin(2*x) = 2 * sin(x) * cos(x) + samples_out[1] = 2.0 * m_psin * m_pcos; // 2f Pilot sin + // cos(2*x) = 2 * cos(x) * cos(x) - 1 + samples_out[2] = (2.0 * m_pcos * m_pcos) - 1.0; // 2f Pilot cos + } +}; + + +class RDSPhaseLock : public PhaseLock +{ +public: + RDSPhaseLock(Real freq, Real bandwidth, Real minsignal) : + PhaseLock(freq, bandwidth, minsignal) + {} + + virtual ~RDSPhaseLock() + {} + +protected: + virtual void processPhase(Real *samples_out) const + { + samples_out[0] = m_psin; // Pilot signal (f) + // Generate double-frequency output. + // sin(2*x) = 2 * sin(x) * cos(x) + samples_out[1] = 2.0 * m_psin * m_pcos; // Pilot signal (2f) + // cos(2*x) = 2 * cos(x) * cos(x) - 1 + samples_out[2] = (2.0 * m_pcos * m_pcos) - 1.0; // 2f Pilot cos + samples_out[3] = m_phase; // Pilot phase + } +}; diff --git a/sdrbase/dsp/samplefifo.h b/sdrbase/dsp/samplefifo.h new file mode 100644 index 000000000..f2cfe1648 --- /dev/null +++ b/sdrbase/dsp/samplefifo.h @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // +// written by Christian Daniel // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_SAMPLEFIFO_H +#define INCLUDE_SAMPLEFIFO_H + +#include +#include +#include +#include "dsp/dsptypes.h" +#include "util/export.h" + +class SDRANGEL_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 size() const { return m_size; } + inline uint fill() { QMutexLocker mutexLocker(&m_mutex); uint fill = m_fill; return 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 diff --git a/sdrbase/dsp/samplesink.h b/sdrbase/dsp/samplesink.h new file mode 100644 index 000000000..e4e39f5da --- /dev/null +++ b/sdrbase/dsp/samplesink.h @@ -0,0 +1,33 @@ +#ifndef INCLUDE_SAMPLESINK_H +#define INCLUDE_SAMPLESINK_H + +#include +#include "dsptypes.h" +#include "util/export.h" +#include "util/messagequeue.h" + +class Message; + +class SDRANGEL_API SampleSink : public QObject { + Q_OBJECT +public: + SampleSink(); + virtual ~SampleSink(); + + virtual void start() = 0; + virtual void stop() = 0; + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly) = 0; + virtual bool handleMessage(const Message& cmd) = 0; //!< Processing of a message. Returns true if message has actually been processed + + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication + MessageQueue *getOutputMessageQueue() { return &m_outputMessageQueue; } //!< Get the queue for asynchronous outbound communication + +protected: + MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication + MessageQueue m_outputMessageQueue; //!< Queue for asynchronous outbound communication + +protected slots: + void handleInputMessages(); +}; + +#endif // INCLUDE_SAMPLESINK_H diff --git a/sdrbase/dsp/samplesource.h b/sdrbase/dsp/samplesource.h new file mode 100644 index 000000000..944a539f3 --- /dev/null +++ b/sdrbase/dsp/samplesource.h @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_SAMPLESOURCE_H +#define INCLUDE_SAMPLESOURCE_H + +#include +#include "dsp/samplefifo.h" +#include "util/message.h" +#include "util/messagequeue.h" +#include "util/export.h" + +class SDRANGEL_API SampleSource : public QObject { + Q_OBJECT +public: + SampleSource(); + virtual ~SampleSource(); + + virtual bool init(const Message& cmd) = 0; + virtual bool start(int device) = 0; + virtual void stop() = 0; + + virtual const QString& getDeviceDescription() const = 0; + virtual int getSampleRate() const = 0; //!< Sample rate exposed by the source + virtual quint64 getCenterFrequency() const = 0; //!< Center frequency exposed by the source + + virtual bool handleMessage(const Message& message) = 0; + + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + MessageQueue *getOutputMessageQueueToGUI() { return &m_outputMessageQueueToGUI; } + SampleFifo* getSampleFifo() { return &m_sampleFifo; } + +protected slots: + void handleInputMessages(); + +protected: + SampleFifo m_sampleFifo; + MessageQueue m_inputMessageQueue; //!< Input queue to the source + MessageQueue m_outputMessageQueueToGUI; //!< Output queue specialized for the source GUI +}; + +#endif // INCLUDE_SAMPLESOURCE_H diff --git a/sdrbase/dsp/scopevis.h b/sdrbase/dsp/scopevis.h new file mode 100644 index 000000000..5cfeb51b8 --- /dev/null +++ b/sdrbase/dsp/scopevis.h @@ -0,0 +1,169 @@ +#ifndef INCLUDE_SCOPEVIS_H +#define INCLUDE_SCOPEVIS_H + +#include +#include "dsp/samplesink.h" +#include "util/export.h" +#include "util/message.h" + +class GLScope; +class MessageQueue; + +class SDRANGEL_API ScopeVis : public SampleSink { +public: + enum TriggerChannel { + TriggerFreeRun, + TriggerChannelI, + TriggerChannelQ, + TriggerMagLin, + TriggerMagDb, + TriggerPhase, + TriggerDPhase + }; + + static const uint m_traceChunkSize; + static const uint m_nbTriggers = 10; + + ScopeVis(GLScope* glScope = NULL); + virtual ~ScopeVis(); + + void configure(MessageQueue* msgQueue, + uint triggerIndex, + TriggerChannel triggerChannel, + Real triggerLevel, + bool triggerPositiveEdge, + bool triggerBothEdges, + uint triggerPre, + uint triggerDelay, + uint triggerCounts, + uint traceSize); + void setOneShot(bool oneShot); + + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); + virtual void start(); + virtual void stop(); + virtual bool handleMessage(const Message& message); + + void setSampleRate(int sampleRate); + int getSampleRate() const { return m_sampleRate; } + SampleVector::const_iterator getTriggerPoint() const { return m_triggerPoint; } + +private: + class MsgConfigureScopeVis : public Message { + MESSAGE_CLASS_DECLARATION + + public: + uint getTriggerIndex() const { return m_triggerIndex; } + int getTriggerChannel() const { return m_triggerChannel; } + Real getTriggerLevel() const { return m_triggerLevel; } + Real getTriggerPositiveEdge() const { return m_triggerPositiveEdge; } + Real getTriggerBothEdges() const { return m_triggerBothEdges; } + uint getTriggerPre() const { return m_triggerPre; } + uint getTriggerDelay() const { return m_triggerDelay; } + uint getTriggerCounts() const { return m_triggerCounts; } + uint getTraceSize() const { return m_traceSize; } + + static MsgConfigureScopeVis* create(uint triggerIndex, + int triggerChannel, + Real triggerLevel, + bool triggerPositiveEdge, + bool triggerBothEdges, + uint triggerPre, + uint triggerDelay, + uint triggerCounts, + uint traceSize) + { + return new MsgConfigureScopeVis(triggerIndex, + triggerChannel, + triggerLevel, + triggerPositiveEdge, + triggerBothEdges, + triggerPre, + triggerDelay, + triggerCounts, + traceSize); + } + + private: + uint m_triggerIndex; + int m_triggerChannel; + Real m_triggerLevel; + bool m_triggerPositiveEdge; + bool m_triggerBothEdges; + uint m_triggerPre; + uint m_triggerDelay; + uint m_triggerCounts; + uint m_traceSize; + + MsgConfigureScopeVis(uint triggerIndex, + int triggerChannel, + Real triggerLevel, + bool triggerPositiveEdge, + bool triggerBothEdges, + uint triggerPre, + uint triggerDelay, + uint triggerCounts, + uint traceSize) : + Message(), + m_triggerIndex(triggerIndex), + m_triggerChannel(triggerChannel), + m_triggerLevel(triggerLevel), + m_triggerPositiveEdge(triggerPositiveEdge), + m_triggerBothEdges(triggerBothEdges), + m_triggerPre(triggerPre), + m_triggerDelay(triggerDelay), + m_triggerCounts(triggerCounts), + m_traceSize(traceSize) + { } + }; + + /** + * TriggerState: (repeat at each successive non freerun trigger) + * + * send a Trigger condition +--------------------+ + * dummy trace - Immediate m_triggerOneShot | | + * Config -------------> Untriggered ----------------------------------> Triggered ----------------> WaitForReset | + * ^ ^ | ^ | | ^ | + * | | | - Delayed Delay expired | | | | setOneShot(true)| + * | | +---------------------> Delay ----------------+ | | +-----------------+ + * | | !m_triggerOneShot | | + * | +--------------------------------------------------+ setOneShot(false) | + * +-------------------------------------------------------------------------------+ + */ + enum TriggerState { + Untriggered, //!< Search for trigger + Config, //!< New configuration has just been received + Triggered, //!< Trigger was kicked off + WaitForReset, //!< Wait for release from GUI + Delay //!< Trigger delay engaged + }; + + GLScope* m_glScope; + std::vector m_trace; //!< Raw trace to be used by GLScope + boost::circular_buffer m_traceback; //!< FIFO for samples prior to triggering point to support pre-trigger (when in triggered mode) + uint m_tracebackCount; //!< Count of samples stored into trace memory since triggering is active up to trace memory size + uint m_fill; + TriggerState m_triggerState; + uint m_triggerIndex; //!< current active trigger index + TriggerChannel m_triggerChannel[m_nbTriggers]; + Real m_triggerLevel[m_nbTriggers]; + bool m_triggerPositiveEdge[m_nbTriggers]; + bool m_triggerBothEdges[m_nbTriggers]; + bool m_prevTrigger; + uint m_triggerPre; //!< Pre-trigger delay in number of samples + bool m_triggerOneShot; + bool m_armed; + uint m_triggerDelay[m_nbTriggers]; //!< Trigger delay in number of trace sizes + uint m_triggerDelayCount; //!< trace sizes delay counter + uint m_triggerCounts[m_nbTriggers]; //!< Number of trigger events before the actual trigger is kicked off + uint m_triggerCount; + int m_sampleRate; + SampleVector::const_iterator m_triggerPoint; + Real m_prevArg; + bool m_firstArg; + + bool triggerCondition(SampleVector::const_iterator& it); + bool nextTrigger(); //!< move to next trigger. Returns true if next trigger is active. +}; + +#endif // INCLUDE_SCOPEVIS_H diff --git a/sdrbase/dsp/spectrumscopecombovis.h b/sdrbase/dsp/spectrumscopecombovis.h new file mode 100644 index 000000000..04b2ceccd --- /dev/null +++ b/sdrbase/dsp/spectrumscopecombovis.h @@ -0,0 +1,27 @@ +#ifndef INCLUDE_SPECTRUMSCOPECOMBOVIS_H +#define INCLUDE_SPECTRUMSCOPECOMBOVIS_H + +#include "dsp/samplesink.h" +#include "dsp/spectrumvis.h" +#include "dsp/scopevis.h" +#include "util/export.h" + +class Message; + +class SDRANGEL_API SpectrumScopeComboVis : public SampleSink { +public: + + SpectrumScopeComboVis(SpectrumVis* spectrumVis, ScopeVis* scopeVis); + virtual ~SpectrumScopeComboVis(); + + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); + virtual void start(); + virtual void stop(); + virtual bool handleMessage(const Message& message); + +private: + SpectrumVis* m_spectrumVis; + ScopeVis* m_scopeVis; +}; + +#endif // INCLUDE_SPECTRUMSCOPECOMBOVIS_H diff --git a/sdrbase/dsp/spectrumvis.h b/sdrbase/dsp/spectrumvis.h new file mode 100644 index 000000000..46820dbd6 --- /dev/null +++ b/sdrbase/dsp/spectrumvis.h @@ -0,0 +1,70 @@ +#ifndef INCLUDE_SPECTRUMVIS_H +#define INCLUDE_SPECTRUMVIS_H + +#include +#include "dsp/samplesink.h" +#include "dsp/fftengine.h" +#include "fftwindow.h" +#include "util/export.h" +#include "util/message.h" + +class GLSpectrum; +class MessageQueue; + +class SDRANGEL_API SpectrumVis : public SampleSink { + +public: + class SDRANGEL_API MsgConfigureSpectrumVis : public Message { + MESSAGE_CLASS_DECLARATION + + public: + MsgConfigureSpectrumVis(int fftSize, int overlapPercent, FFTWindow::Function window) : + Message(), + m_fftSize(fftSize), + m_overlapPercent(overlapPercent), + m_window(window) + { } + + int getFFTSize() const { return m_fftSize; } + int getOverlapPercent() const { return m_overlapPercent; } + FFTWindow::Function getWindow() const { return m_window; } + + private: + int m_fftSize; + int m_overlapPercent; + FFTWindow::Function m_window; + }; + + SpectrumVis(GLSpectrum* glSpectrum = NULL); + virtual ~SpectrumVis(); + + void configure(MessageQueue* msgQueue, int fftSize, int overlapPercent, FFTWindow::Function window); + + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); + void feedTriggered(const SampleVector::const_iterator& triggerPoint, const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); + virtual void start(); + virtual void stop(); + virtual bool handleMessage(const Message& message); + +private: + FFTEngine* m_fft; + FFTWindow m_window; + + std::vector m_fftBuffer; + std::vector m_logPowerSpectrum; + + std::size_t m_fftSize; + std::size_t m_overlapPercent; + std::size_t m_overlapSize; + std::size_t m_refillSize; + std::size_t m_fftBufferFill; + bool m_needMoreSamples; + + GLSpectrum* m_glSpectrum; + + QMutex m_mutex; + + void handleConfigure(int fftSize, int overlapPercent, FFTWindow::Function window); +}; + +#endif // INCLUDE_SPECTRUMVIS_H diff --git a/sdrbase/dsp/threadedsamplesink.h b/sdrbase/dsp/threadedsamplesink.h new file mode 100644 index 000000000..82999d3d8 --- /dev/null +++ b/sdrbase/dsp/threadedsamplesink.h @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_THREADEDSAMPLESINK_H +#define INCLUDE_THREADEDSAMPLESINK_H + +#include +#include "samplesink.h" +#include "dsp/samplefifo.h" +#include "util/messagequeue.h" +#include "util/export.h" + +class SampleSink; +class QThread; + +/** + * Because Qt is a piece of shit this class cannot be a nested protected class of ThreadedSampleSink + * So let's make everything public + */ +class ThreadedSampleFifo : public QObject { + Q_OBJECT + +public: + ThreadedSampleFifo(SampleSink* sampleSink, std::size_t size = 1<<18); + ~ThreadedSampleFifo(); + void writeToFifo(SampleVector::const_iterator& begin, SampleVector::const_iterator& end); + + SampleSink* m_sampleSink; + SampleFifo m_sampleFifo; + +public slots: + void handleFifoData(); +}; + +/** + * This class is a wrapper for SampleSink that runs the SampleSink object in its own thread + */ +class SDRANGEL_API ThreadedSampleSink : public QObject { + Q_OBJECT + +public: + ThreadedSampleSink(SampleSink* sampleSink, QObject *parent = 0); + ~ThreadedSampleSink(); + + const SampleSink *getSink() const { return m_sampleSink; } + MessageQueue* getInputMessageQueue() { return m_sampleSink->getInputMessageQueue(); } //!< Return pointer to sample sink's input message queue + MessageQueue* getOutputMessageQueue() { return m_sampleSink->getOutputMessageQueue(); } //!< Return pointer to sample sink's output message queue + + void start(); //!< this thread start() + void stop(); //!< this thread exit() and wait() + + bool handleSinkMessage(const Message& cmd); //!< Send message to sink synchronously + void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly); //!< Feed sink with samples + + QString getSampleSinkObjectName() const; + +protected: + + QThread *m_thread; //!< The thead object + ThreadedSampleFifo *m_threadedSampleFifo; + SampleSink* m_sampleSink; +}; + +#endif // INCLUDE_THREADEDSAMPLESINK_H diff --git a/sdrbase/gui/aboutdialog.h b/sdrbase/gui/aboutdialog.h new file mode 100644 index 000000000..cf5559d6f --- /dev/null +++ b/sdrbase/gui/aboutdialog.h @@ -0,0 +1,21 @@ +#ifndef INCLUDE_ABOUTDIALOG_H +#define INCLUDE_ABOUTDIALOG_H + +#include + +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 diff --git a/sdrbase/gui/addpresetdialog.h b/sdrbase/gui/addpresetdialog.h new file mode 100644 index 000000000..0273544ce --- /dev/null +++ b/sdrbase/gui/addpresetdialog.h @@ -0,0 +1,32 @@ +#ifndef INCLUDE_ADDPRESETDIALOG_H +#define INCLUDE_ADDPRESETDIALOG_H + +#include + +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; + void setGroup(QString& group); + void setDescription(QString& description); + +private: + enum Audio { + ATDefault, + ATInterface, + ATDevice + }; + + Ui::AddPresetDialog* ui; +}; + +#endif // INCLUDE_ADDPRESETDIALOG_H diff --git a/sdrbase/gui/basicchannelsettingswidget.h b/sdrbase/gui/basicchannelsettingswidget.h new file mode 100644 index 000000000..2f96b6db6 --- /dev/null +++ b/sdrbase/gui/basicchannelsettingswidget.h @@ -0,0 +1,34 @@ +#ifndef INCLUDE_BASICCHANNELSETTINGSWIDGET_H +#define INCLUDE_BASICCHANNELSETTINGSWIDGET_H + +#include +#include "util/export.h" + +namespace Ui { + class BasicChannelSettingsWidget; +} + +class ChannelMarker; + +class SDRANGEL_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 diff --git a/sdrbase/gui/buttonswitch.h b/sdrbase/gui/buttonswitch.h new file mode 100644 index 000000000..672a5afea --- /dev/null +++ b/sdrbase/gui/buttonswitch.h @@ -0,0 +1,19 @@ +#ifndef INCLUDE_BUTTONSWITCH_H +#define INCLUDE_BUTTONSWITCH_H + +#include + +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 diff --git a/sdrbase/gui/channelwindow.h b/sdrbase/gui/channelwindow.h new file mode 100644 index 000000000..6bc87e3e5 --- /dev/null +++ b/sdrbase/gui/channelwindow.h @@ -0,0 +1,25 @@ +#ifndef INCLUDE_CHANNELWINDOW_H +#define INCLUDE_CHANNELWINDOW_H + +#include + +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 diff --git a/sdrbase/gui/colormapper.h b/sdrbase/gui/colormapper.h new file mode 100644 index 000000000..0aa3e5407 --- /dev/null +++ b/sdrbase/gui/colormapper.h @@ -0,0 +1,46 @@ +/* + * colormap.h + * + * Created on: Jul 19, 2015 + * Author: f4exb + */ +#ifndef INCLUDE_GPL_GUI_COLORMAPPER_H_ +#define INCLUDE_GPL_GUI_COLORMAPPER_H_ + +#include +#include +#include +#include "util/export.h" + +class SDRANGEL_API ColorMapper +{ +public: + enum Theme { + Normal, + Gold, + ReverseGold, + }; + + typedef std::vector > colormap; + + ColorMapper(Theme theme = Normal); + ~ColorMapper(); + + const colormap& getDialBackgroundColorMap() const { return m_dialBackgroundcolorMap; }; + const QColor& getForegroundColor() const { return m_foregroundColor; }; + const QColor& getSecondaryForegroundColor() const { return m_secondaryForegroundColor; }; + const QColor& getHighlightColor() const { return m_highlightColor; }; + const QColor& getBoundaryColor() const { return m_boundaryColor; }; + const QColor& getBoundaryAlphaColor() const { return m_boundaryAlphaColor; }; + +private: + Theme m_theme; + std::vector > m_dialBackgroundcolorMap; + QColor m_foregroundColor; + QColor m_secondaryForegroundColor; + QColor m_highlightColor; + QColor m_boundaryColor; + QColor m_boundaryAlphaColor; +}; + +#endif /* INCLUDE_GPL_GUI_COLORMAPPER_H_ */ diff --git a/sdrbase/gui/glscope.h b/sdrbase/gui/glscope.h new file mode 100644 index 000000000..991e66cdb --- /dev/null +++ b/sdrbase/gui/glscope.h @@ -0,0 +1,190 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 F4EXB // +// written by Edouard Griffiths // +// // +// OpenGL interface modernization. // +// See: http://doc.qt.io/qt-5/qopenglshaderprogram.html // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_GLSCOPE_H +#define INCLUDE_GLSCOPE_H + +#include +#include +#include +#include +#include +#include +#include "dsp/dsptypes.h" +#include "dsp/scopevis.h" +#include "gui/scaleengine.h" +#include "gui/glshadersimple.h" +#include "gui/glshadertextured.h" +#include "util/export.h" +#include "util/bitfieldindex.h" + +class DSPEngine; +class ScopeVis; +class QPainter; + +class SDRANGEL_API GLScope: public QGLWidget { + Q_OBJECT + +public: + enum Mode { + ModeIQ, + ModeMagLinPha, + ModeMagdBPha, + ModeMagLinDPha, + ModeMagdBDPha, + ModeDerived12, + ModeCyclostationary, + ModeIQPolar + }; + + enum Displays { + DisplayBoth, + DisplayFirstOnly, + DisplaySecondOnly + }; + + GLScope(QWidget* parent = NULL); + ~GLScope(); + + void setDSPEngine(DSPEngine* dspEngine); + void setAmp1(Real amp); + void setAmp1Ofs(Real ampOfs); + void setAmp2(Real amp); + void setAmp2Ofs(Real ampOfs); + void setTimeBase(int timeBase); + void setTimeOfsProMill(int timeOfsProMill); + void setMode(Mode mode); + void setDisplays(Displays displays); + void setOrientation(Qt::Orientation orientation); + void setDisplayGridIntensity(int intensity); + void setDisplayTraceIntensity(int intensity); + void setTriggerChannel(ScopeVis::TriggerChannel triggerChannel); + void setTriggerLevel(Real triggerLevel); + void setTriggerPre(Real triggerPre); + void setMemHistoryShift(int value); + + void newTrace(const std::vector& trace, int sampleRate); + int getTraceSize() const { return m_rawTrace[m_memTraceIndex - m_memTraceHistory].size(); } + + void setSampleRate(int sampleRate); + int getSampleRate() const { return m_sampleRates[m_memTraceIndex - m_memTraceHistory]; } + Mode getDataMode() const { return m_mode; } + void connectTimer(const QTimer& timer); + + static const int m_memHistorySizeLog2 = 5; + +signals: + void traceSizeChanged(int); + void sampleRateChanged(int); + +private: + // state + QTimer m_timer; + QMutex m_mutex; + bool m_dataChanged; + bool m_configChanged; + Mode m_mode; + Displays m_displays; + Qt::Orientation m_orientation; + + // traces + std::vector m_rawTrace[1< m_memTraceIndex; //!< current index of trace being written + BitfieldIndex m_memTraceHistory; //!< trace index shift into history + int m_memTraceIndexMax; + bool m_memTraceRecall; + std::vector m_mathTrace; + std::vector* m_displayTrace; + std::vector m_powTrace; + Real m_maxPow; + Real m_sumPow; + 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 + int m_timeBase; + int m_timeOfsProMill; + ScopeVis::TriggerChannel m_triggerChannel; + Real m_triggerLevel; + Real m_triggerPre; + Real m_triggerLevelDis1; + Real m_triggerLevelDis2; + int m_nbPow; + Real m_prevArg; + + // graphics stuff + QRectF m_glScopeRect1; + QRectF m_glScopeRect2; + QMatrix4x4 m_glScopeMatrix1; + QMatrix4x4 m_glScopeMatrix2; + QMatrix4x4 m_glLeft1ScaleMatrix; + QMatrix4x4 m_glRight1ScaleMatrix; + QMatrix4x4 m_glLeft2ScaleMatrix; + QMatrix4x4 m_glBot1ScaleMatrix; + QMatrix4x4 m_glBot2ScaleMatrix; + + QPixmap m_left1ScalePixmap; + QPixmap m_left2ScalePixmap; + QPixmap m_bot1ScalePixmap; + QPixmap m_bot2ScalePixmap; + QPixmap m_powerOverlayPixmap1; + + int m_displayGridIntensity; + int m_displayTraceIntensity; + + ScaleEngine m_x1Scale; + ScaleEngine m_x2Scale; + ScaleEngine m_y1Scale; + ScaleEngine m_y2Scale; + + QFont m_powerOverlayFont; + + GLShaderSimple m_glShaderSimple; + GLShaderTextured m_glShaderLeft1Scale; + GLShaderTextured m_glShaderBottom1Scale; + GLShaderTextured m_glShaderLeft2Scale; + GLShaderTextured m_glShaderBottom2Scale; + GLShaderTextured m_glShaderPowerOverlay; + + void initializeGL(); + void resizeGL(int width, int height); + void paintGL(); + + void mousePressEvent(QMouseEvent*); + + void handleMode(); + void applyConfig(); + void drawPowerOverlay(); + +protected slots: + void cleanup(); + void tick(); +}; + +#endif // INCLUDE_GLSCOPE_H diff --git a/sdrbase/gui/glscopegui.h b/sdrbase/gui/glscopegui.h new file mode 100644 index 000000000..6e21fc295 --- /dev/null +++ b/sdrbase/gui/glscopegui.h @@ -0,0 +1,117 @@ +#ifndef INCLUDE_GLSCOPEGUI_H +#define INCLUDE_GLSCOPEGUI_H + +#include +#include "dsp/dsptypes.h" +#include "util/export.h" +#include "util/message.h" +#include "dsp/scopevis.h" + +namespace Ui { + class GLScopeGUI; +} + +class MessageQueue; +class GLScope; + +class SDRANGEL_API GLScopeGUI : public QWidget { + Q_OBJECT + +public: + explicit GLScopeGUI(QWidget* parent = NULL); + ~GLScopeGUI(); + + void setBuddies(MessageQueue* messageQueue, ScopeVis* scopeVis, GLScope* glScope); + + void setSampleRate(int sampleRate); + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + + bool handleMessage(Message* message); + +private: + Ui::GLScopeGUI* ui; + + MessageQueue* m_messageQueue; + ScopeVis* m_scopeVis; + GLScope* m_glScope; + + int m_sampleRate; + + qint32 m_displayData; + qint32 m_displayOrientation; + qint32 m_displays; + qint32 m_timeBase; + qint32 m_timeOffset; + qint32 m_amplification1; + qint32 m_amp1OffsetCoarse; + qint32 m_amp1OffsetFine; + qint32 m_amplification2; + qint32 m_amp2OffsetCoarse; + qint32 m_amp2OffsetFine; + int m_displayGridIntensity; + int m_displayTraceIntensity; + quint32 m_triggerIndex; + qint32 m_triggerChannel[ScopeVis::m_nbTriggers]; + qint32 m_triggerLevelCoarse[ScopeVis::m_nbTriggers]; // percent of full range + qint32 m_triggerLevelFine[ScopeVis::m_nbTriggers]; // percent of coarse + bool m_triggerPositiveEdge[ScopeVis::m_nbTriggers]; + bool m_triggerBothEdges[ScopeVis::m_nbTriggers]; + qint32 m_triggerPre; + qint32 m_triggerDelay[ScopeVis::m_nbTriggers]; + qint32 m_triggerCounts[ScopeVis::m_nbTriggers]; + qint32 m_traceLenMult; + + static const qreal amps[11]; + + void applySettings(); + void applyTriggerSettings(); + void setTimeScaleDisplay(); + void setTraceLenDisplay(); + void setTimeOfsDisplay(); + void setAmp1ScaleDisplay(); + void setAmp1OfsDisplay(); + void setAmp2ScaleDisplay(); + void setAmp2OfsDisplay(); + void setTrigLevelDisplay(); + void setTrigPreDisplay(); + void setTrigDelayDisplay(); + void setTrigUI(uint index); + +private slots: + void on_amp1_valueChanged(int value); + void on_amp1OfsCoarse_valueChanged(int value); + void on_amp1OfsFine_valueChanged(int value); + void on_amp2_valueChanged(int value); + void on_amp2OfsCoarse_valueChanged(int value); + void on_amp2OfsFine_valueChanged(int value); + void on_scope_traceSizeChanged(int value); + void on_scope_sampleRateChanged(int value); + void on_time_valueChanged(int value); + void on_traceLen_valueChanged(int value); + void on_timeOfs_valueChanged(int value); + void on_dataMode_currentIndexChanged(int index); + void on_gridIntensity_valueChanged(int index); + void on_traceIntensity_valueChanged(int index); + void on_trigPre_valueChanged(int value); + void on_trigDelay_valueChanged(int value); + void on_memIndex_valueChanged(int value); + void on_trigCount_valueChanged(int value); + void on_trigIndex_valueChanged(int value); + + void on_horizView_clicked(); + void on_vertView_clicked(); + void on_onlyPrimeView_clicked(); + void on_onlySecondView_clicked(); + + void on_trigMode_currentIndexChanged(int index); + void on_slopePos_clicked(); + void on_slopeNeg_clicked(); + void on_slopeBoth_clicked(); + void on_oneShot_clicked(); + void on_trigLevelCoarse_valueChanged(int value); + void on_trigLevelFine_valueChanged(int value); +}; + +#endif // INCLUDE_GLSCOPEGUI_H diff --git a/sdrbase/gui/glshadersimple.h b/sdrbase/gui/glshadersimple.h new file mode 100644 index 000000000..633f621f8 --- /dev/null +++ b/sdrbase/gui/glshadersimple.h @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 F4EXB // +// written by Edouard Griffiths // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_GUI_GLSHADERSIMPLE_H_ +#define INCLUDE_GUI_GLSHADERSIMPLE_H_ + +#include +#include + +class QOpenGLShaderProgram; +class QMatrix4x4; +class QVector4D; + +class GLShaderSimple +{ +public: + GLShaderSimple(); + ~GLShaderSimple(); + + void initializeGL(); + void drawPolyline(const QMatrix4x4& transformMatrix, const QVector4D& color, GLfloat *vertices, int nbVertices); + void drawSegments(const QMatrix4x4& transformMatrix, const QVector4D& color, GLfloat *vertices, int nbVertices); + void drawContour(const QMatrix4x4& transformMatrix, const QVector4D& color, GLfloat *vertices, int nbVertices); + void drawSurface(const QMatrix4x4& transformMatrix, const QVector4D& color, GLfloat *vertices, int nbVertices); + void cleanup(); + +private: + void draw(unsigned int mode, const QMatrix4x4& transformMatrix, const QVector4D& color, GLfloat *vertices, int nbVertices); + + QOpenGLShaderProgram *m_program; + int m_matrixLoc; + int m_colorLoc; + static const QString m_vertexShaderSourceSimple; + static const QString m_fragmentShaderSourceColored; +}; + +#endif /* INCLUDE_GUI_GLSHADERSIMPLE_H_ */ diff --git a/sdrbase/gui/glshadertextured.h b/sdrbase/gui/glshadertextured.h new file mode 100644 index 000000000..a20daacc5 --- /dev/null +++ b/sdrbase/gui/glshadertextured.h @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 F4EXB // +// written by Edouard Griffiths // +// // +// See: http://glslstudio.com/primer/#gl2frag // +// https://gitlab.com/pteam/korvins-qtbase/blob/5.4/examples/opengl/cube/mainwidget.cpp // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_GUI_GLSHADERTEXTURED_H_ +#define INCLUDE_GUI_GLSHADERTEXTURED_H_ + +#include +#include +#include + +class QOpenGLShaderProgram; +class QMatrix4x4; +class QImage; + +class GLShaderTextured +{ +public: + GLShaderTextured(); + ~GLShaderTextured(); + + void initializeGL(); + void initTexture(const QImage& image, QOpenGLTexture::WrapMode wrapMode = QOpenGLTexture::Repeat); + void subTexture(int xOffset, int yOffset, int width, int height, const void *pixels); + void drawSurface(const QMatrix4x4& transformMatrix, GLfloat* textureCoords, GLfloat *vertices, int nbVertices); + void cleanup(); + +private: + void draw(unsigned int mode, const QMatrix4x4& transformMatrix, GLfloat *textureCoords, GLfloat *vertices, int nbVertices); + + QOpenGLShaderProgram *m_program; + QOpenGLTexture *m_texture; + int m_matrixLoc; + int m_textureLoc; + static const QString m_vertexShaderSourceTextured; + static const QString m_fragmentShaderSourceTextured; +}; + +#endif /* INCLUDE_GUI_GLSHADERTEXTURED_H_ */ diff --git a/sdrbase/gui/glspectrum.h b/sdrbase/gui/glspectrum.h new file mode 100644 index 000000000..981971310 --- /dev/null +++ b/sdrbase/gui/glspectrum.h @@ -0,0 +1,193 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 F4EXB // +// written by Edouard Griffiths // +// // +// OpenGL interface modernization. // +// See: http://doc.qt.io/qt-5/qopenglshaderprogram.html // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_GLSPECTRUM_H +#define INCLUDE_GLSPECTRUM_H + +#include +#include +#include +#include +#include +#include +#include +#include "dsp/dsptypes.h" +#include "gui/scaleengine.h" +#include "gui/glshadersimple.h" +#include "gui/glshadertextured.h" +#include "dsp/channelmarker.h" +#include "util/export.h" + +class QOpenGLShaderProgram; + +class SDRANGEL_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 setHistoLateHoldoff(int lateHoldoff); + void setHistoStroke(int stroke); + void setDisplayWaterfall(bool display); + void setSsbSpectrum(bool ssbSpectrum); + void setInvertedWaterfall(bool inv); + void setDisplayMaxHold(bool display); + void setDisplayCurrent(bool display); + void setDisplayHistogram(bool display); + void setDisplayGrid(bool display); + void setDisplayGridIntensity(int intensity); + void setDisplayTraceIntensity(int intensity); + + void addChannelMarker(ChannelMarker* channelMarker); + void removeChannelMarker(ChannelMarker* channelMarker); + + void newSpectrum(const std::vector& spectrum, int fftSize); + void clearSpectrumHistogram(); + + Real getWaterfallShare() const { return m_waterfallShare; } + void setWaterfallShare(Real waterfallShare); + void connectTimer(const QTimer& timer); + +private: + struct ChannelMarkerState { + ChannelMarker* m_channelMarker; + QMatrix4x4 m_glMatrixWaterfall; + QMatrix4x4 m_glMatrixDsbWaterfall; + QMatrix4x4 m_glMatrixFreqScale; + QMatrix4x4 m_glMatrixDsbFreqScale; + QMatrix4x4 m_glMatrixHistogram; + QMatrix4x4 m_glMatrixDsbHistogram; + QRect m_rect; + + ChannelMarkerState(ChannelMarker* channelMarker) : + m_channelMarker(channelMarker) + { } + }; + QList 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; + int m_displayGridIntensity; + int m_displayTraceIntensity; + bool m_invertedWaterfall; + + std::vector m_maxHold; + bool m_displayMaxHold; + const std::vector *m_currentSpectrum; + bool m_displayCurrent; + + Real m_waterfallShare; + + QPixmap m_leftMarginPixmap; + QPixmap m_frequencyPixmap; + ScaleEngine m_timeScale; + ScaleEngine m_powerScale; + ScaleEngine m_frequencyScale; + QRect m_frequencyScaleRect; + QMatrix4x4 m_glFrequencyScaleBoxMatrix; + QMatrix4x4 m_glLeftScaleBoxMatrix; + + QRgb m_waterfallPalette[240]; + QImage* m_waterfallBuffer; + int m_waterfallBufferPos; + int m_waterfallTextureHeight; + int m_waterfallTexturePos; + QMatrix4x4 m_glWaterfallBoxMatrix; + bool m_displayWaterfall; + bool m_ssbSpectrum; + + QRgb m_histogramPalette[240]; + QImage* m_histogramBuffer; + quint8* m_histogram; + quint8* m_histogramHoldoff; + int m_histogramHoldoffBase; + int m_histogramHoldoffCount; + int m_histogramLateHoldoff; + int m_histogramStroke; + QMatrix4x4 m_glHistogramSpectrumMatrix; + QMatrix4x4 m_glHistogramBoxMatrix; + bool m_displayHistogram; + + bool m_displayChanged; + + GLShaderSimple m_glShaderSimple; + GLShaderTextured m_glShaderLeftScale; + GLShaderTextured m_glShaderFrequencyScale; + GLShaderTextured m_glShaderWaterfall; + GLShaderTextured m_glShaderHistogram; + int m_matrixLoc; + int m_colorLoc; + + static const int m_waterfallBufferHeight = 256; + + void updateWaterfall(const std::vector& spectrum); + void updateHistogram(const std::vector& 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 cleanup(); + void tick(); + void channelMarkerChanged(); + void channelMarkerDestroyed(QObject* object); +}; + +#endif // INCLUDE_GLSPECTRUM_H diff --git a/sdrbase/gui/glspectrumgui.h b/sdrbase/gui/glspectrumgui.h new file mode 100644 index 000000000..51514aeb5 --- /dev/null +++ b/sdrbase/gui/glspectrumgui.h @@ -0,0 +1,76 @@ +#ifndef INCLUDE_GLSPECTRUMGUI_H +#define INCLUDE_GLSPECTRUMGUI_H + +#include +#include "dsp/dsptypes.h" +#include "util/export.h" + +namespace Ui { + class GLSpectrumGUI; +} + +class MessageQueue; +class SpectrumVis; +class GLSpectrum; + +class SDRANGEL_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; + int m_histogramLateHoldoff; + int m_histogramStroke; + int m_displayGridIntensity; + int m_displayTraceIntensity; + bool m_displayWaterfall; + bool m_invertedWaterfall; + bool m_displayMaxHold; + bool m_displayCurrent; + 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_valueChanged(int index); + void on_holdoff_valueChanged(int index); + void on_stroke_valueChanged(int index); + void on_gridIntensity_valueChanged(int index); + void on_traceIntensity_valueChanged(int index); + + void on_waterfall_toggled(bool checked); + void on_histogram_toggled(bool checked); + void on_maxHold_toggled(bool checked); + void on_current_toggled(bool checked); + void on_invert_toggled(bool checked); + void on_grid_toggled(bool checked); + void on_clearSpectrum_clicked(bool checked); +}; + +#endif // INCLUDE_GLSPECTRUMGUI_H diff --git a/sdrbase/gui/indicator.h b/sdrbase/gui/indicator.h new file mode 100644 index 000000000..66e505810 --- /dev/null +++ b/sdrbase/gui/indicator.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_INDICATOR_H +#define INCLUDE_INDICATOR_H + +#include +#include "util/export.h" + +class SDRANGEL_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 diff --git a/sdrbase/gui/physicalunit.h b/sdrbase/gui/physicalunit.h new file mode 100644 index 000000000..778637845 --- /dev/null +++ b/sdrbase/gui/physicalunit.h @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_PHYSICALUNIT_H +#define INCLUDE_PHYSICALUNIT_H + +namespace Unit { + enum Physical { + None, + Frequency, + Information, + Percent, + Decibel, + DecibelMilliWatt, + DecibelMicroVolt, + AngleDegrees, + Time, + Volt + }; +}; + +#endif // INCLUDE_PHYSICALUNIT_H diff --git a/sdrbase/gui/pluginsdialog.h b/sdrbase/gui/pluginsdialog.h new file mode 100644 index 000000000..d0f7305a7 --- /dev/null +++ b/sdrbase/gui/pluginsdialog.h @@ -0,0 +1,22 @@ +#ifndef INCLUDE_PLUGINSDIALOG_H +#define INCLUDE_PLUGINSDIALOG_H + +#include +#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 diff --git a/sdrbase/gui/preferencesdialog.h b/sdrbase/gui/preferencesdialog.h new file mode 100644 index 000000000..86247f325 --- /dev/null +++ b/sdrbase/gui/preferencesdialog.h @@ -0,0 +1,34 @@ +#ifndef INCLUDE_PREFERENCESDIALOG_H +#define INCLUDE_PREFERENCESDIALOG_H + +#include + +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 diff --git a/sdrbase/gui/presetitem.h b/sdrbase/gui/presetitem.h new file mode 100644 index 000000000..fa455435b --- /dev/null +++ b/sdrbase/gui/presetitem.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +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; +}; diff --git a/sdrbase/gui/rollupwidget.h b/sdrbase/gui/rollupwidget.h new file mode 100644 index 000000000..f695f395c --- /dev/null +++ b/sdrbase/gui/rollupwidget.h @@ -0,0 +1,42 @@ +#ifndef INCLUDE_ROLLUPWIDGET_H +#define INCLUDE_ROLLUPWIDGET_H + +#include +#include "util/export.h" + +class SDRANGEL_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 diff --git a/sdrbase/gui/scale.h b/sdrbase/gui/scale.h new file mode 100644 index 000000000..4a5356cfa --- /dev/null +++ b/sdrbase/gui/scale.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include "gui/scaleengine.h" +#include "util/export.h" + +class SDRANGEL_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*); +}; diff --git a/sdrbase/gui/scaleengine.h b/sdrbase/gui/scaleengine.h new file mode 100644 index 000000000..c9353f76a --- /dev/null +++ b/sdrbase/gui/scaleengine.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_SCALEENGINE_H +#define INCLUDE_SCALEENGINE_H + +#include +#include +#include +#include "physicalunit.h" +#include "util/export.h" + +class SDRANGEL_API ScaleEngine { +public: + struct Tick { + float pos; + bool major; + float textPos; + float textSize; + QString text; + }; + typedef QList 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 diff --git a/sdrbase/gui/valuedial.h b/sdrbase/gui/valuedial.h new file mode 100644 index 000000000..28db2a2bb --- /dev/null +++ b/sdrbase/gui/valuedial.h @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include "gui/colormapper.h" +#include "util/export.h" + +class SDRANGEL_API ValueDial : public QWidget { + Q_OBJECT + +public: + ValueDial(QWidget* parent = NULL, ColorMapper colorMapper = ColorMapper(ColorMapper::Normal)); + + void setValue(quint64 value); + void setValueRange(uint numDigits, quint64 min, quint64 max); + void setFont(const QFont& font); + void setBold(bool bold); + void setColorMapper(ColorMapper colorMapper); + quint64 getValue() const { return m_value; } + +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; + + ColorMapper m_colorMapper; + + 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(); +}; diff --git a/sdrbase/mainwindow.h b/sdrbase/mainwindow.h new file mode 100644 index 000000000..88f5f45db --- /dev/null +++ b/sdrbase/mainwindow.h @@ -0,0 +1,158 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_MAINWINDOW_H +#define INCLUDE_MAINWINDOW_H + +#include +#include +#include "settings/mainsettings.h" +#include "util/messagequeue.h" +#include "util/export.h" + +class QLabel; +class QTreeWidgetItem; +class QDir; +class QComboBox; + +class AudioDeviceInfo; +class DSPEngine; +class Indicator; +class SpectrumVis; +class GLSpectrum; +class GLSpectrumGUI; +class ChannelWindow; +class FileSink; +class SampleSource; +class PluginAPI; +class PluginGUI; +class ChannelMarker; +class PluginManager; +class PluginInterface; + +namespace Ui { + class MainWindow; +} + +class SDRANGEL_API MainWindow : public QMainWindow { + Q_OBJECT + +public: + struct DeviceUISet + { + SpectrumVis *m_spectrumVis; + GLSpectrum *m_spectrum; + GLSpectrumGUI *m_spectrumGUI; + ChannelWindow *m_channelWindow; + QComboBox *m_sampleSource; + + DeviceUISet(QTimer& timer); + ~DeviceUISet(); + }; + + explicit MainWindow(QWidget* parent = 0); + ~MainWindow(); + + MessageQueue* getInputMessageQueue() { return &m_inputMessageQueue; } + + void addChannelCreateAction(QAction* action); + void addChannelRollup(QWidget* widget); + void addViewAction(QAction* action); + + void addChannelMarker(ChannelMarker* channelMarker); + void removeChannelMarker(ChannelMarker* channelMarker); + + void setInputGUI(QWidget* gui); + const QTimer& getMasterTimer() const { return m_masterTimer; } + +private: + enum { + PGroup, + PItem + }; + + Ui::MainWindow* ui; + + AudioDeviceInfo* m_audioDeviceInfo; + + MessageQueue m_inputMessageQueue; + + MainSettings m_settings; + + SpectrumVis* m_rxSpectrumVis; + FileSink *m_fileSink; + + std::vector m_deviceUIs; + + DSPEngine* m_dspEngine; + + QTimer m_masterTimer; + QTimer m_statusTimer; + int m_lastEngineState; + + QLabel* m_sampleRateWidget; + Indicator* m_recording; + Indicator* m_engineIdle; + Indicator* m_engineRunning; + Indicator* m_engineError; + + bool m_startOsmoSDRUpdateAfterStop; + + QWidget* m_inputGUI; + + int m_sampleRate; + quint64 m_centerFrequency; + std::string m_sampleFileName; + + PluginManager* m_pluginManager; + + void loadSettings(); + void loadPresetSettings(const Preset* preset); + void savePresetSettings(Preset* preset); + void saveSettings(); + + void createStatusBar(); + void closeEvent(QCloseEvent*); + void updateCenterFreqDisplay(); + void updateSampleRate(); + void updatePresetControls(); + QTreeWidgetItem* addPresetToTree(const Preset* preset); + void applySettings(); + +private slots: + void handleDSPMessages(); + void handleMessages(); + void updateStatus(); + void on_action_Start_triggered(); + void on_action_Stop_triggered(); + void on_action_Start_Recording_triggered(); + void on_action_Stop_Recording_triggered(); + void on_action_View_Fullscreen_toggled(bool checked); + void on_presetSave_clicked(); + void on_presetUpdate_clicked(); + void on_settingsSave_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_Loaded_Plugins_triggered(); + void on_action_Preferences_triggered(); + void on_sampleSource_currentIndexChanged(int index); + void on_action_About_triggered(); +}; + +#endif // INCLUDE_MAINWINDOW_H diff --git a/sdrbase/plugin/pluginapi.h b/sdrbase/plugin/pluginapi.h new file mode 100644 index 000000000..89077e6f9 --- /dev/null +++ b/sdrbase/plugin/pluginapi.h @@ -0,0 +1,65 @@ +#ifndef INCLUDE_PLUGINAPI_H +#define INCLUDE_PLUGINAPI_H + +#include +#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 SDRANGEL_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 + /* Direct access with DSP engine singleton + 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); + + // R/O access to main window + const MainWindow* getMainWindow() const { return m_mainWindow; } + +protected: + PluginManager* m_pluginManager; + MainWindow* m_mainWindow; + DSPEngine* m_dspEngine; + + PluginAPI(PluginManager* pluginManager, MainWindow* mainWindow, DSPEngine* dspEngine); + + friend class PluginManager; +}; + +#endif // INCLUDE_PLUGINAPI_H diff --git a/sdrbase/plugin/plugingui.h b/sdrbase/plugin/plugingui.h new file mode 100644 index 000000000..ff31d4b08 --- /dev/null +++ b/sdrbase/plugin/plugingui.h @@ -0,0 +1,30 @@ +#ifndef INCLUDE_PLUGINGUI_H +#define INCLUDE_PLUGINGUI_H + +#include +#include "util/export.h" + +class Message; + +class SDRANGEL_API PluginGUI { +public: + PluginGUI() { }; + virtual ~PluginGUI() { }; + + virtual void destroy() = 0; + + virtual void setName(const QString& name) = 0; + virtual QString getName() const = 0; + + virtual void resetToDefaults() = 0; + + virtual qint64 getCenterFrequency() const = 0; + virtual void setCenterFrequency(qint64 centerFrequency) = 0; + + virtual QByteArray serialize() const = 0; + virtual bool deserialize(const QByteArray& data) = 0; + + virtual bool handleMessage(const Message& message) = 0; +}; + +#endif // INCLUDE_PLUGINGUI_H diff --git a/sdrbase/plugin/plugininterface.h b/sdrbase/plugin/plugininterface.h new file mode 100644 index 000000000..5a0150621 --- /dev/null +++ b/sdrbase/plugin/plugininterface.h @@ -0,0 +1,54 @@ +#ifndef INCLUDE_PLUGININTERFACE_H +#define INCLUDE_PLUGININTERFACE_H + +#include +#include + +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 id; + QString serial; + int sequence; + + SampleSourceDevice(const QString& _displayedName, + const QString& _id, + const QString& _serial, + int _sequence) : + displayedName(_displayedName), + id(_id), + serial(_serial), + sequence(_sequence) + { } + }; + typedef QList SampleSourceDevices; + + virtual ~PluginInterface() { }; + + virtual const PluginDescriptor& getPluginDescriptor() const = 0; + virtual void initPlugin(PluginAPI* pluginAPI) = 0; + + virtual PluginGUI* createChannel(const QString& channelName) { return 0; } + + virtual SampleSourceDevices enumSampleSources() { return SampleSourceDevices(); } + virtual PluginGUI* createSampleSourcePluginGUI(const QString& sourceId) { return 0; } +}; + +Q_DECLARE_INTERFACE(PluginInterface, "SDRangel.PluginInterface/0.1"); + +#endif // INCLUDE_PLUGININTERFACE_H diff --git a/sdrbase/plugin/pluginmanager.h b/sdrbase/plugin/pluginmanager.h new file mode 100644 index 000000000..da713f35e --- /dev/null +++ b/sdrbase/plugin/pluginmanager.h @@ -0,0 +1,146 @@ +#ifndef INCLUDE_PLUGINMANAGER_H +#define INCLUDE_PLUGINMANAGER_H + +#include +#include +#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 SDRANGEL_API PluginManager : public QObject { + Q_OBJECT + +public: + struct Plugin + { + QString filename; + QPluginLoader* loader; + PluginInterface* pluginInterface; + + Plugin(const QString& _filename, QPluginLoader* pluginLoader, PluginInterface* _plugin) : + filename(_filename), + loader(pluginLoader), + pluginInterface(_plugin) + { } + }; + + typedef QList 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 loadSourceSettings(const Preset* preset); + void saveSettings(Preset* preset); + void saveSourceSettings(Preset* preset); + + void freeAll(); + + bool handleMessage(const Message& message); + + void updateSampleSourceDevices(); + void fillSampleSourceSelector(QComboBox* comboBox); + int selectSampleSourceByIndex(int index); + int selectFirstSampleSource(const QString& sourceId); + int selectSampleSourceBySerialOrSequence(const QString& sourceId, const QString& sourceSerial, int sourceSequence); + +private: + struct ChannelRegistration { + QString m_channelName; + PluginInterface* m_plugin; + ChannelRegistration(const QString& channelName, PluginInterface* plugin) : + m_channelName(channelName), + m_plugin(plugin) + { } + }; + typedef QList 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) + { } + bool operator<(const ChannelInstanceRegistration& other) const; + }; + typedef QList ChannelInstanceRegistrations; + + struct SampleSourceRegistration { + QString m_sourceId; + PluginInterface* m_plugin; + SampleSourceRegistration(const QString& sourceId, PluginInterface* plugin) : + m_sourceId(sourceId), + m_plugin(plugin) + { } + }; + typedef QList SampleSourceRegistrations; + + struct SampleSourceDevice { + PluginInterface* m_plugin; + QString m_displayName; + QString m_sourceId; + QString m_sourceSerial; + int m_sourceSequence; + + SampleSourceDevice(PluginInterface* plugin, + const QString& displayName, + const QString& sourceId, + const QString& sourceSerial, + int sourceSequence) : + m_plugin(plugin), + m_displayName(displayName), + m_sourceId(sourceId), + m_sourceSerial(sourceSerial), + m_sourceSequence(sourceSequence) + { } + }; + typedef QList 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_sampleSourceId; + QString m_sampleSourceSerial; + int m_sampleSourceSequence; + PluginGUI* m_sampleSourcePluginGUI; + + void loadPlugins(const QDir& dir); + void renameChannelInstances(); +}; + +static inline bool operator<(const PluginManager::Plugin& a, const PluginManager::Plugin& b) +{ + return a.pluginInterface->getPluginDescriptor().displayedName < b.pluginInterface->getPluginDescriptor().displayedName; +} + +#endif // INCLUDE_PLUGINMANAGER_H diff --git a/sdrbase/settings/mainsettings.h b/sdrbase/settings/mainsettings.h new file mode 100644 index 000000000..f54d1b5c8 --- /dev/null +++ b/sdrbase/settings/mainsettings.h @@ -0,0 +1,34 @@ +#ifndef INCLUDE_SETTINGS_H +#define INCLUDE_SETTINGS_H + +#include +#include "preferences.h" +#include "preset.h" + +class MainSettings { +public: + MainSettings(); + ~MainSettings(); + + 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* getWorkingPreset() { return &m_workingPreset; } + int getSourceIndex() const { return m_preferences.getSourceIndex(); } + void setSourceIndex(int value) { m_preferences.setSourceIndex(value); } + +protected: + Preferences m_preferences; + Preset m_workingPreset; + typedef QList Presets; + Presets m_presets; +}; + +#endif // INCLUDE_SETTINGS_H diff --git a/sdrbase/settings/preferences.h b/sdrbase/settings/preferences.h new file mode 100644 index 000000000..5e6bec1fe --- /dev/null +++ b/sdrbase/settings/preferences.h @@ -0,0 +1,35 @@ +#ifndef INCLUDE_PREFERENCES_H +#define INCLUDE_PREFERENCES_H + +#include + +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 setSourceIndex(const int value) { m_sourceIndex = value; } + int getSourceIndex() const { return m_sourceIndex; } + + 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; + int m_sourceIndex; + + QString m_audioType; + QString m_audioDevice; +}; + +#endif // INCLUDE_PREFERENCES_H diff --git a/sdrbase/settings/preset.h b/sdrbase/settings/preset.h new file mode 100644 index 000000000..df85bb7e3 --- /dev/null +++ b/sdrbase/settings/preset.h @@ -0,0 +1,110 @@ +#ifndef INCLUDE_PRESET_H +#define INCLUDE_PRESET_H + +#include +#include +#include + +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 ChannelConfigs; + + struct SourceConfig + { + QString m_sourceId; + QString m_sourceSerial; + int m_sourceSequence; + QByteArray m_config; + + SourceConfig(const QString& sourceId, + const QString& sourceSerial, + int sourceSequence, + const QByteArray& config) : + m_sourceId(sourceId), + m_sourceSerial(sourceSerial), + m_sourceSequence(sourceSequence), + m_config(config) + { } + }; + typedef QList SourceConfigs; + + 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 setLayout(const QByteArray& data) { m_layout = data; } + const QByteArray& getLayout() const { return m_layout; } + + 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& sourceId, const QString& sourceSerial, int sourceSequence, const QByteArray& config) + { + addOrUpdateSourceConfig(sourceId, sourceSerial, sourceSequence, config); + } + + void addOrUpdateSourceConfig(const QString& sourceId, + const QString& sourceSerial, + int sourceSequence, + const QByteArray& config); + + const QByteArray* findBestSourceConfig(const QString& sourceId, + const QString& sourceSerial, + int sourceSequence) const; + +protected: + // group and preset description + QString m_group; + QString m_description; + quint64 m_centerFrequency; + + // general configuration + QByteArray m_spectrumConfig; + + // dc offset and i/q imbalance correction TODO: move it into the source data + bool m_dcOffsetCorrection; + bool m_iqImbalanceCorrection; + + // sample source and sample source configuration + QString m_sourceId; + QString m_sourceSerial; + int m_sourceSequence; + QByteArray m_sourceConfig; + + // channels and configurations + ChannelConfigs m_channelConfigs; + + // sources and configurations + SourceConfigs m_sourceConfigs; + + // screen and dock layout + QByteArray m_layout; +}; + +Q_DECLARE_METATYPE(const Preset*); +Q_DECLARE_METATYPE(Preset*); + +#endif // INCLUDE_PRESET_H diff --git a/sdrbase/util/CRC64.h b/sdrbase/util/CRC64.h new file mode 100644 index 000000000..bede1df55 --- /dev/null +++ b/sdrbase/util/CRC64.h @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_CRC64_H_ +#define INCLUDE_CRC64_H_ + +#include + +class CRC64 +{ +public: + CRC64(); + ~CRC64(); + uint64_t calculate_crc(uint8_t *stream, int length); + +private: + void build_crc_table(); + + uint64_t m_crcTable[256]; + static const uint64_t m_poly; +}; + + + +#endif /* INCLUDE_CRC64_H_ */ diff --git a/sdrbase/util/bitfieldindex.h b/sdrbase/util/bitfieldindex.h new file mode 100644 index 000000000..166712282 --- /dev/null +++ b/sdrbase/util/bitfieldindex.h @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef _UTIL_BITFIELDINDEX_H_ +#define _UTIL_BITFIELDINDEX_H_ + +#include + +template +struct BitfieldIndex +{ + uint32_t v : size; + + BitfieldIndex() {} + BitfieldIndex(int i) { v = i; } + + BitfieldIndex& operator=(const BitfieldIndex& rhs) { v = rhs.v; return *this; } + BitfieldIndex& operator=(const int& rhi) { v = rhi; return *this; } + BitfieldIndex& operator++() { v++; return *this; } + BitfieldIndex operator++(int) { BitfieldIndex x(*this); ++(*this); return x; } + BitfieldIndex& operator+=(const BitfieldIndex& b) { v += b.v; return *this; } + BitfieldIndex& operator-=(const BitfieldIndex& b) { v -= b.v; return *this; } + BitfieldIndex& operator+=(int i) { v += i; return *this; } + BitfieldIndex& operator-=(int i) { v -= i; return *this; } + BitfieldIndex operator+(const BitfieldIndex& b) const { BitfieldIndex x(*this); x.v += b.v; return x; } + BitfieldIndex operator-(const BitfieldIndex& b) const { BitfieldIndex x(*this); x.v -= b.v; return x; } + BitfieldIndex operator+(int i) const { BitfieldIndex x(*this); x.v += i; return x; } + BitfieldIndex operator-(int i) const { BitfieldIndex x(*this); x.v -= i; return x; } + + operator int() const { return v; } +}; + +template +BitfieldIndex operator+(const BitfieldIndex &a, const BitfieldIndex &b) { BitfieldIndex x; x.v = x.a + x.b; return x; } + +template +BitfieldIndex operator-(const BitfieldIndex &a, const BitfieldIndex &b) { BitfieldIndex x; x.v = x.a - x.b; return x; } + +#endif // _UTIL_BITFIELDINDEX_H_ diff --git a/sdrbase/util/db.h b/sdrbase/util/db.h new file mode 100644 index 000000000..3446c678c --- /dev/null +++ b/sdrbase/util/db.h @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_UTIL_DB_H_ +#define INCLUDE_UTIL_DB_H_ + +#include "dsp/dsptypes.h" + +class CalcDb +{ +public: + static Real dbPower(Real magsq); +}; + +#endif /* INCLUDE_UTIL_DB_H_ */ diff --git a/sdrbase/util/export.h b/sdrbase/util/export.h new file mode 100644 index 000000000..49e2b68e7 --- /dev/null +++ b/sdrbase/util/export.h @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB. // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef __SDRANGEL_EXPORT_H +#define __SDRANGEL_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 sdrangel_STATIC +# ifdef sdrangel_EXPORTS +# define SDRANGEL_API __SDR_EXPORT +# else +# define SDRANGEL_API __SDR_IMPORT +# endif +#else +#define SDRANGEL_API +#endif +#endif /* __SDRANGEL_EXPORT_H */ diff --git a/sdrbase/util/message.h b/sdrbase/util/message.h new file mode 100644 index 000000000..758368a68 --- /dev/null +++ b/sdrbase/util/message.h @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_MESSAGE_H +#define INCLUDE_MESSAGE_H + +#include +#include "util/export.h" + +class SDRANGEL_API Message { +public: + Message(); + virtual ~Message(); + + virtual const char* getIdentifier() const; + virtual bool matchIdentifier(const char* identifier) const; + static bool match(const Message* message); + + void* getDestination() const { return m_destination; } + void setDestination(void *destination) { m_destination = destination; } + +protected: + // addressing + static const char* m_identifier; + void* m_destination; +}; + +#define MESSAGE_CLASS_DECLARATION \ + public: \ + const char* getIdentifier() const; \ + bool matchIdentifier(const char* identifier) const; \ + static bool match(const 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(const Message& message) { return message.matchIdentifier(m_identifier); } + +#endif // INCLUDE_MESSAGE_H diff --git a/sdrbase/util/messagequeue.h b/sdrbase/util/messagequeue.h new file mode 100644 index 000000000..1e718978b --- /dev/null +++ b/sdrbase/util/messagequeue.h @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_MESSAGEQUEUE_H +#define INCLUDE_MESSAGEQUEUE_H + +#include +#include +#include +#include "util/export.h" + +class Message; + +class SDRANGEL_API MessageQueue : public QObject { + Q_OBJECT + +public: + MessageQueue(QObject* parent = NULL); + ~MessageQueue(); + + void push(Message* message, bool emitSignal = true); //!< Push message onto queue + Message* pop(); //!< Pop message from queue + + int size(); //!< Returns queue size + void clear(); //!< Empty queue + +signals: + void messageEnqueued(); + +private: + QMutex m_lock; + QQueue m_queue; +}; + +#endif // INCLUDE_MESSAGEQUEUE_H diff --git a/sdrbase/util/prettyprint.h b/sdrbase/util/prettyprint.h new file mode 100644 index 000000000..f53839532 --- /dev/null +++ b/sdrbase/util/prettyprint.h @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_UTIL_PRETTYPRINT_H_ +#define INCLUDE_UTIL_PRETTYPRINT_H_ + +#include + +class EscapeColors +{ +public: + static const QString red; + static const QString blue; + static const QString green; + static const QString cyan; + static const QString purple; + static const QString yellow; + static const QString lightRed; + static const QString lightBlue; + static const QString lightGreen; + static const QString lightCyan; + static const QString lightPurple; + static const QString brown; + static const QString terminator; +}; + +#endif /* INCLUDE_UTIL_PRETTYPRINT_H_ */ diff --git a/sdrbase/util/samplesourceserializer.h b/sdrbase/util/samplesourceserializer.h new file mode 100644 index 000000000..c1dad5af2 --- /dev/null +++ b/sdrbase/util/samplesourceserializer.h @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_UTIL_SAMPLESOURCESERIALIZER_H_ +#define INCLUDE_UTIL_SAMPLESOURCESERIALIZER_H_ + +#include "util/simpleserializer.h" + +class SampleSourceSerializer +{ +public: + struct Data + { + quint64 m_frequency; //!< RF center frequency + qint32 m_correction; //!< LO correction factor + qint32 m_rate; //!< RF sampler sample rate + quint32 m_log2Decim; //!< Decimation ratio log2 + qint32 m_bandwidth; //!< RF bandwidth + qint32 m_fcPosition; //!< Decimated band placement (infradyne, supradyne, centered) + qint32 m_lnaGain; //!< RF LNA gain + qint32 m_RxGain1; //!< Rx first stage amplifier gain + qint32 m_RxGain2; //!< Rx second stage amplifier gain + qint32 m_RxGain3; //!< Rx third stage amplifier gain + }; + + static void writeSerializedData(const Data& data, QByteArray& serializedData); + static bool readSerializedData(const QByteArray& serializedData, Data& data); + static void setDefaults(Data& data); + static uint getSerializerVersion() { return m_version; } + +protected: + static const uint m_version; +}; + + + +#endif /* INCLUDE_UTIL_SAMPLESOURCESERIALIZER_H_ */ diff --git a/sdrbase/util/simpleserializer.h b/sdrbase/util/simpleserializer.h new file mode 100644 index 000000000..cc76c2006 --- /dev/null +++ b/sdrbase/util/simpleserializer.h @@ -0,0 +1,112 @@ +#ifndef INCLUDE_SIMPLESERIALIZER_H +#define INCLUDE_SIMPLESERIALIZER_H + +#include +#include +#include "dsp/dsptypes.h" +#include "util/export.h" + +class SDRANGEL_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 SDRANGEL_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 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 diff --git a/sdrbase/util/spinlock.h b/sdrbase/util/spinlock.h new file mode 100644 index 000000000..78af0b61a --- /dev/null +++ b/sdrbase/util/spinlock.h @@ -0,0 +1,39 @@ +#ifndef INCLUDE_SPINLOCK_H +#define INCLUDE_SPINLOCK_H + +#include + +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 diff --git a/sdrbase/util/stacktrace.h b/sdrbase/util/stacktrace.h new file mode 100644 index 000000000..49a5e5c0a --- /dev/null +++ b/sdrbase/util/stacktrace.h @@ -0,0 +1,93 @@ +// stacktrace.h (c) 2008, Timo Bingmann from http://idlebox.net/ +// published under the WTFPL v2.0 + +#ifndef _STACKTRACE_H_ +#define _STACKTRACE_H_ + +#include +#include +#include +#include + +/** Print a demangled stack backtrace of the caller function to FILE* out. */ +static inline void print_stacktrace(FILE *out = stderr, unsigned int max_frames = 63) +{ + fprintf(out, "stack trace:\n"); + + // storage array for stack trace address data + void* addrlist[max_frames+1]; + + // retrieve current stack addresses + int addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void*)); + + if (addrlen == 0) { + fprintf(out, " \n"); + return; + } + + // resolve addresses into strings containing "filename(function+address)", + // this array must be free()-ed + char** symbollist = backtrace_symbols(addrlist, addrlen); + + // allocate string which will be filled with the demangled function name + size_t funcnamesize = 256; + char* funcname = (char*)malloc(funcnamesize); + + // iterate over the returned symbol lines. skip the first, it is the + // address of this function. + for (int i = 1; i < addrlen; i++) + { + char *begin_name = 0, *begin_offset = 0, *end_offset = 0; + + // find parentheses and +address offset surrounding the mangled name: + // ./module(function+0x15c) [0x8048a6d] + for (char *p = symbollist[i]; *p; ++p) + { + if (*p == '(') + begin_name = p; + else if (*p == '+') + begin_offset = p; + else if (*p == ')' && begin_offset) { + end_offset = p; + break; + } + } + + if (begin_name && begin_offset && end_offset + && begin_name < begin_offset) + { + *begin_name++ = '\0'; + *begin_offset++ = '\0'; + *end_offset = '\0'; + + // mangled name is now in [begin_name, begin_offset) and caller + // offset in [begin_offset, end_offset). now apply + // __cxa_demangle(): + + int status; + char* ret = abi::__cxa_demangle(begin_name, + funcname, &funcnamesize, &status); + if (status == 0) { + funcname = ret; // use possibly realloc()-ed string + fprintf(out, " %s : %s+%s\n", + symbollist[i], funcname, begin_offset); + } + else { + // demangling failed. Output function name as a C function with + // no arguments. + fprintf(out, " %s : %s()+%s\n", + symbollist[i], begin_name, begin_offset); + } + } + else + { + // couldn't parse the line? print the whole line. + fprintf(out, " %s\n", symbollist[i]); + } + } + + free(funcname); + free(symbollist); +} + +#endif // _STACKTRACE_H_ diff --git a/sdrbase/util/syncmessenger.h b/sdrbase/util/syncmessenger.h new file mode 100644 index 000000000..d9c59ddba --- /dev/null +++ b/sdrbase/util/syncmessenger.h @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_UTIL_SYNCMESSENGER_H_ +#define INCLUDE_UTIL_SYNCMESSENGER_H_ + +#include +#include +#include +#include +#include "util/export.h" + +class Message; + +/** + * This class is responsible of managing the synchronous processing of a message across threads + */ +class SDRANGEL_API SyncMessenger : public QObject { + Q_OBJECT + +public: + SyncMessenger(); + ~SyncMessenger(); + + int sendWait(Message& message, unsigned long msPollTime = 100); //!< Send message and waits for its process completion + Message* getMessage() const { return m_message; } + void storeMessage(Message& message) { m_message = &message; } + void done(int result = 0); //!< Processing of the message is complete + +signals: + void messageSent(); + +protected: + QWaitCondition m_waitCondition; + QMutex m_mutex; + QAtomicInt m_complete; + Message *m_message; + int m_result; +}; + + + +#endif /* INCLUDE_UTIL_SYNCMESSENGER_H_ */ diff --git a/sdrbase/util/threadsafesingleton.h b/sdrbase/util/threadsafesingleton.h new file mode 100644 index 000000000..349c00d37 --- /dev/null +++ b/sdrbase/util/threadsafesingleton.h @@ -0,0 +1,39 @@ +/* + * threadsafesingleton.h + * + * Created on: Aug 7, 2015 + * Author: f4exb + */ + +#ifndef INCLUDE_UTIL_THREADSAFESINGLETON_H_ +#define INCLUDE_UTIL_THREADSAFESINGLETON_H_ + +//! Template class used to make from a class a singleton. +/** S is the type of the singleton object to instantiate. +
This class permits to gather all singleton code in the same place + and not to pollute object interface with singleton methods. +
Accessing to the instance of S is made through ThreadSafeSingleton::instance() +
Note : The class using this adapter should have constructor and + destructor protected and copy constructor and operator= private. +
Besides it should declare the class ThreadSafeSingleton friend in order + this class can make the construction. +*/ + +template class ThreadSafeSingleton +{ +public: + static S& instance() { return *_pInstance; } + +protected: + ThreadSafeSingleton() {} + ~ThreadSafeSingleton() { delete _pInstance; } + static S* _pInstance; + +private: + ThreadSafeSingleton(const ThreadSafeSingleton&) {} + ThreadSafeSingleton& operator = (const ThreadSafeSingleton&) {return *this;} +}; + +template S *ThreadSafeSingleton::_pInstance = new S; + +#endif /* INCLUDE_UTIL_THREADSAFESINGLETON_H_ */ diff --git a/sdrbase/util/udpsink.h b/sdrbase/util/udpsink.h new file mode 100644 index 000000000..c2d0ce098 --- /dev/null +++ b/sdrbase/util/udpsink.h @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 F4EXB // +// written by Edouard Griffiths // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_UTIL_UDPSINK_H_ +#define INCLUDE_UTIL_UDPSINK_H_ + +#include +#include +#include + +template +class UDPSink +{ +public: + UDPSink(QObject *parent, unsigned int udpSize, unsigned int port) : + m_udpSize(udpSize), + m_address(QHostAddress::LocalHost), + m_port(port), + m_sampleBufferIndex(0) + { + m_sampleBuffer = new T[m_udpSize]; + m_socket = new QUdpSocket(parent); + } + + UDPSink (QObject *parent, unsigned int udpSize, QHostAddress& address, unsigned int port) : + m_udpSize(udpSize), + m_address(address), + m_port(port), + m_sampleBufferIndex(0) + { + m_sampleBuffer = new T[m_udpSize]; + m_socket = new QUdpSocket(parent); + } + + ~UDPSink() + { + delete[] m_sampleBuffer; + delete m_socket; + } + + void write(T sample) + { + m_sampleBuffer[m_sampleBufferIndex] = sample; + + if (m_sampleBufferIndex < m_udpSize) + { + m_sampleBufferIndex++; + } + else + { + m_socket->writeDatagram((const char*)&m_sampleBuffer[0], (qint64 ) (m_udpSize * sizeof(T)), m_address, m_port); + m_sampleBufferIndex = 0; + } + } + +private: + unsigned int m_udpSize; + QHostAddress m_address; + unsigned int m_port; + QUdpSocket *m_socket; + T *m_sampleBuffer;; + int m_sampleBufferIndex; +}; + + + +#endif /* INCLUDE_UTIL_UDPSINK_H_ */